From 5eb49e6d015e6a6743cb59244df5fbe462468319 Mon Sep 17 00:00:00 2001 From: yuyongmao Date: Tue, 12 Jan 2021 23:10:37 +0800 Subject: [PATCH 1/5] feat: save last selected account --- .../add-transaction-next-screen.tsx | 19 +++++++++++-- .../quick-add-accounts-selector.tsx | 28 +++++++++++++++++++ src/screens/mine-screen/about.tsx | 2 +- 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/screens/add-transaction-screen/add-transaction-next-screen.tsx b/src/screens/add-transaction-screen/add-transaction-next-screen.tsx index 1e4dd72..2e1742a 100644 --- a/src/screens/add-transaction-screen/add-transaction-next-screen.tsx +++ b/src/screens/add-transaction-screen/add-transaction-next-screen.tsx @@ -1,5 +1,12 @@ import React, { useState, useEffect } from "react"; -import { ScrollView, StyleSheet, Text, View, Platform } from "react-native"; +import { + ScrollView, + StyleSheet, + Text, + View, + Platform, + AsyncStorage, +} from "react-native"; import { Portal, Toast, List, DatePicker } from "@ant-design/react-native"; import { connect } from "react-redux"; import { NavigationBar } from "@/common/navigation-bar"; @@ -120,7 +127,15 @@ export const AddTransactionNextScreen = connect( Portal.remove(loadingKey); if (!error) { - Toast.success(i18n.t("saveSuccess"), 2, () => { + Toast.success(i18n.t("saveSuccess"), 2, async () => { + try { + await AsyncStorage.setItem("@LastSelectedAssets:key", assets); + await AsyncStorage.setItem("@LastSelectedExpenses:key", expenses); + } catch (aserror) { + console.error( + `failed to set last selected assets or expenses value: ${aserror}` + ); + } props.navigation.pop(); if (onRefresh) { onRefresh(); diff --git a/src/screens/add-transaction-screen/quick-add-accounts-selector.tsx b/src/screens/add-transaction-screen/quick-add-accounts-selector.tsx index e9ef57d..cb40264 100644 --- a/src/screens/add-transaction-screen/quick-add-accounts-selector.tsx +++ b/src/screens/add-transaction-screen/quick-add-accounts-selector.tsx @@ -6,6 +6,7 @@ import { StyleSheet, Text, View, + AsyncStorage, } from "react-native"; import { connect } from "react-redux"; import { List } from "@ant-design/react-native"; @@ -80,6 +81,7 @@ export const QuickAddAccountsSelector = connect( loading, refetch, } = useLedgerMeta(userId); + const [selectedAssets, setSelectedAssets] = useState( assetsOptionTabs.length > 0 ? assetsOptionTabs[0].options[0] : "" ); @@ -87,6 +89,32 @@ export const QuickAddAccountsSelector = connect( expensesOptionTabs.length > 0 ? expensesOptionTabs[0].options[0] : "" ); + React.useEffect(() => { + async function init() { + try { + const lastSelectedAssets = await AsyncStorage.getItem( + "@LastSelectedAssets:key" + ); + if (lastSelectedAssets !== null) { + setSelectedAssets(lastSelectedAssets); + } + const lastSelectedExpenses = await AsyncStorage.getItem( + "@LastSelectedExpenses:key" + ); + if (lastSelectedExpenses !== null) { + setSelectedExpenses(lastSelectedExpenses); + } + } catch (aserror) { + console.error( + `failed to get get last selected assets or expenses value: ${aserror}` + ); + } + } + if (!loading) { + init(); + } + }, [loading]); + useEffect(() => { const currency = currencies.length > 0 ? currencies[0] : ""; if (onChange) { diff --git a/src/screens/mine-screen/about.tsx b/src/screens/mine-screen/about.tsx index 8533582..4b3ffab 100644 --- a/src/screens/mine-screen/about.tsx +++ b/src/screens/mine-screen/about.tsx @@ -25,8 +25,8 @@ import { useUpdateReportSubscribeToRemote } from "@/screens/mine-screen/hooks/us import { useFeatureFlags } from "@/common/feature-flags/use-feature-flags"; import { AccountHeader } from "@/screens/mine-screen/account-header"; import { InviteSection } from "@/screens/referral-screen/components/invite-section"; -import { ReportStatus } from "../../../__generated__/globalTypes"; import { useIsFocused } from "@react-navigation/native"; +import { ReportStatus } from "../../../__generated__/globalTypes"; const { Item } = List; const { Brief } = Item; From a137e8b207272be663e9b9d6fe72e93e9091ed4b Mon Sep 17 00:00:00 2001 From: yuyongmao Date: Sat, 27 Mar 2021 23:01:37 +0800 Subject: [PATCH 2/5] feat: use async storage --- .npmrc | 1 + package.json | 1 + src/common/announcement.tsx | 42 ++++++------------- src/common/expo-mixpanel-analytics.ts | 3 +- src/common/hooks/use-async-storage.ts | 33 +++++++++++++++ .../add-transaction-next-screen.tsx | 28 ++++++++----- .../quick-add-accounts-selector.tsx | 31 +++++--------- src/screens/mine-screen/about.tsx | 16 +++---- 8 files changed, 83 insertions(+), 72 deletions(-) create mode 100644 src/common/hooks/use-async-storage.ts diff --git a/.npmrc b/.npmrc index 43c97e7..4fd15ed 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,2 @@ package-lock=false +legacy-peer-deps=true diff --git a/package.json b/package.json index 40be81c..f6486d1 100644 --- a/package.json +++ b/package.json @@ -103,6 +103,7 @@ "@ant-design/react-native": "^4.0.4", "@callstack/react-theme-provider": "^2.1.0", "@expo/vector-icons": "^12.0.0", + "@react-native-async-storage/async-storage": "^1.14.1", "@react-native-community/async-storage": "~1.12.0", "@react-native-community/cameraroll": "^4.0.0", "@react-native-community/masked-view": "0.1.10", diff --git a/src/common/announcement.tsx b/src/common/announcement.tsx index 81f5a10..eb45778 100644 --- a/src/common/announcement.tsx +++ b/src/common/announcement.tsx @@ -1,14 +1,9 @@ import * as React from "react"; -import { - View, - Text, - StyleSheet, - TouchableOpacity, - AsyncStorage, -} from "react-native"; +import { View, Text, StyleSheet, TouchableOpacity } from "react-native"; import { useTheme } from "@/common/theme"; import { ColorTheme } from "@/types/theme-props"; import { contentPadding, ScreenWidth } from "@/common/screen-util"; +import { useAsyncStorage } from "@/common/hooks/use-async-storage"; type Props = { navigation: any; @@ -68,22 +63,17 @@ const getStyles = (theme: ColorTheme) => export function Announcement(props: Props): JSX.Element { const [hide, setHide] = React.useState(true); + const [hideAnnouncement, setHideAnnouncement, synced] = useAsyncStorage( + "@HideAnnouncement:key", + "" + ); + const [_, setSubFlash] = useAsyncStorage("@SubscriptionFlash:key", ""); React.useEffect(() => { - async function init() { - try { - const value = await AsyncStorage.getItem("@HideAnnouncement:key"); - if (value !== null) { - setHide(value === "true"); - } else { - setHide(false); - } - } catch (error) { - console.error(`failed to get hide announcement value: ${error}`); - } + if (synced) { + setHide(hideAnnouncement === "true"); } - init(); - }, []); + }, [synced]); const { title, subtitle, icon, navigation } = props; @@ -99,11 +89,7 @@ export function Announcement(props: Props): JSX.Element { { - try { - await AsyncStorage.setItem("@SubscriptionFlash:key", "true"); - } catch (error) { - console.error(`failed to set subscription flash value: ${error}`); - } + await setSubFlash("true"); navigation.navigate("Mine"); }} > @@ -120,11 +106,7 @@ export function Announcement(props: Props): JSX.Element { activeOpacity={0.9} onPress={async () => { setHide(true); - try { - await AsyncStorage.setItem("@HideAnnouncement:key", "true"); - } catch (error) { - console.error(`failed to set hide announcement value: ${error}`); - } + await setHideAnnouncement("true"); }} > diff --git a/src/common/expo-mixpanel-analytics.ts b/src/common/expo-mixpanel-analytics.ts index 3eea233..6e8c0f0 100644 --- a/src/common/expo-mixpanel-analytics.ts +++ b/src/common/expo-mixpanel-analytics.ts @@ -1,7 +1,8 @@ /* eslint-disable camelcase */ -import { Platform, Dimensions, AsyncStorage } from "react-native"; +import { Platform, Dimensions } from "react-native"; import Constants from "expo-constants"; import { Buffer } from "buffer"; +import AsyncStorage from "@react-native-async-storage/async-storage"; const { width, height } = Dimensions.get("window"); diff --git a/src/common/hooks/use-async-storage.ts b/src/common/hooks/use-async-storage.ts new file mode 100644 index 0000000..853538f --- /dev/null +++ b/src/common/hooks/use-async-storage.ts @@ -0,0 +1,33 @@ +import AsyncStorage from "@react-native-community/async-storage"; +import { useEffect, useState } from "react"; + +export function useAsyncStorage( + key: string, + defaultValue: string +): [string, (newValue: string) => Promise, boolean] { + const [state, setState] = useState({ + synced: false, + storageValue: defaultValue, + }); + const { synced, storageValue } = state; + + async function pullFromStorage() { + const fromStorage = await AsyncStorage.getItem(key); + let value = defaultValue; + if (fromStorage) { + value = fromStorage; + } + setState({ synced: true, storageValue: value }); + } + + async function updateStorage(newValue: string) { + await AsyncStorage.setItem(key, newValue); + await pullFromStorage(); + } + + useEffect(() => { + pullFromStorage(); + }, []); + + return [storageValue, updateStorage, synced]; +} diff --git a/src/screens/add-transaction-screen/add-transaction-next-screen.tsx b/src/screens/add-transaction-screen/add-transaction-next-screen.tsx index 2e1742a..8d4ca06 100644 --- a/src/screens/add-transaction-screen/add-transaction-next-screen.tsx +++ b/src/screens/add-transaction-screen/add-transaction-next-screen.tsx @@ -1,12 +1,5 @@ import React, { useState, useEffect } from "react"; -import { - ScrollView, - StyleSheet, - Text, - View, - Platform, - AsyncStorage, -} from "react-native"; +import { ScrollView, StyleSheet, Text, View, Platform } from "react-native"; import { Portal, Toast, List, DatePicker } from "@ant-design/react-native"; import { connect } from "react-redux"; import { NavigationBar } from "@/common/navigation-bar"; @@ -20,6 +13,7 @@ import { TextStyled } from "@/common/text-styled"; import { getCurrencySymbol } from "@/common/currency-util"; import { analytics } from "@/common/analytics"; import { ColorTheme } from "@/types/theme-props"; +import { useAsyncStorage } from "@/common/hooks/use-async-storage"; const { Item } = List; const { Brief } = Item; @@ -94,6 +88,14 @@ export const AddTransactionNextScreen = connect( const [date, setDate] = useState(getFormatDate(new Date())); const [narration, setNarration] = useState(""); const { mutate, error } = useAddEntriesToRemote(); + const [lastAssets, setLastAssets] = useAsyncStorage( + "@LastSelectedAssets:key", + "" + ); + const [lastExpenses, setLastExpenses] = useAsyncStorage( + "@LastSelectedExpenses:key", + "" + ); const currencySymbol = getCurrencySymbol(currentCurrency); @@ -127,10 +129,14 @@ export const AddTransactionNextScreen = connect( Portal.remove(loadingKey); if (!error) { - Toast.success(i18n.t("saveSuccess"), 2, async () => { + Toast.success(i18n.t("saveSuccess"), 2, () => { try { - await AsyncStorage.setItem("@LastSelectedAssets:key", assets); - await AsyncStorage.setItem("@LastSelectedExpenses:key", expenses); + if (lastAssets !== assets) { + setLastAssets(assets); + } + if (lastExpenses !== expenses) { + setLastExpenses(expenses); + } } catch (aserror) { console.error( `failed to set last selected assets or expenses value: ${aserror}` diff --git a/src/screens/add-transaction-screen/quick-add-accounts-selector.tsx b/src/screens/add-transaction-screen/quick-add-accounts-selector.tsx index cb40264..e89302c 100644 --- a/src/screens/add-transaction-screen/quick-add-accounts-selector.tsx +++ b/src/screens/add-transaction-screen/quick-add-accounts-selector.tsx @@ -6,7 +6,6 @@ import { StyleSheet, Text, View, - AsyncStorage, } from "react-native"; import { connect } from "react-redux"; import { List } from "@ant-design/react-native"; @@ -19,6 +18,7 @@ import { ListItemStyled } from "@/screens/add-transaction-screen/components/list import { analytics } from "@/common/analytics"; import { i18n } from "@/translations"; import { ColorTheme } from "@/types/theme-props"; +import { useAsyncStorage } from "@/common/hooks/use-async-storage"; const { Item } = List; const { Brief } = Item; @@ -89,31 +89,22 @@ export const QuickAddAccountsSelector = connect( expensesOptionTabs.length > 0 ? expensesOptionTabs[0].options[0] : "" ); + const [lastAssets] = useAsyncStorage("@LastSelectedAssets:key", ""); + const [lastExpenses] = useAsyncStorage("@LastSelectedExpenses:key", ""); + React.useEffect(() => { - async function init() { - try { - const lastSelectedAssets = await AsyncStorage.getItem( - "@LastSelectedAssets:key" - ); - if (lastSelectedAssets !== null) { - setSelectedAssets(lastSelectedAssets); - } - const lastSelectedExpenses = await AsyncStorage.getItem( - "@LastSelectedExpenses:key" - ); - if (lastSelectedExpenses !== null) { - setSelectedExpenses(lastSelectedExpenses); - } - } catch (aserror) { - console.error( - `failed to get get last selected assets or expenses value: ${aserror}` - ); + function init() { + if (lastAssets !== "") { + setSelectedAssets(lastAssets); + } + if (lastExpenses !== "") { + setSelectedExpenses(lastExpenses); } } if (!loading) { init(); } - }, [loading]); + }, [loading, lastExpenses, lastAssets]); useEffect(() => { const currency = currencies.length > 0 ? currencies[0] : ""; diff --git a/src/screens/mine-screen/about.tsx b/src/screens/mine-screen/about.tsx index 4b3ffab..895ebba 100644 --- a/src/screens/mine-screen/about.tsx +++ b/src/screens/mine-screen/about.tsx @@ -3,14 +3,7 @@ import { List, Picker, Toast, Portal } from "@ant-design/react-native"; import Constants from "expo-constants"; import * as WebBrowser from "expo-web-browser"; import React, { useEffect, useState } from "react"; -import { - Alert, - Platform, - ScrollView, - Switch, - View, - AsyncStorage, -} from "react-native"; +import { Alert, Platform, ScrollView, Switch, View } from "react-native"; import { connect } from "react-redux"; import { analytics } from "@/common/analytics"; import { ListHeader } from "@/common/list-header"; @@ -27,6 +20,7 @@ import { AccountHeader } from "@/screens/mine-screen/account-header"; import { InviteSection } from "@/screens/referral-screen/components/invite-section"; import { useIsFocused } from "@react-navigation/native"; import { ReportStatus } from "../../../__generated__/globalTypes"; +import { useAsyncStorage } from "@react-native-async-storage/async-storage"; const { Item } = List; const { Brief } = Item; @@ -85,18 +79,20 @@ export const About = connect( const [reportAnimateCount, setReportAnimateCount] = useState(0); const [subscriptionFlash, setSubscriptionFlash] = useState(false); + + const { getItem, setItem } = useAsyncStorage("@SubscriptionFlash:key"); const isFocused = useIsFocused(); React.useEffect(() => { async function init() { try { - const value = await AsyncStorage.getItem("@SubscriptionFlash:key"); + const value = await getItem(); if (value !== null) { setSubscriptionFlash(value === "true"); } else { setSubscriptionFlash(false); } - await AsyncStorage.setItem("@SubscriptionFlash:key", "false"); + await setItem("false"); } catch (error) { console.error(`failed to get subscription flash value: ${error}`); } From 71290a7abe00dd66b0ddf47744e1f7ecaf4d495f Mon Sep 17 00:00:00 2001 From: yuyongmao Date: Sat, 27 Mar 2021 23:32:55 +0800 Subject: [PATCH 3/5] fix:tslint --- src/common/announcement.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/announcement.tsx b/src/common/announcement.tsx index eb45778..6e1e443 100644 --- a/src/common/announcement.tsx +++ b/src/common/announcement.tsx @@ -67,7 +67,8 @@ export function Announcement(props: Props): JSX.Element { "@HideAnnouncement:key", "" ); - const [_, setSubFlash] = useAsyncStorage("@SubscriptionFlash:key", ""); + /* tslint:disable:no-unused-variable */ + const [subFlash, setSubFlash] = useAsyncStorage("@SubscriptionFlash:key", ""); React.useEffect(() => { if (synced) { From 61d5f2b1be02327a53c4461e463ae9f87ef84938 Mon Sep 17 00:00:00 2001 From: yuyongmao Date: Sat, 27 Mar 2021 23:42:21 +0800 Subject: [PATCH 4/5] fix:unused-variable --- src/common/announcement.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/announcement.tsx b/src/common/announcement.tsx index 6e1e443..b6c9c2c 100644 --- a/src/common/announcement.tsx +++ b/src/common/announcement.tsx @@ -67,8 +67,8 @@ export function Announcement(props: Props): JSX.Element { "@HideAnnouncement:key", "" ); - /* tslint:disable:no-unused-variable */ - const [subFlash, setSubFlash] = useAsyncStorage("@SubscriptionFlash:key", ""); + + const [, setSubFlash] = useAsyncStorage("@SubscriptionFlash:key", ""); React.useEffect(() => { if (synced) { From 34c821e77518c5500d4cfb8b57ffbf87f60984b1 Mon Sep 17 00:00:00 2001 From: yuyongmao Date: Sun, 28 Mar 2021 07:55:35 +0800 Subject: [PATCH 5/5] change: SubscriptionFlash to SubscriptionFlashCard --- src/common/announcement.tsx | 2 +- src/common/hooks/use-async-storage.ts | 2 +- src/screens/mine-screen/about.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/announcement.tsx b/src/common/announcement.tsx index b6c9c2c..60151ce 100644 --- a/src/common/announcement.tsx +++ b/src/common/announcement.tsx @@ -68,7 +68,7 @@ export function Announcement(props: Props): JSX.Element { "" ); - const [, setSubFlash] = useAsyncStorage("@SubscriptionFlash:key", ""); + const [, setSubFlash] = useAsyncStorage("@SubscriptionFlashCard:key", ""); React.useEffect(() => { if (synced) { diff --git a/src/common/hooks/use-async-storage.ts b/src/common/hooks/use-async-storage.ts index 853538f..88d27e3 100644 --- a/src/common/hooks/use-async-storage.ts +++ b/src/common/hooks/use-async-storage.ts @@ -1,4 +1,4 @@ -import AsyncStorage from "@react-native-community/async-storage"; +import AsyncStorage from "@react-native-async-storage/async-storage"; import { useEffect, useState } from "react"; export function useAsyncStorage( diff --git a/src/screens/mine-screen/about.tsx b/src/screens/mine-screen/about.tsx index 895ebba..5bf438f 100644 --- a/src/screens/mine-screen/about.tsx +++ b/src/screens/mine-screen/about.tsx @@ -80,7 +80,7 @@ export const About = connect( const [reportAnimateCount, setReportAnimateCount] = useState(0); const [subscriptionFlash, setSubscriptionFlash] = useState(false); - const { getItem, setItem } = useAsyncStorage("@SubscriptionFlash:key"); + const { getItem, setItem } = useAsyncStorage("@SubscriptionFlashCard:key"); const isFocused = useIsFocused(); React.useEffect(() => {