react-native#AsyncStorage TypeScript Examples

The following examples show how to use react-native#AsyncStorage. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: LoadAssets.tsx    From react-native-meetio with MIT License 7 votes vote down vote up
LoadAssets = ({ assets, fonts, children }: LoadAssetsProps) => {
  const [isNavigationReady, setIsNavigationReady] = useState(!__DEV__);
  const [initialState, setInitialState] = useState<InitialState | undefined>();
  const ready = useLoadAssets(assets || [], fonts || {});
  useEffect(() => {
    const restoreState = async () => {
      try {
        const savedStateString = await AsyncStorage.getItem(
          NAVIGATION_STATE_KEY
        );
        const state = savedStateString
          ? JSON.parse(savedStateString)
          : undefined;
        setInitialState(state);
      } finally {
        setIsNavigationReady(true);
      }
    };

    if (!isNavigationReady) {
      restoreState();
    }
  }, [isNavigationReady]);

  const onStateChange = useCallback((state) => {
    AsyncStorage.setItem(NAVIGATION_STATE_KEY, JSON.stringify(state));
  }, []);
  if (!ready || !isNavigationReady) {
    return <AppLoading />;
  }
  return (
    <NavigationContainer {...{ onStateChange, initialState }}>
      {children}
    </NavigationContainer>
  );
}
Example #2
Source File: Login.tsx    From jmix-frontend with Apache License 2.0 6 votes vote down vote up
onSubmit = () => {
    if (!this.isSubmitEnabled) {
      return;
    }

    this.clearErrors();
    this.loading = true;

    this.props
      .onLoginSubmit(this.login, this.password)
      .then(_value => {
        return AsyncStorage.setItem(REST_TOKEN_STORAGE_KEY, jmixREST.restApiToken);
      })
      .catch(err => {
        if (err.response) {
          err.response.json().then((message) => {
            if (message.error === OAUTH2_INVALID_GRANT) {
              this.badCredentialsError = true;
            } else {
              this.serverError = true;
            }
          });
        } else {
          this.serverError = true;
        }
      })
      .finally(() => {
        this.loading = false;
      });
  };
