feat: can scan Casdoor QR code to login (#27)
This commit is contained in:
parent
e74a0ed1c7
commit
37a03e45cc
35
App.js
35
App.js
|
@ -17,7 +17,8 @@ import {NavigationContainer} from "@react-navigation/native";
|
|||
import {PaperProvider} from "react-native-paper";
|
||||
import {SafeAreaView, Text} from "react-native";
|
||||
import ContentLoader, {Circle, Rect} from "react-content-loader/native";
|
||||
import Toast from "react-native-toast-message";
|
||||
import {ZoomInDownZoomOutUp, createNotifications} from "react-native-notificated";
|
||||
import {GestureHandlerRootView} from "react-native-gesture-handler";
|
||||
import {useMigrations} from "drizzle-orm/expo-sqlite/migrator";
|
||||
|
||||
import Header from "./Header";
|
||||
|
@ -27,6 +28,21 @@ import migrations from "./drizzle/migrations";
|
|||
|
||||
const App = () => {
|
||||
const {success, error} = useMigrations(db, migrations);
|
||||
const {NotificationsProvider} = createNotifications({
|
||||
duration: 800,
|
||||
notificationPosition: "top",
|
||||
animationConfig: ZoomInDownZoomOutUp,
|
||||
isNotch: true,
|
||||
notificationWidth: 350,
|
||||
defaultStylesSettings: {
|
||||
globalConfig: {
|
||||
borderRadius: 15,
|
||||
borderWidth: 2,
|
||||
multiline: 3,
|
||||
defaultIconType: "no-icon",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
|
@ -59,13 +75,16 @@ const App = () => {
|
|||
}
|
||||
|
||||
return (
|
||||
<NavigationContainer>
|
||||
<PaperProvider>
|
||||
<Header />
|
||||
<NavigationBar />
|
||||
</PaperProvider>
|
||||
<Toast />
|
||||
</NavigationContainer>
|
||||
<GestureHandlerRootView style={{flex: 1}}>
|
||||
<NotificationsProvider>
|
||||
<NavigationContainer>
|
||||
<PaperProvider>
|
||||
<Header />
|
||||
<NavigationBar />
|
||||
</PaperProvider>
|
||||
</NavigationContainer>
|
||||
</NotificationsProvider>
|
||||
</GestureHandlerRootView>
|
||||
);
|
||||
};
|
||||
export default App;
|
||||
|
|
|
@ -16,10 +16,9 @@ import React, {useEffect, useState} from "react";
|
|||
import {WebView} from "react-native-webview";
|
||||
import {Platform, SafeAreaView, StatusBar, StyleSheet, Text, TouchableOpacity} from "react-native";
|
||||
import {Portal} from "react-native-paper";
|
||||
import {useNotifications} from "react-native-notificated";
|
||||
import SDK from "casdoor-react-native-sdk";
|
||||
import PropTypes from "prop-types";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
import EnterCasdoorSdkConfig from "./EnterCasdoorSdkConfig";
|
||||
import useStore from "./useStorage";
|
||||
// import {LogBox} from "react-native";
|
||||
|
@ -31,6 +30,7 @@ const CasdoorLoginPage = ({onWebviewClose}) => {
|
|||
onWebviewClose: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const {notify} = useNotifications();
|
||||
const [casdoorLoginURL, setCasdoorLoginURL] = useState("");
|
||||
const [showConfigPage, setShowConfigPage] = useState(true);
|
||||
|
||||
|
@ -40,6 +40,7 @@ const CasdoorLoginPage = ({onWebviewClose}) => {
|
|||
redirectPath,
|
||||
appName,
|
||||
organizationName,
|
||||
token,
|
||||
getCasdoorConfig,
|
||||
setUserInfo,
|
||||
setToken,
|
||||
|
@ -65,6 +66,12 @@ const CasdoorLoginPage = ({onWebviewClose}) => {
|
|||
}
|
||||
}, [serverUrl, clientId, redirectPath, appName, organizationName]);
|
||||
|
||||
useEffect(() => {
|
||||
if (token) {
|
||||
onWebviewClose();
|
||||
}
|
||||
}, [token]);
|
||||
|
||||
const onNavigationStateChange = async(navState) => {
|
||||
const {redirectPath} = getCasdoorConfig();
|
||||
if (navState.url.startsWith(redirectPath)) {
|
||||
|
@ -77,11 +84,11 @@ const CasdoorLoginPage = ({onWebviewClose}) => {
|
|||
};
|
||||
|
||||
const handleErrorResponse = (error) => {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Error",
|
||||
text2: error.description,
|
||||
autoHide: true,
|
||||
notify("error", {
|
||||
params: {
|
||||
text1: "Error",
|
||||
text2: error.description,
|
||||
},
|
||||
});
|
||||
setShowConfigPage(true);
|
||||
};
|
||||
|
@ -95,7 +102,7 @@ const CasdoorLoginPage = ({onWebviewClose}) => {
|
|||
onWebviewClose={onWebviewClose}
|
||||
/>
|
||||
)}
|
||||
{!showConfigPage && casdoorLoginURL !== "" && (
|
||||
{!showConfigPage && casdoorLoginURL !== "" && !token && (
|
||||
<>
|
||||
<TouchableOpacity
|
||||
style={styles.backButton}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
import React, {useCallback, useState} from "react";
|
||||
import {View} from "react-native";
|
||||
import {Button, IconButton, Menu, Text, TextInput} from "react-native-paper";
|
||||
import Toast from "react-native-toast-message";
|
||||
import {useNotifications} from "react-native-notificated";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const EnterAccountDetails = ({onClose, onAdd, validateSecret}) => {
|
||||
|
@ -25,6 +25,8 @@ const EnterAccountDetails = ({onClose, onAdd, validateSecret}) => {
|
|||
validateSecret: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const {notify} = useNotifications();
|
||||
|
||||
const [accountName, setAccountName] = useState("");
|
||||
const [secretKey, setSecretKey] = useState("");
|
||||
const [secretError, setSecretError] = useState("");
|
||||
|
@ -51,21 +53,17 @@ const EnterAccountDetails = ({onClose, onAdd, validateSecret}) => {
|
|||
}
|
||||
|
||||
if (accountName.trim() === "" || secretKey.trim() === "") {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Error",
|
||||
text2: "Please fill in all the fields!",
|
||||
autoHide: true,
|
||||
notify("error", {
|
||||
title: "Error",
|
||||
description: "Please fill in all the fields!",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (secretError) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Invalid Secret Key",
|
||||
text2: "Please check your secret key and try again.",
|
||||
autoHide: true,
|
||||
notify("error", {
|
||||
title: "Error",
|
||||
description: "Invalid Secret Key",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
import React, {useState} from "react";
|
||||
import {ScrollView, Text, View} from "react-native";
|
||||
import {Button, IconButton, Portal, TextInput} from "react-native-paper";
|
||||
import Toast from "react-native-toast-message";
|
||||
import {useNotifications} from "react-native-notificated";
|
||||
import SDK from "casdoor-react-native-sdk";
|
||||
import DefaultCasdoorSdkConfig from "./DefaultCasdoorSdkConfig";
|
||||
import PropTypes from "prop-types";
|
||||
import ScanQRCodeForLogin from "./ScanLogin";
|
||||
|
@ -38,8 +39,13 @@ const EnterCasdoorSdkConfig = ({onClose, onWebviewClose}) => {
|
|||
setAppName,
|
||||
setOrganizationName,
|
||||
setCasdoorConfig,
|
||||
getCasdoorConfig,
|
||||
setToken,
|
||||
setUserInfo,
|
||||
} = useStore();
|
||||
|
||||
const {notify} = useNotifications();
|
||||
|
||||
const [showScanner, setShowScanner] = useState(false);
|
||||
|
||||
const closeConfigPage = () => {
|
||||
|
@ -49,11 +55,11 @@ const EnterCasdoorSdkConfig = ({onClose, onWebviewClose}) => {
|
|||
|
||||
const handleSave = () => {
|
||||
if (!serverUrl || !clientId || !appName || !organizationName || !redirectPath) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Error",
|
||||
text2: "Please fill in all the fields!",
|
||||
autoHide: true,
|
||||
notify("error", {
|
||||
params: {
|
||||
title: "Error",
|
||||
description: "Please fill in all the fields!",
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -66,11 +72,36 @@ const EnterCasdoorSdkConfig = ({onClose, onWebviewClose}) => {
|
|||
|
||||
const handleLogin = (loginInfo) => {
|
||||
setServerUrl(loginInfo.serverUrl);
|
||||
setClientId(loginInfo.clientId);
|
||||
setAppName(loginInfo.appName);
|
||||
setOrganizationName(loginInfo.organizationName);
|
||||
setShowScanner(false);
|
||||
onClose();
|
||||
setClientId("");
|
||||
setAppName("");
|
||||
setOrganizationName("");
|
||||
|
||||
const sdk = new SDK(getCasdoorConfig());
|
||||
|
||||
try {
|
||||
const accessToken = loginInfo.accessToken;
|
||||
const userInfo = sdk.JwtDecode(accessToken);
|
||||
setToken(accessToken);
|
||||
setUserInfo(userInfo);
|
||||
|
||||
notify("success", {
|
||||
params: {
|
||||
title: "Success",
|
||||
description: "Logged in successfully!",
|
||||
},
|
||||
});
|
||||
|
||||
setShowScanner(false);
|
||||
onClose();
|
||||
onWebviewClose();
|
||||
} catch (error) {
|
||||
notify("error", {
|
||||
params: {
|
||||
title: "Error in login",
|
||||
description: error,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleUseDefault = () => {
|
||||
|
@ -147,7 +178,7 @@ const EnterCasdoorSdkConfig = ({onClose, onWebviewClose}) => {
|
|||
style={[styles.button, styles.outlinedButton]}
|
||||
labelStyle={styles.outlinedButtonLabel}
|
||||
>
|
||||
Use Casdoor Demo Site
|
||||
Try with Casdoor Demo Site
|
||||
</Button>
|
||||
</View>
|
||||
</ScrollView>
|
||||
|
|
134
Header.js
134
Header.js
|
@ -15,7 +15,8 @@
|
|||
import * as React from "react";
|
||||
import {Dimensions, StyleSheet, View} from "react-native";
|
||||
import {Appbar, Avatar, Menu, Text, TouchableRipple} from "react-native-paper";
|
||||
import Toast from "react-native-toast-message";
|
||||
import {useNotifications} from "react-native-notificated";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import CasdoorLoginPage, {CasdoorLogout} from "./CasdoorLoginPage";
|
||||
import useStore from "./useStorage";
|
||||
import {useAccountSync} from "./useAccountStore";
|
||||
|
@ -24,9 +25,10 @@ const {width} = Dimensions.get("window");
|
|||
|
||||
const Header = () => {
|
||||
const {userInfo, clearAll} = useStore();
|
||||
const {syncError, clearSyncError} = useAccountSync();
|
||||
const {isSyncing, syncError, clearSyncError} = useAccountSync();
|
||||
const [showLoginPage, setShowLoginPage] = React.useState(false);
|
||||
const [menuVisible, setMenuVisible] = React.useState(false);
|
||||
const {notify} = useNotifications();
|
||||
|
||||
const openMenu = () => setMenuVisible(true);
|
||||
const closeMenu = () => setMenuVisible(false);
|
||||
|
@ -46,32 +48,40 @@ const Header = () => {
|
|||
};
|
||||
|
||||
const handleSyncErrorPress = () => {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Sync Error",
|
||||
text2: syncError || "An unknown error occurred during synchronization.",
|
||||
autoHide: true,
|
||||
notify("error", {
|
||||
params: {
|
||||
title: "Error",
|
||||
description: syncError || "An unknown error occurred during synchronization.",
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Appbar.Header mode="center-aligned">
|
||||
<View style={styles.leftContainer}>
|
||||
{true && syncError && (
|
||||
<Appbar.Action
|
||||
icon="sync-alert"
|
||||
color="#E53935"
|
||||
size={24}
|
||||
onPress={handleSyncErrorPress}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
<Appbar.Header mode="small" style={styles.header}>
|
||||
<Appbar.Content
|
||||
title="Casdoor"
|
||||
titleStyle={styles.titleText}
|
||||
style={styles.titleContainer}
|
||||
title={
|
||||
<View style={styles.titleContainer}>
|
||||
<Text style={styles.titleTextCasdoor}>Casdoor</Text>
|
||||
</View>
|
||||
}
|
||||
style={styles.titleWrapper}
|
||||
/>
|
||||
<View style={styles.rightContainer}>
|
||||
{userInfo !== null && (
|
||||
<Icon
|
||||
name={
|
||||
isSyncing
|
||||
? "cloud-sync-outline"
|
||||
: syncError
|
||||
? "cloud-off-outline"
|
||||
: "cloud-check-outline"
|
||||
}
|
||||
size={22}
|
||||
color={isSyncing ? "#FFC107" : syncError ? "#E53935" : "#4CAF50"}
|
||||
style={styles.syncIcon}
|
||||
onPress={(isSyncing || syncError === null) ? null : handleSyncErrorPress}
|
||||
/>
|
||||
)}
|
||||
<Menu
|
||||
visible={menuVisible}
|
||||
onDismiss={closeMenu}
|
||||
|
@ -84,21 +94,19 @@ const Header = () => {
|
|||
style={styles.buttonContainer}
|
||||
>
|
||||
<View style={styles.buttonContent}>
|
||||
<Text
|
||||
style={[
|
||||
styles.buttonText,
|
||||
userInfo !== null && {marginRight: 8},
|
||||
]}
|
||||
>
|
||||
{userInfo === null ? "Login" : userInfo.name}
|
||||
</Text>
|
||||
{userInfo !== null && (
|
||||
<Avatar.Image
|
||||
size={24}
|
||||
size={28}
|
||||
source={{uri: userInfo.avatar}}
|
||||
style={styles.avatar}
|
||||
/>
|
||||
)}
|
||||
<Text style={[
|
||||
styles.buttonText,
|
||||
userInfo === null && {marginLeft: 0},
|
||||
]}>
|
||||
{userInfo === null ? "Login" : userInfo.name}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableRipple>
|
||||
}
|
||||
|
@ -112,63 +120,63 @@ const Header = () => {
|
|||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
leftContainer: {
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
justifyContent: "center",
|
||||
paddingLeft: width * 0.03,
|
||||
header: {
|
||||
backgroundColor: "#F2F2F2",
|
||||
height: 56,
|
||||
},
|
||||
rightContainer: {
|
||||
position: "absolute",
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
justifyContent: "center",
|
||||
paddingRight: width * 0.03,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
paddingRight: width * 0.04,
|
||||
},
|
||||
titleWrapper: {
|
||||
alignItems: "flex-start",
|
||||
},
|
||||
titleContainer: {
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
flexDirection: "row",
|
||||
alignItems: "baseline",
|
||||
},
|
||||
titleText: {
|
||||
fontSize: Math.max(20, width * 0.045),
|
||||
titleTextCasdoor: {
|
||||
fontSize: Math.max(24, width * 0.05),
|
||||
fontWeight: "bold",
|
||||
textAlign: "center",
|
||||
color: "#212121",
|
||||
fontFamily: "Lato-Bold",
|
||||
},
|
||||
buttonContainer: {
|
||||
borderRadius: 20,
|
||||
borderRadius: 24,
|
||||
overflow: "hidden",
|
||||
borderWidth: 0.5,
|
||||
borderColor: "#DDDDDD",
|
||||
},
|
||||
buttonContent: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 6,
|
||||
paddingHorizontal: 14,
|
||||
},
|
||||
buttonText: {
|
||||
fontSize: Math.max(14, width * 0.035),
|
||||
fontWeight: "bold",
|
||||
fontSize: Math.max(14, width * 0.042),
|
||||
fontWeight: "600",
|
||||
marginLeft: 8,
|
||||
color: "#424242",
|
||||
fontFamily: "Roboto-Medium",
|
||||
},
|
||||
menuContent: {
|
||||
backgroundColor: "#FFFFFF",
|
||||
backgroundColor: "#FAFAFA",
|
||||
borderRadius: 8,
|
||||
elevation: 3,
|
||||
elevation: 2,
|
||||
shadowColor: "#000000",
|
||||
shadowOffset: {width: 0, height: 2},
|
||||
shadowOpacity: 0.2,
|
||||
shadowRadius: 3,
|
||||
shadowOffset: {width: 0, height: 1},
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 2,
|
||||
},
|
||||
avatar: {
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
syncIcon: {
|
||||
marginRight: 12,
|
||||
},
|
||||
});
|
||||
|
||||
export default Header;
|
||||
|
|
39
HomePage.js
39
HomePage.js
|
@ -19,7 +19,7 @@ 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 Toast from "react-native-toast-message";
|
||||
import {useNotifications} from "react-native-notificated";
|
||||
|
||||
import SearchBar from "./SearchBar";
|
||||
import EnterAccountDetails from "./EnterAccountDetails";
|
||||
|
@ -50,12 +50,12 @@ export default function HomePage() {
|
|||
const {isConnected} = useNetInfo();
|
||||
const [canSync, setCanSync] = useState(false);
|
||||
const [key, setKey] = useState(0);
|
||||
|
||||
const swipeableRef = useRef(null);
|
||||
const {userInfo, serverUrl, token} = useStore();
|
||||
const {startSync} = useAccountSync();
|
||||
const {accounts, refreshAccounts} = useAccountStore();
|
||||
const {setAccount, updateAccount, insertAccount, insertAccounts, deleteAccount} = useEditAccount();
|
||||
const {notify} = useNotifications();
|
||||
|
||||
useEffect(() => {
|
||||
refreshAccounts();
|
||||
|
@ -79,25 +79,25 @@ export default function HomePage() {
|
|||
}
|
||||
}, REFRESH_INTERVAL);
|
||||
return () => clearInterval(timer);
|
||||
}, [startSync, canSync]);
|
||||
}, [startSync, canSync, token]);
|
||||
|
||||
const onRefresh = async() => {
|
||||
setRefreshing(true);
|
||||
if (canSync) {
|
||||
const syncError = await startSync(userInfo, serverUrl, token);
|
||||
if (syncError) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Sync error",
|
||||
text2: syncError,
|
||||
autoHide: true,
|
||||
notify("error", {
|
||||
params: {
|
||||
title: "Sync error",
|
||||
description: syncError,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Sync success",
|
||||
text2: "All your accounts are up to date.",
|
||||
autoHide: true,
|
||||
notify("success", {
|
||||
params: {
|
||||
title: "Sync success",
|
||||
description: "All your accounts are up to date.",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -151,11 +151,11 @@ export default function HomePage() {
|
|||
|
||||
const handleScanError = (error) => {
|
||||
setShowScanner(false);
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Scan error",
|
||||
text2: error,
|
||||
autoHide: true,
|
||||
notify("error", {
|
||||
params: {
|
||||
title: "Error scanning QR code",
|
||||
description: error,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -226,7 +226,8 @@ export default function HomePage() {
|
|||
<List.Item
|
||||
style={{
|
||||
height: 80,
|
||||
paddingHorizontal: 25,
|
||||
paddingVertical: 6,
|
||||
paddingHorizontal: 16,
|
||||
justifyContent: "center",
|
||||
}}
|
||||
title={
|
||||
|
|
|
@ -26,7 +26,7 @@ const ScanQRCodeForLogin = ({onClose, showScanner, onLogin}) => {
|
|||
};
|
||||
|
||||
const isValidLoginQR = (data) => {
|
||||
return data.startsWith("casdoor-app://login/into?");
|
||||
return data.startsWith("casdoor-app://login?");
|
||||
};
|
||||
|
||||
const parseLoginQR = (data) => {
|
||||
|
@ -34,10 +34,11 @@ const ScanQRCodeForLogin = ({onClose, showScanner, onLogin}) => {
|
|||
const params = new URLSearchParams(url.search);
|
||||
|
||||
return {
|
||||
// clientId: params.get("clientId"),
|
||||
// appName: params.get("appName"),
|
||||
// organizationName: params.get("organizationName"),
|
||||
serverUrl: params.get("serverUrl"),
|
||||
clientId: params.get("clientId"),
|
||||
appName: params.get("appName"),
|
||||
organizationName: params.get("organizationName"),
|
||||
accessToken: params.get("accessToken"),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
38
SearchBar.js
38
SearchBar.js
|
@ -13,25 +13,47 @@
|
|||
// limitations under the License.
|
||||
|
||||
import * as React from "react";
|
||||
import {View} from "react-native";
|
||||
import {Searchbar} from "react-native-paper";
|
||||
|
||||
const SearchBar = ({onSearch}) => {
|
||||
const [searchQuery, setSearchQuery] = React.useState("");
|
||||
|
||||
const onChangeSearch = query => {
|
||||
const onChangeSearch = (query) => {
|
||||
setSearchQuery(query);
|
||||
onSearch(query);
|
||||
};
|
||||
|
||||
return (
|
||||
<Searchbar
|
||||
placeholder="Search"
|
||||
onChangeText={onChangeSearch}
|
||||
value={searchQuery}
|
||||
style={{height: 48, backgroundColor: "#E6DFF3"}}
|
||||
inputStyle={{textAlignVertical: "center", justifyContent: "center", alignItems: "center", minHeight: 0}}
|
||||
/>
|
||||
<View style={styles.container}>
|
||||
<Searchbar
|
||||
placeholder="Search"
|
||||
onChangeText={onChangeSearch}
|
||||
value={searchQuery}
|
||||
style={styles.searchbar}
|
||||
inputStyle={styles.inputStyle}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
alignItems: "center",
|
||||
paddingTop: 2,
|
||||
},
|
||||
searchbar: {
|
||||
height: 56,
|
||||
backgroundColor: "#E6DFF3",
|
||||
borderRadius: 99,
|
||||
width: "95%",
|
||||
},
|
||||
inputStyle: {
|
||||
minHeight: 0,
|
||||
textAlignVertical: "center",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
},
|
||||
};
|
||||
|
||||
export default SearchBar;
|
||||
|
|
13
api.js
13
api.js
|
@ -32,6 +32,12 @@ export const getMfaAccounts = async(serverUrl, owner, name, token, timeoutMs = T
|
|||
]);
|
||||
|
||||
const res = await result.json();
|
||||
|
||||
// Check the response status and message
|
||||
if (res.status === "error") {
|
||||
throw new Error(res.msg);
|
||||
}
|
||||
|
||||
return {updatedTime: res.data.updatedTime, mfaAccounts: res.data.mfaAccounts};
|
||||
} catch (error) {
|
||||
if (error.name === "AbortError") {
|
||||
|
@ -58,6 +64,7 @@ export const updateMfaAccounts = async(serverUrl, owner, name, newMfaAccounts, t
|
|||
]);
|
||||
|
||||
const userData = await getUserResult.json();
|
||||
|
||||
userData.data.mfaAccounts = newMfaAccounts;
|
||||
|
||||
const updateResult = await Promise.race([
|
||||
|
@ -74,6 +81,12 @@ export const updateMfaAccounts = async(serverUrl, owner, name, newMfaAccounts, t
|
|||
]);
|
||||
|
||||
const res = await updateResult.json();
|
||||
|
||||
// Check the response status and message
|
||||
if (res.status === "error") {
|
||||
throw new Error(res.msg);
|
||||
}
|
||||
|
||||
return {status: res.status, data: res.data};
|
||||
} catch (error) {
|
||||
if (error.name === "AbortError") {
|
||||
|
|
|
@ -2,6 +2,14 @@ module.exports = function(api) {
|
|||
api.cache(true);
|
||||
return {
|
||||
presets: ["babel-preset-expo"],
|
||||
plugins: [["inline-import", {"extensions": [".sql"]}]],
|
||||
plugins: [
|
||||
[
|
||||
"inline-import",
|
||||
{
|
||||
"extensions": [".sql"],
|
||||
},
|
||||
],
|
||||
"react-native-reanimated/plugin",
|
||||
],
|
||||
};
|
||||
};
|
||||
|
|
|
@ -40,12 +40,12 @@
|
|||
"react-native": "0.74.5",
|
||||
"react-native-countdown-circle-timer": "^3.2.1",
|
||||
"react-native-gesture-handler": "~2.16.1",
|
||||
"react-native-notificated": "^0.1.6",
|
||||
"react-native-paper": "^5.10.3",
|
||||
"react-native-reanimated": "~3.10.1",
|
||||
"react-native-safe-area-context": "4.10.5",
|
||||
"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",
|
||||
|
@ -15644,6 +15644,18 @@
|
|||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-notificated": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/react-native-notificated/-/react-native-notificated-0.1.6.tgz",
|
||||
"integrity": "sha512-thv+uhQlDHzdKOL2QJcaDC/IT7F9NivdIPutbnr6YZ+MfINr5qeVOeEAuUs+4xIxADvP3MVGehyTGIYnAUU8Og==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-native": "*",
|
||||
"react-native-gesture-handler": "^2.9.0",
|
||||
"react-native-reanimated": "^2.14.4 || ^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-paper": {
|
||||
"version": "5.10.6",
|
||||
"resolved": "https://registry.npmjs.org/react-native-paper/-/react-native-paper-5.10.6.tgz",
|
||||
|
@ -15807,15 +15819,6 @@
|
|||
"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",
|
||||
|
|
|
@ -42,12 +42,12 @@
|
|||
"react-native": "0.74.5",
|
||||
"react-native-countdown-circle-timer": "^3.2.1",
|
||||
"react-native-gesture-handler": "~2.16.1",
|
||||
"react-native-notificated": "^0.1.6",
|
||||
"react-native-paper": "^5.10.3",
|
||||
"react-native-reanimated": "~3.10.1",
|
||||
"react-native-safe-area-context": "4.10.5",
|
||||
"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",
|
||||
|
|
Loading…
Reference in New Issue