Skip to content

Commit

Permalink
Add Activities Tab
Browse files Browse the repository at this point in the history
  • Loading branch information
HeesungB committed May 27, 2024
1 parent 79604f4 commit 3f4c40c
Show file tree
Hide file tree
Showing 29 changed files with 3,081 additions and 0 deletions.
19 changes: 19 additions & 0 deletions apps/mobile/src/components/icon/document-fill.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React, {FunctionComponent} from 'react';
import {IconProps} from './types';
import {Path, Svg} from 'react-native-svg';

export const DocumentFillIcon: FunctionComponent<IconProps> = ({
size,
color,
}) => {
return (
<Svg width={size} height={size} viewBox="0 0 29 29" fill="none">
<Path
fillRule="evenodd"
clipRule="evenodd"
d="M7.5 4.5C7.23478 4.5 6.98043 4.60536 6.79289 4.79289C6.60536 4.98043 6.5 5.23478 6.5 5.5V21.5C6.5 21.9398 6.50009 22.8101 6.50017 23.5005C6.50023 24.0527 6.94757 24.5 7.4998 24.5C8.19013 24.5 9.06021 24.5 9.5 24.5H19.5C19.9398 24.5 20.8099 24.5 21.5004 24.5C22.0526 24.5 22.5 24.0526 22.5 23.5004C22.5 22.8099 22.5 21.9398 22.5 21.5V10.5C22.4999 10.2348 22.3946 9.98049 22.207 9.793L17.207 4.793C17.0195 4.60545 16.7652 4.50006 16.5 4.5H7.5ZM16.5 6L20.5 10H16.5V6ZM9.5 15.5C9.5 15.2348 9.60536 14.9804 9.79289 14.7929C9.98043 14.6054 10.2348 14.5 10.5 14.5H18.5C18.7652 14.5 19.0196 14.6054 19.2071 14.7929C19.3946 14.9804 19.5 15.2348 19.5 15.5C19.5 15.7652 19.3946 16.0196 19.2071 16.2071C19.0196 16.3946 18.7652 16.5 18.5 16.5H10.5C10.2348 16.5 9.98043 16.3946 9.79289 16.2071C9.60536 16.0196 9.5 15.7652 9.5 15.5ZM9.5 19.5C9.5 19.2348 9.60536 18.9804 9.79289 18.7929C9.98043 18.6054 10.2348 18.5 10.5 18.5H18.5C18.7652 18.5 19.0196 18.6054 19.2071 18.7929C19.3946 18.9804 19.5 19.2348 19.5 19.5C19.5 19.7652 19.3946 20.0196 19.2071 20.2071C19.0196 20.3946 18.7652 20.5 18.5 20.5H10.5C10.2348 20.5 9.98043 20.3946 9.79289 20.2071C9.60536 20.0196 9.5 19.7652 9.5 19.5Z"
fill={color || 'currentColor'}
/>
</Svg>
);
};
17 changes: 17 additions & 0 deletions apps/mobile/src/components/icon/document-outliend.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React, {FunctionComponent} from 'react';
import {IconProps} from './types';
import {Path, Svg} from 'react-native-svg';

export const DocumentOutlinedIcon: FunctionComponent<IconProps> = ({
size,
color,
}) => {
return (
<Svg width={size} height={size} viewBox="0 0 29 29" fill="none">
<Path
d="M17.7507 6.16667H8.00065V23.5H21.0007V9.41667H17.7507V6.16667ZM8.00065 4H18.834L23.1673 8.33333V23.5C23.1673 24.0746 22.939 24.6257 22.5327 25.0321C22.1264 25.4384 21.5753 25.6667 21.0007 25.6667H8.00065C7.42602 25.6667 6.87492 25.4384 6.46859 25.0321C6.06226 24.6257 5.83398 24.0746 5.83398 23.5V6.16667C5.83398 5.59203 6.06226 5.04093 6.46859 4.6346C6.87492 4.22827 7.42602 4 8.00065 4ZM10.1673 13.75H18.834V15.9167H10.1673V13.75ZM10.1673 18.0833H18.834V20.25H10.1673V18.0833Z"
fill={color || 'currentColor'}
/>
</Svg>
);
};
1 change: 1 addition & 0 deletions apps/mobile/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './use-effect-once';
export * from './use-pagination-cursor-query.ts';
194 changes: 194 additions & 0 deletions apps/mobile/src/hooks/use-pagination-cursor-query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {simpleFetch} from '@keplr-wallet/simple-fetch';