Example #3
Source File: StorageAsync.ts    From companion-kit with MIT License 6 votes vote down vote up
StorageAsync: IStorage = {
    getValue(key: string): Promise<string> {
        return AsyncStorage.getItem(key);
    },

    setValue(key: string, value: string): Promise<void> {
        if (!key) {
            throw new Error('[StorageAsync] key must be not empty! got:' + key);
        }
        if (!value) {
            throw new Error('[StorageAsync] value must be not null! use \'remove\' instead. Got: ' + `${key} | ${value}` );
        }

        return AsyncStorage.setItem(key, value);
    },

    async hasValue(key: string): Promise<boolean> {
        try {
            const value = await StorageAsync.getValue(key);
            return !!value;
        } catch (err) {
            return false;
        }
    },

    remove(key: string): Promise<void> {
        return AsyncStorage.removeItem(key);
    },
}
Example #4
Source File: theme.tsx    From ecoleta with MIT License 6 votes vote down vote up
ThemeProvider: React.FC = ({ children }) => {
  const deviceTheme = useColorScheme();
  const [theme, setTheme] = useState(deviceTheme === 'light' ? light : dark);

  useEffect(() => {
    async function getPersistedTheme(): Promise<void> {
      const persistedTheme = await AsyncStorage.getItem('theme');

      if (persistedTheme) {
        setTheme(persistedTheme === 'light' ? light : dark);
      }
    }

    getPersistedTheme();
  }, []);

  const persistTheme = useCallback(async themeToPersist => {
    setTheme(themeToPersist === 'light' ? light : dark);
    await AsyncStorage.setItem('theme', themeToPersist);
  }, []);

  useEffect(() => {
    persistTheme(deviceTheme);
  }, [deviceTheme, persistTheme]);

  const toggleTheme = useCallback(() => {
    persistTheme(theme.title === 'light' ? 'dark' : 'light');
  }, [theme.title, persistTheme]);

  return (
    <StyledProvider theme={theme}>
      <ThemeContext.Provider value={{ toggleTheme }}>
        {children}
      </ThemeContext.Provider>
    </StyledProvider>
  );
}
Example #5
Source File: status.ts    From selftrace with MIT License 6 votes vote down vote up
subscribeToAuthStateChange = () => (dispatch: Dispatch) => {
  API.initialize();

  return API.requestAuthStateListener((async (user: UserInfo) => {
    if (!user) {
      // Case 1: Signed out
      await Promise.all([
        AsyncStorage.removeItem('wellbeing'),
        AsyncStorage.removeItem('locationLastUpdatedAt'),
        AsyncStorage.removeItem('lastMapCenter'),
      ]);

      return dispatch(setAuthStatusToSignedOut());
    }

    // Case 2: Signed in
    const userInfo = {
      email: user.email,
      uid: user.uid,
    };

    await downloadUserInfoToLocalDB(user.uid);
    await pullUserInfoFromLocalDBToRedux(dispatch);
    return dispatch(setAuthStatusToSignedIn(userInfo));
  }) as any);
}
Example #6
Source File: userInfo.ts    From selftrace with MIT License 6 votes vote down vote up
export async function downloadUserInfoToLocalDB(uid: string): Promise<void> {
  try {
    const userDoc = await API.requestUserInfo(uid);

    if (!userDoc) {
      // The user has just signed up. The server is creating the user document in Firestore.
      return undefined;
    }
    const { wellbeing, symptomMap } = userDoc;

    const storageTasks: Promise<void>[] = [];

    if (wellbeing) {
      storageTasks.push(AsyncStorage.setItem('wellbeing', wellbeing.toString()));
    }

    if (symptomMap) {
      storageTasks.push(AsyncStorage.setItem('symptomMap', JSON.stringify(symptomMap)));
    }

    if (storageTasks.length > 0) {
      await Promise.all(storageTasks);
    }

    return Promise.resolve();
  } catch (err) {
    return Promise.reject(err);
  }
}
Example #7
Source File: helpers.tsx    From js-examples with MIT License 6 votes vote down vote up
generateIdentity = async (): Promise<PrivateKey> => {
  let idStr = await AsyncStorage.getItem(IDENTITY_KEY)
  if (idStr) {
    return PrivateKey.fromString(idStr)
  } else {
    const id = PrivateKey.fromRandom()
    idStr = id.toString()
    await AsyncStorage.setItem(IDENTITY_KEY, idStr)
    return id
  }
}
Example #8
Source File: helpers.ts    From selftrace with MIT License 6 votes vote down vote up
export async function pullUserInfoFromLocalDBToRedux(dispatch: Dispatch) {
  try {
    const [wellbeingRaw, symptomMapRaw] = await Promise.all([
      AsyncStorage.getItem('wellbeing'),
      AsyncStorage.getItem('symptomMap'),
    ]);

    dispatch(
      receiveUpdateUserInfoResponse({
        wellbeing: wellbeingRaw === null ? undefined : Number(wellbeingRaw),
        symptomMap: symptomMapRaw === null ? {} : JSON.parse(symptomMapRaw),
      })
    );
    dispatch(clearUpdateUserInfoProgress());
  } catch (err) {
    //
  }
}
Example #9
Source File: helpers.tsx    From js-examples with MIT License 6 votes vote down vote up
getCachedUserThread = async (): Promise<ThreadID | undefined> => {
  /**
   * All storage should be scoped to the identity
   *
   * If the identity changes and you try to use an old database,
   * it will error due to not authorized.
   */
  const idStr = await AsyncStorage.getItem(USER_THREAD_ID)
  if (idStr) {
    /**
     * Temporary hack to get ThreadID working in RN
     */
    const id: ThreadID = ThreadID.fromString(idStr)
    return id
  }
  return undefined
}
Example #10
Source File: CustomAppearanceProvider.tsx    From selftrace with MIT License 6 votes vote down vote up
async function rehydrateAppearanceState() {
  if (!shouldRehydrate || !AsyncStorage) {
    return defaultState;
  }

  try {
    const item = await AsyncStorage.getItem(appearanceStorageKey);
    return item ? JSON.parse(item) : null;
  } catch (ignored) {
    return defaultState;
  }
}
Example #11
Source File: expo-mixpanel-analytics.ts    From beancount-mobile with MIT License 6 votes vote down vote up
constructor(token: string) {
    this.ready = false;
    this.queue = [];

    this.token = token;
    this.userId = null;
    this.clientId = Constants.deviceId;
    this.osVersion = Platform.Version;
    this.superProps;

    Constants.getWebViewUserAgentAsync().then((userAgent) => {
      this.userAgent = userAgent;
      this.appName = Constants.manifest.name;
      this.appId = Constants.manifest.slug;
      this.appVersion = Constants.manifest.version;
      this.screenSize = `${width}x${height}`;
      this.deviceName = Constants.deviceName;
      if (isIosPlatform && Constants.platform && Constants.platform.ios) {
        this.platform = Constants.platform.ios.platform;
        this.model = Constants.platform.ios.model;
      } else {
        this.platform = "android";
      }

      AsyncStorage.getItem(ASYNC_STORAGE_KEY, (_, result) => {
        if (result) {
          try {
            this.superProps = JSON.parse(result) || {};
            // eslint-disable-next-line no-empty
          } catch {}
        }

        this.ready = true;
        this.identify(this.clientId);
        this._flush();
      });
    });
  }
