Skip to content

Commit

Permalink
[menu-bar] Migrate AsyncStorage records to react-native-mmkv (#82)
Browse files Browse the repository at this point in the history
* [menu-bar] Migrate AsyncStorage records to react-native-mmkv

* Add changelog entry
  • Loading branch information
gabrieldonadel authored Oct 17, 2023
1 parent 6056712 commit b19df9d
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 82 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
- Upgrade react-native-svg to 13.14.0 and remove patch. ([#70](https://github.com/expo/orbit/pull/70) by [@gabrieldonadel](https://github.com/gabrieldonadel))
- Upgrade react-native to 0.72.5. ([#71](https://github.com/expo/orbit/pull/71) by [@gabrieldonadel](https://github.com/gabrieldonadel))
- Setup Lerna. ([#72](https://github.com/expo/orbit/pull/72) by [@gabrieldonadel](https://github.com/gabrieldonadel))
- Migrate AsyncStorage records to react-native-mmkv. ([#82](https://github.com/expo/orbit/pull/82) by [@gabrieldonadel](https://github.com/gabrieldonadel))

## 0.1.3 — 2023-09-21

Expand Down
22 changes: 7 additions & 15 deletions apps/menu-bar/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,27 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import React, { useEffect } from 'react';
import React from 'react';
import { StyleSheet } from 'react-native';

import AutoResizerRootView from './components/AutoResizerRootView';
import { SAFE_AREA_FACTOR } from './hooks/useSafeDisplayDimensions';
import { PersistGate } from './modules/PersistGate';
import Popover from './popover';
import { ThemeProvider } from './providers/ThemeProvider';
import { WindowsNavigator } from './windows';
import { hasSeenOnboardingStorageKey } from './windows/Onboarding';

type Props = {
isDevWindow: boolean;
};

function App(props: Props) {
useEffect(() => {
AsyncStorage.getItem(hasSeenOnboardingStorageKey).then((value) => {
if (!value) {
WindowsNavigator.open('Onboarding');
}
});
}, []);

return (
<AutoResizerRootView
style={styles.container}
enabled={!props.isDevWindow}
maxRelativeHeight={SAFE_AREA_FACTOR}>
<ThemeProvider themePreference="no-preference">
<Popover isDevWindow={props.isDevWindow} />
</ThemeProvider>
<PersistGate>
<ThemeProvider themePreference="no-preference">
<Popover isDevWindow={props.isDevWindow} />
</ThemeProvider>
</PersistGate>
</AutoResizerRootView>
);
}
Expand Down
48 changes: 48 additions & 0 deletions apps/menu-bar/src/modules/PersistGate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useEffect, useState } from 'react';

import { storage } from './Storage';

// TODO: Remove after v1.0.0
const migratedStorageKey = 'hasMigratedFromAsyncStorage';

interface Props {
children: React.ReactElement;
}

export function PersistGate({ children }: Props) {
const [hasMigrated, setHasMigrated] = useState(storage.getBoolean(migratedStorageKey) || false);

useEffect(() => {
async function migrateFromAsyncStorage() {
const keys = await AsyncStorage.getAllKeys();

for (const key of keys) {
const value = await AsyncStorage.getItem(key);

if (value != null) {
if (['true', 'false'].includes(value)) {
storage.set(key, value === 'true');
} else {
storage.set(key, value);
}

AsyncStorage.removeItem(key);
}
}

storage.set(migratedStorageKey, true);
setHasMigrated(true);
}

if (!hasMigrated) {
migrateFromAsyncStorage();
}
}, [hasMigrated]);

if (!hasMigrated) {
return null;
}

return children;
}
30 changes: 19 additions & 11 deletions apps/menu-bar/src/modules/Storage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import { MMKV } from 'react-native-mmkv';

const userPreferencesStorageKey = 'user-preferences';
Expand All @@ -22,14 +21,14 @@ export const defaultUserPreferences: UserPreferences = {
showAndroidEmulators: true,
};

export const getUserPreferences = async () => {
const stringValue = await AsyncStorage.getItem(userPreferencesStorageKey);
export const getUserPreferences = () => {
const stringValue = storage.getString(userPreferencesStorageKey);
const value = (stringValue ? JSON.parse(stringValue) : defaultUserPreferences) as UserPreferences;
return value;
};

export const saveUserPreferences = async (preferences: UserPreferences) => {
await AsyncStorage.setItem(userPreferencesStorageKey, JSON.stringify(preferences));
export const saveUserPreferences = (preferences: UserPreferences) => {
storage.set(userPreferencesStorageKey, JSON.stringify(preferences));
};

const selectedDevicesIdsStorageKey = 'selected-devices-ids';
Expand All @@ -38,16 +37,25 @@ export type SelectedDevicesIds = {
ios?: string;
};

export const getSelectedDevicesIds = async () => {
const value = await AsyncStorage.getItem(selectedDevicesIdsStorageKey);
const selectedDevicesIds = JSON.parse(value ?? '{}') as SelectedDevicesIds;
export const getSelectedDevicesIds = () => {
const value = storage.getString(selectedDevicesIdsStorageKey);
const selectedDevicesIds = (
value
? JSON.parse(value)
: {
android: undefined,
ios: undefined,
}
) as SelectedDevicesIds;
return selectedDevicesIds;
};

export const saveSelectedDevicesIds = async (devicesIds: SelectedDevicesIds) => {
await AsyncStorage.setItem(selectedDevicesIdsStorageKey, JSON.stringify(devicesIds));
export const saveSelectedDevicesIds = (devicesIds: SelectedDevicesIds) => {
storage.set(selectedDevicesIdsStorageKey, JSON.stringify(devicesIds));
};

export const resetStorage = async () => AsyncStorage.clear();
export const resetStorage = () => {
storage.clearAll();
};

export const storage = new MMKV();
44 changes: 15 additions & 29 deletions apps/menu-bar/src/popover/Core.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { InternalError } from 'common-types';
import { MultipleAppsInTarballErrorDetails } from 'common-types/build/InternalError';
import { Device } from 'common-types/build/devices';
import React, { memo, useCallback, useEffect, useState } from 'react';
import React, { memo, useCallback, useState } from 'react';
import { Alert, SectionList } from 'react-native';

import DeviceListSectionHeader from './DeviceListSectionHeader';
Expand Down Expand Up @@ -32,7 +32,6 @@ import {
getSelectedDevicesIds,
saveSelectedDevicesIds,
UserPreferences,
defaultUserPreferences,
getUserPreferences,
saveUserPreferences,
} from '../modules/Storage';
Expand All @@ -48,24 +47,17 @@ enum Status {
}

const BUILDS_SECTION_HEIGHT = 88;
const DEVICES_TO_SHOW_SECTION_HEIGHT = 22;

type Props = {
isDevWindow: boolean;
};

function Core(props: Props) {
const [selectedDevicesIds, setSelectedDevicesIds] = useState<SelectedDevicesIds>({
android: undefined,
ios: undefined,
});
const [userPreferences, setUserPreferences] = useState<UserPreferences>(defaultUserPreferences);

useEffect(() => {
getUserPreferences().then((value) => {
saveUserPreferences(value);
setUserPreferences(value);
});
}, []);
const [selectedDevicesIds, setSelectedDevicesIds] = useState<SelectedDevicesIds>(
getSelectedDevicesIds()
);
const [userPreferences, setUserPreferences] = useState<UserPreferences>(getUserPreferences());

const { apps } = useGetPinnedApps();
const showProjectsSection = Boolean(apps?.length);
Expand All @@ -84,6 +76,7 @@ function Core(props: Props) {
(displayDimensions.height || 0) -
FOOTER_HEIGHT -
BUILDS_SECTION_HEIGHT -
DEVICES_TO_SHOW_SECTION_HEIGHT -
(showProjectsSection ? PROJECTS_SECTION_HEIGHT : 0) -
30;
const heightOfAllDevices =
Expand All @@ -93,10 +86,6 @@ function Core(props: Props) {
? heightOfAllDevices
: estimatedAvailableSizeForDevices;

useEffect(() => {
getSelectedDevicesIds().then(setSelectedDevicesIds);
}, []);

const getFirstAvailableDevice = useCallback(
(_?: boolean) => {
return (
Expand Down Expand Up @@ -131,29 +120,26 @@ function Core(props: Props) {
...userPreferences,
showIosSimulators: value,
};
saveUserPreferences(newPreferences).then(() => {
setUserPreferences(newPreferences);
});
saveUserPreferences(newPreferences);
setUserPreferences(newPreferences);
};

const onPressShowTvosSimulators = async (value: boolean) => {
const newPreferences = {
...userPreferences,
showTvosSimulators: value,
};
saveUserPreferences(newPreferences).then(() => {
setUserPreferences(newPreferences);
});
saveUserPreferences(newPreferences);
setUserPreferences(newPreferences);
};

const onPressShowAndroidEmulators = async (value: boolean) => {
const newPreferences = {
...userPreferences,
showAndroidEmulators: value,
};
saveUserPreferences(newPreferences).then(() => {
setUserPreferences(newPreferences);
});
saveUserPreferences(newPreferences);
setUserPreferences(newPreferences);
};

// @TODO: create another hook
Expand Down Expand Up @@ -320,7 +306,8 @@ function Core(props: Props) {
</View>
) : null}
</View>
<View px="medium" style={{ flexDirection: 'row' }}>
{apps?.length ? <ProjectsSection apps={apps} /> : null}
<View px="medium" style={{ flexDirection: 'row', height: DEVICES_TO_SHOW_SECTION_HEIGHT }}>
<Text size="small" weight="normal">
Devices to show:
</Text>
Expand All @@ -342,7 +329,6 @@ function Core(props: Props) {
label="Android"
/>
</View>
{apps?.length ? <ProjectsSection apps={apps} /> : null}
<View shrink="1" pt="tiny">
<SectionList
sections={sections}
Expand Down
12 changes: 11 additions & 1 deletion apps/menu-bar/src/popover/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import React from 'react';
import React, { useEffect } from 'react';

import Core from './Core';
import { ErrorBoundary, FallbackProps } from './ErrorBoundary';
import Footer from './Footer';
import { Text, View } from '../components';
import { useSafeDisplayDimensions } from '../hooks/useSafeDisplayDimensions';
import { storage } from '../modules/Storage';
import { WindowsNavigator } from '../windows';
import { hasSeenOnboardingStorageKey } from '../windows/Onboarding';

type Props = {
isDevWindow: boolean;
Expand All @@ -13,6 +16,13 @@ type Props = {
function Popover(props: Props) {
const { height } = useSafeDisplayDimensions();

useEffect(() => {
const hasSeenOnboarding = storage.getBoolean(hasSeenOnboardingStorageKey);
if (!hasSeenOnboarding) {
WindowsNavigator.open('Onboarding');
}
}, []);

return (
<View
style={{
Expand Down
7 changes: 3 additions & 4 deletions apps/menu-bar/src/windows/Onboarding.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useEffect, useState } from 'react';
import { Image, ScrollView, StyleSheet } from 'react-native';

Expand All @@ -12,6 +11,7 @@ import { Text, View } from '../components';
import Button from '../components/Button';
import CommandCheckItem from '../components/CommandCheckItem';
import MenuBarModule from '../modules/MenuBarModule';
import { storage } from '../modules/Storage';
import { useExpoTheme } from '../utils/useExpoTheme';

export const hasSeenOnboardingStorageKey = 'has-seen-onboarding';
Expand All @@ -28,9 +28,8 @@ const Onboarding = () => {
const [platformToolsCheck, setPlatformToolsCheck] = useState<PlatformToolsCheck>({});

const closeOnboarding = () => {
AsyncStorage.setItem(hasSeenOnboardingStorageKey, 'true').then(() => {
WindowsNavigator.close('Onboarding');
});
storage.set(hasSeenOnboardingStorageKey, true);
WindowsNavigator.close('Onboarding');
};

useEffect(() => {
Expand Down
Loading

0 comments on commit b19df9d

Please sign in to comment.