From c46482cdaaf883971736488750513663dd023076 Mon Sep 17 00:00:00 2001 From: MohamedBassem Date: Wed, 17 Apr 2024 17:56:21 +0100 Subject: mobile: Add dark mode support --- apps/mobile/app.json | 27 +++++++++++--- apps/mobile/app/_layout.tsx | 16 ++++++-- apps/mobile/app/dashboard/(tabs)/_layout.tsx | 16 +++----- apps/mobile/app/dashboard/(tabs)/lists.tsx | 4 +- apps/mobile/app/dashboard/(tabs)/search.tsx | 2 +- apps/mobile/app/dashboard/(tabs)/settings.tsx | 4 +- apps/mobile/app/dashboard/_layout.tsx | 8 +++- apps/mobile/app/dashboard/add-link.tsx | 1 - apps/mobile/app/dashboard/add-note.tsx | 1 - apps/mobile/app/sharing.tsx | 6 +-- apps/mobile/app/signin.tsx | 12 +++++- apps/mobile/assets/splash-white.png | Bin 0 -> 13550 bytes apps/mobile/components/TailwindResolver.tsx | 16 ++++++++ apps/mobile/components/bookmarks/BookmarkCard.tsx | 41 ++++++++++++++++----- apps/mobile/components/bookmarks/BookmarkList.tsx | 2 +- .../components/bookmarks/UpdatingBookmarkList.tsx | 4 +- apps/mobile/components/navigation/stack.tsx | 27 ++++++++++++++ apps/mobile/components/navigation/tabs.tsx | 25 +++++++++++++ apps/mobile/components/ui/Divider.tsx | 5 +-- apps/mobile/components/ui/FullPageSpinner.tsx | 2 +- apps/mobile/components/ui/Input.tsx | 21 ++++++++--- apps/mobile/components/ui/PageTitle.tsx | 4 +- 22 files changed, 188 insertions(+), 56 deletions(-) create mode 100644 apps/mobile/assets/splash-white.png create mode 100644 apps/mobile/components/TailwindResolver.tsx create mode 100644 apps/mobile/components/navigation/stack.tsx create mode 100644 apps/mobile/components/navigation/tabs.tsx (limited to 'apps') diff --git a/apps/mobile/app.json b/apps/mobile/app.json index 592824b7..0cba2b94 100644 --- a/apps/mobile/app.json +++ b/apps/mobile/app.json @@ -6,16 +6,21 @@ "version": "1.3.2", "orientation": "portrait", "icon": "./assets/icon.png", - "userInterfaceStyle": "light", - "splash": { - "image": "./assets/splash.png", - "resizeMode": "contain", - "backgroundColor": "#ffffff" - }, + "userInterfaceStyle": "automatic", "assetBundlePatterns": ["**/*"], "ios": { "supportsTablet": true, "bundleIdentifier": "app.hoarder.hoardermobile", + "splash": { + "image": "./assets/splash.png", + "resizeMode": "contain", + "backgroundColor": "#ffffff", + "dark": { + "image": "./assets/splash-white.png", + "resizeMode": "contain", + "backgroundColor": "#000000" + } + }, "config": { "usesNonExemptEncryption": false }, @@ -31,6 +36,16 @@ "foregroundImage": "./assets/adaptive-icon.png", "backgroundColor": "#000000" }, + "splash": { + "image": "./assets/splash.png", + "resizeMode": "contain", + "backgroundColor": "#ffffff", + "dark": { + "image": "./assets/splash-white.png", + "resizeMode": "contain", + "backgroundColor": "#000000" + } + }, "package": "app.hoarder.hoardermobile", "versionCode": 2 }, diff --git a/apps/mobile/app/_layout.tsx b/apps/mobile/app/_layout.tsx index f36c9eec..a5aafb8c 100644 --- a/apps/mobile/app/_layout.tsx +++ b/apps/mobile/app/_layout.tsx @@ -7,11 +7,15 @@ import { useRouter } from "expo-router"; import { Stack } from "expo-router/stack"; import { ShareIntentProvider, useShareIntent } from "expo-share-intent"; import { StatusBar } from "expo-status-bar"; +import { StyledStack } from "@/components/navigation/stack"; import { Providers } from "@/lib/providers"; +import { cn } from "@/lib/utils"; +import { useColorScheme } from "nativewind"; export default function RootLayout() { const router = useRouter(); const { hasShareIntent } = useShareIntent(); + const { colorScheme } = useColorScheme(); useEffect(() => { if (hasShareIntent) { @@ -24,8 +28,14 @@ export default function RootLayout() { return ( - - + - + diff --git a/apps/mobile/app/dashboard/(tabs)/_layout.tsx b/apps/mobile/app/dashboard/(tabs)/_layout.tsx index 7967b5c6..cf7e473f 100644 --- a/apps/mobile/app/dashboard/(tabs)/_layout.tsx +++ b/apps/mobile/app/dashboard/(tabs)/_layout.tsx @@ -1,17 +1,13 @@ -import React, { useEffect } from "react"; -import { Platform } from "react-native"; -import * as NavigationBar from "expo-navigation-bar"; +import React from "react"; import { Tabs } from "expo-router"; +import { StyledTabs } from "@/components/navigation/tabs"; import { ClipboardList, Home, Search, Settings } from "lucide-react-native"; export default function TabLayout() { - useEffect(() => { - if (Platform.OS == "android") { - NavigationBar.setBackgroundColorAsync("white"); - } - }, []); return ( - , }} /> - + ); } diff --git a/apps/mobile/app/dashboard/(tabs)/lists.tsx b/apps/mobile/app/dashboard/(tabs)/lists.tsx index 767b9256..53817bf2 100644 --- a/apps/mobile/app/dashboard/(tabs)/lists.tsx +++ b/apps/mobile/app/dashboard/(tabs)/lists.tsx @@ -57,8 +57,8 @@ export default function Lists() { }} renderItem={(l) => ( - - + + {l.item.logo} {l.item.name} diff --git a/apps/mobile/app/dashboard/(tabs)/search.tsx b/apps/mobile/app/dashboard/(tabs)/search.tsx index 0a4dcbfd..1a79b921 100644 --- a/apps/mobile/app/dashboard/(tabs)/search.tsx +++ b/apps/mobile/app/dashboard/(tabs)/search.tsx @@ -34,7 +34,7 @@ export default function Search() { - - + + {isLoading ? "Loading ..." : data?.email} diff --git a/apps/mobile/app/dashboard/_layout.tsx b/apps/mobile/app/dashboard/_layout.tsx index c9f7355b..7548a6db 100644 --- a/apps/mobile/app/dashboard/_layout.tsx +++ b/apps/mobile/app/dashboard/_layout.tsx @@ -3,6 +3,7 @@ import { useEffect } from "react"; import { AppState, Platform } from "react-native"; import { useRouter } from "expo-router"; import { Stack } from "expo-router/stack"; +import { StyledStack } from "@/components/navigation/stack"; import { useIsLoggedIn } from "@/lib/session"; import { focusManager } from "@tanstack/react-query"; @@ -29,7 +30,10 @@ export default function Dashboard() { }, []); return ( - + - + ); } diff --git a/apps/mobile/app/dashboard/add-link.tsx b/apps/mobile/app/dashboard/add-link.tsx index 568a36b6..d913ac01 100644 --- a/apps/mobile/app/dashboard/add-link.tsx +++ b/apps/mobile/app/dashboard/add-link.tsx @@ -39,7 +39,6 @@ export default function AddNote() { {error} )} {error} )} void }) { return ( - Hoarding + Hoarding ); @@ -83,11 +83,11 @@ export default function Sharing() { break; } case "success": { - comp = Hoarded!; + comp = Hoarded!; break; } case "error": { - comp = Error!; + comp = Error!; break; } } diff --git a/apps/mobile/app/signin.tsx b/apps/mobile/app/signin.tsx index 80a5f219..c524d1e0 100644 --- a/apps/mobile/app/signin.tsx +++ b/apps/mobile/app/signin.tsx @@ -9,6 +9,7 @@ import { } from "react-native"; import { Redirect } from "expo-router"; import Logo from "@/components/Logo"; +import { TailwindResolver } from "@/components/TailwindResolver"; import { Button } from "@/components/ui/Button"; import { Input } from "@/components/ui/Input"; import useAppSettings from "@/lib/settings"; @@ -57,7 +58,16 @@ export default function Signin() { - + ( + + )} + /> {error && ( {error} diff --git a/apps/mobile/assets/splash-white.png b/apps/mobile/assets/splash-white.png new file mode 100644 index 00000000..23df57d7 Binary files /dev/null and b/apps/mobile/assets/splash-white.png differ diff --git a/apps/mobile/components/TailwindResolver.tsx b/apps/mobile/components/TailwindResolver.tsx new file mode 100644 index 00000000..d5e084cd --- /dev/null +++ b/apps/mobile/components/TailwindResolver.tsx @@ -0,0 +1,16 @@ +import { TextStyle, ViewStyle } from "react-native"; +import { cssInterop } from "nativewind"; + +function TailwindResolverImpl({ + comp, + style, +}: { + comp: (style?: ViewStyle & TextStyle) => React.ReactNode; + style?: ViewStyle & TextStyle; +}) { + return comp(style); +} + +export const TailwindResolver = cssInterop(TailwindResolverImpl, { + className: "style", +}); diff --git a/apps/mobile/components/bookmarks/BookmarkCard.tsx b/apps/mobile/components/bookmarks/BookmarkCard.tsx index 76a05aef..9e5febe3 100644 --- a/apps/mobile/components/bookmarks/BookmarkCard.tsx +++ b/apps/mobile/components/bookmarks/BookmarkCard.tsx @@ -22,6 +22,7 @@ import { useUpdateBookmark, } from "@hoarder/shared-react/hooks/bookmarks"; +import { TailwindResolver } from "../TailwindResolver"; import { Divider } from "../ui/Divider"; import { Skeleton } from "../ui/Skeleton"; import { useToast } from "../ui/Toast"; @@ -162,9 +163,11 @@ function TagList({ bookmark }: { bookmark: ZBookmark }) { {tags.map((t) => ( - {t.name} + + {t.name} + ))} @@ -198,7 +201,7 @@ function LinkCard({ bookmark }: { bookmark: ZBookmark }) { {imageComp} WebBrowser.openBrowserAsync(url)} > {bookmark.title ?? bookmark.content.title ?? parsedUrl.host} @@ -206,7 +209,9 @@ function LinkCard({ bookmark }: { bookmark: ZBookmark }) { - {parsedUrl.host} + + {parsedUrl.host} + @@ -218,13 +223,29 @@ function TextCard({ bookmark }: { bookmark: ZBookmark }) { if (bookmark.content.type !== "text") { throw new Error("Wrong content type rendered"); } + const content = bookmark.content.text; return ( {bookmark.title && ( - {bookmark.title} + + {bookmark.title} + )} - - {bookmark.content.text} + + ( + + {content} + + )} + /> @@ -256,7 +277,9 @@ function AssetCard({ bookmark }: { bookmark: ZBookmark }) { /> {title && ( - {title} + + {title} + )} @@ -307,5 +330,5 @@ export default function BookmarkCard({ break; } - return {comp}; + return {comp}; } diff --git a/apps/mobile/components/bookmarks/BookmarkList.tsx b/apps/mobile/components/bookmarks/BookmarkList.tsx index 7477992d..3ad23072 100644 --- a/apps/mobile/components/bookmarks/BookmarkList.tsx +++ b/apps/mobile/components/bookmarks/BookmarkList.tsx @@ -37,7 +37,7 @@ export default function BookmarkList({ renderItem={(b) => } ListEmptyComponent={ - No Bookmarks + No Bookmarks } data={bookmarks} diff --git a/apps/mobile/components/bookmarks/UpdatingBookmarkList.tsx b/apps/mobile/components/bookmarks/UpdatingBookmarkList.tsx index 8495ee22..efc0d5e7 100644 --- a/apps/mobile/components/bookmarks/UpdatingBookmarkList.tsx +++ b/apps/mobile/components/bookmarks/UpdatingBookmarkList.tsx @@ -4,7 +4,7 @@ import { api } from "@/lib/trpc"; import type { ZGetBookmarksRequest } from "@hoarder/trpc/types/bookmarks"; import FullPageSpinner from "../ui/FullPageSpinner"; -import BookmarkList2 from "./BookmarkList"; +import BookmarkList from "./BookmarkList"; export default function UpdatingBookmarkList({ query, @@ -40,7 +40,7 @@ export default function UpdatingBookmarkList({ }; return ( - p.bookmarks)} header={header} onRefresh={onRefresh} diff --git a/apps/mobile/components/navigation/stack.tsx b/apps/mobile/components/navigation/stack.tsx new file mode 100644 index 00000000..f53b3652 --- /dev/null +++ b/apps/mobile/components/navigation/stack.tsx @@ -0,0 +1,27 @@ +import { TextStyle, ViewStyle } from "react-native"; +import { Stack } from "expo-router/stack"; +import { cssInterop } from "nativewind"; + +interface StackProps extends React.ComponentProps { + contentStyle?: ViewStyle; + headerStyle?: TextStyle; +} + +function StackImpl({ contentStyle, headerStyle, ...props }: StackProps) { + props.screenOptions = { + ...props.screenOptions, + contentStyle, + headerStyle: { + backgroundColor: headerStyle?.backgroundColor?.toString(), + }, + navigationBarColor: contentStyle?.backgroundColor?.toString(), + headerTintColor: headerStyle?.color?.toString(), + }; + return ; +} + +// Changing this requires reloading the app +export const StyledStack = cssInterop(StackImpl, { + contentClassName: "contentStyle", + headerClassName: "headerStyle", +}); diff --git a/apps/mobile/components/navigation/tabs.tsx b/apps/mobile/components/navigation/tabs.tsx new file mode 100644 index 00000000..976731bc --- /dev/null +++ b/apps/mobile/components/navigation/tabs.tsx @@ -0,0 +1,25 @@ +import { ViewStyle } from "react-native"; +import { Tabs } from "expo-router"; +import { cssInterop } from "nativewind"; + +function StyledTabsImpl({ + tabBarStyle, + headerStyle, + ...props +}: React.ComponentProps & { + tabBarStyle?: ViewStyle; + headerStyle?: ViewStyle; +}) { + props.screenOptions = { + ...props.screenOptions, + tabBarStyle, + headerStyle, + }; + return ; +} + +export const StyledTabs = cssInterop(StyledTabsImpl, { + tabBarClassName: "tabBarStyle", + headerClassName: "headerStyle", + sceneContainerClassName: "sceneContainerStyle", +}); diff --git a/apps/mobile/components/ui/Divider.tsx b/apps/mobile/components/ui/Divider.tsx index cf1b4624..fbc5cf64 100644 --- a/apps/mobile/components/ui/Divider.tsx +++ b/apps/mobile/components/ui/Divider.tsx @@ -2,7 +2,6 @@ import { View } from "react-native"; import { cn } from "@/lib/utils"; function Divider({ - color = "#DFE4EA", className, orientation, ...props @@ -10,15 +9,13 @@ function Divider({ color?: string; orientation: "horizontal" | "vertical"; } & React.ComponentPropsWithoutRef) { - const dividerStyles = [{ backgroundColor: color }]; - return ( ); diff --git a/apps/mobile/components/ui/FullPageSpinner.tsx b/apps/mobile/components/ui/FullPageSpinner.tsx index 89b66090..5436937a 100644 --- a/apps/mobile/components/ui/FullPageSpinner.tsx +++ b/apps/mobile/components/ui/FullPageSpinner.tsx @@ -2,7 +2,7 @@ import { ActivityIndicator, View } from "react-native"; export default function FullPageSpinner() { return ( - + ); diff --git a/apps/mobile/components/ui/Input.tsx b/apps/mobile/components/ui/Input.tsx index 01c9fb2f..57d16f5d 100644 --- a/apps/mobile/components/ui/Input.tsx +++ b/apps/mobile/components/ui/Input.tsx @@ -2,6 +2,8 @@ import { forwardRef } from "react"; import { Text, TextInput, View } from "react-native"; import { cn } from "@/lib/utils"; +import { TailwindResolver } from "../TailwindResolver"; + export interface InputProps extends React.ComponentPropsWithoutRef { label?: string; @@ -13,13 +15,20 @@ const Input = forwardRef, InputProps>( ({ className, label, labelClasses, inputClasses, ...props }, ref) => ( {label && {label}} - ( + )} - {...props} /> ), diff --git a/apps/mobile/components/ui/PageTitle.tsx b/apps/mobile/components/ui/PageTitle.tsx index 57b19e7d..1c1543ce 100644 --- a/apps/mobile/components/ui/PageTitle.tsx +++ b/apps/mobile/components/ui/PageTitle.tsx @@ -1,5 +1,7 @@ import { Text } from "react-native"; export default function PageTitle({ title }: { title: string }) { - return {title}; + return ( + {title} + ); } -- cgit v1.2.3-70-g09d2