Example #12
Source File: TutorialScreen.tsx    From lets-fork-native with MIT License 6 votes vote down vote up
TutorialScreen = React.memo((props: Props): React.ReactElement => {
  const { setShowApp } = props


  const renderItem = (item: Item): React.ReactElement => (
    <View style={styles.slide}>
      <Text style={styles.text}>{item.item.text}</Text>
    </View>
  )


  const onDone = (): void => {
    setShowApp(true)
    AsyncStorage.setItem('showApp', 'true')
  }

  return (
    <View style={styles.container}>
      <View style={styles.headerContainer}>
        <MaterialIcons style={styles.icon} name="restaurant" color={colors.white} size={26} />
        <RNText style={styles.header}>Let&apos;s Fork</RNText>
      </View>
      <AppIntroSlider
        renderItem={renderItem}
        data={slides}
        onDone={onDone}
      />
    </View>
  )
})
Example #13
Source File: App.tsx    From jmix-frontend with Apache License 2.0 5 votes vote down vote up
export default function App() {
  return (
    <JmixAppProvider jmixREST={jmixREST} retrieveRestApiToken={() => AsyncStorage.getItem(REST_TOKEN_STORAGE_KEY)}>
      <Root/>
    </JmixAppProvider>
  );
}
Example #14
Source File: helpers.tsx    From js-examples with MIT License 5 votes vote down vote up
cacheUserThread = async (id: ThreadID) => {
  await AsyncStorage.setItem(USER_THREAD_ID, id.toString())
}
Example #15
Source File: expo-mixpanel-analytics.ts    From beancount-mobile with MIT License 5 votes vote down vote up
reset() {
    this.identify(this.clientId);
    try {
      AsyncStorage.setItem(ASYNC_STORAGE_KEY, JSON.stringify({}));
      // eslint-disable-next-line no-empty
    } catch {}
  }
Example #16
Source File: expo-mixpanel-analytics.ts    From beancount-mobile with MIT License 5 votes vote down vote up
register(props: any) {
    this.superProps = props;
    try {
      AsyncStorage.setItem(ASYNC_STORAGE_KEY, JSON.stringify(props));
      // eslint-disable-next-line no-empty
    } catch {}
  }
