diff --git a/Account.js b/Account.js deleted file mode 100644 index f520a47..0000000 --- a/Account.js +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2023 The Casdoor Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import totp from "totp-generator"; - -class Account { - constructor(accountName, issuer, secretKey, onUpdate) { - this.accountName = accountName; - this.secretKey = secretKey; - this.onUpdate = onUpdate; - this.token = this.generateToken(); - this.isEditing = false; - this.issuer = issuer; - } - - calculateCountdown() { - const currentTime = Math.floor(Date.now() / 1000); - return 30 - (currentTime % 30); - } - - generateToken() { - if (this.secretKey !== null && this.secretKey !== undefined && this.secretKey !== "") { - const token = totp(this.secretKey); - const tokenWithSpace = token.slice(0, 3) + " " + token.slice(3); - return tokenWithSpace; - } - } - - generateAndSetToken() { - this.token = this.generateToken(); - this.onUpdate(); - } - - setAccountName(accountName) { - this.accountName = accountName; - this.setEditingStatus(false); - } - - setEditingStatus(status) { - this.isEditing = status; - this.onUpdate(); - } - - getEditStatus() { - return this.isEditing; - } - - deleteAccount() { - this.onUpdate(); - } -} - -export default Account; diff --git a/App.js b/App.js index 8ef8ee7..9061e7f 100644 --- a/App.js +++ b/App.js @@ -15,28 +15,24 @@ import * as React from "react"; import {PaperProvider} from "react-native-paper"; import {NavigationContainer} from "@react-navigation/native"; +import {BulletList} from "react-content-loader/native"; +import {SQLiteProvider} from "expo-sqlite"; import Header from "./Header"; import NavigationBar from "./NavigationBar"; -import {UserProvider} from "./UserContext"; -import {CasdoorServerProvider} from "./CasdoorServerContext"; +import {migrateDb} from "./TotpDatabase"; const App = () => { - - const [userInfo, setUserInfo] = React.useState(null); - const [token, setToken] = React.useState(null); - const [casdoorServer, setCasdoorServer] = React.useState(null); - return ( - - + }> +
- - + + ); }; export default App; diff --git a/CasdoorLoginPage.js b/CasdoorLoginPage.js index 76c96af..2c4b1b1 100644 --- a/CasdoorLoginPage.js +++ b/CasdoorLoginPage.js @@ -12,15 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -import React, {useEffect} from "react"; +import React, {useEffect, useState} from "react"; import {WebView} from "react-native-webview"; import {View} from "react-native"; import {Portal} from "react-native-paper"; import SDK from "casdoor-react-native-sdk"; -import UserContext from "./UserContext"; import PropTypes from "prop-types"; import EnterCasdoorSdkConfig from "./EnterCasdoorSdkConfig"; -import CasdoorServerContext from "./CasdoorServerContext"; +import useStore from "./useStorage"; // import {LogBox} from "react-native"; // LogBox.ignoreAllLogs(); @@ -29,10 +28,20 @@ const CasdoorLoginPage = ({onWebviewClose}) => { CasdoorLoginPage.propTypes = { onWebviewClose: PropTypes.func.isRequired, }; - const [casdoorLoginURL, setCasdoorLoginURL] = React.useState(""); - const {setUserInfo, setToken} = React.useContext(UserContext); - const [showConfigPage, setShowConfigPage] = React.useState(true); - const {casdoorServer} = React.useContext(CasdoorServerContext); + const [casdoorLoginURL, setCasdoorLoginURL] = useState(""); + const [showConfigPage, setShowConfigPage] = useState(true); + + const { + serverUrl, + clientId, + redirectPath, + appName, + organizationName, + getCasdoorConfig, + setUserInfo, + setToken, + } = useStore(); + const handleHideConfigPage = () => { setShowConfigPage(false); }; @@ -42,14 +51,15 @@ const CasdoorLoginPage = ({onWebviewClose}) => { }; useEffect(() => { - if (casdoorServer) { - sdk = new SDK(casdoorServer); + if (serverUrl && clientId && redirectPath && appName && organizationName) { + sdk = new SDK(getCasdoorConfig()); getCasdoorSignInUrl(); } - }, [casdoorServer]); + }, [serverUrl, clientId, redirectPath, appName, organizationName]); const onNavigationStateChange = async(navState) => { - if (navState.url.startsWith(casdoorServer.redirectPath)) { + const {redirectPath} = getCasdoorConfig(); + if (navState.url.startsWith(redirectPath)) { onWebviewClose(); const token = await sdk.getAccessToken(navState.url); const userInfo = sdk.JwtDecode(token); @@ -75,7 +85,9 @@ const CasdoorLoginPage = ({onWebviewClose}) => { ); }; + export const CasdoorLogout = () => { - sdk.clearState(); + if (sdk) {sdk.clearState();} }; + export default CasdoorLoginPage; diff --git a/CasdoorServerContext.js b/CasdoorServerContext.js deleted file mode 100644 index fe3d569..0000000 --- a/CasdoorServerContext.js +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2023 The Casdoor Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import React from "react"; - -const CasdoorServerContext = React.createContext(); -export const CasdoorServerProvider = CasdoorServerContext.Provider; -export const CasdoorServerConsumer = CasdoorServerContext.Consumer; -export default CasdoorServerContext; diff --git a/EditAccountDetails.js b/EditAccountDetails.js index 30bcd51..a74cc12 100644 --- a/EditAccountDetails.js +++ b/EditAccountDetails.js @@ -29,6 +29,7 @@ export default function EnterAccountDetails({onClose, onEdit, placeholder}) { const handleConfirm = () => { onEdit(accountName); }; + return ( Enter new account name diff --git a/EnterCasdoorSdkConfig.js b/EnterCasdoorSdkConfig.js index aca4460..2de356b 100644 --- a/EnterCasdoorSdkConfig.js +++ b/EnterCasdoorSdkConfig.js @@ -12,52 +12,46 @@ // See the License for the specific language governing permissions and // limitations under the License. -import React, {useState} from "react"; +import React from "react"; import {Alert, Text, View} from "react-native"; import {Button, IconButton, Portal, TextInput} from "react-native-paper"; import DefaultCasdoorSdkConfig from "./DefaultCasdoorSdkConfig"; -import CasdoorServerContext from "./CasdoorServerContext"; import PropTypes from "prop-types"; +import useStore from "./useStorage"; const EnterCasdoorSdkConfig = ({onClose, onWebviewClose}) => { EnterCasdoorSdkConfig.propTypes = { onClose: PropTypes.func.isRequired, }; - const {setCasdoorServer} = React.useContext(CasdoorServerContext); - const [CasdoorSdkConfig, setCasdoorSdkConfig] = useState({ - serverUrl: "", - clientId: "", - appName: "", - organizationName: "", - redirectPath: "http://casdoor-app", - signinPath: "/api/signin", - }); - - const handleInputChange = (key, value) => { - setCasdoorSdkConfig({...CasdoorSdkConfig, [key]: value}); - }; + const { + serverUrl, + clientId, + redirectPath, + appName, + organizationName, + setServerUrl, + setClientId, + setAppName, + setOrganizationName, + setCasdoorConfig, + } = useStore(); const closeConfigPage = () => { onClose(); onWebviewClose(); }; + const handleSave = () => { - if ( - !CasdoorSdkConfig.serverUrl || - !CasdoorSdkConfig.clientId || - !CasdoorSdkConfig.appName || - !CasdoorSdkConfig.organizationName || - !CasdoorSdkConfig.redirectPath - ) { + if (!serverUrl || !clientId || !appName || !organizationName || !redirectPath) { Alert.alert("Please fill in all the fields!"); return; } - setCasdoorServer(CasdoorSdkConfig); onClose(); }; + const handleUseDefault = () => { - setCasdoorServer(DefaultCasdoorSdkConfig); + setCasdoorConfig(DefaultCasdoorSdkConfig); onClose(); }; @@ -68,8 +62,8 @@ const EnterCasdoorSdkConfig = ({onClose, onWebviewClose}) => { Casdoor server handleInputChange("serverUrl", text)} + value={serverUrl} + onChangeText={setServerUrl} autoCapitalize="none" style={{ borderWidth: 3, @@ -85,8 +79,8 @@ const EnterCasdoorSdkConfig = ({onClose, onWebviewClose}) => { /> handleInputChange("clientId", text)} + value={clientId} + onChangeText={setClientId} autoCapitalize="none" style={{ borderWidth: 3, @@ -102,8 +96,8 @@ const EnterCasdoorSdkConfig = ({onClose, onWebviewClose}) => { /> handleInputChange("appName", text)} + value={appName} + onChangeText={setAppName} autoCapitalize="none" style={{ borderWidth: 3, @@ -119,8 +113,8 @@ const EnterCasdoorSdkConfig = ({onClose, onWebviewClose}) => { /> handleInputChange("organizationName", text)} + value={organizationName} + onChangeText={setOrganizationName} autoCapitalize="none" style={{ borderWidth: 3, @@ -173,4 +167,5 @@ const EnterCasdoorSdkConfig = ({onClose, onWebviewClose}) => { ); }; + export default EnterCasdoorSdkConfig; diff --git a/Header.js b/Header.js index f471973..e814259 100644 --- a/Header.js +++ b/Header.js @@ -13,82 +13,139 @@ // limitations under the License. import * as React from "react"; -import {Appbar, Avatar, Button, Menu, Text} from "react-native-paper"; -import UserContext from "./UserContext"; -import {StyleSheet, View} from "react-native"; +import {Dimensions, StyleSheet, View} from "react-native"; +import {Appbar, Avatar, Menu, Text, TouchableRipple} from "react-native-paper"; import CasdoorLoginPage, {CasdoorLogout} from "./CasdoorLoginPage"; +import useStore from "./useStorage"; + +const {width} = Dimensions.get("window"); const Header = () => { - const {userInfo, setUserInfo, setToken} = React.useContext(UserContext); + const {userInfo, clearAll} = useStore(); const [showLoginPage, setShowLoginPage] = React.useState(false); const [menuVisible, setMenuVisible] = React.useState(false); + const openMenu = () => setMenuVisible(true); const closeMenu = () => setMenuVisible(false); + const handleMenuLogoutClicked = () => { handleCasdoorLogout(); closeMenu(); }; - const handleCasdoorLogin = () => { - setShowLoginPage(true); - }; + const handleCasdoorLogin = () => setShowLoginPage(true); + const handleHideLoginPage = () => setShowLoginPage(false); + const handleCasdoorLogout = () => { CasdoorLogout(); - setUserInfo(null); - setToken(null); - }; - const handleHideLoginPage = () => { - setShowLoginPage(false); + clearAll(); }; return ( - - - - - - + + + - - - - {userInfo === null ? "Login" : userInfo.name} - - + + + {userInfo === null ? "Login" : userInfo.name} + {userInfo !== null && ( )} - + } - onDismiss={closeMenu} > - handleMenuLogoutClicked()} title="Logout" /> + - + {showLoginPage && } - + ); }; +const styles = StyleSheet.create({ + leftContainer: { + position: "absolute", + left: 0, + top: 0, + bottom: 0, + justifyContent: "center", + paddingLeft: width * 0.03, + }, + rightContainer: { + position: "absolute", + right: 0, + top: 0, + bottom: 0, + justifyContent: "center", + paddingRight: width * 0.03, + }, + titleContainer: { + position: "absolute", + left: 0, + right: 0, + top: 0, + bottom: 0, + justifyContent: "center", + alignItems: "center", + }, + titleText: { + fontSize: Math.max(20, width * 0.045), + fontWeight: "bold", + textAlign: "center", + }, + buttonContainer: { + borderRadius: 20, + overflow: "hidden", + }, + buttonContent: { + flexDirection: "row", + alignItems: "center", + justifyContent: "center", + paddingVertical: 8, + paddingHorizontal: 16, + }, + buttonText: { + fontSize: Math.max(14, width * 0.035), + fontWeight: "bold", + }, + menuContent: { + backgroundColor: "#FFFFFF", + borderRadius: 8, + elevation: 3, + shadowColor: "#000000", + shadowOffset: {width: 0, height: 2}, + shadowOpacity: 0.2, + shadowRadius: 3, + }, + avatar: { + backgroundColor: "transparent", + }, +}); + export default Header; diff --git a/HomePage.js b/HomePage.js index 380104f..d07e3bb 100644 --- a/HomePage.js +++ b/HomePage.js @@ -12,21 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. -import React, {useContext, useEffect, useRef, useState} from "react"; -import {Dimensions, FlatList, RefreshControl, Text, TouchableOpacity, View} from "react-native"; -import {Divider, IconButton, List, Modal, Portal} from "react-native-paper"; +import React, {useEffect, useRef, useState} from "react"; +import {Dimensions, RefreshControl, TouchableOpacity, View} from "react-native"; +import {Divider, IconButton, List, Modal, Portal, Text} from "react-native-paper"; import {GestureHandlerRootView, Swipeable} from "react-native-gesture-handler"; import {CountdownCircleTimer} from "react-native-countdown-circle-timer"; +import {useNetInfo} from "@react-native-community/netinfo"; +import {FlashList} from "@shopify/flash-list"; +import * as SQLite from "expo-sqlite/next"; import SearchBar from "./SearchBar"; import EnterAccountDetails from "./EnterAccountDetails"; import ScanQRCode from "./ScanQRCode"; import EditAccountDetails from "./EditAccountDetails"; import AvatarWithFallback from "./AvatarWithFallback"; -import Account from "./Account"; -import UserContext from "./UserContext"; -import CasdoorServerContext from "./CasdoorServerContext"; -import useSync, {SYNC_STATUS} from "./useSync"; +import * as TotpDatabase from "./TotpDatabase"; +import useStore from "./useStorage"; +import useSyncStore from "./useSyncStore"; const {width, height} = Dimensions.get("window"); const REFRESH_INTERVAL = 10000; @@ -37,56 +39,83 @@ export default function HomePage() { const [isPlusButton, setIsPlusButton] = useState(true); const [showOptions, setShowOptions] = useState(false); const [showEnterAccountModal, setShowEnterAccountModal] = useState(false); - const [accountList, setAccountList] = useState([]); const [searchQuery, setSearchQuery] = useState(""); - const [filteredData, setFilteredData] = useState(accountList); + const [accounts, setAccounts] = useState([]); + const [filteredData, setFilteredData] = useState(accounts); const [showScanner, setShowScanner] = useState(false); const [showEditAccountModal, setShowEditAccountModal] = useState(false); + const [editingAccount, setEditingAccount] = useState(null); const [placeholder, setPlaceholder] = useState(""); const [refreshing, setRefreshing] = useState(false); + const {isConnected} = useNetInfo(); + const [canSync, setCanSync] = useState(false); const swipeableRef = useRef(null); - const isSyncing = useRef(false); + const {userInfo, serverUrl, token} = useStore(); + const {startSync} = useSyncStore(); + const db = SQLite.useSQLiteContext(); - const {userInfo, token} = useContext(UserContext); - const {casdoorServer} = useContext(CasdoorServerContext); - const {syncAccounts, syncSignal, resetSyncSignal, addToSyncData} = useSync(userInfo, token, casdoorServer); - - const handleSync = async() => { - if (isSyncing.current) {return;} - isSyncing.current = true; - try { - const syncedAccounts = await syncAccounts(); - if (syncedAccounts.success && syncedAccounts.accountList) { - accountList.forEach(account => account.deleteAccount()); - const newAccountList = syncedAccounts.accountList.map(account => new Account( - account.accountName, - account.issuer, - account.secretKey, - onUpdate - )); - setAccountList(newAccountList); - } - } finally { - isSyncing.current = false; - setRefreshing(false); - resetSyncSignal(); + useEffect(() => { + if (db) { + const subscription = SQLite.addDatabaseChangeListener((event) => {loadAccounts();}); + return () => {if (subscription) {subscription.remove();}}; } - }; + }, [db]); useEffect(() => { - if ((syncSignal || refreshing) && !isSyncing.current) { - handleSync(); - } - }, [syncSignal, refreshing]); + setCanSync(Boolean(isConnected && userInfo && serverUrl)); + }, [isConnected, userInfo, serverUrl]); + + useEffect(() => { + setFilteredData(accounts); + }, [accounts]); + + useEffect(() => { + loadAccounts(); + }, []); useEffect(() => { - const timer = setInterval(handleSync, REFRESH_INTERVAL); + const timer = setInterval(() => { + if (canSync) {startSync(db, userInfo, serverUrl, token);} + }, REFRESH_INTERVAL); return () => clearInterval(timer); - }, [handleSync]); + }, [startSync]); + + const loadAccounts = async() => { + const loadedAccounts = await TotpDatabase.getAllAccounts(db); + setAccounts(loadedAccounts); + setFilteredData(loadedAccounts); + }; - const onRefresh = () => { + const onRefresh = async() => { setRefreshing(true); + if (canSync) {await startSync(db, userInfo, serverUrl, token);} + setRefreshing(false); + }; + + const handleAddAccount = async(accountData) => { + await TotpDatabase.insertAccount(db, accountData); + closeEnterAccountModal(); + }; + + const handleDeleteAccount = async(id) => { + await TotpDatabase.deleteAccount(db, id); + }; + + const handleEditAccount = (account) => { + closeSwipeableMenu(); + setEditingAccount(account); + setPlaceholder(account.accountName); + setShowEditAccountModal(true); + }; + + const onAccountEdit = async(newAccountName) => { + if (editingAccount) { + await TotpDatabase.updateAccountName(db, editingAccount.id, newAccountName); + setPlaceholder(""); + setEditingAccount(null); + closeEditAccountModal(); + } }; const closeEditAccountModal = () => setShowEditAccountModal(false); @@ -117,46 +146,6 @@ export default function HomePage() { const closeEnterAccountModal = () => setShowEnterAccountModal(false); - const onUpdate = () => setAccountList(prev => [...prev]); - - const handleAddAccount = (accountData) => { - const newAccount = new Account(accountData.accountName, accountData.issuer, accountData.secretKey, onUpdate); - addToSyncData(newAccount, SYNC_STATUS.ADD); - newAccount.token = newAccount.generateToken(); - - setAccountList(prev => [...prev, newAccount]); - closeEnterAccountModal(); - }; - - const handleDeleteAccount = (accountName) => { - const accountToDelete = accountList.find(account => account.accountName === accountName); - if (accountToDelete) { - accountToDelete.deleteAccount(); - addToSyncData(accountToDelete, SYNC_STATUS.DELETE); - } - setAccountList(prevList => prevList.filter(account => account.accountName !== accountName)); - }; - - const handleEditAccount = (accountName) => { - closeSwipeableMenu(); - const accountToEdit = accountList.find(account => account.accountName === accountName); - if (accountToEdit) { - setPlaceholder(accountToEdit.accountName); - setShowEditAccountModal(true); - accountToEdit.setEditingStatus(true); - } - }; - - const onAccountEdit = (newAccountName) => { - const accountToEdit = accountList.find(account => account.getEditStatus() === true); - if (accountToEdit) { - addToSyncData(accountToEdit, SYNC_STATUS.EDIT, newAccountName); - accountToEdit.setAccountName(newAccountName); - } - setPlaceholder(""); - closeEditAccountModal(); - }; - const closeSwipeableMenu = () => { if (swipeableRef.current) { swipeableRef.current.close(); @@ -166,17 +155,18 @@ export default function HomePage() { const handleSearch = (query) => { setSearchQuery(query); setFilteredData(query.trim() !== "" - ? accountList.filter(item => item.accountName.toLowerCase().includes(query.toLowerCase())) - : accountList + ? accounts.filter(item => item.accountName.toLowerCase().includes(query.toLowerCase())) + : accounts ); }; return ( - index.toString()} + `${item.id}`} + estimatedItemSize={10} refreshControl={ } @@ -184,17 +174,17 @@ export default function HomePage() { ( + renderRightActions={() => ( handleEditAccount(item)} > Edit handleDeleteAccount(item.id)} > Delete @@ -202,36 +192,49 @@ export default function HomePage() { )} > - {item.accountName} - {item.token} + + {item.accountName} + {item.token} } - left={(props) => ( + left={() => ( )} - right={(props) => ( - {item.generateAndSetToken(); return {shouldRepeat: true};}} - strokeWidth={5} - > - {({remainingTime}) => ( - {remainingTime}s - )} - + right={() => ( + + { + TotpDatabase.updateToken(db, item.id); + return {shouldRepeat: true, delay: 0}; + }} + strokeWidth={5} + > + {({remainingTime}) => ( + {remainingTime}s + )} + + )} /> diff --git a/SearchBar.js b/SearchBar.js index cd474df..1561d5b 100644 --- a/SearchBar.js +++ b/SearchBar.js @@ -22,6 +22,7 @@ const SearchBar = ({onSearch}) => { setSearchQuery(query); onSearch(query); }; + return ( { const [showLoginPage, setShowLoginPage] = React.useState(false); - const {userInfo, setUserInfo, setToken} = React.useContext(UserContext); - const handleCasdoorLogin = () => { - setShowLoginPage(true); - }; + const {userInfo, clearAll} = useStore(); + + const handleCasdoorLogin = () => setShowLoginPage(true); + const handleHideLoginPage = () => setShowLoginPage(false); + const handleCasdoorLogout = () => { CasdoorLogout(); - setUserInfo(null); - setToken(null); - }; - - const handleHideLoginPage = () => { - setShowLoginPage(false); + clearAll(); }; return ( diff --git a/TotpDatabase.js b/TotpDatabase.js new file mode 100644 index 0000000..0c7b5bb --- /dev/null +++ b/TotpDatabase.js @@ -0,0 +1,298 @@ +// Copyright 2024 The Casdoor Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import totp from "totp-generator"; +import * as api from "./api"; + +export async function migrateDb(db) { + const DATABASE_VERSION = 1; + const result = await db.getFirstAsync("PRAGMA user_version"); + let currentVersion = result?.user_version ?? 0; + if (currentVersion === DATABASE_VERSION) { + return; + } + + if (currentVersion === 0) { + await db.execAsync(` +PRAGMA journal_mode = 'wal'; +CREATE TABLE accounts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + issuer TEXT, + account_name TEXT NOT NULL, + old_account_name TEXT DEFAULT NULL, + secret TEXT NOT NULL, + token TEXT, + is_deleted INTEGER DEFAULT 0, + last_change_time INTEGER DEFAULT (strftime('%s', 'now')), + last_sync_time INTEGER DEFAULT NULL +); +`); + await db.execAsync(`PRAGMA user_version = ${DATABASE_VERSION}`); + currentVersion = 1; + } + + await db.execAsync(`PRAGMA user_version = ${DATABASE_VERSION}`); +} + +const generateToken = (secretKey) => { + if (secretKey !== null && secretKey !== undefined && secretKey !== "") { + try { + const token = totp(secretKey); + const tokenWithSpace = token.slice(0, 3) + " " + token.slice(3); + return tokenWithSpace; + } catch (error) { + return "Secret Invalid"; + } + } else { + return "Secret Empty"; + } +}; + +export async function insertAccount(db, account) { + const token = generateToken(account.secretKey); + const currentTime = Math.floor(Date.now() / 1000); + return await db.runAsync( + "INSERT INTO accounts (issuer, account_name, secret, token, last_change_time) VALUES (?, ?, ?, ?, ?)", + account.issuer ?? "", + account.accountName, + account.secretKey, + token ?? "", + currentTime + ); +} + +export async function updateAccountName(db, id, newAccountName) { + const account = await db.getFirstAsync("SELECT * FROM accounts WHERE id = ?", id); + const currentTime = Math.floor(Date.now() / 1000); + + // Only update old_account_name if it's null or if last_sync_time is more recent than last_change_time + if (account.old_account_name === null || (account.last_sync_time && account.last_sync_time > account.last_change_time)) { + await db.runAsync(` + UPDATE accounts + SET account_name = ?, + old_account_name = ?, + last_change_time = ? + WHERE id = ? + `, newAccountName, account.account_name, currentTime, id); + } else { + await db.runAsync(` + UPDATE accounts + SET account_name = ?, + last_change_time = ? + WHERE id = ? + `, newAccountName, currentTime, id); + } +} + +export async function updateAccount(db, account, id) { + const token = generateToken(account.secretKey); + const currentTime = Math.floor(Date.now() / 1000); + const result = await db.runAsync( + "UPDATE accounts SET issuer = ?, account_name = ?, old_account_name = ?, secret = ?, token = ?, last_change_time = ? WHERE id = ?", + account.issuer, + account.accountName, + account.oldAccountName ?? null, + account.secretKey, + token ?? "", + currentTime, + id + ); + + if (result.changes === 0) { + throw new Error(`No account updated for id: ${id}`); + } + return result; +} + +export async function deleteAccount(db, id) { + const currentTime = Math.floor(Date.now() / 1000); + await db.runAsync("UPDATE accounts SET is_deleted = 1, last_change_time = ? WHERE id = ?", currentTime, id); +} + +export async function trueDeleteAccount(db, id) { + return await db.runAsync("DELETE FROM accounts WHERE id = ?", id); +} + +export function updateToken(db, id) { + const result = db.getFirstSync("SELECT secret FROM accounts WHERE id = ?", id); + if (result.secret === null) { + return; + } + const token = generateToken(result.secret); + return db.runSync("UPDATE accounts SET token = ? WHERE id = ?", token, id); +} + +export async function updateTokenForAll(db) { + const accounts = await db.getAllAsync("SELECT * FROM accounts WHERE is_deleted = 0"); + for (const account of accounts) { + const token = generateToken(account.secret); + await db.runAsync("UPDATE accounts SET token = ? WHERE id = ?", token, account.id); + } +} + +export async function getAllAccounts(db) { + const accounts = await db.getAllAsync("SELECT * FROM accounts WHERE is_deleted = 0"); + return accounts.map(account => { + const mappedAccount = { + ...account, + accountName: account.account_name, + secretKey: account.secret, + }; + return mappedAccount; + }); +} + +async function getLocalAccounts(db) { + const accounts = await db.getAllAsync("SELECT * FROM accounts"); + return accounts.map(account => ({ + id: account.id, + issuer: account.issuer, + accountName: account.account_name, + oldAccountName: account.old_account_name, + secretKey: account.secret, + isDeleted: account.is_deleted === 1, + lastChangeTime: account.last_change_time, + lastSyncTime: account.last_sync_time, + })); +} + +async function updateSyncTimeForAll(db) { + const currentTime = Math.floor(Date.now() / 1000); + await db.runAsync("UPDATE accounts SET last_sync_time = ?", currentTime); +} + +export function calculateCountdown() { + const now = Math.floor(Date.now() / 1000); + return 30 - (now % 30); +} + +async function updateLocalDatabase(db, mergedAccounts) { + for (const account of mergedAccounts) { + if (account.id) { + if (account.isDeleted) { + await db.runAsync("DELETE FROM accounts WHERE id = ?", account.id); + } else { + await updateAccount(db, account, account.id); + } + } else { + await insertAccount(db, account); + } + } +} + +function getAccountKey(account) { + return `${account.issuer}:${account.accountName}`; +} + +function mergeAccounts(localAccounts, serverAccounts, serverTimestamp) { + const mergedAccounts = new Map(); + const localAccountKeys = new Map(); + + // Process local accounts + for (const local of localAccounts) { + const key = getAccountKey(local); + mergedAccounts.set(key, { + ...local, + synced: false, + }); + + // Store both current and old account keys for local accounts + localAccountKeys.set(key, local); + if (local.oldAccountName) { + const oldKey = getAccountKey({...local, accountName: local.oldAccountName}); + localAccountKeys.set(oldKey, local); + } + } + + const processedLocalKeys = new Set(); + + // Merge with server accounts + for (const server of serverAccounts) { + const serverKey = getAccountKey(server); + const localAccount = localAccountKeys.get(serverKey); + + if (!localAccount) { + // New account from server + mergedAccounts.set(serverKey, {...server, synced: true}); + } else { + const localKey = getAccountKey(localAccount); + const local = mergedAccounts.get(localKey); + + if (serverTimestamp > local.lastChangeTime) { + // Server has newer changes + mergedAccounts.set(localKey, { + ...server, + id: local.id, + oldAccountName: local.accountName !== server.accountName ? local.accountName : local.oldAccountName, + synced: true, + }); + } else if (local.accountName !== server.accountName) { + // Local name change is newer, update the server account name + mergedAccounts.set(localKey, { + ...local, + oldAccountName: server.accountName, + synced: false, + }); + } + // If local is newer or deleted, keep the local version (already in mergedAccounts) + processedLocalKeys.add(localKey); + } + } + // Handle server-side deletions + for (const [key, local] of mergedAccounts) { + if (!processedLocalKeys.has(key) && local.lastSyncTime && local.lastSyncTime < serverTimestamp) { + // This account was not found on the server and was previously synced + // Mark it as deleted + mergedAccounts.set(key, {...local, isDeleted: true, synced: true}); + } + } + + return Array.from(mergedAccounts.values()); +} + +export async function syncWithCloud(db, userInfo, serverUrl, token) { + const localAccounts = await getLocalAccounts(db); + const {updatedTime, mfaAccounts: serverAccounts} = await api.getMfaAccounts( + serverUrl, + userInfo.owner, + userInfo.name, + token + ); + + const serverTimestamp = Math.floor(new Date(updatedTime).getTime() / 1000); + + const mergedAccounts = mergeAccounts(localAccounts, serverAccounts, serverTimestamp); + await updateLocalDatabase(db, mergedAccounts); + + const accountsToSync = mergedAccounts.filter(account => !account.isDeleted).map(account => ({ + issuer: account.issuer, + accountName: account.accountName, + secretKey: account.secretKey, + })); + + const {status} = await api.updateMfaAccounts( + serverUrl, + userInfo.owner, + userInfo.name, + accountsToSync, + token + ); + + if (status !== "ok") { + throw new Error("Sync failed"); + } + + await updateSyncTimeForAll(db); + await updateTokenForAll(db); +} diff --git a/UserContext.js b/UserContext.js deleted file mode 100644 index 79faa5d..0000000 --- a/UserContext.js +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2023 The Casdoor Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import React from "react"; - -const UserContext = React.createContext(); -export const UserProvider = UserContext.Provider; -export const UserConsumer = UserContext.Consumer; -export default UserContext; diff --git a/package-lock.json b/package-lock.json index b130c7d..1d9af7c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,31 +13,35 @@ "@react-native-community/netinfo": "11.3.1", "@react-navigation/bottom-tabs": "^6.5.8", "@react-navigation/native": "^6.1.7", + "@shopify/flash-list": "1.6.4", "casdoor-react-native-sdk": "1.1.0", "eslint-plugin-import": "^2.28.1", - "expo": "~51.0.24", + "expo": "~51.0.26", "expo-camera": "~15.0.14", - "expo-dev-client": "^4.0.21", + "expo-dev-client": "~4.0.22", "expo-image": "^1.12.13", + "expo-sqlite": "~14.0.6", "expo-status-bar": "~1.12.1", "expo-system-ui": "~3.0.7", - "expo-updates": "~0.25.21", + "expo-updates": "~0.25.22", "hotp-totp": "^1.0.6", "prop-types": "^15.8.1", "react": "18.2.0", + "react-content-loader": "^7.0.2", "react-dom": "18.2.0", - "react-native": "0.74.3", + "react-native": "0.74.5", "react-native-countdown-circle-timer": "^3.2.1", "react-native-gesture-handler": "~2.16.1", "react-native-paper": "^5.10.3", "react-native-reanimated": "~3.10.1", - "react-native-root-toast": "^3.6.0", "react-native-safe-area-context": "4.10.5", - "react-native-screens": "^3.31.1", + "react-native-screens": "3.31.1", "react-native-svg": "15.2.0", + "react-native-toast-message": "^2.2.0", "react-native-web": "~0.19.6", "react-native-webview": "13.8.6", - "totp-generator": "^0.0.14" + "totp-generator": "^0.0.14", + "zustand": "^4.5.4" }, "devDependencies": { "@babel/core": "^7.24.0", @@ -2363,24 +2367,20 @@ } }, "node_modules/@expo/bunyan": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@expo/bunyan/-/bunyan-4.0.0.tgz", - "integrity": "sha512-Ydf4LidRB/EBI+YrB+cVLqIseiRfjUI/AeHBgjGMtq3GroraDu81OV7zqophRgupngoL3iS3JUMDMnxO7g39qA==", - "engines": [ - "node >=0.10.0" - ], + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@expo/bunyan/-/bunyan-4.0.1.tgz", + "integrity": "sha512-+Lla7nYSiHZirgK+U/uYzsLv/X+HaJienbD5AKX1UQZHYfWaP+9uuQluRB4GrEVWF0GZ7vEVp/jzaOT9k/SQlg==", "dependencies": { "uuid": "^8.0.0" }, - "optionalDependencies": { - "mv": "~2", - "safe-json-stringify": "~1" + "engines": { + "node": ">=0.10.0" } }, "node_modules/@expo/cli": { - "version": "0.18.26", - "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-0.18.26.tgz", - "integrity": "sha512-u9bTTXgcjaTloE9CHwxgrb8Me/Al4jiPykbVQpJydakH3GsIZfHy1zaLc7O39CoLjRz37WWi6Y5ZdgtQw9dCPQ==", + "version": "0.18.28", + "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-0.18.28.tgz", + "integrity": "sha512-fvbVPId6s6etindzP6Nzos/CS1NurMVy4JKozjebArHr63tBid5i/UY5Pp+4wTCAM20gB2SjRdwcwoL6HFC4Iw==", "dependencies": { "@babel/runtime": "^7.20.0", "@expo/code-signing-certificates": "0.0.5", @@ -2390,7 +2390,7 @@ "@expo/env": "~0.3.0", "@expo/image-utils": "^0.5.0", "@expo/json-file": "^8.3.0", - "@expo/metro-config": "~0.18.6", + "@expo/metro-config": "0.18.11", "@expo/osascript": "^2.0.31", "@expo/package-manager": "^1.5.0", "@expo/plist": "^0.1.0", @@ -2792,25 +2792,32 @@ } }, "node_modules/@expo/devcert": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@expo/devcert/-/devcert-1.1.2.tgz", - "integrity": "sha512-FyWghLu7rUaZEZSTLt/XNRukm0c9GFfwP0iFaswoDWpV6alvVg+zRAfCLdIVQEz1SVcQ3zo1hMZFDrnKGvkCuQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@expo/devcert/-/devcert-1.1.4.tgz", + "integrity": "sha512-fqBODr8c72+gBSX5Ty3SIzaY4bXainlpab78+vEYEKL3fXmsOswMLf0+KE36mUEAa36BYabX7K3EiXOXX5OPMw==", "dependencies": { "application-config-path": "^0.1.0", "command-exists": "^1.2.4", "debug": "^3.1.0", "eol": "^0.9.1", "get-port": "^3.2.0", - "glob": "^7.1.2", + "glob": "^10.4.2", "lodash": "^4.17.21", "mkdirp": "^0.5.1", "password-prompt": "^1.0.4", - "rimraf": "^2.6.2", "sudo-prompt": "^8.2.0", "tmp": "^0.0.33", "tslib": "^2.4.0" } }, + "node_modules/@expo/devcert/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/@expo/devcert/node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -2820,20 +2827,33 @@ } }, "node_modules/@expo/devcert/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@expo/devcert/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dependencies": { + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -3208,9 +3228,9 @@ } }, "node_modules/@expo/metro-config": { - "version": "0.18.10", - "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-0.18.10.tgz", - "integrity": "sha512-HTYQqKfV0JSuRp5aDvrPHezj5udXOWoXqHOjfTSnce2m13j6D0yYXTJNaKRhlgpPBrkg5DL7z1fL3zwDUpLM4w==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-0.18.11.tgz", + "integrity": "sha512-/uOq55VbSf9yMbUO1BudkUM2SsGW1c5hr9BnhIqYqcsFv0Jp5D3DtJ4rljDKaUeNLbwr6m7pqIrkSMq5NrYf4Q==", "dependencies": { "@babel/core": "^7.20.0", "@babel/generator": "^7.20.5", @@ -3585,6 +3605,18 @@ "prop-types": "^15.8.1" } }, + "node_modules/@expo/websql": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@expo/websql/-/websql-1.0.1.tgz", + "integrity": "sha512-H9/t1V7XXyKC343FJz/LwaVBfDhs6IqhDtSYWpt8LNSQDVjf5NvVJLc5wp+KCpRidZx8+0+YeHJN45HOXmqjFA==", + "dependencies": { + "argsarray": "^0.0.1", + "immediate": "^3.2.2", + "noop-fn": "^1.0.0", + "pouchdb-collections": "^1.0.1", + "tiny-queue": "^0.2.1" + } + }, "node_modules/@expo/xcpretty": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@expo/xcpretty/-/xcpretty-4.3.1.tgz", @@ -3978,12 +4010,12 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", - "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, "node_modules/@jridgewell/sourcemap-codec": { @@ -5591,28 +5623,28 @@ } }, "node_modules/@react-native/assets-registry": { - "version": "0.74.85", - "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.74.85.tgz", - "integrity": "sha512-59YmIQxfGDw4aP9S/nAM+sjSFdW8fUP6fsqczCcXgL2YVEjyER9XCaUT0J1K+PdHep8pi05KUgIKUds8P3jbmA==", + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.74.87.tgz", + "integrity": "sha512-1XmRhqQchN+pXPKEKYdpJlwESxVomJOxtEnIkbo7GAlaN2sym84fHEGDXAjLilih5GVPpcpSmFzTy8jx3LtaFg==", "engines": { "node": ">=18" } }, "node_modules/@react-native/babel-plugin-codegen": { - "version": "0.74.85", - "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.74.85.tgz", - "integrity": "sha512-48TSDclRB5OMXiImiJkLxyCfRyLsqkCgI8buugCZzvXcYslfV7gCvcyFyQldtcOmerV+CK4RAj7QS4hmB5Mr8Q==", + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.74.87.tgz", + "integrity": "sha512-+vJYpMnENFrwtgvDfUj+CtVJRJuUnzAUYT0/Pb68Sq9RfcZ5xdcCuUgyf7JO+akW2VTBoJY427wkcxU30qrWWw==", "dependencies": { - "@react-native/codegen": "0.74.85" + "@react-native/codegen": "0.74.87" }, "engines": { "node": ">=18" } }, "node_modules/@react-native/babel-preset": { - "version": "0.74.85", - "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.74.85.tgz", - "integrity": "sha512-yMHUlN8INbK5BBwiBuQMftdWkpm1IgCsoJTKcGD2OpSgZhwwm8RUSvGhdRMzB2w7bsqqBmaEMleGtW6aCR7B9w==", + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.74.87.tgz", + "integrity": "sha512-hyKpfqzN2nxZmYYJ0tQIHG99FQO0OWXp/gVggAfEUgiT+yNKas1C60LuofUsK7cd+2o9jrpqgqW4WzEDZoBlTg==", "dependencies": { "@babel/core": "^7.20.0", "@babel/plugin-proposal-async-generator-functions": "^7.0.0", @@ -5654,7 +5686,7 @@ "@babel/plugin-transform-typescript": "^7.5.0", "@babel/plugin-transform-unicode-regex": "^7.0.0", "@babel/template": "^7.0.0", - "@react-native/babel-plugin-codegen": "0.74.85", + "@react-native/babel-plugin-codegen": "0.74.87", "babel-plugin-transform-flow-enums": "^0.0.2", "react-refresh": "^0.14.0" }, @@ -5666,9 +5698,9 @@ } }, "node_modules/@react-native/codegen": { - "version": "0.74.85", - "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.74.85.tgz", - "integrity": "sha512-N7QwoS4Hq/uQmoH83Ewedy6D0M7xbQsOU3OMcQf0eY3ltQ7S2hd9/R4UTalQWRn1OUJfXR6OG12QJ4FStKgV6Q==", + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.74.87.tgz", + "integrity": "sha512-GMSYDiD+86zLKgMMgz9z0k6FxmRn+z6cimYZKkucW4soGbxWsbjUAZoZ56sJwt2FJ3XVRgXCrnOCgXoH/Bkhcg==", "dependencies": { "@babel/parser": "^7.20.0", "glob": "^7.1.1", @@ -5706,14 +5738,14 @@ } }, "node_modules/@react-native/community-cli-plugin": { - "version": "0.74.85", - "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.74.85.tgz", - "integrity": "sha512-ODzND33eA2owAY3g9jgCdqB+BjAh8qJ7dvmSotXgrgDYr3MJMpd8gvHTIPe2fg4Kab+wk8uipRhrE0i0RYMwtQ==", + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.74.87.tgz", + "integrity": "sha512-EgJG9lSr8x3X67dHQKQvU6EkO+3ksVlJHYIVv6U/AmW9dN80BEFxgYbSJ7icXS4wri7m4kHdgeq2PQ7/3vvrTQ==", "dependencies": { "@react-native-community/cli-server-api": "13.6.9", "@react-native-community/cli-tools": "13.6.9", - "@react-native/dev-middleware": "0.74.85", - "@react-native/metro-babel-transformer": "0.74.85", + "@react-native/dev-middleware": "0.74.87", + "@react-native/metro-babel-transformer": "0.74.87", "chalk": "^4.0.0", "execa": "^5.1.1", "metro": "^0.80.3", @@ -5727,6 +5759,37 @@ "node": ">=18" } }, + "node_modules/@react-native/community-cli-plugin/node_modules/@react-native/debugger-frontend": { + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.74.87.tgz", + "integrity": "sha512-MN95DJLYTv4EqJc+9JajA3AJZSBYJz2QEJ3uWlHrOky2vKrbbRVaW1ityTmaZa2OXIvNc6CZwSRSE7xCoHbXhQ==", + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-native/community-cli-plugin/node_modules/@react-native/dev-middleware": { + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.74.87.tgz", + "integrity": "sha512-7TmZ3hTHwooYgIHqc/z87BMe1ryrIqAUi+AF7vsD+EHCGxHFdMjSpf1BZ2SUPXuLnF2cTiTfV2RwhbPzx0tYIA==", + "dependencies": { + "@isaacs/ttlcache": "^1.4.1", + "@react-native/debugger-frontend": "0.74.87", + "@rnx-kit/chromium-edge-launcher": "^1.0.0", + "chrome-launcher": "^0.15.2", + "connect": "^3.6.5", + "debug": "^2.2.0", + "node-fetch": "^2.2.0", + "nullthrows": "^1.1.1", + "open": "^7.0.3", + "selfsigned": "^2.4.1", + "serve-static": "^1.13.1", + "temp-dir": "^2.0.0", + "ws": "^6.2.2" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@react-native/community-cli-plugin/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -5772,6 +5835,14 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/@react-native/community-cli-plugin/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, "node_modules/@react-native/community-cli-plugin/node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -5832,6 +5903,11 @@ "node": ">=6" } }, + "node_modules/@react-native/community-cli-plugin/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, "node_modules/@react-native/community-cli-plugin/node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -5857,6 +5933,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@react-native/community-cli-plugin/node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@react-native/community-cli-plugin/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -5868,6 +5959,14 @@ "node": ">=8" } }, + "node_modules/@react-native/community-cli-plugin/node_modules/ws": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", + "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, "node_modules/@react-native/debugger-frontend": { "version": "0.74.85", "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.74.85.tgz", @@ -5936,28 +6035,28 @@ } }, "node_modules/@react-native/gradle-plugin": { - "version": "0.74.85", - "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.74.85.tgz", - "integrity": "sha512-1VQSLukJzaVMn1MYcs8Weo1nUW8xCas2XU1KuoV7OJPk6xPnEBFJmapmEGP5mWeEy7kcTXJmddEgy1wwW0tcig==", + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.74.87.tgz", + "integrity": "sha512-T+VX0N1qP+U9V4oAtn7FTX7pfsoVkd1ocyw9swYXgJqU2fK7hC9famW7b3s3ZiufPGPr1VPJe2TVGtSopBjL6A==", "engines": { "node": ">=18" } }, "node_modules/@react-native/js-polyfills": { - "version": "0.74.85", - "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.74.85.tgz", - "integrity": "sha512-gp4Rg9le3lVZeW7Cie6qLfekvRKZuhJ3LKgi1SFB4N154z1wIclypAJXVXgWBsy8JKJfTwRI+sffC4qZDlvzrg==", + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.74.87.tgz", + "integrity": "sha512-M5Evdn76CuVEF0GsaXiGi95CBZ4IWubHqwXxV9vG9CC9kq0PSkoM2Pn7Lx7dgyp4vT7ccJ8a3IwHbe+5KJRnpw==", "engines": { "node": ">=18" } }, "node_modules/@react-native/metro-babel-transformer": { - "version": "0.74.85", - "resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.74.85.tgz", - "integrity": "sha512-JIrXqEwhTvWPtGArgMptIPGstMdXQIkwSjKVYt+7VC4a9Pw1GurIWanIJheEW6ZuCVvTc0VZkwglFz9JVjzDjA==", + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.74.87.tgz", + "integrity": "sha512-UsJCO24sNax2NSPBmV1zLEVVNkS88kcgAiYrZHtYSwSjpl4WZ656tIeedBfiySdJ94Hr3kQmBYLipV5zk0NI1A==", "dependencies": { "@babel/core": "^7.20.0", - "@react-native/babel-preset": "0.74.85", + "@react-native/babel-preset": "0.74.87", "hermes-parser": "0.19.1", "nullthrows": "^1.1.1" }, @@ -5968,16 +6067,33 @@ "@babel/core": "*" } }, - "node_modules/@react-native/normalize-color": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@react-native/normalize-color/-/normalize-color-2.1.0.tgz", - "integrity": "sha512-Z1jQI2NpdFJCVgpY+8Dq/Bt3d+YUi1928Q+/CZm/oh66fzM0RUl54vvuXlPJKybH4pdCZey1eDTPaLHkMPNgWA==" - }, "node_modules/@react-native/normalize-colors": { "version": "0.74.85", "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.85.tgz", "integrity": "sha512-pcE4i0X7y3hsAE0SpIl7t6dUc0B0NZLd1yv7ssm4FrLhWG+CGyIq4eFDXpmPU1XHmL5PPySxTAjEMiwv6tAmOw==" }, + "node_modules/@react-native/virtualized-lists": { + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.74.87.tgz", + "integrity": "sha512-lsGxoFMb0lyK/MiplNKJpD+A1EoEUumkLrCjH4Ht+ZlG8S0BfCxmskLZ6qXn3BiDSkLjfjI/qyZ3pnxNBvkXpQ==", + "dependencies": { + "invariant": "^2.2.4", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/react": "^18.2.6", + "react": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@react-navigation/bottom-tabs": { "version": "6.5.9", "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-6.5.9.tgz", @@ -6069,26 +6185,6 @@ "undici-types": "~5.26.4" } }, - "node_modules/@rnx-kit/chromium-edge-launcher/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@rnx-kit/chromium-edge-launcher/node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -6100,21 +6196,6 @@ "node": ">=10" } }, - "node_modules/@rnx-kit/chromium-edge-launcher/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@segment/loosely-validate-event": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@segment/loosely-validate-event/-/loosely-validate-event-2.0.0.tgz", @@ -6124,6 +6205,25 @@ "join-component": "^1.1.0" } }, + "node_modules/@shopify/flash-list": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/@shopify/flash-list/-/flash-list-1.6.4.tgz", + "integrity": "sha512-M2momcnY7swsvmpHIFDVbdOaFw4aQocJXA/lFP0Gpz+alQjFylqVKvszxl4atYO2SNbjxlb2L6hEP9WEcAknGQ==", + "dependencies": { + "recyclerlistview": "4.2.0", + "tslib": "2.4.0" + }, + "peerDependencies": { + "@babel/runtime": "*", + "react": "*", + "react-native": "*" + } + }, + "node_modules/@shopify/flash-list/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, "node_modules/@sideway/address": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", @@ -6769,6 +6869,11 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, + "node_modules/argsarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/argsarray/-/argsarray-0.0.1.tgz", + "integrity": "sha512-u96dg2GcAKtpTrBdDoFIM7PjcBA+6rSP0OR94MOReNRyUECL6MtQt5XXmRr4qrftYaef9+l5hcpO5te7sML1Cg==" + }, "node_modules/array-buffer-byte-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", @@ -7023,236 +7128,91 @@ } }, "node_modules/babel-plugin-react-compiler": { - "version": "0.0.0-experimental-696af53-20240625", - "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-0.0.0-experimental-696af53-20240625.tgz", - "integrity": "sha512-OUDKms8qmcm5bX0D+sJWC1YcKcd7AZ2aJ7eY6gkR+Xr7PDfkXLbqAld4Qs9B0ntjVbUMEtW/PjlQrxDtY4raHg==", + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-0.0.0.tgz", + "integrity": "sha512-Kigl0V36a/6hLVH7+CCe1CCtU3mFBqBd829V//VtuG7I/pyq+B2QZJqOefd63snQmdfCryNhO9XW1FbGPBvYDA==" + }, + "node_modules/babel-plugin-react-native-web": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.19.12.tgz", + "integrity": "sha512-eYZ4+P6jNcB37lObWIg0pUbi7+3PKoU1Oie2j0C8UF3cXyXoR74tO2NBjI/FORb2LJyItJZEAmjU5pSaJYEL1w==" + }, + "node_modules/babel-plugin-transform-flow-enums": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-enums/-/babel-plugin-transform-flow-enums-0.0.2.tgz", + "integrity": "sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==", "dependencies": { - "@babel/generator": "7.2.0", - "@babel/types": "^7.19.0", - "chalk": "4", - "invariant": "^2.2.4", - "pretty-format": "^24", - "zod": "^3.22.4", - "zod-validation-error": "^2.1.0" + "@babel/plugin-syntax-flow": "^7.12.1" } }, - "node_modules/babel-plugin-react-compiler/node_modules/@babel/generator": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.2.0.tgz", - "integrity": "sha512-BA75MVfRlFQG2EZgFYIwyT1r6xSkwfP2bdkY/kLZusEYWiJs4xCowab/alaEaT0wSvmVuXGqiefeBlP+7V1yKg==", + "node_modules/babel-preset-expo": { + "version": "11.0.13", + "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-11.0.13.tgz", + "integrity": "sha512-5oUXyxnIVyDUhG4a306PpT2c9HhKx0tG4LXjpSIA/dERzwGvU8LUxDSD6yCohhRSsPZIoR7u2mnH0PypqFqYnQ==", "dependencies": { - "@babel/types": "^7.2.0", - "jsesc": "^2.5.1", - "lodash": "^4.17.10", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" + "@babel/plugin-proposal-decorators": "^7.12.9", + "@babel/plugin-transform-export-namespace-from": "^7.22.11", + "@babel/plugin-transform-object-rest-spread": "^7.12.13", + "@babel/plugin-transform-parameters": "^7.22.15", + "@babel/preset-react": "^7.22.15", + "@babel/preset-typescript": "^7.23.0", + "@react-native/babel-preset": "0.74.87", + "babel-plugin-react-compiler": "^0.0.0-experimental-592953e-20240517", + "babel-plugin-react-native-web": "~0.19.10", + "react-refresh": "^0.14.2" } }, - "node_modules/babel-plugin-react-compiler/node_modules/@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/better-opn": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-3.0.2.tgz", + "integrity": "sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==", "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "open": "^8.0.4" }, "engines": { - "node": ">= 6" + "node": ">=12.0.0" } }, - "node_modules/babel-plugin-react-compiler/node_modules/@types/istanbul-reports": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", - "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", - "dependencies": { - "@types/istanbul-lib-coverage": "*", - "@types/istanbul-lib-report": "*" + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "engines": { + "node": ">=0.6" } }, - "node_modules/babel-plugin-react-compiler/node_modules/@types/yargs": { - "version": "13.0.12", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", - "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/babel-plugin-react-compiler/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "engines": { - "node": ">=6" - } - }, - "node_modules/babel-plugin-react-compiler/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/babel-plugin-react-compiler/node_modules/chalk/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/babel-plugin-react-compiler/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/babel-plugin-react-compiler/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/babel-plugin-react-compiler/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-react-compiler/node_modules/pretty-format": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", - "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", - "dependencies": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/babel-plugin-react-compiler/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-plugin-react-compiler/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-react-native-web": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.19.12.tgz", - "integrity": "sha512-eYZ4+P6jNcB37lObWIg0pUbi7+3PKoU1Oie2j0C8UF3cXyXoR74tO2NBjI/FORb2LJyItJZEAmjU5pSaJYEL1w==" - }, - "node_modules/babel-plugin-transform-flow-enums": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-enums/-/babel-plugin-transform-flow-enums-0.0.2.tgz", - "integrity": "sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==", - "dependencies": { - "@babel/plugin-syntax-flow": "^7.12.1" - } - }, - "node_modules/babel-preset-expo": { - "version": "11.0.12", - "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-11.0.12.tgz", - "integrity": "sha512-hUuKdzSo8+H1oXQvKvlHRMHTxl+nN6YhFGlKiIxPa0E+gYfMEp8FnnStc/2Hwmip5rgJzQs6KF63KKRUc75xAg==", - "dependencies": { - "@babel/plugin-proposal-decorators": "^7.12.9", - "@babel/plugin-transform-export-namespace-from": "^7.22.11", - "@babel/plugin-transform-object-rest-spread": "^7.12.13", - "@babel/plugin-transform-parameters": "^7.22.15", - "@babel/preset-react": "^7.22.15", - "@babel/preset-typescript": "^7.23.0", - "@react-native/babel-preset": "0.74.85", - "babel-plugin-react-compiler": "^0.0.0-experimental-592953e-20240517", - "babel-plugin-react-native-web": "~0.19.10", - "react-refresh": "^0.14.2" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/better-opn": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-3.0.2.tgz", - "integrity": "sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==", - "dependencies": { - "open": "^8.0.4" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/big-integer": { - "version": "1.6.52", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", - "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" } }, "node_modules/bl/node_modules/readable-stream": { @@ -8225,41 +8185,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/del/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/del/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -8281,16 +8206,6 @@ "node": ">= 0.8" } }, - "node_modules/deprecated-react-native-prop-types": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-2.3.0.tgz", - "integrity": "sha512-pWD0voFtNYxrVqvBMYf5gq3NA2GCpfodS1yNynTPc93AYA/KEMGeWDqqeUB6R2Z9ZofVhks2aeJXiuQqKNpesA==", - "dependencies": { - "@react-native/normalize-color": "*", - "invariant": "*", - "prop-types": "*" - } - }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -9220,17 +9135,17 @@ } }, "node_modules/expo": { - "version": "51.0.24", - "resolved": "https://registry.npmjs.org/expo/-/expo-51.0.24.tgz", - "integrity": "sha512-HoOuNIWXzS6Gxifcb0N+qRt5K6iR9YitQaWIVNB8elyupvQdyI566IMgMBiO45NgpO5es0sfFNNBasxBHLkbUw==", + "version": "51.0.26", + "resolved": "https://registry.npmjs.org/expo/-/expo-51.0.26.tgz", + "integrity": "sha512-7ThQa6CVbiVtk4GNw0E9CewSzXYOeHkcRTCGorBZVcjAu6SuxbdWEtl0RI01/VpRKV+6I+yy/BmGnCl7lFRILg==", "dependencies": { "@babel/runtime": "^7.20.0", - "@expo/cli": "0.18.26", + "@expo/cli": "0.18.28", "@expo/config": "9.0.3", "@expo/config-plugins": "8.0.8", - "@expo/metro-config": "0.18.10", + "@expo/metro-config": "0.18.11", "@expo/vector-icons": "^14.0.0", - "babel-preset-expo": "~11.0.12", + "babel-preset-expo": "~11.0.13", "expo-asset": "~10.0.10", "expo-file-system": "~17.0.1", "expo-font": "~12.0.9", @@ -9281,12 +9196,12 @@ } }, "node_modules/expo-dev-client": { - "version": "4.0.21", - "resolved": "https://registry.npmjs.org/expo-dev-client/-/expo-dev-client-4.0.21.tgz", - "integrity": "sha512-+zuVsKyp5tXTQUwDnTjtaOLf3TdmZ483Il9slg/LO15EsEFpl0zwI42DrQpzYmv617cgvywmp6iybGXjV3eZbQ==", + "version": "4.0.22", + "resolved": "https://registry.npmjs.org/expo-dev-client/-/expo-dev-client-4.0.22.tgz", + "integrity": "sha512-tT/9kRqvN3r2MLJL70FdQIgv2ycstN17/vUnxeDVGunW1Q+OdNPupZqyVk0qMeI9Cy1H4ZFBllZ6I1nlOI+mlg==", "dependencies": { - "expo-dev-launcher": "4.0.23", - "expo-dev-menu": "5.0.17", + "expo-dev-launcher": "4.0.24", + "expo-dev-menu": "5.0.18", "expo-dev-menu-interface": "1.8.3", "expo-manifests": "~0.14.0", "expo-updates-interface": "~0.16.2" @@ -9296,12 +9211,12 @@ } }, "node_modules/expo-dev-launcher": { - "version": "4.0.23", - "resolved": "https://registry.npmjs.org/expo-dev-launcher/-/expo-dev-launcher-4.0.23.tgz", - "integrity": "sha512-XG9VyFUoslBsBpJqtuOsen93u3hEw4rK7VSNqz8g+d8+AmHBv2rSuQheirPfxuSrLqGm49/ybWkHYqfs8CdlQw==", + "version": "4.0.24", + "resolved": "https://registry.npmjs.org/expo-dev-launcher/-/expo-dev-launcher-4.0.24.tgz", + "integrity": "sha512-s9j2lBK5Da/btYnwX1TRV1aXWh54Qnc+fFnH8bCzY/cImDcIxy49+GHVCyw2AQj3ZlUcPRuxj29bzi6vSN214Q==", "dependencies": { "ajv": "8.11.0", - "expo-dev-menu": "5.0.17", + "expo-dev-menu": "5.0.18", "expo-manifests": "~0.14.0", "resolve-from": "^5.0.0", "semver": "^7.6.0" @@ -9342,9 +9257,9 @@ } }, "node_modules/expo-dev-menu": { - "version": "5.0.17", - "resolved": "https://registry.npmjs.org/expo-dev-menu/-/expo-dev-menu-5.0.17.tgz", - "integrity": "sha512-BnFSd6PKDaJenRKn2C4X50dWyqvvkVz9pE/6IKuUvGIsshkk9pCoZvsFOlJjM61/ojz1KXyY9O1FUPPI7B1A7w==", + "version": "5.0.18", + "resolved": "https://registry.npmjs.org/expo-dev-menu/-/expo-dev-menu-5.0.18.tgz", + "integrity": "sha512-6edBlagCbgastLTazLUNJLGlO29/K80VVkZ+Nxt66k34NKINhnSb/fsguQr8w8HeI+xh7N6l0C9rlzWQEJn0Wg==", "dependencies": { "expo-dev-menu-interface": "1.8.3", "semver": "^7.5.4" @@ -9549,6 +9464,17 @@ "invariant": "^2.2.4" } }, + "node_modules/expo-sqlite": { + "version": "14.0.6", + "resolved": "https://registry.npmjs.org/expo-sqlite/-/expo-sqlite-14.0.6.tgz", + "integrity": "sha512-T3YNx7LT7lM4UQRgi8ml+cj0Wf3Ep09+B4CVaWtUCjdyYJIZjsHDT65hypKG+r6btTLLEd11hjlrstNQhzt5gQ==", + "dependencies": { + "@expo/websql": "^1.0.1" + }, + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-status-bar": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/expo-status-bar/-/expo-status-bar-1.12.1.tgz", @@ -9572,9 +9498,9 @@ } }, "node_modules/expo-updates": { - "version": "0.25.21", - "resolved": "https://registry.npmjs.org/expo-updates/-/expo-updates-0.25.21.tgz", - "integrity": "sha512-eNElrzLFHky2au0H8MqdAJdqh40412PHCdANdXtB/P/XIS1xVte74T9rUf053iPswnLLuQfCUpZYSfRKTcsDlg==", + "version": "0.25.22", + "resolved": "https://registry.npmjs.org/expo-updates/-/expo-updates-0.25.22.tgz", + "integrity": "sha512-ebu+wPnISzJ/H70CFAJAybS4ZZrACxRbgr9SMqOI/h84CwXGmdxzLmgxoNhy5q/Fa1u7JBmSr6x9a7psBDT4Cw==", "dependencies": { "@expo/code-signing-certificates": "0.0.5", "@expo/config": "~9.0.0-beta.0", @@ -9671,6 +9597,11 @@ "node": ">=8" } }, + "node_modules/exponential-backoff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", + "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -9907,39 +9838,6 @@ "node": ">=12.0.0" } }, - "node_modules/flat-cache/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/flat-cache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/flatted": { "version": "3.2.9", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", @@ -9972,9 +9870,9 @@ } }, "node_modules/foreground-child": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", - "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -10576,6 +10474,11 @@ "node": ">=16.x" } }, + "node_modules/immediate": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz", + "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==" + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -11536,6 +11439,42 @@ "node": ">=8" } }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/jimp-compact": { "version": "0.16.1", "resolved": "https://registry.npmjs.org/jimp-compact/-/jimp-compact-0.16.1.tgz", @@ -12340,9 +12279,9 @@ } }, "node_modules/metro": { - "version": "0.80.9", - "resolved": "https://registry.npmjs.org/metro/-/metro-0.80.9.tgz", - "integrity": "sha512-Bc57Xf3GO2Xe4UWQsBj/oW6YfLPABEu8jfDVDiNmJvoQW4CO34oDPuYKe4KlXzXhcuNsqOtSxpbjCRRVjhhREg==", + "version": "0.80.10", + "resolved": "https://registry.npmjs.org/metro/-/metro-0.80.10.tgz", + "integrity": "sha512-FDPi0X7wpafmDREXe1lgg3WzETxtXh6Kpq8+IwsG35R2tMyp2kFIqDdshdohuvDt1J/qDARcEPq7V/jElTb1kA==", "dependencies": { "@babel/code-frame": "^7.0.0", "@babel/core": "^7.20.0", @@ -12358,34 +12297,34 @@ "debug": "^2.2.0", "denodeify": "^1.2.1", "error-stack-parser": "^2.0.6", + "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", - "hermes-parser": "0.20.1", + "hermes-parser": "0.23.0", "image-size": "^1.0.2", "invariant": "^2.2.4", "jest-worker": "^29.6.3", "jsc-safe-url": "^0.2.2", "lodash.throttle": "^4.1.1", - "metro-babel-transformer": "0.80.9", - "metro-cache": "0.80.9", - "metro-cache-key": "0.80.9", - "metro-config": "0.80.9", - "metro-core": "0.80.9", - "metro-file-map": "0.80.9", - "metro-resolver": "0.80.9", - "metro-runtime": "0.80.9", - "metro-source-map": "0.80.9", - "metro-symbolicate": "0.80.9", - "metro-transform-plugins": "0.80.9", - "metro-transform-worker": "0.80.9", + "metro-babel-transformer": "0.80.10", + "metro-cache": "0.80.10", + "metro-cache-key": "0.80.10", + "metro-config": "0.80.10", + "metro-core": "0.80.10", + "metro-file-map": "0.80.10", + "metro-resolver": "0.80.10", + "metro-runtime": "0.80.10", + "metro-source-map": "0.80.10", + "metro-symbolicate": "0.80.10", + "metro-transform-plugins": "0.80.10", + "metro-transform-worker": "0.80.10", "mime-types": "^2.1.27", "node-fetch": "^2.2.0", "nullthrows": "^1.1.1", - "rimraf": "^3.0.2", "serialize-error": "^2.1.0", "source-map": "^0.5.6", "strip-ansi": "^6.0.0", "throat": "^5.0.0", - "ws": "^7.5.1", + "ws": "^7.5.10", "yargs": "^17.6.2" }, "bin": { @@ -12396,12 +12335,13 @@ } }, "node_modules/metro-babel-transformer": { - "version": "0.80.9", - "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.80.9.tgz", - "integrity": "sha512-d76BSm64KZam1nifRZlNJmtwIgAeZhZG3fi3K+EmPOlrR8rDtBxQHDSN3fSGeNB9CirdTyabTMQCkCup6BXFSQ==", + "version": "0.80.10", + "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.80.10.tgz", + "integrity": "sha512-GXHueUzgzcazfzORDxDzWS9jVVRV6u+cR6TGvHOfGdfLzJCj7/D0PretLfyq+MwN20twHxLW+BUXkoaB8sCQBg==", "dependencies": { "@babel/core": "^7.20.0", - "hermes-parser": "0.20.1", + "flow-enums-runtime": "^0.0.6", + "hermes-parser": "0.23.0", "nullthrows": "^1.1.1" }, "engines": { @@ -12409,110 +12349,82 @@ } }, "node_modules/metro-babel-transformer/node_modules/hermes-estree": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.20.1.tgz", - "integrity": "sha512-SQpZK4BzR48kuOg0v4pb3EAGNclzIlqMj3Opu/mu7bbAoFw6oig6cEt/RAi0zTFW/iW6Iz9X9ggGuZTAZ/yZHg==" + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.23.0.tgz", + "integrity": "sha512-Rkp0PNLGpORw4ktsttkVbpYJbrYKS3hAnkxu8D9nvQi6LvSbuPa+tYw/t2u3Gjc35lYd/k95YkjqyTcN4zspag==" }, "node_modules/metro-babel-transformer/node_modules/hermes-parser": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.20.1.tgz", - "integrity": "sha512-BL5P83cwCogI8D7rrDCgsFY0tdYUtmFP9XaXtl2IQjC+2Xo+4okjfXintlTxcIwl4qeGddEl28Z11kbVIw0aNA==", - "dependencies": { - "hermes-estree": "0.20.1" - } - }, - "node_modules/metro-cache": { - "version": "0.80.9", - "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.80.9.tgz", - "integrity": "sha512-ujEdSI43QwI+Dj2xuNax8LMo8UgKuXJEdxJkzGPU6iIx42nYa1byQ+aADv/iPh5sh5a//h5FopraW5voXSgm2w==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.23.0.tgz", + "integrity": "sha512-xLwM4ylfHGwrm+2qXfO1JT/fnqEDGSnpS/9hQ4VLtqTexSviu2ZpBgz07U8jVtndq67qdb/ps0qvaWDZ3fkTyg==", "dependencies": { - "metro-core": "0.80.9", - "rimraf": "^3.0.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/metro-cache-key": { - "version": "0.80.9", - "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.80.9.tgz", - "integrity": "sha512-hRcYGhEiWIdM87hU0fBlcGr+tHDEAT+7LYNCW89p5JhErFt/QaAkVx4fb5bW3YtXGv5BTV7AspWPERoIb99CXg==", - "engines": { - "node": ">=18" + "hermes-estree": "0.23.0" } }, - "node_modules/metro-cache/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "node_modules/metro-cache": { + "version": "0.80.10", + "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.80.10.tgz", + "integrity": "sha512-8CBtDJwMguIE5RvV3PU1QtxUG8oSSX54mIuAbRZmcQ0MYiOl9JdrMd4JCBvIyhiZLoSStph425SMyCSnjtJsdA==", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "exponential-backoff": "^3.1.1", + "flow-enums-runtime": "^0.0.6", + "metro-core": "0.80.10" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=18" } }, - "node_modules/metro-cache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", + "node_modules/metro-cache-key": { + "version": "0.80.10", + "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.80.10.tgz", + "integrity": "sha512-57qBhO3zQfoU/hP4ZlLW5hVej2jVfBX6B4NcSfMj4LgDPL3YknWg80IJBxzQfjQY/m+fmMLmPy8aUMHzUp/guA==", "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" + "flow-enums-runtime": "^0.0.6" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">=18" } }, "node_modules/metro-config": { - "version": "0.80.9", - "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.80.9.tgz", - "integrity": "sha512-28wW7CqS3eJrunRGnsibWldqgwRP9ywBEf7kg+uzUHkSFJNKPM1K3UNSngHmH0EZjomizqQA2Zi6/y6VdZMolg==", + "version": "0.80.10", + "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.80.10.tgz", + "integrity": "sha512-0GYAw0LkmGbmA81FepKQepL1KU/85Cyv7sAiWm6QWeV6AcVCpsKg6jGLqGHJ0LLPL60rWzA4TV1DQAlzdJAEtA==", "dependencies": { "connect": "^3.6.5", "cosmiconfig": "^5.0.5", + "flow-enums-runtime": "^0.0.6", "jest-validate": "^29.6.3", - "metro": "0.80.9", - "metro-cache": "0.80.9", - "metro-core": "0.80.9", - "metro-runtime": "0.80.9" + "metro": "0.80.10", + "metro-cache": "0.80.10", + "metro-core": "0.80.10", + "metro-runtime": "0.80.10" }, "engines": { "node": ">=18" } }, "node_modules/metro-core": { - "version": "0.80.9", - "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.80.9.tgz", - "integrity": "sha512-tbltWQn+XTdULkGdzHIxlxk4SdnKxttvQQV3wpqqFbHDteR4gwCyTR2RyYJvxgU7HELfHtrVbqgqAdlPByUSbg==", + "version": "0.80.10", + "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.80.10.tgz", + "integrity": "sha512-nwBB6HbpGlNsZMuzxVqxqGIOsn5F3JKpsp8PziS7Z4mV8a/jA1d44mVOgYmDa2q5WlH5iJfRIIhdz24XRNDlLA==", "dependencies": { + "flow-enums-runtime": "^0.0.6", "lodash.throttle": "^4.1.1", - "metro-resolver": "0.80.9" + "metro-resolver": "0.80.10" }, "engines": { "node": ">=18" } }, "node_modules/metro-file-map": { - "version": "0.80.9", - "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.80.9.tgz", - "integrity": "sha512-sBUjVtQMHagItJH/wGU9sn3k2u0nrCl0CdR4SFMO1tksXLKbkigyQx4cbpcyPVOAmGTVuy3jyvBlELaGCAhplQ==", + "version": "0.80.10", + "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.80.10.tgz", + "integrity": "sha512-ytsUq8coneaN7ZCVk1IogojcGhLIbzWyiI2dNmw2nnBgV/0A+M5WaTTgZ6dJEz3dzjObPryDnkqWPvIGLCPtiw==", "dependencies": { "anymatch": "^3.0.3", "debug": "^2.2.0", "fb-watchman": "^2.0.0", + "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "invariant": "^2.2.4", "jest-worker": "^29.6.3", @@ -12536,52 +12448,17 @@ "ms": "2.0.0" } }, - "node_modules/metro-file-map/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/metro-file-map/node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/metro-file-map/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/metro-file-map/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/metro-minify-terser": { - "version": "0.80.9", - "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.80.9.tgz", - "integrity": "sha512-FEeCeFbkvvPuhjixZ1FYrXtO0araTpV6UbcnGgDUpH7s7eR5FG/PiJz3TsuuPP/HwCK19cZtQydcA2QrCw446A==", + "version": "0.80.10", + "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.80.10.tgz", + "integrity": "sha512-Xyv9pEYpOsAerrld7cSLIcnCCpv8ItwysOmTA+AKf1q4KyE9cxrH2O2SA0FzMCkPzwxzBWmXwHUr+A89BpEM6g==", "dependencies": { + "flow-enums-runtime": "^0.0.6", "terser": "^5.15.0" }, "engines": { @@ -12589,35 +12466,40 @@ } }, "node_modules/metro-resolver": { - "version": "0.80.9", - "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.80.9.tgz", - "integrity": "sha512-wAPIjkN59BQN6gocVsAvvpZ1+LQkkqUaswlT++cJafE/e54GoVkMNCmrR4BsgQHr9DknZ5Um/nKueeN7kaEz9w==", + "version": "0.80.10", + "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.80.10.tgz", + "integrity": "sha512-EYC5CL7f+bSzrqdk1bylKqFNGabfiI5PDctxoPx70jFt89Jz+ThcOscENog8Jb4LEQFG6GkOYlwmPpsi7kx3QA==", + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, "engines": { "node": ">=18" } }, "node_modules/metro-runtime": { - "version": "0.80.9", - "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.80.9.tgz", - "integrity": "sha512-8PTVIgrVcyU+X/rVCy/9yxNlvXsBCk5JwwkbAm/Dm+Abo6NBGtNjWF0M1Xo/NWCb4phamNWcD7cHdR91HhbJvg==", + "version": "0.80.10", + "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.80.10.tgz", + "integrity": "sha512-Xh0N589ZmSIgJYAM+oYwlzTXEHfASZac9TYPCNbvjNTn0EHKqpoJ/+Im5G3MZT4oZzYv4YnvzRtjqS5k0tK94A==", "dependencies": { - "@babel/runtime": "^7.0.0" + "@babel/runtime": "^7.0.0", + "flow-enums-runtime": "^0.0.6" }, "engines": { "node": ">=18" } }, "node_modules/metro-source-map": { - "version": "0.80.9", - "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.80.9.tgz", - "integrity": "sha512-RMn+XS4VTJIwMPOUSj61xlxgBvPeY4G6s5uIn6kt6HB6A/k9ekhr65UkkDD7WzHYs3a9o869qU8tvOZvqeQzgw==", + "version": "0.80.10", + "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.80.10.tgz", + "integrity": "sha512-EyZswqJW8Uukv/HcQr6K19vkMXW1nzHAZPWJSEyJFKIbgp708QfRZ6vnZGmrtFxeJEaFdNup4bGnu8/mIOYlyA==", "dependencies": { "@babel/traverse": "^7.20.0", "@babel/types": "^7.20.0", + "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", - "metro-symbolicate": "0.80.9", + "metro-symbolicate": "0.80.10", "nullthrows": "^1.1.1", - "ob1": "0.80.9", + "ob1": "0.80.10", "source-map": "^0.5.6", "vlq": "^1.0.0" }, @@ -12634,12 +12516,13 @@ } }, "node_modules/metro-symbolicate": { - "version": "0.80.9", - "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.80.9.tgz", - "integrity": "sha512-Ykae12rdqSs98hg41RKEToojuIW85wNdmSe/eHUgMkzbvCFNVgcC0w3dKZEhSsqQOXapXRlLtHkaHLil0UD/EA==", + "version": "0.80.10", + "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.80.10.tgz", + "integrity": "sha512-qAoVUoSxpfZ2DwZV7IdnQGXCSsf2cAUExUcZyuCqGlY5kaWBb0mx2BL/xbMFDJ4wBp3sVvSBPtK/rt4J7a0xBA==", "dependencies": { + "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", - "metro-source-map": "0.80.9", + "metro-source-map": "0.80.10", "nullthrows": "^1.1.1", "source-map": "^0.5.6", "through2": "^2.0.1", @@ -12661,14 +12544,15 @@ } }, "node_modules/metro-transform-plugins": { - "version": "0.80.9", - "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.80.9.tgz", - "integrity": "sha512-UlDk/uc8UdfLNJhPbF3tvwajyuuygBcyp+yBuS/q0z3QSuN/EbLllY3rK8OTD9n4h00qZ/qgxGv/lMFJkwP4vg==", + "version": "0.80.10", + "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.80.10.tgz", + "integrity": "sha512-leAx9gtA+2MHLsCeWK6XTLBbv2fBnNFu/QiYhWzMq8HsOAP4u1xQAU0tSgPs8+1vYO34Plyn79xTLUtQCRSSUQ==", "dependencies": { "@babel/core": "^7.20.0", "@babel/generator": "^7.20.0", "@babel/template": "^7.0.0", "@babel/traverse": "^7.20.0", + "flow-enums-runtime": "^0.0.6", "nullthrows": "^1.1.1" }, "engines": { @@ -12676,21 +12560,22 @@ } }, "node_modules/metro-transform-worker": { - "version": "0.80.9", - "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.80.9.tgz", - "integrity": "sha512-c/IrzMUVnI0hSVVit4TXzt3A1GiUltGVlzCmLJWxNrBGHGrJhvgePj38+GXl1Xf4Fd4vx6qLUkKMQ3ux73bFLQ==", + "version": "0.80.10", + "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.80.10.tgz", + "integrity": "sha512-zNfNLD8Rz99U+JdOTqtF2o7iTjcDMMYdVS90z6+81Tzd2D0lDWVpls7R1hadS6xwM+ymgXFQTjM6V6wFoZaC0g==", "dependencies": { "@babel/core": "^7.20.0", "@babel/generator": "^7.20.0", "@babel/parser": "^7.20.0", "@babel/types": "^7.20.0", - "metro": "0.80.9", - "metro-babel-transformer": "0.80.9", - "metro-cache": "0.80.9", - "metro-cache-key": "0.80.9", - "metro-minify-terser": "0.80.9", - "metro-source-map": "0.80.9", - "metro-transform-plugins": "0.80.9", + "flow-enums-runtime": "^0.0.6", + "metro": "0.80.10", + "metro-babel-transformer": "0.80.10", + "metro-cache": "0.80.10", + "metro-cache-key": "0.80.10", + "metro-minify-terser": "0.80.10", + "metro-source-map": "0.80.10", + "metro-transform-plugins": "0.80.10", "nullthrows": "^1.1.1" }, "engines": { @@ -12755,26 +12640,6 @@ "ms": "2.0.0" } }, - "node_modules/metro/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/metro/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -12784,44 +12649,16 @@ } }, "node_modules/metro/node_modules/hermes-estree": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.20.1.tgz", - "integrity": "sha512-SQpZK4BzR48kuOg0v4pb3EAGNclzIlqMj3Opu/mu7bbAoFw6oig6cEt/RAi0zTFW/iW6Iz9X9ggGuZTAZ/yZHg==" + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.23.0.tgz", + "integrity": "sha512-Rkp0PNLGpORw4ktsttkVbpYJbrYKS3hAnkxu8D9nvQi6LvSbuPa+tYw/t2u3Gjc35lYd/k95YkjqyTcN4zspag==" }, "node_modules/metro/node_modules/hermes-parser": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.20.1.tgz", - "integrity": "sha512-BL5P83cwCogI8D7rrDCgsFY0tdYUtmFP9XaXtl2IQjC+2Xo+4okjfXintlTxcIwl4qeGddEl28Z11kbVIw0aNA==", - "dependencies": { - "hermes-estree": "0.20.1" - } - }, - "node_modules/metro/node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/metro/node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.23.0.tgz", + "integrity": "sha512-xLwM4ylfHGwrm+2qXfO1JT/fnqEDGSnpS/9hQ4VLtqTexSviu2ZpBgz07U8jVtndq67qdb/ps0qvaWDZ3fkTyg==", "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "hermes-estree": "0.23.0" } }, "node_modules/metro/node_modules/ms": { @@ -12829,21 +12666,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/metro/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/metro/node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -13069,50 +12891,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "node_modules/mv": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", - "integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==", - "optional": true, - "dependencies": { - "mkdirp": "~0.5.1", - "ncp": "~2.0.0", - "rimraf": "~2.4.0" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/mv/node_modules/glob": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "optional": true, - "dependencies": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/mv/node_modules/rimraf": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", - "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "optional": true, - "dependencies": { - "glob": "^6.0.1" - }, - "bin": { - "rimraf": "bin.js" - } - }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -13151,15 +12929,6 @@ "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", "dev": true }, - "node_modules/ncp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", - "optional": true, - "bin": { - "ncp": "bin/ncp" - } - }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -13256,6 +13025,11 @@ "url": "https://github.com/sponsors/antelle" } }, + "node_modules/noop-fn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/noop-fn/-/noop-fn-1.0.0.tgz", + "integrity": "sha512-pQ8vODlgXt2e7A3mIbFDlizkr46r75V+BJxVAyat8Jl7YmI513gG5cfyRL0FedKraoZ+VAouI1h4/IWpus5pcQ==" + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -13319,9 +13093,12 @@ "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==" }, "node_modules/ob1": { - "version": "0.80.9", - "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.80.9.tgz", - "integrity": "sha512-v9yOxowkZbxWhKOaaTyLjIm1aLy4ebMNcSn4NYJKOAI/Qv+SkfEfszpLr2GIxsccmb2Y2HA9qtsqiIJ80ucpVA==", + "version": "0.80.10", + "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.80.10.tgz", + "integrity": "sha512-dJHyB0S6JkMorUSfSGcYGkkg9kmq3qDUu3ygZUKIfkr47XOPuG35r2Sk6tbwtHXbdKIXmcMvM8DF2CwgdyaHfQ==", + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, "engines": { "node": ">=18" } @@ -13893,9 +13670,9 @@ } }, "node_modules/postcss": { - "version": "8.4.40", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.40.tgz", - "integrity": "sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==", + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", "funding": [ { "type": "opencollective", @@ -13924,6 +13701,11 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "node_modules/pouchdb-collections": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pouchdb-collections/-/pouchdb-collections-1.0.1.tgz", + "integrity": "sha512-31db6JRg4+4D5Yzc2nqsRqsA2oOkZS8DpFav3jf/qVNBxusKa2ClkEIZ2bJNpaDbMfWtnuSq59p6Bn+CipPMdg==" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -14211,6 +13993,17 @@ "node": ">=0.10.0" } }, + "node_modules/react-content-loader": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/react-content-loader/-/react-content-loader-7.0.2.tgz", + "integrity": "sha512-773S98JTyC8VB2nu7LXUhpHx8tZMieGxMcx3qTe7IkohT6Br7d9AXnIXs/wQ6IhlUdKQcw6JLKk1QKigYCWDRA==", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16.0.0" + } + }, "node_modules/react-devtools-core": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-5.3.1.tgz", @@ -14269,21 +14062,21 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react-native": { - "version": "0.74.3", - "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.74.3.tgz", - "integrity": "sha512-UFutCC6WEw6HkxlcpQ2BemKqi0JkwrgDchYB5Svi8Sp4Xwt4HA6LGEjNQgZ+3KM44bjyFRpofQym0uh0jACGng==", + "version": "0.74.5", + "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.74.5.tgz", + "integrity": "sha512-Bgg2WvxaGODukJMTZFTZBNMKVaROHLwSb8VAGEdrlvKwfb1hHg/3aXTUICYk7dwgAnb+INbGMwnF8yeAgIUmqw==", "dependencies": { "@jest/create-cache-key-function": "^29.6.3", "@react-native-community/cli": "13.6.9", "@react-native-community/cli-platform-android": "13.6.9", "@react-native-community/cli-platform-ios": "13.6.9", - "@react-native/assets-registry": "0.74.85", - "@react-native/codegen": "0.74.85", - "@react-native/community-cli-plugin": "0.74.85", - "@react-native/gradle-plugin": "0.74.85", - "@react-native/js-polyfills": "0.74.85", - "@react-native/normalize-colors": "0.74.85", - "@react-native/virtualized-lists": "0.74.85", + "@react-native/assets-registry": "0.74.87", + "@react-native/codegen": "0.74.87", + "@react-native/community-cli-plugin": "0.74.87", + "@react-native/gradle-plugin": "0.74.87", + "@react-native/js-polyfills": "0.74.87", + "@react-native/normalize-colors": "0.74.87", + "@react-native/virtualized-lists": "0.74.87", "abort-controller": "^3.0.0", "anser": "^1.4.9", "ansi-regex": "^5.0.0", @@ -14415,24 +14208,6 @@ "react-native": "*" } }, - "node_modules/react-native-root-siblings": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/react-native-root-siblings/-/react-native-root-siblings-4.1.1.tgz", - "integrity": "sha512-sdmLElNs5PDWqmZmj4/aNH4anyxreaPm61c4ZkRiR8SO/GzLg6KjAbb0e17RmMdnBdD0AIQbS38h/l55YKN4ZA==" - }, - "node_modules/react-native-root-toast": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/react-native-root-toast/-/react-native-root-toast-3.6.0.tgz", - "integrity": "sha512-HgZ2OS84ZbInJGuejGghBCPmBwoSBuJFO/bAJoR/NS6rCl9eiXSRegIG/gLEo7lH9BijeA63is1noUqR8Y1DpQ==", - "dependencies": { - "deprecated-react-native-prop-types": "^2.3.0", - "prop-types": "^15.5.10", - "react-native-root-siblings": "^4.0.0" - }, - "peerDependencies": { - "react-native": ">=0.47.0" - } - }, "node_modules/react-native-safe-area-context": { "version": "4.10.5", "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.10.5.tgz", @@ -14443,9 +14218,9 @@ } }, "node_modules/react-native-screens": { - "version": "3.33.0", - "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-3.33.0.tgz", - "integrity": "sha512-3bKeT/kS1g/6XqraBqjDtyyci35LDeDIHMoko74o+Z5p1oLEi697GWFVwsG272FF0iuOullUbuRNzCcEfRBASQ==", + "version": "3.31.1", + "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-3.31.1.tgz", + "integrity": "sha512-8fRW362pfZ9y4rS8KY5P3DFScrmwo/vu1RrRMMx0PNHbeC9TLq0Kw1ubD83591yz64gLNHFLTVkTJmWeWCXKtQ==", "dependencies": { "react-freeze": "^1.0.0", "warn-once": "^0.1.0" @@ -14534,6 +14309,15 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/react-native-toast-message": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/react-native-toast-message/-/react-native-toast-message-2.2.0.tgz", + "integrity": "sha512-AFti8VzUk6JvyGAlLm9/BknTNDXrrhqnUk7ak/pM7uCTxDPveAu2ekszU0on6vnUPFnG04H/QfYE2IlETqeaWw==", + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/react-native-vector-icons": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-10.0.0.tgz", @@ -14633,27 +14417,10 @@ "node": ">=8" } }, - "node_modules/react-native/node_modules/@react-native/virtualized-lists": { - "version": "0.74.85", - "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.74.85.tgz", - "integrity": "sha512-jx2Zw0qlZteoQ+0KxRc7s4drsljLBEP534FaNZ950e9+CN9nVkLsV6rigcTjDR8wjKMSBWhKf0C0C3egYz7Ehg==", - "dependencies": { - "invariant": "^2.2.4", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/react": "^18.2.6", - "react": "*", - "react-native": "*" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } + "node_modules/react-native/node_modules/@react-native/normalize-colors": { + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.87.tgz", + "integrity": "sha512-Xh7Nyk/MPefkb0Itl5Z+3oOobeG9lfLb7ZOY2DKpFnoCE1TzBmib9vMNdFaLdSxLIP+Ec6icgKtdzYg8QUPYzA==" }, "node_modules/react-native/node_modules/ansi-styles": { "version": "4.3.0", @@ -14806,6 +14573,20 @@ "node": ">= 4" } }, + "node_modules/recyclerlistview": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/recyclerlistview/-/recyclerlistview-4.2.0.tgz", + "integrity": "sha512-uuBCi0c+ggqHKwrzPX4Z/mJOzsBbjZEAwGGmlwpD/sD7raXixdAbdJ6BTcAmuWG50Cg4ru9p12M94Njwhr/27A==", + "dependencies": { + "lodash.debounce": "4.0.8", + "prop-types": "15.8.1", + "ts-object-utils": "0.0.5" + }, + "peerDependencies": { + "react": ">= 15.2.1", + "react-native": ">= 0.30.0" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", @@ -15027,15 +14808,18 @@ } }, "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/rimraf/node_modules/glob": { @@ -15102,12 +14886,6 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, - "node_modules/safe-json-stringify": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", - "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", - "optional": true - }, "node_modules/safe-regex-test": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", @@ -16000,9 +15778,9 @@ } }, "node_modules/terser": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.20.0.tgz", - "integrity": "sha512-e56ETryaQDyebBwJIWYB2TT6f2EZ0fL0sW/JRXNMN26zZdKi2u/E/5my5lG6jNxym6qsrVXfFRmOdV42zlAgLQ==", + "version": "5.31.5", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.5.tgz", + "integrity": "sha512-YPmas0L0rE1UyLL/llTWA0SiDOqIcAQYLeUj7cJYzXHlRTAnMSg9pPe4VJ5PlKvTrPQsdVFuiRiwyeNlYgwh2Q==", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -16072,6 +15850,11 @@ "xtend": "~4.0.1" } }, + "node_modules/tiny-queue": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tiny-queue/-/tiny-queue-0.2.1.tgz", + "integrity": "sha512-EijGsv7kzd9I9g0ByCl6h42BWNGUZrlCSejfrb3AKeHC33SGbASu1VDf5O3rRiiUOhAC9CHdZxFPbZu0HmR70A==" + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -16144,19 +15927,16 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" }, + "node_modules/ts-object-utils": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/ts-object-utils/-/ts-object-utils-0.0.5.tgz", + "integrity": "sha512-iV0GvHqOmilbIKJsfyfJY9/dNHCs969z3so90dQWsO1eMMozvTpnB1MEaUbb3FYtZTGjv5sIy/xmslEz0Rg2TA==" + }, "node_modules/tsconfig-paths": { "version": "3.14.2", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", @@ -16513,6 +16293,14 @@ "react": ">=16.8" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -16957,23 +16745,31 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-validation-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-2.1.0.tgz", - "integrity": "sha512-VJh93e2wb4c3tWtGgTa0OF/dTt/zoPCPzXq4V11ZjxmEAFaPi/Zss1xIZdEB5RD8GD00U0/iVXgqkF77RV7pdQ==", + "node_modules/zustand": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.4.tgz", + "integrity": "sha512-/BPMyLKJPtFEvVL0E9E9BTUM63MNyhPGlvxk1XjrfWTUlV+BR8jufjsovHzrtR6YNcBEcL7cMHovL1n9xHawEg==", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, "engines": { - "node": ">=18.0.0" + "node": ">=12.7.0" }, "peerDependencies": { - "zod": "^3.18.0" + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } } } } diff --git a/package.json b/package.json index 365c5c2..dc96b4b 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "main": "node_modules/expo/AppEntry.js", "scripts": { - "start": "expo start", + "start": "expo start --tunnel", "android": "expo start --android", "ios": "expo start --ios", "web": "expo start --web", @@ -15,31 +15,35 @@ "@react-native-community/netinfo": "11.3.1", "@react-navigation/bottom-tabs": "^6.5.8", "@react-navigation/native": "^6.1.7", + "@shopify/flash-list": "1.6.4", "casdoor-react-native-sdk": "1.1.0", "eslint-plugin-import": "^2.28.1", - "expo": "~51.0.24", + "expo": "~51.0.26", "expo-camera": "~15.0.14", - "expo-dev-client": "^4.0.21", + "expo-dev-client": "~4.0.22", "expo-image": "^1.12.13", + "expo-sqlite": "~14.0.6", "expo-status-bar": "~1.12.1", "expo-system-ui": "~3.0.7", - "expo-updates": "~0.25.21", + "expo-updates": "~0.25.22", "hotp-totp": "^1.0.6", "prop-types": "^15.8.1", "react": "18.2.0", + "react-content-loader": "^7.0.2", "react-dom": "18.2.0", - "react-native": "0.74.3", + "react-native": "0.74.5", "react-native-countdown-circle-timer": "^3.2.1", "react-native-gesture-handler": "~2.16.1", "react-native-paper": "^5.10.3", "react-native-reanimated": "~3.10.1", - "react-native-root-toast": "^3.6.0", "react-native-safe-area-context": "4.10.5", - "react-native-screens": "^3.31.1", + "react-native-screens": "3.31.1", "react-native-svg": "15.2.0", + "react-native-toast-message": "^2.2.0", "react-native-web": "~0.19.6", "react-native-webview": "13.8.6", - "totp-generator": "^0.0.14" + "totp-generator": "^0.0.14", + "zustand": "^4.5.4" }, "verifyConditions": [ "semantic-release-expo", diff --git a/useStorage.js b/useStorage.js new file mode 100644 index 0000000..ae7cad2 --- /dev/null +++ b/useStorage.js @@ -0,0 +1,70 @@ +// Copyright 2024 The Casdoor Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {create} from "zustand"; +import {createJSONStorage, persist} from "zustand/middleware"; +import AsyncStorage from "@react-native-async-storage/async-storage"; + +const asyncStoragePersistConfig = { + setItem: async(key, value) => await AsyncStorage.setItem(key, value), + getItem: async(key) => await AsyncStorage.getItem(key), + removeItem: async(key) => await AsyncStorage.removeItem(key), +}; + +const useStore = create( + persist( + (set, get) => ({ + serverUrl: "", + clientId: "", + redirectPath: "http://casdoor-app", + appName: "", + organizationName: "", + signinPath: "/api/signin", + userInfo: null, + token: null, + setServerUrl: (url) => set({serverUrl: url}), + setClientId: (id) => set({clientId: id}), + setRedirectPath: (path) => set({redirectPath: path}), + setAppName: (name) => set({appName: name}), + setOrganizationName: (name) => set({organizationName: name}), + setSigninPath: (path) => set({signinPath: path}), + setUserInfo: (info) => set({userInfo: info}), + setToken: (token) => set({token: token}), + clearAll: () => set({userInfo: null, token: null}), + + getCasdoorConfig: () => ({ + serverUrl: get().serverUrl, + clientId: get().clientId, + appName: get().appName, + organizationName: get().organizationName, + redirectPath: get().redirectPath, + signinPath: get().signinPath, + }), + setCasdoorConfig: (config) => set({ + serverUrl: config.serverUrl || get().serverUrl, + clientId: config.clientId || get().clientId, + appName: config.appName || get().appName, + organizationName: config.organizationName || get().organizationName, + redirectPath: config.redirectPath || get().redirectPath, + signinPath: config.signinPath || get().signinPath, + }), + }), + { + name: "casdoor-storage", + storage: createJSONStorage(() => asyncStoragePersistConfig), + } + ) +); + +export default useStore; diff --git a/useSync.js b/useSync.js deleted file mode 100644 index 812869c..0000000 --- a/useSync.js +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2024 The Casdoor Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import {useCallback, useEffect, useState} from "react"; -import * as api from "./api"; -import {useNetInfo} from "@react-native-community/netinfo"; - -export const SYNC_STATUS = { - ADD: "add", - EDIT: "edit", - DELETE: "delete", -}; - -const applySync = (serverAccountList, toSyncData) => { - return toSyncData.reduce((acc, syncItem) => { - switch (syncItem.status) { - case SYNC_STATUS.ADD: - if (!acc.some(account => account.accountName === syncItem.data.accountName && account.secretKey === syncItem.data.secretKey)) { - acc.push(syncItem.data); - } - break; - case SYNC_STATUS.EDIT: - const indexToEdit = acc.findIndex(account => account.accountName === syncItem.data.accountName && account.secretKey === syncItem.data.secretKey); - if (indexToEdit !== -1) { - acc[indexToEdit] = {...acc[indexToEdit], ...syncItem.data, accountName: syncItem.newAccountName}; - } - break; - case SYNC_STATUS.DELETE: - return acc.filter(account => !(account.accountName === syncItem.data.accountName && account.secretKey === syncItem.data.secretKey)); - default: - break; - } - return acc; - }, [...serverAccountList]); -}; - -const useSync = (userInfo, token, casdoorServer) => { - const [toSyncData, setToSyncData] = useState([]); - const [syncSignal, setSyncSignal] = useState(false); - const {isConnected} = useNetInfo(); - const [canSync, setCanSync] = useState(false); - - useEffect(() => { - setCanSync(userInfo && casdoorServer && isConnected); - }, [userInfo, casdoorServer, isConnected]); - - const triggerSync = useCallback(() => { - if (canSync) { - setSyncSignal(true); - } - }, [canSync]); - - const resetSyncSignal = useCallback(() => { - setSyncSignal(false); - }, []); - - const addToSyncData = useCallback((toSyncAccount, status, newAccountName = null) => { - setToSyncData([...toSyncData, { - data: { - accountName: toSyncAccount.accountName, - issuer: toSyncAccount.issuer, - secretKey: toSyncAccount.secretKey, - }, - status, - newAccountName: newAccountName || "", - }]); - }, []); - - const syncAccounts = useCallback(async() => { - if (!canSync) {return {success: false, error: "Cannot sync"};} - - try { - const {mfaAccounts: serverAccountList} = await api.getMfaAccounts( - casdoorServer.serverUrl, - userInfo.owner, - userInfo.name, - token - ); - - if (!serverAccountList) { - return {success: false, error: "Failed to get accounts"}; - } - - if (toSyncData.length === 0) { - return {success: true, accountList: serverAccountList}; - } - - const updatedServerAccountList = applySync(serverAccountList, toSyncData); - - const {status} = await api.updateMfaAccounts( - casdoorServer.serverUrl, - userInfo.owner, - userInfo.name, - updatedServerAccountList, - token - ); - - if (status === "ok") {setToSyncData([]);} - return {success: status === "ok", accountList: updatedServerAccountList}; - - } catch (error) { - return {success: false, error: error.message}; - } - }, [canSync, casdoorServer, userInfo, token, toSyncData]); - - useEffect(() => { - if (canSync) {triggerSync();} - }, [canSync, toSyncData]); - - return { - syncSignal, - resetSyncSignal, - syncAccounts, - addToSyncData, - }; -}; - -export default useSync; diff --git a/useSyncStore.js b/useSyncStore.js new file mode 100644 index 0000000..513cb74 --- /dev/null +++ b/useSyncStore.js @@ -0,0 +1,38 @@ +// Copyright 2024 The Casdoor Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {create} from "zustand"; +import * as TotpDatabase from "./TotpDatabase"; + +const useSyncStore = create((set, get) => ({ + isSyncing: false, + syncError: null, + + startSync: async(db, userInfo, casdoorServer, token) => { + if (!get().isSyncing) { + set({isSyncing: true, syncError: null}); + try { + await TotpDatabase.syncWithCloud(db, userInfo, casdoorServer, token); + } catch (error) { + set({syncError: error.message}); + } finally { + set({isSyncing: false}); + } + } + }, + + clearSyncError: () => set({syncError: null}), +})); + +export default useSyncStore;