export const usePaginatedCursorQuery = <R>(
baseURL: string,
initialUriFn: () => string,
nextCursorQueryString: (page: number, prev: R) => Record<string, string>,
isEndedFn: (prev: R) => boolean,
refreshKey?: string,
isValidKey?: (key: string) => boolean,
): {
isFetching: boolean;
pages: {
response: R | undefined;
error?: Error;
}[];
next: () => void;
refresh: () => void;
} => {
const [pages, setPages] = useState<
{
response: R | undefined;
error?: Error;
}[]
>([]);

const baseURLRef = useRef(baseURL);
baseURLRef.current = baseURL;
const initialUriFnRef = useRef(initialUriFn);
initialUriFnRef.current = initialUriFn;
const nextCursorQueryStringRef = useRef(nextCursorQueryString);
nextCursorQueryStringRef.current = nextCursorQueryString;
const isEndedFnRef = useRef(isEndedFn);
isEndedFnRef.current = isEndedFn;

const isValidKeyRef = useRef(isValidKey);
isValidKeyRef.current = isValidKey;

// refresh를 할때 이전에 쿼리가 진행 중이면
// 두 쿼리 중에 뭐가 먼저 끝나느냐에 따라서 결과가 달라진다...
// 쿼리는 cancel하는게 맞겠지만
// 귀찮으니 그냥 seq를 이용해서 처리한다.
const currentQuerySeq = useRef(0);
useEffect(() => {
currentQuerySeq.current++;
}, [refreshKey]);

// 어차피 바로 useEffect에 의해서 fetch되기 때문에 true로 시작...
const [isFetching, setIsFetching] = useState(true);
const _initialFetchIsDuringDeferred = useRef(false);
const _initialFetch = () => {
const fetch = () => {
if (_initialFetchIsDuringDeferred.current) {
return;
}

const querySeq = currentQuerySeq.current;
simpleFetch<R>(baseURLRef.current, initialUriFnRef.current())
.then(r => {
if (querySeq === currentQuerySeq.current) {
setPages([
{
response: r.data,
},
]);
}
})
.catch(e => {
if (querySeq === currentQuerySeq.current) {
setPages([
{
response: undefined,
error: e,
},
]);
}
})
.finally(() => {
if (querySeq === currentQuerySeq.current) {
setIsFetching(false);
}
});
};

fetch();
};
const initialFetchRef = useRef(_initialFetch);
initialFetchRef.current = _initialFetch;
useEffect(() => {
if (
!isValidKeyRef.current ||
!refreshKey ||
isValidKeyRef.current(refreshKey)
) {
initialFetchRef.current();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const [prevRefreshKey, setPrevRefreshKey] = useState(refreshKey);
useEffect(() => {
// 위에서 빈 deps의 useEffect에 의해서 처음에 쿼리가 발생한다.
// prevRefreshKey 로직이 없으면 이 로직도 실행되면서 쿼리가 두번 발생한다.
// 이 문제를 해결하려고 prevRefreshKey를 사용한다.
if (prevRefreshKey !== refreshKey) {
if (
!isValidKeyRef.current ||
!refreshKey ||
isValidKeyRef.current(refreshKey)
) {
setPages([]);
setIsFetching(true);
initialFetchRef.current();
}

setPrevRefreshKey(refreshKey);
}
}, [prevRefreshKey, refreshKey]);

const next = useCallback(() => {
if (isFetching || pages.length === 0) {
return;
}

const res = pages[pages.length - 1].response;
if (!res) {
return;
}
if (isEndedFnRef.current(res)) {
return;
}

const nextPage = pages.length + 1;
let uri = initialUriFnRef.current();
const qs = nextCursorQueryStringRef.current(nextPage, res);
const params = new URLSearchParams(qs);
if (uri.length === 0 || uri === '/') {
uri = `?${params.toString()}`;
} else {
uri += `&${params.toString()}`;
}

setIsFetching(true);
const querySeq = currentQuerySeq.current;
simpleFetch<R>(baseURLRef.current, uri)
.then(r => {
if (querySeq === currentQuerySeq.current) {
setPages(prev => {
const newPages = prev.slice();
newPages.push({
response: r.data,
});
return newPages;
});
}
})
.catch(e => {
if (querySeq === currentQuerySeq.current) {
setPages(prev => {
const newPages = prev.slice();
newPages.push({
response: undefined,
error: e,
});
return newPages;
});
}
})
.finally(() => {
if (querySeq === currentQuerySeq.current) {
setIsFetching(false);
}
});
}, [isFetching, pages]);

const refresh = useCallback(() => {
if (isFetching) {
return;
}

setPages([]);
setIsFetching(true);

initialFetchRef.current();
}, [isFetching]);

return useMemo(() => {
return {
isFetching,
pages,
next,
refresh,
};
}, [isFetching, next, pages, refresh]);
};
3 changes: 3 additions & 0 deletions apps/mobile/src/languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,9 @@
"page.ibc-swap.components.slippage-modal.title": "Slippage Settings",
"page.ibc-swap.components.slippage-modal.label.slippage-tolerance": "Slippage Tolerance",
"page.ibc-swap.components.slippage-modal.label.slippage-custom": "Custom Slippage",

"page.activity.title": "Activity",

"page.permission.unknown-permission": "Unknown permission",
"page.permission.requesting-connection-title": "Requesting Connection",
"page.permission.paragraph": "By approving this request, the website will:{br}• Get the list of all chain on your Keplr Wallet",
Expand Down
3 changes: 3 additions & 0 deletions apps/mobile/src/languages/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,9 @@
"page.ibc-swap.components.slippage-modal.title": "슬리피지 설정",
"page.ibc-swap.components.slippage-modal.label.slippage-tolerance": "슬리피지 허용치",
"page.ibc-swap.components.slippage-modal.label.slippage-custom": "커스텀 설정",

"page.activity.title": "활동내역",

"page.permission.unknown-permission": "알수없는 권한",
"page.permission.requesting-connection-title": "연결 요청",
"page.permission.paragraph": "승인시 해당 웹사이트에서 아래와 같은 권한을 받습니다 :{br}• 유저의 케플러 지갑에 있는 모든 체인 리스트 조회, 사용",
Expand Down
26 changes: 26 additions & 0 deletions apps/mobile/src/navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ import {EditFavoriteUrlScreen} from './screen/web/edit-favorite';
import {SearchUrlScreen} from './screen/web/search';
import {FavoriteUrl} from './stores/webpage/types.ts';
import {Text} from 'react-native';
import {ActivitiesScreen} from './screen/activities';
import {DocumentFillIcon} from './components/icon/document-fill.tsx';
import {DocumentOutlinedIcon} from './components/icon/document-outliend.tsx';

type DefaultRegisterParams = {
hideBackButton?: boolean;
Expand Down Expand Up @@ -340,6 +343,8 @@ export type RootStackParamList = {
initialGasAdjustment?: string;
tempSwitchAmount?: string;
};

Activities: undefined;
};

export type StakeNavigation = {
Expand Down Expand Up @@ -584,6 +589,12 @@ const DrawerBottomTabLabel: FunctionComponent<{
{intl.formatMessage({id: 'bottom-tabs.settings'})}
</Text>
);
case 'Activities':
return (
<Text style={{color}}>
{intl.formatMessage({id: 'bottom-tabs.activity'})}
</Text>
);
}

return <></>;
Expand All @@ -604,6 +615,7 @@ export const MainTabNavigation: FunctionComponent = () => {
if (
focusedScreen.name !== 'Home' &&
focusedScreen.name !== 'Swap' &&
focusedScreen.name !== 'Activities' &&
isDrawerOpen
) {
navigation.dispatch(DrawerActions.toggleDrawer());
Expand Down Expand Up @@ -633,6 +645,12 @@ export const MainTabNavigation: FunctionComponent = () => {
) : (
<SettingOutlinedIcon size={size} color={color} />
);
case 'Activities':
return focused ? (
<DocumentFillIcon size={size} color={color} />
) : (
<DocumentOutlinedIcon size={size} color={color} />
);
}
},
tabBarLabel: ({color}) =>
Expand Down Expand Up @@ -669,6 +687,14 @@ export const MainTabNavigation: FunctionComponent = () => {
options={{headerShown: false}}
component={WebNavigation}
/>
<Tab.Screen
name="Activities"
options={{
headerTitle: HomeScreenHeaderTitleFunc,
...homeHeaderOptions,
}}
component={ActivitiesScreen}
/>
<Tab.Screen
name="Settings"
options={{headerShown: false}}
Expand Down
18 changes: 18 additions & 0 deletions apps/mobile/src/screen/activities/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const Relations = [
'send',
'receive',
'ibc-send',
'ibc-send-receive',
'ibc-send-refunded',
'delegate',
'undelegate',
'redelegate',
'cancel-undelegate',
'vote',
'custom/merged-claim-rewards',
'ibc-swap-skip-osmosis',
'ibc-swap-skip-osmosis-receive',
'ibc-swap-skip-osmosis-refunded',
];

export const PaginationLimit = 20;
Loading

0 comments on commit 3f4c40c

Please sign in to comment.