Skip to content
This repository was archived by the owner on May 28, 2024. It is now read-only.

Commit 451f944

Browse files
authored
fix(My): fix text when no-items (#36)
* feat(TimeStamp): TimeStamp not null * feat(records): add hasItems state * feat(My): add hasItems fork * refactor(My): separate components * refactor(My): to short key * chore(h: records): add comment * refactor(h: records): summary get function * chore(h: my): add comments
1 parent 6ee2fd0 commit 451f944

File tree

5 files changed

+124
-81
lines changed

5 files changed

+124
-81
lines changed

src/components/pages/My/My.tsx

+50-27
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,16 @@ export interface MyProps {
2525
isLoading: boolean;
2626
isNextLoading: boolean;
2727
items: RecordViewInterface[];
28+
hasItems: boolean;
2829
hasNext: boolean;
2930
hasToken: boolean;
3031
getNextRecords: () => void;
3132
signOut: () => Promise<void>;
3233
}
3334

35+
/**
36+
* エラー表示をするコンポーネント
37+
*/
3438
const Error: React.FC<Pick<MyProps, 'hasToken'>> = ({ hasToken }) => {
3539
if (!hasToken) {
3640
return (
@@ -42,48 +46,64 @@ const Error: React.FC<Pick<MyProps, 'hasToken'>> = ({ hasToken }) => {
4246
return null;
4347
};
4448

45-
const Inner: React.FC<Pick<MyProps, 'items'>> = ({ items }) => {
46-
const existsItems = items.length > 0;
49+
/**
50+
* アイテムがないことを表示するコンポーネント
51+
*/
52+
const NoItem: React.FC = () => {
53+
return (
54+
<div>
55+
<p style={{ fontSize: '0.8em', color: '#999', display: 'flex', flexWrap: 'wrap', justifyContent: 'center', margin: '8px 16px' }}>
56+
<span style={{ whiteSpace: 'nowrap' }}>※ データ取得までに時間が掛かります。</span>
57+
<span style={{ whiteSpace: 'nowrap' }}>気長にお待ちください。</span>
58+
</p>
59+
</div>
60+
);
61+
};
62+
63+
/**
64+
* 表示するデータがないことを表示するコンポーネント
65+
*/
66+
const NoViewItem: React.FC = () => {
67+
return (
68+
<div>
69+
<p style={{ fontSize: '0.8em', color: '#999', display: 'flex', flexWrap: 'wrap', justifyContent: 'center', margin: '8px 16px' }}>
70+
<span style={{ whiteSpace: 'nowrap' }}>データの取得は完了していますが、</span>
71+
<span style={{ whiteSpace: 'nowrap' }}>今のところフォロワーの増減がありません。</span>
72+
</p>
73+
</div>
74+
);
75+
};
76+
77+
/**
78+
* メインエリア
79+
*/
80+
const Main: React.FC<Pick<MyProps, 'items' | 'hasItems'>> = ({ items, hasItems }) => {
4781
const filteredItems = items.filter(({ cameUsers, leftUsers }) => {
4882
return cameUsers.length > 0 || leftUsers.length > 0;
4983
});
5084
const existsFilteredItems = filteredItems.length > 0;
5185

52-
if (!existsItems) {
53-
return (
54-
<div>
55-
<p style={{ fontSize: '0.8em', color: '#999', display: 'flex', flexWrap: 'wrap', justifyContent: 'center', margin: '8px 16px' }}>
56-
<span style={{ whiteSpace: 'nowrap' }}>※ データ取得までに時間が掛かります。</span>
57-
<span style={{ whiteSpace: 'nowrap' }}>気長にお待ちください。</span>
58-
</p>
59-
</div>
60-
);
86+
if (!hasItems) {
87+
return <NoItem />;
6188
}
6289
if (!existsFilteredItems) {
63-
return (
64-
<div>
65-
<p style={{ fontSize: '0.8em', color: '#999', display: 'flex', flexWrap: 'wrap', justifyContent: 'center', margin: '8px 16px' }}>
66-
<span style={{ whiteSpace: 'nowrap' }}>データの取得は完了していますが、</span>
67-
<span style={{ whiteSpace: 'nowrap' }}>今のところフォロワーの増減がありません。</span>
68-
</p>
69-
</div>
70-
);
90+
return <NoViewItem />;
7191
}
7292

7393
return (
7494
<React.Fragment>
75-
{items.map((item, i) => {
95+
{items.map((item, itemIndex) => {
7696
const date = new Date(item.date * 24 * 60 * 60 * 1000);
7797
const dateText = `${date.getMonth() + 1}${date.getDate()}日`;
7898
return (
79-
<section css={RecordSectionStyle} key={`item-${i}`}>
99+
<section css={RecordSectionStyle} key={itemIndex}>
80100
<h2 css={RecordHeadStyle}>{dateText}の記録</h2>
81101
<section css={CameSectionStyle}>
82102
<h3 css={LeftHeadStyle}>ゆくひと ({item.leftUsers.filter((user) => user).length})</h3>
83103
{item.leftUsers.length ? (
84104
<ul style={{ listStyle: 'none', marginBottom: 64 }}>
85-
{item.leftUsers.map((user, j) => (
86-
<li key={`item-${i}-leftuser-${j}`}>
105+
{item.leftUsers.map((user, userIndex) => (
106+
<li key={userIndex}>
87107
<UserCard {...user} />
88108
</li>
89109
))}
@@ -96,8 +116,8 @@ const Inner: React.FC<Pick<MyProps, 'items'>> = ({ items }) => {
96116
<h3 css={CameHeadStyle}>くるひと ({item.cameUsers.filter((user) => user).length})</h3>
97117
{item.cameUsers.length ? (
98118
<ul style={{ listStyle: 'none', marginBottom: 64 }}>
99-
{item.cameUsers.map((user, j) => (
100-
<li key={`item-${i}-cameuser-${j}`}>
119+
{item.cameUsers.map((user, userIndex) => (
120+
<li key={userIndex}>
101121
<UserCard {...user} />
102122
</li>
103123
))}
@@ -113,7 +133,10 @@ const Inner: React.FC<Pick<MyProps, 'items'>> = ({ items }) => {
113133
);
114134
};
115135

116-
export const My: React.FC<MyProps> = ({ isLoading, isNextLoading, items, hasNext, hasToken, signOut, getNextRecords }) => (
136+
/**
137+
* マイページ全体のコンポーネント
138+
*/
139+
export const My: React.FC<MyProps> = ({ isLoading, isNextLoading, items, hasItems, hasNext, hasToken, signOut, getNextRecords }) => (
117140
<div css={WrapperStyle}>
118141
{!isLoading && <Error hasToken={hasToken} />}
119142
<header css={HeaderStyle}>
@@ -125,7 +148,7 @@ export const My: React.FC<MyProps> = ({ isLoading, isNextLoading, items, hasNext
125148
ログアウト
126149
</button>
127150
</header>
128-
{isLoading ? <p style={{ margin: 16 }}>読み込み中</p> : <Inner items={items} />}
151+
{isLoading ? <p style={{ margin: 16 }}>読み込み中</p> : <Main items={items} hasItems={hasItems} />}
129152
{!isLoading && isNextLoading && <p style={{ margin: 16 }}>読み込み中</p>}
130153
{!isLoading && hasNext && (
131154
<button css={GetNextButtonStyle} disabled={isNextLoading} onClick={() => getNextRecords()}>

src/components/pages/My/MyContainer.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ interface Props extends RouteComponentProps, Partial<MyProps> {}
99

1010
const MyInner: React.FC<Props> = ({ history }) => {
1111
const { isLoading: userIsLoading, signedIn, user, signOut } = UserContainer.useContainer();
12-
const { isLoading: recordsIsLoading, isNextLoading, items, hasNext, setUid: setRecordsUid, getNextRecords } = RecordsContainer.useContainer();
12+
const { isLoading: recordsIsLoading, isNextLoading, items, hasItems, hasNext, setUid: setRecordsUid, getNextRecords } = RecordsContainer.useContainer();
1313
const { isLoading: tokenIsLoading, setUid: setTokenUid, hasToken } = TokenContainer.useContainer();
1414

1515
useEffect(() => {
@@ -31,6 +31,7 @@ const MyInner: React.FC<Props> = ({ history }) => {
3131
isLoading,
3232
isNextLoading,
3333
items,
34+
hasItems,
3435
hasNext,
3536
hasToken,
3637
getNextRecords,

src/modules/firebase.ts

+1
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ provider.setCustomParameters({
1717
lang: 'ja',
1818
});
1919

20+
export default firebase;
2021
export const auth = firebase.auth();
2122
export const firestore = firebase.firestore();

src/stores/database/records.ts

+62-47
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { useState, useEffect } from 'react';
1+
import { useState, useEffect, useCallback } from 'react';
22
import { createContainer } from 'unstated-next';
3-
import { firestore } from '../../modules/firebase';
3+
import firebase, { firestore } from '../../modules/firebase';
44
import { convertRecords } from '../../utils/records';
55

66
const usersCollection = firestore.collection('users');
@@ -47,73 +47,88 @@ const convertRecordItems = (snapshot: firebase.firestore.QueryDocumentSnapshot)
4747
return item;
4848
};
4949

50+
const getRecordsFromFirestore = async (uid: string, end: firebase.firestore.Timestamp): Promise<firebase.firestore.QuerySnapshot> => {
51+
const qs = await usersCollection
52+
.doc(uid)
53+
.collection('records')
54+
.orderBy('durationEnd', 'desc')
55+
.startAfter(end)
56+
.limit(20)
57+
.get();
58+
return qs;
59+
};
60+
5061
const useRecords = () => {
62+
/** 読み込み中かどうか */
5163
const [isLoading, setLoading] = useState<boolean>(true);
64+
65+
/** 続きのデータが読み込み中かどうか */
5266
const [isNextLoading, setNextLoading] = useState<boolean>(true);
67+
68+
/** 続きのデータがあるかどうか */
5369
const [hasNext, setHasNext] = useState<boolean>(true);
70+
71+
/** アイテム */
5472
const [items, setItems] = useState<RecordViewInterface[]>([]);
73+
74+
/** Firebase UID */
5575
const [uid, setUid] = useState<string | null>(null);
56-
const [lastDurationEnd, setLastDurationEnd] = useState<firebase.firestore.Timestamp | null>(null);
5776

77+
/** アイテムの読み込みのカーソル代わり */
78+
const [lastDurationEnd, setLastDurationEnd] = useState<firebase.firestore.Timestamp>(firebase.firestore.Timestamp.now());
79+
80+
/**
81+
* Records を取得し処理する
82+
*/
83+
const getRecords = useCallback(async () => {
84+
if (!uid) {
85+
return;
86+
}
87+
const { docs, size } = await getRecordsFromFirestore(uid, lastDurationEnd);
88+
const tmpItems = docs.map(convertRecordItems).sort((a, b) => b.data.durationEnd.seconds - a.data.durationEnd.seconds);
89+
const [newItems, newLastDurationEnd] = convertRecords(tmpItems);
90+
91+
setItems([...items, ...newItems]);
92+
setLastDurationEnd(newLastDurationEnd);
93+
setHasNext(size >= 20);
94+
95+
setLoading(false);
96+
setNextLoading(false);
97+
}, [items, lastDurationEnd, uid]);
98+
99+
/**
100+
* 初回 Records を取得する
101+
*/
102+
useEffect(() => {
103+
if (isLoading || !uid) {
104+
return;
105+
}
106+
getRecords();
107+
}, [getRecords, isLoading, uid]);
108+
109+
/**
110+
* 続きの Records を取得する
111+
*/
58112
const getNextRecords = () => {
59113
if (isNextLoading || !uid) {
60114
return;
61115
}
62116
setNextLoading(true);
63-
64-
(async () => {
65-
const query = await usersCollection
66-
.doc(uid)
67-
.collection('records')
68-
.orderBy('durationEnd', 'desc')
69-
.startAfter(lastDurationEnd)
70-
.limit(20)
71-
.get();
72-
73-
const tmpItems = query.docs.map(convertRecordItems).sort((a, b) => b.data.durationEnd.seconds - a.data.durationEnd.seconds);
74-
const [newItems, newLastDurationEnd] = convertRecords(tmpItems);
75-
setItems([...items, ...newItems]);
76-
setLastDurationEnd(newLastDurationEnd);
77-
78-
if (query.size < 20) {
79-
setHasNext(false);
80-
}
81-
82-
setNextLoading(false);
83-
})();
117+
getRecords();
84118
};
85119

86120
useEffect(() => {
87-
if (!uid) {
121+
if (!isLoading || !uid) {
88122
return;
89123
}
90-
91-
(async () => {
92-
const query = await usersCollection
93-
.doc(uid)
94-
.collection('records')
95-
.orderBy('durationEnd', 'desc')
96-
.limit(20)
97-
.get();
98-
99-
const tmpItems = query.docs.map(convertRecordItems).sort((a, b) => b.data.durationEnd.seconds - a.data.durationEnd.seconds);
100-
const [newItems, newLastDurationEnd] = convertRecords(tmpItems);
101-
setItems(newItems);
102-
setLastDurationEnd(newLastDurationEnd);
103-
104-
if (query.size < 20) {
105-
setHasNext(false);
106-
}
107-
108-
setLoading(false);
109-
setNextLoading(false);
110-
})();
111-
}, [uid]);
124+
getRecords();
125+
}, [getRecords, isLoading, uid]);
112126

113127
return {
114128
isLoading,
115129
isNextLoading,
116130
items,
131+
hasItems: items.length > 0,
117132
hasNext,
118133
setUid,
119134
getNextRecords,

src/utils/records.ts

+9-6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import firebase, { firestore } from '../modules/firebase';
12
import { RecordInterface, RecordItemUserInterface, RecordViewInterface, RecordViewUserInterface } from '../stores/database/records';
23

34
const convertRecordUsers = (users: RecordItemUserInterface[], durationStart: firebase.firestore.Timestamp, durationEnd: firebase.firestore.Timestamp) => {
@@ -14,7 +15,11 @@ const convertRecordUsers = (users: RecordItemUserInterface[], durationStart: fir
1415
);
1516
};
1617

17-
export const convertRecords = (items: RecordInterface[]): [RecordViewInterface[], firebase.firestore.Timestamp | null] => {
18+
export const convertRecords = (items: RecordInterface[]): [RecordViewInterface[], firebase.firestore.Timestamp] => {
19+
if (!items.length) {
20+
return [[], firebase.firestore.Timestamp.now()];
21+
}
22+
1823
const newItems: RecordViewInterface[] = [];
1924
const timeZoneOffset = 9;
2025

@@ -26,21 +31,19 @@ export const convertRecords = (items: RecordInterface[]): [RecordViewInterface[]
2631
const newLeftUsers = convertRecordUsers(leftUsers, durationStart, durationEnd);
2732

2833
if (!newItem) {
29-
const newData: RecordViewInterface = {
34+
const newItem: RecordViewInterface = {
3035
date,
3136
cameUsers: newCameUsers,
3237
leftUsers: newLeftUsers,
3338
};
34-
newItems.push(newData);
39+
newItems.push(newItem);
3540
return;
3641
}
3742
newItem.cameUsers.push(...newCameUsers);
3843
newItem.leftUsers.push(...newLeftUsers);
3944
});
4045

41-
const lastDurationEnd = items.length
42-
? items.sort((a, b) => b.data.durationEnd.seconds - a.data.durationEnd.seconds)[items.length - 1].data.durationEnd
43-
: null;
46+
const lastDurationEnd = items.sort((a, b) => a.data.durationEnd.seconds - b.data.durationEnd.seconds)[0].data.durationEnd;
4447

4548
return [newItems, lastDurationEnd];
4649
};

0 commit comments

Comments
 (0)