From 44e0809219ad32aa79aedf58a139560dad1943a2 Mon Sep 17 00:00:00 2001 From: lukestahl Date: Wed, 20 Dec 2023 14:58:35 +0100 Subject: [PATCH] add import/export data --- apps/expo/app/settings/index.tsx | 69 ++++++++++++++++++++++---------- apps/expo/data/app.statistics.ts | 5 +++ apps/expo/data/apps.ts | 5 +++ apps/expo/data/settings.store.ts | 44 ++++++++++++++++++++ apps/expo/ios/Podfile.lock | 12 ++++++ apps/expo/package.json | 2 + pnpm-lock.yaml | 37 +++++++++++++++++ 7 files changed, 152 insertions(+), 22 deletions(-) diff --git a/apps/expo/app/settings/index.tsx b/apps/expo/app/settings/index.tsx index cb4ff0a..38331d1 100644 --- a/apps/expo/app/settings/index.tsx +++ b/apps/expo/app/settings/index.tsx @@ -1,4 +1,6 @@ +import { Share } from "react-native"; import { router } from "expo-router"; +import * as Sharing from "expo-sharing"; import { Box, ChevronRight, @@ -12,7 +14,7 @@ import { Info, Mail, Repeat, - Share, + Share as ShareIcon, ShieldCheck, SunMoon, Trash, @@ -119,7 +121,12 @@ const Settings = () => ( } - iconAfter={Share} + iconAfter={ShareIcon} + onPress={() => { + void Share.share({ + url: "https://apps.apple.com//app//id", // TODO: Add app store link + }); + }} > {"Share Digital Break with friends"} @@ -137,6 +144,14 @@ const Settings = () => ( } iconAfter={Import} + onPress={async () => { + await SettingsStore.importData().then(() => { + void OverviewStore.init(); + if (router.canGoBack()) { + router.back(); + } + }); + }} > {"Import Data"} @@ -151,30 +166,40 @@ const Settings = () => ( } iconAfter={Upload} - > - {"Export Data"} - - - - - - - } - onPress={() => { - void SettingsStore.generateRandomTestData().then(() => { - router.back(); - void OverviewStore.init(); - }); + onPress={async () => { + const fileUri = await SettingsStore.generateExportFile(); + if (fileUri) { + void Sharing.shareAsync(fileUri); + } }} - iconAfter={Dices} > - {"Generate Random Data"} + {"Export Data"} + {process.env.NODE_ENV === "development" && ( + + + + + } + onPress={() => { + void SettingsStore.generateRandomTestData().then(() => { + void OverviewStore.init(); + if (router.canGoBack()) { + router.back(); + } + }); + }} + iconAfter={Dices} + > + {"Generate Random Data"} + + + )}
{"Legal Information"}
diff --git a/apps/expo/data/app.statistics.ts b/apps/expo/data/app.statistics.ts index 33788d4..51b5a99 100644 --- a/apps/expo/data/app.statistics.ts +++ b/apps/expo/data/app.statistics.ts @@ -68,4 +68,9 @@ export class AppStatisticsStore { await this.storage.deleteAll(); await this.init(); } + + public async importEvents(events: Event[]) { + await this.storage.batchUpdate(events); + await this.init(); + } } diff --git a/apps/expo/data/apps.ts b/apps/expo/data/apps.ts index 11d415e..2cdccc7 100644 --- a/apps/expo/data/apps.ts +++ b/apps/expo/data/apps.ts @@ -139,6 +139,11 @@ export class AppsStore { await this.init(); }; + public importApps = async (apps: App[]): Promise => { + await this.storage.batchUpdate(apps); + await this.init(); + }; + public get apps() { return this._apps; } diff --git a/apps/expo/data/settings.store.ts b/apps/expo/data/settings.store.ts index 53ae299..e9b0fc3 100644 --- a/apps/expo/data/settings.store.ts +++ b/apps/expo/data/settings.store.ts @@ -1,3 +1,5 @@ +import * as DocumentPicker from "expo-document-picker"; +import * as FileSystem from "expo-file-system"; import { makeAutoObservable } from "mobx"; import { AppStatisticsStore } from "./app.statistics"; @@ -12,6 +14,48 @@ class SettingsStoreSingleton { makeAutoObservable(this); } + public async importData(): Promise { + try { + const result = await DocumentPicker.getDocumentAsync({ type: "application/json" }); + if (!result.canceled && result.assets.length > 0) { + const [file] = result.assets; + if (!file) { + throw new Error("No file uri"); + } + const fileContents = await FileSystem.readAsStringAsync(file.uri); + const data = JSON.parse(fileContents) as { + apps: AppsStore["apps"]; + appStatistics: AppStatisticsStore["events"]; + }; + await Promise.all([ + this.appsStore.importApps(data.apps), + this.appStatisticsStore.importEvents(data.appStatistics), + ]); + } else { + throw new Error("No file selected"); + } + } catch (err) { + console.error("Error reading JSON file", err); + } + } + + public async generateExportFile(): Promise { + const fileName = "digital-break-app.export.json"; + const fileUri = FileSystem.documentDirectory + fileName; + await Promise.all([this.appsStore.init(), this.appStatisticsStore.init()]); + const jsonData = JSON.stringify({ + apps: this.appsStore.apps, + appStatistics: this.appStatisticsStore.events, + }); + try { + await FileSystem.writeAsStringAsync(fileUri, jsonData); + console.log("File saved successfully!"); + return fileUri; + } catch (error) { + console.error("Error creating JSON file", error); + } + } + public async generateRandomTestData() { try { await this.appsStore.getOrCreateApp({ diff --git a/apps/expo/ios/Podfile.lock b/apps/expo/ios/Podfile.lock index d20757c..d6572c2 100644 --- a/apps/expo/ios/Podfile.lock +++ b/apps/expo/ios/Podfile.lock @@ -79,6 +79,8 @@ PODS: - ExpoModulesCore - ExpoCrypto (12.6.0): - ExpoModulesCore + - ExpoDocumentPicker (11.5.4): + - ExpoModulesCore - ExpoDynamicAppIcon (1.2.0): - ExpoModulesCore - ExpoExitApp (0.1.0): @@ -97,6 +99,8 @@ PODS: - React-NativeModulesApple - React-RCTAppDelegate - ReactCommon/turbomodule/core + - ExpoSharing (11.5.0): + - ExpoModulesCore - ExpoSystemUI (2.6.0): - ExpoModulesCore - ExpoWebBrowser (12.5.0): @@ -573,6 +577,7 @@ DEPENDENCIES: - expo-dev-menu-interface (from `../node_modules/expo-dev-menu-interface/ios`) - ExpoBlur (from `../../../node_modules/expo-blur/ios`) - ExpoCrypto (from `../../../node_modules/expo-crypto/ios`) + - ExpoDocumentPicker (from `../../../node_modules/expo-document-picker/ios`) - ExpoDynamicAppIcon (from `../../../node_modules/expo-dynamic-app-icon/ios`) - ExpoExitApp (from `../../../packages/expo-exit-app/ios`) - ExpoHead (from `../../../node_modules/expo-head/ios`) @@ -580,6 +585,7 @@ DEPENDENCIES: - ExpoLinearGradient (from `../../../node_modules/expo-linear-gradient/ios`) - ExpoLocalization (from `../../../node_modules/expo-localization/ios`) - ExpoModulesCore (from `../../../node_modules/expo-modules-core`) + - ExpoSharing (from `../../../node_modules/expo-sharing/ios`) - ExpoSystemUI (from `../../../node_modules/expo-system-ui/ios`) - ExpoWebBrowser (from `../../../node_modules/expo-web-browser/ios`) - EXSplashScreen (from `../node_modules/expo-splash-screen/ios`) @@ -671,6 +677,8 @@ EXTERNAL SOURCES: :path: "../../../node_modules/expo-blur/ios" ExpoCrypto: :path: "../../../node_modules/expo-crypto/ios" + ExpoDocumentPicker: + :path: "../../../node_modules/expo-document-picker/ios" ExpoDynamicAppIcon: :path: "../../../node_modules/expo-dynamic-app-icon/ios" ExpoExitApp: @@ -685,6 +693,8 @@ EXTERNAL SOURCES: :path: "../../../node_modules/expo-localization/ios" ExpoModulesCore: :path: "../../../node_modules/expo-modules-core" + ExpoSharing: + :path: "../../../node_modules/expo-sharing/ios" ExpoSystemUI: :path: "../../../node_modules/expo-system-ui/ios" ExpoWebBrowser: @@ -802,6 +812,7 @@ SPEC CHECKSUMS: expo-dev-menu-interface: 25a94ce9bead4668f624732d1f981b1791bbe8e2 ExpoBlur: 9e6da7c2bd4a0c5de7e57124694d0a380d7962f7 ExpoCrypto: 42485127a5968dda6f67ac5f2b42d3c0af31d7db + ExpoDocumentPicker: 5cb7389ff935b4addefdd466a606de51a512e922 ExpoDynamicAppIcon: b0f96e53d0bf00412bbe97da0cfc15b8c94a54ce ExpoExitApp: 5ae87b4d35a0d3b28b5b26bdddcd734e0a41e7f3 ExpoHead: ac95331e2a45cb94f6aee8ba6b8cb3d0a2cc6817 @@ -809,6 +820,7 @@ SPEC CHECKSUMS: ExpoLinearGradient: 5d18293f89c063036121281175c4653d6a7c34a2 ExpoLocalization: 2d5f47577d67ce991ebdd951edf14fe1db85fa06 ExpoModulesCore: c480fd4e3c7c8e81f0a6ba3a7c56869f25fe016d + ExpoSharing: 825b2b3fc919a2656f75def0069f584bbd6e359a ExpoSystemUI: e4afa05539f42fe3be09d21a6498e5495019a5b2 ExpoWebBrowser: b6e56949734089d75f758f21cfe93fad02bd828c EXSplashScreen: 5ed09ea490155ef603d007d9f194c9e04a4b7980 diff --git a/apps/expo/package.json b/apps/expo/package.json index 50a38f1..b749c85 100644 --- a/apps/expo/package.json +++ b/apps/expo/package.json @@ -46,12 +46,14 @@ "expo-crypto": "~12.6.0", "expo-dev-client": "~2.4.12", "expo-dev-launcher": "^3.1.0", + "expo-document-picker": "~11.5.4", "expo-dynamic-app-icon": "^1.2.0", "expo-font": "~11.6.0", "expo-linear-gradient": "~12.5.0", "expo-linking": "~6.0.0", "expo-localization": "^14.5.0", "expo-router": "2.0.14", + "expo-sharing": "~11.5.0", "expo-splash-screen": "~0.22.0", "expo-status-bar": "~1.7.1", "expo-system-ui": "~2.6.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0244cd4..c150c95 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -120,6 +120,12 @@ importers: expo-dev-launcher: specifier: ^3.1.0 version: 3.1.0(expo@49.0.21) + expo-document-picker: + specifier: ~11.5.4 + version: 11.5.4(expo@49.0.21) + expo-dynamic-app-icon: + specifier: ^1.2.0 + version: 1.2.0(expo@49.0.21)(react-native@0.72.7)(react@18.2.0) expo-font: specifier: ~11.6.0 version: 11.6.0(expo@49.0.21) @@ -135,6 +141,9 @@ importers: expo-router: specifier: 2.0.14 version: 2.0.14(expo-constants@14.4.2)(expo-linking@6.0.0)(expo-modules-autolinking@1.5.1)(expo-status-bar@1.7.1)(expo@49.0.21)(metro@0.80.2)(react-dom@18.2.0)(react-native-gesture-handler@2.14.0)(react-native-reanimated@3.6.1)(react-native-safe-area-context@4.8.1)(react-native-screens@3.29.0)(react-native@0.72.7)(react@18.2.0) + expo-sharing: + specifier: ~11.5.0 + version: 11.5.0(expo@49.0.21) expo-splash-screen: specifier: ~0.22.0 version: 0.22.0(expo-modules-autolinking@1.5.1)(expo@49.0.21) @@ -8649,6 +8658,26 @@ packages: semver: 7.5.4 dev: false + /expo-document-picker@11.5.4(expo@49.0.21): + resolution: {integrity: sha512-4lpRixi33kjoQQy9lfrGs48yFRMOgBgdnqjYWVFe85WFy/WkEoFC2E8usxcFdrIigtfy9Z+HY3/je4lHEYqyLg==} + peerDependencies: + expo: '*' + dependencies: + expo: 49.0.21(@babel/core@7.23.6) + dev: false + + /expo-dynamic-app-icon@1.2.0(expo@49.0.21)(react-native@0.72.7)(react@18.2.0): + resolution: {integrity: sha512-l8qNUVl9lhADtdgyL6hf6xWFOaDayFCXdXnqDGzh1H3P58n380WkzibjVxbL/w0Nbu4FUUGVqMLAY1BETTzo9g==} + peerDependencies: + expo: '>=49' + react: ^18.2.0 + react-native: '*' + dependencies: + expo: 49.0.21(@babel/core@7.23.6) + react: 18.2.0 + react-native: 0.72.7(@babel/core@7.23.6)(@babel/preset-env@7.23.3)(react@18.2.0) + dev: false + /expo-file-system@15.4.5(expo@49.0.21): resolution: {integrity: sha512-xy61KaTaDgXhT/dllwYDHm3ch026EyO8j4eC6wSVr/yE12MMMxAC09yGwy4f7kkOs6ztGVQF5j7ldRzNLN4l0Q==} peerDependencies: @@ -8874,6 +8903,14 @@ packages: - supports-color dev: false + /expo-sharing@11.5.0(expo@49.0.21): + resolution: {integrity: sha512-uerM5YH1FKDZXfkP9ORebvlMVOPP/AfoYgYBez6a8G9fztNYHnRCA6mgK+3aQmpnb3ltmjnAZC39kH18bTNcVw==} + peerDependencies: + expo: '*' + dependencies: + expo: 49.0.21(@babel/core@7.23.6) + dev: false + /expo-splash-screen@0.20.5(expo-modules-autolinking@1.5.1)(expo@49.0.21): resolution: {integrity: sha512-nTALYdjHpeEA30rdOWSguxn72ctv8WM8ptuUgpfRgsWyn4i6rwYds/rBXisX69XO5fg+XjHAQqijGx/b28+3tg==} peerDependencies: