Skip to content

Commit

Permalink
WALLET AccessPrompt Confirm request access (#46)
Browse files Browse the repository at this point in the history
* Show loading, and handle error

* Show loading, and handle error

* Handle error

* Show loading when search Resource and handle error

* Revert icon file

* Revert file aoo.config.js

* fixup! Revert file aoo.config.js

---------

Co-authored-by: Nicolas Ayral Seydoux <[email protected]>
Co-authored-by: Chelsea Pinka <[email protected]>
  • Loading branch information
3 people authored Sep 11, 2024
1 parent 5366e7c commit d003ca7
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 23 deletions.
5 changes: 5 additions & 0 deletions api/apiRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ export const makeApiRequest = async <T>(
router.replace("/login?logout=true");
throw new Error(`Unauthorized: ${response.status}`);
}

if (response.status === 404) {
return null as T;
}

if (!response.ok) {
throw new Error(`Network response was not ok: ${response.statusText}`);
}
Expand Down
4 changes: 3 additions & 1 deletion app/(tabs)/home/download.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import IconResourceName from "@/components/common/IconResourceName";
import { RDF_CONTENT_TYPE } from "@/utils/constants";
import type { WalletFile } from "@/types/WalletFile";
import { isDownloadQR } from "@/types/accessPrompt";
import { useError } from "@/hooks/useError";

interface FileDetailProps {
file: WalletFile;
Expand All @@ -34,6 +35,7 @@ interface FileDetailProps {

const Page: React.FC<FileDetailProps> = () => {
const params = useLocalSearchParams();
const { showErrorMsg } = useError();
if (!isDownloadQR(params)) {
throw new Error(
"Incorrect params for download request: uri and contentType are required"
Expand All @@ -50,8 +52,8 @@ const Page: React.FC<FileDetailProps> = () => {
await queryClient.invalidateQueries({ queryKey: ["files"] });
},
onError: (error) => {
// TODO: there needs to be better error handling here...
console.warn(error);
showErrorMsg("Unable to save the file into your Wallet.");
},
mutationKey: ["filesMutation"],
});
Expand Down
43 changes: 23 additions & 20 deletions app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { BottomSheetModalProvider } from "@gorhom/bottom-sheet";
import type { AppStateStatus } from "react-native";
import { AppState, Platform } from "react-native";
import { LoginWebViewProvider } from "@/hooks/useInruptLogin";
import { ErrorViewProvider } from "@/hooks/useError";

// Prevent the splash screen from auto-hiding before asset loading is complete.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
Expand Down Expand Up @@ -81,28 +82,30 @@ export default function RootLayout() {
<BottomSheetModalProvider>
<SessionProvider>
<LoginWebViewProvider>
<Stack
screenOptions={{
headerShown: false,
}}
>
<Stack.Screen
name="login"
options={{
animation: "none",
}}
/>
<Stack.Screen
name="scan-qr"
options={{
<ErrorViewProvider>
<Stack
screenOptions={{
headerShown: false,
// Set the presentation mode to modal for our modal route.
// presentation: "fullScreenModal",
animation: "slide_from_bottom",
animationDuration: 200,
}}
/>
</Stack>
>
<Stack.Screen
name="login"
options={{
animation: "none",
}}
/>
<Stack.Screen
name="scan-qr"
options={{
headerShown: false,
// Set the presentation mode to modal for our modal route.
// presentation: "fullScreenModal",
animation: "slide_from_bottom",
animationDuration: 200,
}}
/>
</Stack>
</ErrorViewProvider>
</LoginWebViewProvider>
</SessionProvider>
</BottomSheetModalProvider>
Expand Down
32 changes: 30 additions & 2 deletions app/access-prompt/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,18 @@ import { isAccessPromptQR } from "@/types/accessPrompt";
import { faEye } from "@fortawesome/free-solid-svg-icons/faEye";
import CardInfo from "@/components/common/CardInfo";
import { FontAwesomeIcon } from "@fortawesome/react-native-fontawesome";
import Loading from "@/components/LoadingButton";

const Page: React.FC = () => {
const params = useLocalSearchParams();

if (!isAccessPromptQR(params)) {
throw new Error(
"Incorrect params for access prompt request: webId, client and type are required"
);
}
const router = useRouter();
const { data } = useQuery<AccessPromptResource>({
const { data, isLoading, isFetching } = useQuery<AccessPromptResource>({
queryKey: ["accessPromptResource"],
queryFn: () =>
getAccessPromptResource({
Expand Down Expand Up @@ -87,7 +89,23 @@ const Page: React.FC = () => {
},
});
};
if (!data) return <View style={styles.container} testID="no-prompts" />;

if (isLoading || isFetching)
return (
<View style={styles.container}>
<Loading isLoading={true} />
</View>
);

if (!data) {
return (
<View style={styles.emptyState} testID="no-prompts">
<ThemedText style={styles.emptyStateText}>
Resource not found
</ThemedText>
</View>
);
}

return (
<View style={styles.container}>
Expand Down Expand Up @@ -134,6 +152,7 @@ const Page: React.FC = () => {
customStyle={styles.button}
/>
</View>
<Loading isLoading={isLoading || isFetching} />
</View>
);
};
Expand All @@ -148,6 +167,15 @@ const styles = StyleSheet.create({
content: {
flex: 1,
},
emptyState: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
emptyStateText: {
fontSize: 18,
color: Colors.light.grey,
},
title: {
paddingTop: 20,
paddingBottom: 8,
Expand Down
3 changes: 3 additions & 0 deletions app/scan-qr.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import { View, StyleSheet, Dimensions, TouchableOpacity } from "react-native";
import { useNavigation, useRouter } from "expo-router";
import { useEffect, useState } from "react";
import { useError } from "@/hooks/useError";
import { ThemedText } from "@/components/ThemedText";
import type { BarcodeScanningResult } from "expo-camera";
import { Camera, CameraView } from "expo-camera";
Expand All @@ -31,6 +32,7 @@ class UnrecognisedQrCodeError extends Error {

export default function Logout() {
const { goBack } = useNavigation();
const { showErrorMsg } = useError();
const { replace, navigate } = useRouter();
const [scanned, setScanned] = useState(false);
useEffect(() => {
Expand Down Expand Up @@ -68,6 +70,7 @@ export default function Logout() {
throw new UnrecognisedQrCodeError();
}
} catch (err) {
showErrorMsg("QR code not a valid format");
if (err instanceof UnrecognisedQrCodeError) {
console.warn(err);
} else {
Expand Down
2 changes: 2 additions & 0 deletions assets/images/close.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
69 changes: 69 additions & 0 deletions components/error/ErrorPopup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//
// Copyright Inrupt Inc.
//
// 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";
import { View, StyleSheet, TouchableOpacity } from "react-native";
import Close from "@/assets/images/close.svg";
import { ThemedText } from "../ThemedText";

interface ErrorPopupProps {
errorMsg: string;
onClose: () => void;
}

const ErrorPopup: React.FC<ErrorPopupProps> = ({
errorMsg,
onClose = () => null,
}) => {
return (
<View style={styles.errorPopup}>
<ThemedText style={styles.loadingText}>{errorMsg}</ThemedText>
<TouchableOpacity onPress={onClose} style={styles.closeView}>
<Close width="20" height="20" onPress={onClose} />
</TouchableOpacity>
</View>
);
};

const styles = StyleSheet.create({
errorPopup: {
position: "absolute",
bottom: 80,
left: 24,
right: 24,
backgroundColor: "white",
justifyContent: "space-between",
alignItems: "center",
flexDirection: "row",
shadowColor: "rgba(0, 0, 0)",
shadowOffset: { width: 2, height: 4 },
shadowOpacity: 0.2,
shadowRadius: 30,
elevation: 5,
borderRadius: 16,
paddingVertical: 16,
},
loadingText: {
paddingLeft: 16,
fontSize: 16,
},
closeView: {
alignItems: "center",
paddingLeft: 24,
paddingRight: 16,
},
});

export default ErrorPopup;
53 changes: 53 additions & 0 deletions hooks/useError.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// Copyright Inrupt Inc.
//
// 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, { createContext, useCallback, useState } from "react";
import ErrorPopup from "@/components/error/ErrorPopup";

const ErrorContext = createContext<{
showErrorMsg: (msg: string) => void;
}>({
showErrorMsg: () => null,
});

export const ErrorViewProvider = ({ children }: React.PropsWithChildren) => {
const [showError, setShowError] = useState<boolean>(false);
const [errorMsg, setErrorMsg] = useState<string | undefined>(undefined);

const showErrorMsg = useCallback((msg: string) => {
setShowError(true);
setErrorMsg(msg);
}, []);

const handleClose = useCallback(() => {
setShowError(false);
setErrorMsg(undefined);
}, []);

return (
<ErrorContext.Provider
value={{
showErrorMsg,
}}
>
{children}
{showError && Boolean(errorMsg) && (
<ErrorPopup errorMsg={errorMsg!} onClose={handleClose} />
)}
</ErrorContext.Provider>
);
};

export const useError = () => React.useContext(ErrorContext);

0 comments on commit d003ca7

Please sign in to comment.