Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: onboarding #44

Merged
merged 11 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions app/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import "../lib/applyGlobalPolyfills";

import "~/global.css";

import { Theme, ThemeProvider } from "@react-navigation/native";
import { SplashScreen, Stack } from "expo-router";
import {
router,
SplashScreen,
Stack,
useRootNavigationState,
} from "expo-router";
import { StatusBar } from "expo-status-bar";
import * as React from "react";
import { SafeAreaView } from "react-native";
Expand All @@ -16,6 +20,7 @@ import Toast from "react-native-toast-message";
import { toastConfig } from "~/components/ToastConfig";
import * as Font from "expo-font";
import { useInfo } from "~/hooks/useInfo";
import { secureStorage } from "~/lib/secureStorage";

const LIGHT_THEME: Theme = {
dark: false,
Expand All @@ -41,9 +46,22 @@ SplashScreen.preventAutoHideAsync();
export default function RootLayout() {
const { isDarkColorScheme } = useColorScheme();
const [fontsLoaded, setFontsLoaded] = React.useState(false);
const [showOnboarding, setShowOnboarding] = React.useState(false);
useConnectionChecker();

const rootNavigationState = useRootNavigationState();
const hasNavigationState = !!rootNavigationState?.key;

React.useEffect(() => {
const checkOnboardingStatus = async () => {
const hasOnboarded = await secureStorage.getItem("hasOnboarded");
if (!hasOnboarded && hasNavigationState) {
router.replace("/onboarding");
}
};

checkOnboardingStatus();

(async () => {
await Font.loadAsync({
OpenRunde: require("./../assets/fonts/OpenRunde-Regular.otf"),
Expand All @@ -56,7 +74,7 @@ export default function RootLayout() {
})().finally(() => {
SplashScreen.hideAsync();
});
}, []);
}, [hasNavigationState]);

if (!fontsLoaded) {
return null;
Expand Down
5 changes: 5 additions & 0 deletions app/onboarding.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Onboarding } from "../pages/Onboarding";

export default function Page() {
return <Onboarding />;
}
3 changes: 3 additions & 0 deletions components/Icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
Power,
CameraOff,
Palette,
Egg,
} from "lucide-react-native";
import { cssInterop } from "nativewind";

Expand Down Expand Up @@ -81,6 +82,7 @@ interopIcon(Hotel);
interopIcon(Power);
interopIcon(CameraOff);
interopIcon(Palette);
interopIcon(Egg);

export {
AlertCircle,
Expand Down Expand Up @@ -116,4 +118,5 @@ export {
X,
Power,
Palette,
Egg,
};
8 changes: 4 additions & 4 deletions components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const buttonVariants = cva(
size: {
default: "min-h-10 px-4 py-2 native:min-h-12 native:px-5 native:py-3",
sm: "min-h-9 rounded-md px-3",
lg: "min-h-11 rounded-lg px-8 native:min-h-16",
lg: "min-h-11 rounded-2xl px-8 native:min-h-16",
icon: "min-h-10 min-w-10",
},
},
Expand All @@ -42,14 +42,14 @@ const buttonTextVariants = cva(
destructive: "text-destructive-foreground",
outline: "group-active:text-accent-foreground",
secondary:
"text-secondary-foreground group-active:text-secondary-foreground font-medium2",
"text-secondary-foreground group-active:text-secondary-foreground",
ghost: "group-active:text-accent-foreground",
link: "text-primary group-active:underline",
},
size: {
default: "",
default: "font-medium2",
sm: "",
lg: "native:text-2xl",
lg: "native:text-2xl font-bold2",
icon: "",
},
},
Expand Down
2 changes: 1 addition & 1 deletion global.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
--primary: 47 100% 50%;
--primary-foreground: 217 19% 27%;
--secondary: 240 4.8% 95.9%;
--secondary-foreground: 215 28% 17%;
--secondary-foreground: 217 19% 27%;
--muted: 240 4.8% 95.9%;
--muted-foreground: 240 3.8% 46.1%;
--accent: 240 4.8% 95.9%;
Expand Down
9 changes: 9 additions & 0 deletions lib/state/appStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ interface AppState {
addWallet(wallet: Wallet): void;
addAddressBookEntry(entry: AddressBookEntry): void;
reset(): void;
showOnboarding(): void;
}

const walletKeyPrefix = "wallet";
const addressBookEntryKeyPrefix = "addressBookEntry";
const selectedWalletIdKey = "selectedWalletId";
const fiatCurrencyKey = "fiatCurrency";
const hasOnboardedKey = "hasOnboarded";

type Wallet = {
name?: string;
Expand Down Expand Up @@ -176,6 +178,10 @@ export const useAppStore = create<AppState>()((set, get) => {
addressBookEntries: [...currentAddressBookEntries, addressBookEntry],
});
},
showOnboarding() {
// clear onboarding status
secureStorage.removeItem(hasOnboardedKey);
},
reset() {
// clear wallets
for (let i = 0; i < get().wallets.length; i++) {
Expand All @@ -188,6 +194,9 @@ export const useAppStore = create<AppState>()((set, get) => {
// clear selected wallet ID
secureStorage.removeItem(selectedWalletIdKey);

// clear onboarding status
secureStorage.removeItem(hasOnboardedKey);

set({
nwcClient: undefined,
fiatCurrency: undefined,
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"dayjs": "^1.11.10",
"expo": "^51.0.30",
"expo-camera": "^15.0.14",
"expo": "~51.0.31",
"expo-camera": "~15.0.15",
"expo-clipboard": "~6.0.3",
"expo-constants": "~16.0.2",
"expo-font": "^12.0.9",
Expand Down
39 changes: 39 additions & 0 deletions pages/Onboarding.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { openURL } from "expo-linking";
import { Link, Stack, router } from "expo-router";
import React from "react";
import { View } from "react-native";
import { Button } from "~/components/ui/button";
import { Text } from "~/components/ui/text";
import { secureStorage } from "~/lib/secureStorage";

export function Onboarding() {
async function finish() {
secureStorage.setItem("hasOnboarded", "true");
router.replace("/");
}

return (
<View className="flex-1 flex flex-col p-6 gap-3">
<Stack.Screen
options={{
title: "Onboarding",
headerShown: false,
}}
/>
<View className="flex-1 flex items-center justify-center gap-4">
<Text className="font-semibold2 text-4xl text-center text-foreground">Hello there 👋</Text>
<Text className="font-medium2 text-xl text-primary-foreground text-center">
<Text className="font-semibold2 text-xl text-primary-foreground">Alby Go</Text> is a simple mobile wallet interface for your Alby Hub or other lightning nodes and wallets.
</Text>
</View>
<Link href="/" asChild>
<Button size="lg" onPress={finish}>
<Text>Connect Wallet</Text>
</Button>
</Link>
<Button variant="secondary" size="lg" onPress={() => openURL("https://albyhub.com/")}>
<Text>Learn more</Text>
</Button>
</View>
);
}
8 changes: 7 additions & 1 deletion pages/Wildcard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { usePathname } from "expo-router";
import { Stack, usePathname } from "expo-router";
import { View } from "react-native";
import Loading from "~/components/Loading";
import { Text } from "~/components/ui/text";
Expand All @@ -10,6 +10,12 @@ export function Wildcard() {

return (
<View className="flex-1 justify-center items-center flex flex-col gap-3">
<Stack.Screen
options={{
title: "",
header: () => null, // hide header completely
}}
/>
<Loading />
<Text>Loading {pathname}</Text>
</View>
Expand Down
89 changes: 51 additions & 38 deletions pages/settings/Settings.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Link, router } from "expo-router";
import { Alert, Pressable, TouchableOpacity, View } from "react-native";
import { Bitcoin, Palette, Power, Wallet2 } from "~/components/Icons";
import { Bitcoin, Egg, Palette, Power, Wallet2 } from "~/components/Icons";

import { DEFAULT_WALLET_NAME } from "~/lib/constants";
import { useAppStore } from "~/lib/state/appStore";
Expand All @@ -25,9 +25,7 @@ export function Settings() {
<Link href="/settings/wallets" asChild>
<TouchableOpacity className="flex flex-row items-center gap-4">
<Wallet2 className="text-foreground" />
<Text className="font-medium2 text-xl text-foreground">
Wallets
</Text>
<Text className="font-medium2 text-xl text-foreground">Wallets</Text>
<Text className="text-muted-foreground text-xl">
({wallet.name || DEFAULT_WALLET_NAME})
</Text>
Expand All @@ -43,47 +41,63 @@ export function Settings() {
</TouchableOpacity>
</Link>

<TouchableOpacity className="flex flex-row gap-4" onPress={toggleColorScheme}>
<TouchableOpacity
className="flex flex-row gap-4"
onPress={toggleColorScheme}
>
<Palette className="text-foreground" />
<Text className="text-foreground font-medium2 text-xl">
Theme
</Text>
<Text className="text-foreground font-medium2 text-xl">Theme</Text>
<Text className="text-muted-foreground text-xl">
({colorScheme.charAt(0).toUpperCase() + colorScheme.substring(1)})
</Text>
</TouchableOpacity>

{developerMode && (
<View className="mt-5 flex flex-col gap-4">
<Text className="text-muted-foreground uppercase">Developer mode</Text>
<Pressable
className="flex flex-row gap-4"
onPress={() => {
Alert.alert(
"Reset",
"Are you sure you want to reset? You will be signed out of all your wallets. Your connection secrets and address book will be lost.",
[
{
text: "Cancel",
style: "cancel",
},
{
text: "Confirm",
onPress: () => {
router.dismissAll();
useAppStore.getState().reset();
},
},
],
);
}}
>
<Power className="text-destructive" />
<Text className="text-destructive font-medium2 text-xl">
Reset Wallet
<>
<View className="mt-5 flex flex-col gap-6">
<Text className="text-muted-foreground uppercase">
Developer mode
</Text>
</Pressable>
</View>
<Pressable
className="flex flex-row gap-4"
onPress={() => {
router.dismissAll();
useAppStore.getState().showOnboarding();
router.replace("/onboarding");
}}
>
<Egg className="text-primary-foreground" />
<Text className="font-medium2 text-xl">Open Onboarding</Text>
</Pressable>
<Pressable
className="flex flex-row gap-4"
onPress={() => {
Alert.alert(
"Reset",
"Are you sure you want to reset? You will be signed out of all your wallets. Your connection secrets and address book will be lost.",
[
{
text: "Cancel",
style: "cancel",
},
{
text: "Confirm",
onPress: () => {
router.dismissAll();
useAppStore.getState().reset();
},
},
],
);
}}
>
<Power className="text-destructive" />
<Text className="text-destructive font-medium2 text-xl">
Reset Wallet
</Text>
</Pressable>
</View>
</>
)}
<TouchableOpacity
className="flex-1"
Expand All @@ -95,7 +109,6 @@ export function Settings() {
setDeveloperMode(true);
Toast.show({
text1: "You are now a developer",

});
} else if (newCounter > 1 && newCounter < 5) {
Toast.show({
Expand Down
Loading
Loading