Example #17
Source File: announcement.tsx    From beancount-mobile with MIT License 5 votes vote down vote up
export function Announcement(props: Props): JSX.Element {
  const [hide, setHide] = React.useState(true);

  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}`);
      }
    }
    init();
  }, []);

  const { title, subtitle, icon, navigation } = props;

  const theme = useTheme();

  const styles = getStyles(theme.colorTheme);

  if (hide) {
    return <View />;
  }

  return (
    <TouchableOpacity
      style={styles.container}
      onPress={async () => {
        try {
          await AsyncStorage.setItem("@SubscriptionFlash:key", "true");
        } catch (error) {
          console.error(`failed to set subscription flash value: ${error}`);
        }
        navigation.navigate("Mine");
      }}
    >
      <View style={styles.titleContainer}>
        <Text style={styles.subtitle}>{subtitle}</Text>
        <Text numberOfLines={2} style={styles.title}>
          {title}
        </Text>
      </View>

      <View>{icon}</View>
      <TouchableOpacity
        style={styles.closeButton}
        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}`);
          }
        }}
      >
        <Text style={styles.closeText}>✕</Text>
      </TouchableOpacity>
    </TouchableOpacity>
  );
}
Example #18
Source File: RestaurantScreen.tsx    From lets-fork-native with MIT License 5 votes vote down vote up
RestaurantScreen = React.memo((props: Props) => {
  const { route } = props
  const { restaurant } = route.params

  // Request a review from users every 10 times they view a
  // match restaurant. The app will stop asking once they click
  // 'Sure' on Android, or once every 365 days for iOS
  React.useEffect(() => {
    async function requestReview(): Promise<void> {
      const [restaurantsViewed, hasReviewed] = await Promise.all([
        AsyncStorage.getItem('restaurantsViewed'),
        AsyncStorage.getItem('hasReviewed'),
      ])

      if (Number(restaurantsViewed) % 10 === 0) {
        setTimeout(() => {
          if (Platform.OS === 'android') {
            if (!hasReviewed) {
              Alert.alert(
                "Enjoying Let's Fork?",
                'Leave a review on the Play Store',
                [
                  { text: 'Maybe Later' },
                  {
                    text: 'Sure',
                    onPress: async (): Promise<void> => {
                      await AsyncStorage.setItem('hasReviewed', 'true')
                      return StoreReview.requestReview()
                    },
                  },
                ],
                { cancelable: false },
              )
            }
          } else {
            StoreReview.requestReview()
          }
        }, 3000)
      }

      AsyncStorage.setItem('restaurantsViewed', `${Number(restaurantsViewed) + 1}`)
    }

    requestReview()
  }, [])


  return (
    <SafeAreaView style={styles.container}>
      <ScrollView>
        <Details
          photos
          restaurant={restaurant}
        />
      </ScrollView>
    </SafeAreaView>
  )
})
Example #19
Source File: CustomAppearanceProvider.tsx    From selftrace with MIT License 5 votes vote down vote up
async function cacheAppearanceState(appearance) {
  await AsyncStorage.setItem(appearanceStorageKey, JSON.stringify(appearance));
}
Example #20
Source File: userInfo.ts    From selftrace with MIT License 5 votes vote down vote up
uploadUserInfo = (
  uid: string,
  updatedInfo: Partial<API.FirestoreUserDoc>,
  haveDetailsChanged: boolean
) => async (dispatch: Dispatch) => {
  dispatch(startUpdateUserInfoRequest());
  try {
    const locationLastUpdatedAtRaw = await AsyncStorage.getItem('locationLastUpdatedAt');
    const hasNotUpdatedLocationFor30Mins =
      !locationLastUpdatedAtRaw || Date.now() - Number(locationLastUpdatedAtRaw) > 1000 * 60 * 30;
    const isUserUnwell =
      updatedInfo.wellbeing === Wellbeing.ShowingSymptoms ||
      updatedInfo.wellbeing === Wellbeing.TestedPositive;
    const updatedInfoFinal: Partial<API.FirestoreUserDoc> = { ...updatedInfo };

    if (!isUserUnwell) {
      // Case 1: User is well
      updatedInfoFinal.lastLocation = API.deletionSentinel() as any;
      await API.requestUpdateUserInfo(uid, updatedInfoFinal);
      await AsyncStorage.removeItem('locationLastUpdatedAt');
    } else if (haveDetailsChanged || hasNotUpdatedLocationFor30Mins) {
      // Case 2: User is unwell and their details are not up to date
      if (hasNotUpdatedLocationFor30Mins) {
        dispatch(startRetrievingUserLocation());
        const { latitude, longitude } = await retrieveLastLocationWithPermission();
        updatedInfoFinal.lastLocation = { lat: latitude, lng: longitude };
      }
      await API.requestUpdateUserInfo(uid, updatedInfoFinal);
      await AsyncStorage.setItem('locationLastUpdatedAt', Date.now().toString());
    } else {
      // Case 3: User is unwell but their details are up-to-date
      await PromiseUtils.sleep(750); // Simulate update request
    }

    dispatch(receiveUpdateUserInfoResponse(updatedInfo));
    setTimeout(() => {
      dispatch(clearUpdateUserInfoProgress());
    }, GRACEFUL_EXIT_DURATION);
  } catch (err) {
    const error =
      err instanceof LocationPermissionError
        ? new Error('Update failed because you did not agree to share your location.') // TODO: Localize
        : err;
    dispatch(receiveUpdateUserInfoError(error));
  }
}
Example #21
Source File: App.tsx    From lets-fork-native with MIT License 4 votes vote down vote up
export default function App(): React.ReactElement {
  const [showApp, setShowApp] = React.useState(false)
  const [fontsLoaded] = useFonts({ VarelaRound_400Regular })
  const [loading, setLoading] = React.useState<boolean>(true)
  const [location, setLocation] = React.useState<Location.LocationObject>()
  const [party, setParty] = React.useState<Party>({} as Party)

  const linking = {
    prefixes: ['https://letsfork.app', 'letsfork://', 'exp://192.168.178.76:19000/+'],
    config: {
      screens: {
        Party: 'party/:id',
      },
    },
  }

  React.useEffect(() => {
    // Keep track of current matches
    let matches: Restaurant[] = []

    ws.onopen = (): void => {
      console.log('opened')
    }

    ws.onmessage = (msg): void => {
      console.log(msg.data)
      const data: Party = JSON.parse(msg.data)

      const newMatches = JSON.stringify(data.matches?.map((r) => r.id).sort())
      const oldMatches = JSON.stringify(matches?.map((r) => r.id).sort())

      // Alert when there are new matches
      if (data.matches?.length
        && oldMatches !== newMatches) {
        matches = data.matches
        Alert.alert(
          'You have a new match!',
          'Click the icon in the top right to view your matches',
        )
      }

      setParty(data)
    }

    ws.onclose = (msg): void => {
      console.log('closed', msg.reason)
    }

    ws.onerror = (err): void => {
      console.log('websocket error:', err)
    }
  }, [])

  const loadApplicationAsync = async (): Promise<void> => {
    const { status } = await Location.requestPermissionsAsync()
    if (status !== 'granted') {
      console.log('Permission to access location was denied')
    }

    const [loc, val] = await Promise.all([
      Location.getCurrentPositionAsync({}),
      AsyncStorage.getItem('showApp'),
    ])

    if (loc) setLocation(loc)

    // User has seen intro
    if (val) setShowApp(true)
  }

  if (loading || !fontsLoaded) {
    return (
      <AppLoading
        startAsync={loadApplicationAsync}
        onFinish={(): void => setLoading(false)}
        onError={console.warn}
      />
    )
  }

  if (!showApp) {
    return <TutorialScreen setShowApp={setShowApp} />
  }

  return (
    <NavigationContainer linking={linking} fallback={<Text>Loading...</Text>}>
      <Stack.Navigator initialRouteName="Home">
        <Stack.Screen
          name="Create"
          options={(): object => ({
            headerTitle: (): null => null,
          })}
        >
          {(props): React.ReactElement => (
            <CreateScreen
              {...props}
              ws={ws}
              location={location}
            />
          )}
        </Stack.Screen>
        <Stack.Screen
          name="Home"
          component={HomeScreen}
          options={(): object => ({
            headerShown: false,
          })}
        />
        <Stack.Screen
          name="Join"
          options={(): object => ({
            headerTitle: (): null => null,
          })}
        >
          {(props): React.ReactElement => <JoinScreen {...props} ws={ws} />}
        </Stack.Screen>
        <Stack.Screen
          name="Match"
          options={(): object => ({
            headerTitle: (): null => null,
          })}
        >
          {(props): React.ReactElement => <MatchScreen {...props} party={party} />}
        </Stack.Screen>
        <Stack.Screen
          name="Party"
          options={({ navigation }): object => ({
            gestureEnabled: false,
            headerTitle: (): null => null,
            headerLeft: (): React.ReactElement => (
              <TouchableOpacity
                style={styles.backButton}
                onPress={(): void => Alert.alert(
                  'Are you sure you want to exit?',
                  'Exiting will make you lose all data in this party',
                  [
                    { text: 'Cancel' },
                    {
                      text: 'OK',
                      onPress: (): void => {
                        ws.send(JSON.stringify({ type: 'quit' }))
                        navigation.navigate('Home')
                        setParty({} as Party)
                      },
                    },
                  ],
                  { cancelable: true },
                )}
              >
                <MaterialIcons name="close" color="black" size={24} />
              </TouchableOpacity>
            ),
            headerRight: (): React.ReactElement | null => (
              party.status === 'active'
                ? <Menu navigation={navigation} />
                : null
            ),
          })}
        >
          {(props): React.ReactElement => (
            <PartyScreen
              {...props}
              ws={ws}
              party={party}
              setParty={setParty}
            />
          )}
        </Stack.Screen>
        <Stack.Screen
          name="Restaurant"
          component={RestaurantScreen}
          options={(): object => ({
            headerTitle: (): null => null,
          })}
        />
        <Stack.Screen
          name="Share"
          options={(): object => ({
            headerTitle: (): null => null,
          })}
        >
          {(props): React.ReactElement => (
            <ShareScreen {...props} party={party} />
          )}
        </Stack.Screen>
      </Stack.Navigator>
    </NavigationContainer>
  )
}
Example #22
Source File: about.tsx    From beancount-mobile with MIT License 4 votes vote down vote up
About = connect(
  (state: AppState) => ({
    authToken: state.base.authToken,
    locale: state.base.locale,
    currentTheme: state.base.currentTheme,
    userId: state.base.userId,
  }),
  (dispatch) => ({
    logout(authToken: string): void {
      dispatch(actionLogout(authToken));
    },
    updateReduxState(payload: { base: { locale: string } }): void {
      dispatch(actionUpdateReduxState(payload));
    },
  })
)(
  ({
    authToken,
    locale,
    logout,
    updateReduxState,
    currentTheme,
    userId,
    navigation,
  }: Props) => {
    const theme = useTheme().colorTheme;
    const { setLocale } = React.useContext(LocalizationContext);
    const pickerSource = [
      { value: ReportStatus.WEEKLY, label: i18n.t("weekly") },
      { value: ReportStatus.MONTHLY, label: i18n.t("monthly") },
      { value: ReportStatus.OFF, label: i18n.t("off") },
    ];

    useEffect(() => {
      async function init() {
        await registerForPushNotificationAsync();
      }
      init();
    }, []);

    const [reportAnimateCount, setReportAnimateCount] = useState(0);
    const [subscriptionFlash, setSubscriptionFlash] = useState(false);
    const isFocused = useIsFocused();

    React.useEffect(() => {
      async function init() {
        try {
          const value = await AsyncStorage.getItem("@SubscriptionFlash:key");
          if (value !== null) {
            setSubscriptionFlash(value === "true");
          } else {
            setSubscriptionFlash(false);
          }
          await AsyncStorage.setItem("@SubscriptionFlash:key", "false");
        } catch (error) {
          console.error(`failed to get subscription flash value: ${error}`);
        }
      }
      init();
    }, [isFocused]);

    useEffect(() => {
      if (subscriptionFlash) {
        const interval = setInterval(() => {
          if (reportAnimateCount < 5) {
            setReportAnimateCount(reportAnimateCount + 1);
          }
        }, 300);
        return () => clearInterval(interval);
      }
      setReportAnimateCount(0);
      return undefined;
    }, [subscriptionFlash, reportAnimateCount]);

    const { emailReportStatus } = useUserProfile(userId);
    const [reportStatus, setReportStatue] = useState<string>(
      emailReportStatus ? emailReportStatus.toString() : ""
    );

    useEffect(() => {
      setReportStatue(emailReportStatus ? emailReportStatus.toString() : "");
    }, [emailReportStatus]);

    const { error, mutate } = useUpdateReportSubscribeToRemote();

    const getReportStatusLabel = (status: string) => {
      switch (status) {
        case ReportStatus.OFF:
          return i18n.t("off");
        case ReportStatus.WEEKLY:
          return i18n.t("weekly");
        case ReportStatus.MONTHLY:
          return i18n.t("monthly");
        default:
          return i18n.t("off");
      }
    };

    const getReportStatusEnum = (status: string) => {
      switch (status) {
        case ReportStatus.OFF:
          return ReportStatus.OFF;
        case ReportStatus.WEEKLY:
          return ReportStatus.WEEKLY;
        case ReportStatus.MONTHLY:
          return ReportStatus.MONTHLY;
        default:
          return ReportStatus.OFF;
      }
    };

    const renderAppSection = () => {
      const backgroundColor = {
        backgroundColor: theme.white,
        color: theme.text01,
      };

      const { spendingReportSubscription } = useFeatureFlags(userId);

      return (
        // @ts-ignore
        <List
          style={backgroundColor}
          renderHeader={<ListHeader>{i18n.t("about")}</ListHeader>}
        >
          <Item
            disabled
            extra={Platform.OS === "ios" ? "Apple Store" : "Google Play"}
            arrow="horizontal"
            style={backgroundColor}
            onPress={async () => {
              const storeUrl =
                Platform.OS === "ios"
                  ? "https://apps.apple.com/us/app/id1527950512"
                  : "https://play.google.com/store/apps/details?id=io.beancount.android";
              if (storeUrl) {
                await WebBrowser.openBrowserAsync(storeUrl);
                await analytics.track("tap_review_app", { storeUrl });
              }
            }}
          >
            {i18n.t("reviewApp")}
          </Item>

          {spendingReportSubscription && (
            <Picker
              data={pickerSource}
              cols={1}
              extra={getReportStatusLabel(reportStatus)}
              onChange={async (value) => {
                const newValue = value ? String(value[0]) : "";
                if (newValue === reportStatus) {
                  return;
                }
                setReportStatue(newValue);
                const loadingKey = Toast.loading(i18n.t("updating"));
                await mutate({
                  variables: { userId, status: getReportStatusEnum(newValue) },
                });
                Portal.remove(loadingKey);
                if (!error) {
                  Toast.success(i18n.t("updateSuccess"));
                } else {
                  console.error("failed to update report status", error);
                  Toast.fail(i18n.t("updateFailed"));
                }
              }}
            >
              <Item
                style={[
                  backgroundColor,
                  {
                    backgroundColor:
                      reportAnimateCount % 2 === 1
                        ? theme.warning
                        : theme.white,
                  },
                ]}
                arrow="horizontal"
              >
                {i18n.t("subscribe")}
              </Item>
            </Picker>
          )}

          <Item
            disabled
            style={backgroundColor}
            extra={
              <Switch
                value={String(locale).startsWith("en")}
                onValueChange={async (value) => {
                  const changeTo = value ? "en" : "zh";
                  updateReduxState({
                    base: { locale: changeTo },
                  });
                  i18n.locale = changeTo;
                  setLocale(changeTo);
                  await analytics.track("tap_switch_language", { changeTo });
                }}
              />
            }
          >
            {i18n.t("currentLanguage")}
            <Brief>
              {String(locale).startsWith("en")
                ? i18n.t("english")
                : i18n.t("chinese")}
            </Brief>
          </Item>

          <Item
            style={backgroundColor}
            disabled
            extra={
              <Switch
                value={currentTheme === "dark"}
                onValueChange={async (value) => {
                  const mode = value ? "dark" : "light";
                  updateReduxState({
                    base: { currentTheme: mode },
                  });
                  await analytics.track("tap_switch_theme", { mode });
                }}
              />
            }
          >
            {i18n.t("theme")}
            <Brief>{currentTheme === "dark" ? "Dark" : "Light"}</Brief>
          </Item>
          <Item
            style={backgroundColor}
            disabled
            extra={Constants.nativeAppVersion}
          >
            {i18n.t("currentVersion")}
          </Item>
          {authToken ? (
            <Item
              style={backgroundColor}
              disabled
              onPress={() => {
                Alert.alert(
                  "",
                  i18n.t("logoutAlertMsg"),
                  [
                    { text: i18n.t("logoutAlertCancel"), style: "cancel" },
                    {
                      text: i18n.t("logoutAlertConfirm"),
                      onPress: () => {
                        logout(authToken);
                      },
                    },
                  ],
                  { cancelable: false }
                );
              }}
            >
              {i18n.t("logout")}
            </Item>
          ) : (
            <View />
          )}
        </List>
      );
    };

    return (
      <ScrollView style={{ backgroundColor: theme.white }}>
        <AccountHeader />
        <InviteSection navigation={navigation} />
        {renderAppSection()}
      </ScrollView>
    );
  }
)
Example #23
Source File: index.tsx    From selftrace with MIT License 4 votes vote down vote up
function MapPage({ wellbeing }: Props) {
  const [state, setState] = React.useState<State>({
    clusters: [],
    isLoading: false,
    lastMapCenter: undefined,
    lastMapZoom: undefined,
  });

  React.useEffect(() => {
    (async function loadLastMapConfig() {
      const [lastMapCenterRaw, lastMapZoomRaw] = await Promise.all([
        AsyncStorage.getItem('lastMapCenter'),
        AsyncStorage.getItem('lastMapZoom'),
      ]);

      if (lastMapCenterRaw || lastMapZoomRaw) {
        setState(prev => {
          const newState: State = { ...prev };
          if (lastMapCenterRaw !== null) {
            newState.lastMapCenter = JSON.parse(lastMapCenterRaw);
          }
          if (lastMapZoomRaw !== null) {
            newState.lastMapZoom = Number(lastMapZoomRaw);
          }

          return newState;
        });
      }
    })();
  }, []);

  // TODO: The "delaying" logic should probably lie outside of the component
  async function handleRegionChange({ region, zoom }: { region: Geo.Region; zoom: number }) {
    const requestStartedAt = Date.now();
    let requestEndedAt = requestStartedAt;
    setState(prevState => ({
      ...prevState,
      clusters: [],
      isLoading: true,
    }));
    let newClusters: ClusterObject[] = [];
    const center: Geo.LocationShort = {
      lat: region.latitude,
      lng: region.longitude,
    };

    try {
      [, , newClusters] = await Promise.all([
        AsyncStorage.setItem('lastMapCenter', JSON.stringify(center)),
        AsyncStorage.setItem('lastMapZoom', zoom.toString()),
        API.requestClusters(region),
      ]);
      requestEndedAt = Date.now();
    } catch (err) {
      requestEndedAt = Date.now();
    } finally {
      const executionTime = requestEndedAt - requestStartedAt;

      const endRequest = () =>
        setState(prev => ({
          ...prev,
          clusters: newClusters.map(cluster => ({
            key: ReactUtils.generateListKey(),
            data: cluster,
          })),
          isLoading: false,
        }));

      if (executionTime < MIN_EXECUTION_TIME) {
        setTimeout(endRequest, MIN_EXECUTION_TIME - executionTime);
      } else {
        endRequest();
      }
    }
  }

  const wellbeingIsDefined = !!wellbeing;

  return (
    <PageContainer isFullScreen isProtected>
      {wellbeingIsDefined ? (
        <CoronaMap
          center={state.lastMapCenter}
          zoom={state.lastMapZoom}
          clusters={state.clusters}
          isLoading={state.isLoading}
          onRegionChangeComplete={handleRegionChange}
          style={styles.mapContainer}
        />
      ) : (
        <>
          <CoronaMap clusters={[]} isLoading={false} style={styles.mapContainer} />
          <BlurView
            tint="dark"
            intensity={75}
            style={[StyleSheet.absoluteFill, { justifyContent: 'center', alignItems: 'center' }]}>
            <View style={styles.warningContainer}>
              <Icon.Lock color="white" style={styles.lockIcon} />
              <Text style={styles.warningTitle}>{t('screens.map.chooseWellbeingTitle')}</Text>
              <Text style={styles.warningMessage}>{t('screens.map.chooseWellbeingMessage')}</Text>
            </View>
          </BlurView>
        </>
      )}
    </PageContainer>
  );
}