mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-05-18 21:36:56 -04:00
Mobile changes (#1784)
* privacy link * remove spacedrop screens, add browse * icon * changes and network screen * bump react native
This commit is contained in:
@@ -44,7 +44,7 @@
|
||||
"phosphor-react-native": "^1.1.2",
|
||||
"react": "^18.2.0",
|
||||
"react-hook-form": "^7.47.0",
|
||||
"react-native": "0.72.5",
|
||||
"react-native": "0.72.6",
|
||||
"react-native-document-picker": "^9.0.1",
|
||||
"react-native-fs": "^2.20.0",
|
||||
"react-native-gesture-handler": "~2.12.1",
|
||||
|
||||
@@ -54,9 +54,6 @@ function AppNavigation() {
|
||||
|
||||
initPlausible({ platformType: 'mobile', buildInfo: buildInfo?.data });
|
||||
|
||||
// TODO: Make sure library has actually been loaded by this point - precache with useCachedLibraries?
|
||||
// if (library === undefined) throw new Error("Tried to render AppNavigation before libraries fetched!")
|
||||
|
||||
const navRef = useNavigationContainerRef();
|
||||
const routeNameRef = useRef<string>();
|
||||
|
||||
|
||||
23
apps/mobile/src/components/icons/Icon.tsx
Normal file
23
apps/mobile/src/components/icons/Icon.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { getIcon, iconNames } from '@sd/assets/util';
|
||||
import { Image, ImageProps } from 'react-native';
|
||||
import { isDarkTheme } from '@sd/client';
|
||||
|
||||
export type IconName = keyof typeof iconNames;
|
||||
|
||||
interface IconProps extends Omit<ImageProps, 'source'> {
|
||||
name: IconName;
|
||||
size?: number;
|
||||
theme?: 'dark' | 'light';
|
||||
}
|
||||
|
||||
export const Icon = ({ name, size, theme, ...props }: IconProps) => {
|
||||
const isDark = isDarkTheme();
|
||||
|
||||
return (
|
||||
<Image
|
||||
{...props}
|
||||
style={{ width: size, height: size }}
|
||||
source={getIcon(name, theme ? theme === 'dark' : isDark)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,12 +1,12 @@
|
||||
import { BottomTabScreenProps, createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
||||
import { CompositeScreenProps, NavigatorScreenParams } from '@react-navigation/native';
|
||||
import { Broadcast, CirclesFour, Planet } from 'phosphor-react-native';
|
||||
import { CirclesFour, FolderOpen, Planet } from 'phosphor-react-native';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
|
||||
import type { HomeDrawerScreenProps } from './DrawerNavigator';
|
||||
import BrowseStack, { BrowseStackParamList } from './tabs/BrowseStack';
|
||||
import NetworkStack, { NetworkStackParamList } from './tabs/NetworkStack';
|
||||
import OverviewStack, { OverviewStackParamList } from './tabs/OverviewStack';
|
||||
import SpacedropStack, { SpacedropStackParamList } from './tabs/SpacedropStack';
|
||||
import SpacesStack, { SpacesStackParamList } from './tabs/SpacesStack';
|
||||
|
||||
const Tab = createBottomTabNavigator<TabParamList>();
|
||||
|
||||
@@ -41,8 +41,8 @@ export default function TabNavigator() {
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="SpacesStack"
|
||||
component={SpacesStack}
|
||||
name="NetworkStack"
|
||||
component={NetworkStack}
|
||||
options={{
|
||||
tabBarIcon: ({ focused }) => (
|
||||
<CirclesFour
|
||||
@@ -51,22 +51,22 @@ export default function TabNavigator() {
|
||||
color={focused ? tw.color('accent') : tw.color('ink')}
|
||||
/>
|
||||
),
|
||||
tabBarLabel: 'Spaces',
|
||||
tabBarLabel: 'Network',
|
||||
tabBarLabelStyle: tw`text-[10px] font-semibold`
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="SpacedropStack"
|
||||
component={SpacedropStack}
|
||||
name="BrowseStack"
|
||||
component={BrowseStack}
|
||||
options={{
|
||||
tabBarIcon: ({ focused }) => (
|
||||
<Broadcast
|
||||
<FolderOpen
|
||||
size={22}
|
||||
weight={focused ? 'bold' : 'regular'}
|
||||
color={focused ? tw.color('accent') : tw.color('ink')}
|
||||
/>
|
||||
),
|
||||
tabBarLabel: 'Spacedrop',
|
||||
tabBarLabel: 'Browse',
|
||||
tabBarLabelStyle: tw`text-[10px] font-semibold`
|
||||
}}
|
||||
/>
|
||||
@@ -76,8 +76,8 @@ export default function TabNavigator() {
|
||||
|
||||
export type TabParamList = {
|
||||
OverviewStack: NavigatorScreenParams<OverviewStackParamList>;
|
||||
SpacedropStack: NavigatorScreenParams<SpacedropStackParamList>;
|
||||
SpacesStack: NavigatorScreenParams<SpacesStackParamList>;
|
||||
NetworkStack: NavigatorScreenParams<NetworkStackParamList>;
|
||||
BrowseStack: NavigatorScreenParams<BrowseStackParamList>;
|
||||
};
|
||||
|
||||
export type TabScreenProps<Screen extends keyof TabParamList> = CompositeScreenProps<
|
||||
|
||||
@@ -2,17 +2,17 @@ import { CompositeScreenProps } from '@react-navigation/native';
|
||||
import { createStackNavigator, StackScreenProps } from '@react-navigation/stack';
|
||||
import Header from '~/components/header/Header';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
import BrowseScreen from '~/screens/Browse';
|
||||
|
||||
import SpacesScreen from '../../screens/Spaces';
|
||||
import { SharedScreens, SharedScreensParamList } from '../SharedScreens';
|
||||
import { TabScreenProps } from '../TabNavigator';
|
||||
|
||||
const Stack = createStackNavigator<SpacesStackParamList>();
|
||||
const Stack = createStackNavigator<BrowseStackParamList>();
|
||||
|
||||
export default function SpacesStack() {
|
||||
export default function BrowseStack() {
|
||||
return (
|
||||
<Stack.Navigator
|
||||
initialRouteName="Spaces"
|
||||
initialRouteName="Browse"
|
||||
screenOptions={{
|
||||
headerStyle: { backgroundColor: tw.color('app-box') },
|
||||
headerTintColor: tw.color('ink'),
|
||||
@@ -20,18 +20,18 @@ export default function SpacesStack() {
|
||||
headerBackTitleStyle: tw`text-base`
|
||||
}}
|
||||
>
|
||||
<Stack.Screen name="Spaces" component={SpacesScreen} options={{ header: Header }} />
|
||||
<Stack.Screen name="Browse" component={BrowseScreen} options={{ header: Header }} />
|
||||
{SharedScreens(Stack as any)}
|
||||
</Stack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
export type SpacesStackParamList = {
|
||||
Spaces: undefined;
|
||||
export type BrowseStackParamList = {
|
||||
Browse: undefined;
|
||||
} & SharedScreensParamList;
|
||||
|
||||
export type SpacesStackScreenProps<Screen extends keyof SpacesStackParamList> =
|
||||
export type BrowseStackScreenProps<Screen extends keyof BrowseStackParamList> =
|
||||
CompositeScreenProps<
|
||||
StackScreenProps<SpacesStackParamList, Screen>,
|
||||
TabScreenProps<'SpacesStack'>
|
||||
StackScreenProps<BrowseStackParamList, Screen>,
|
||||
TabScreenProps<'BrowseStack'>
|
||||
>;
|
||||
@@ -2,17 +2,17 @@ import { CompositeScreenProps } from '@react-navigation/native';
|
||||
import { createStackNavigator, StackScreenProps } from '@react-navigation/stack';
|
||||
import Header from '~/components/header/Header';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
import SpacedropScreen from '~/screens/Spacedrop';
|
||||
|
||||
import NetworkScreen from '../../screens/Network';
|
||||
import { SharedScreens, SharedScreensParamList } from '../SharedScreens';
|
||||
import { TabScreenProps } from '../TabNavigator';
|
||||
|
||||
const Stack = createStackNavigator<SpacedropStackParamList>();
|
||||
const Stack = createStackNavigator<NetworkStackParamList>();
|
||||
|
||||
export default function SpacedropStack() {
|
||||
export default function NetworkStack() {
|
||||
return (
|
||||
<Stack.Navigator
|
||||
initialRouteName="Spacedrop"
|
||||
initialRouteName="Network"
|
||||
screenOptions={{
|
||||
headerStyle: { backgroundColor: tw.color('app-box') },
|
||||
headerTintColor: tw.color('ink'),
|
||||
@@ -20,22 +20,18 @@ export default function SpacedropStack() {
|
||||
headerBackTitleStyle: tw`text-base`
|
||||
}}
|
||||
>
|
||||
<Stack.Screen
|
||||
name="Spacedrop"
|
||||
component={SpacedropScreen}
|
||||
options={{ header: Header }}
|
||||
/>
|
||||
<Stack.Screen name="Network" component={NetworkScreen} options={{ header: Header }} />
|
||||
{SharedScreens(Stack as any)}
|
||||
</Stack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
export type SpacedropStackParamList = {
|
||||
Spacedrop: undefined;
|
||||
export type NetworkStackParamList = {
|
||||
Network: undefined;
|
||||
} & SharedScreensParamList;
|
||||
|
||||
export type SpacedropStackScreenProps<Screen extends keyof SpacedropStackParamList> =
|
||||
export type NetworkStackScreenProps<Screen extends keyof NetworkStackParamList> =
|
||||
CompositeScreenProps<
|
||||
StackScreenProps<SpacedropStackParamList, Screen>,
|
||||
TabScreenProps<'SpacedropStack'>
|
||||
StackScreenProps<NetworkStackParamList, Screen>,
|
||||
TabScreenProps<'NetworkStack'>
|
||||
>;
|
||||
6
apps/mobile/src/screens/Browse.tsx
Normal file
6
apps/mobile/src/screens/Browse.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import { View } from 'react-native';
|
||||
import { BrowseStackScreenProps } from '~/navigation/tabs/BrowseStack';
|
||||
|
||||
export default function BrowseScreen({ navigation, route }: BrowseStackScreenProps<'Browse'>) {
|
||||
return <View></View>;
|
||||
}
|
||||
17
apps/mobile/src/screens/Network.tsx
Normal file
17
apps/mobile/src/screens/Network.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Text, View } from 'react-native';
|
||||
import { Icon } from '~/components/icons/Icon';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
import { NetworkStackScreenProps } from '~/navigation/tabs/NetworkStack';
|
||||
|
||||
export default function NetworkScreen({ navigation }: NetworkStackScreenProps<'Network'>) {
|
||||
return (
|
||||
<View style={tw`flex-1 items-center justify-center`}>
|
||||
<Icon name="Globe" size={128} />
|
||||
<Text style={tw`mt-4 text-lg font-bold text-white`}>Your Local Network</Text>
|
||||
<Text style={tw`mt-1 max-w-sm text-center text-sm text-ink-dull`}>
|
||||
Other Spacedrive nodes on your LAN will appear here, along with your default OS
|
||||
network mounts.
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
import { GoogleDrive, iCloud, Mega } from '@sd/assets/images';
|
||||
import { DeviceMobile, Icon, Laptop, User } from 'phosphor-react-native';
|
||||
import { Alert, Image, ImageSourcePropType, Pressable, ScrollView, Text, View } from 'react-native';
|
||||
import { Polygon, Svg } from 'react-native-svg';
|
||||
import { InfoPill } from '~/components/primitive/InfoPill';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
import { SpacedropStackScreenProps } from '~/navigation/tabs/SpacedropStack';
|
||||
|
||||
const testData = [
|
||||
{
|
||||
name: "Jamie's MacBook Pro",
|
||||
receivingNodeOsType: 'macOS',
|
||||
connectionType: 'lan',
|
||||
icon: Laptop
|
||||
},
|
||||
{
|
||||
name: "Jamie's MacBook Pro",
|
||||
receivingNodeOsType: 'iOS',
|
||||
connectionType: 'lan',
|
||||
icon: DeviceMobile
|
||||
},
|
||||
{
|
||||
name: 'Brendan Alan',
|
||||
image: 'https://github.com/brendonovich.png',
|
||||
connectionType: 'p2p'
|
||||
},
|
||||
{
|
||||
name: 'Oscar Beaumont',
|
||||
image: 'https://github.com/oscartbeaumont.png',
|
||||
connectionType: 'usb'
|
||||
},
|
||||
{
|
||||
name: 'maxichrome',
|
||||
image: 'https://github.com/maxichrome.png',
|
||||
connectionType: 'p2p'
|
||||
},
|
||||
{
|
||||
name: 'Utku',
|
||||
image: 'https://github.com/utkubakir.png',
|
||||
connectionType: 'p2p'
|
||||
},
|
||||
{ name: "Jamie's Google Drive", brandIcon: 'google-drive', connectionType: 'cloud' },
|
||||
{ name: 'iCloud', brandIcon: 'icloud', connectionType: 'cloud' },
|
||||
{ name: 'Mega', brandIcon: 'mega', connectionType: 'cloud' }
|
||||
] as DropItemProps[];
|
||||
|
||||
const Hexagon = () => {
|
||||
const width = 180;
|
||||
const height = width * 1.1547;
|
||||
|
||||
return (
|
||||
<Svg width={width} height={height} viewBox="0 0 100 100">
|
||||
<Polygon
|
||||
points="0,25 0,75 50,100 100,75 100,25 50,0"
|
||||
fill={tw.color('bg-app-box/30')}
|
||||
/>
|
||||
</Svg>
|
||||
);
|
||||
};
|
||||
|
||||
type OperatingSystem = 'browser' | 'linux' | 'macOS' | 'windows' | 'iOS' | 'android';
|
||||
|
||||
type DropItemProps = {
|
||||
name: string;
|
||||
connectionType: 'lan' | 'bluetooth' | 'usb' | 'p2p' | 'cloud';
|
||||
receivingNodeOsType: OperatingSystem;
|
||||
} & ({ image: string } | { icon: Icon } | { brandIcon: string });
|
||||
|
||||
function DropItem(props: DropItemProps) {
|
||||
let icon;
|
||||
if ('image' in props) {
|
||||
icon = <Image style={tw`h-12 w-12 rounded-full`} source={{ uri: props.image }} />;
|
||||
} else if ('brandIcon' in props) {
|
||||
let brandIconSrc: ImageSourcePropType | undefined;
|
||||
switch (props.brandIcon) {
|
||||
case 'google-drive':
|
||||
brandIconSrc = GoogleDrive;
|
||||
break;
|
||||
case 'icloud':
|
||||
brandIconSrc = iCloud;
|
||||
break;
|
||||
case 'mega':
|
||||
brandIconSrc = Mega;
|
||||
break;
|
||||
}
|
||||
if (!brandIconSrc) throw new Error('Invalid brand icon url: ' + props.brandIcon);
|
||||
icon = (
|
||||
<View style={tw`flex items-center justify-center p-3`}>
|
||||
{/* // Needs width and height */}
|
||||
<Image source={brandIconSrc} style={tw`h-8 w-8 rounded-full`} />
|
||||
</View>
|
||||
);
|
||||
} else {
|
||||
// Use the custom icon or default to User icon.
|
||||
const Icon = props.icon || User;
|
||||
icon = <Icon size={30} color="white" style={twStyle(!props.name && 'opacity-20')} />;
|
||||
}
|
||||
return (
|
||||
<View style={tw`relative`}>
|
||||
<Hexagon />
|
||||
<View style={tw`absolute h-full w-full items-center justify-center`}>
|
||||
<Pressable
|
||||
style={tw`w-full items-center justify-center`}
|
||||
onPress={() => Alert.alert('TODO')}
|
||||
>
|
||||
<View
|
||||
style={tw`h-12 w-12 items-center justify-center rounded-full bg-app-button`}
|
||||
>
|
||||
{icon}
|
||||
</View>
|
||||
{props.name && (
|
||||
<Text numberOfLines={1} style={tw`mt-1 text-sm font-medium text-white`}>
|
||||
{props.name}
|
||||
</Text>
|
||||
)}
|
||||
<View style={tw`mt-1 flex flex-row gap-x-1`}>
|
||||
{props.receivingNodeOsType && <InfoPill text={props.receivingNodeOsType} />}
|
||||
{props.connectionType && (
|
||||
<InfoPill
|
||||
text={props.connectionType}
|
||||
containerStyle={twStyle(
|
||||
'px-1',
|
||||
props.connectionType === 'lan' && 'bg-green-500',
|
||||
props.connectionType === 'p2p' && 'bg-blue-500'
|
||||
)}
|
||||
textStyle={tw`uppercase text-white`}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</Pressable>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default function SpacedropScreen({ navigation }: SpacedropStackScreenProps<'Spacedrop'>) {
|
||||
return (
|
||||
<View style={tw`flex-1 py-4`}>
|
||||
<ScrollView contentContainerStyle={tw`flex flex-row flex-wrap justify-center gap-x-2`}>
|
||||
{testData.map((item, i) => (
|
||||
<DropItem key={i} {...item} />
|
||||
))}
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Text, View } from 'react-native';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
import { SpacesStackScreenProps } from '~/navigation/tabs/SpacesStack';
|
||||
|
||||
export default function SpacesScreen({ navigation }: SpacesStackScreenProps<'Spaces'>) {
|
||||
return (
|
||||
<View style={tw`flex-1 items-center justify-center`}>
|
||||
<Text style={tw`text-xl font-bold text-ink`}>Spaces</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Database } from '@sd/assets/icons';
|
||||
import { Controller } from 'react-hook-form';
|
||||
import { Alert, Image, Text, View } from 'react-native';
|
||||
import { Alert, Text, View } from 'react-native';
|
||||
import { Input } from '~/components/form/Input';
|
||||
import { Icon } from '~/components/icons/Icon';
|
||||
import { Button } from '~/components/primitive/Button';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
import { OnboardingStackScreenProps } from '~/navigation/OnboardingNavigator';
|
||||
@@ -20,7 +20,7 @@ const NewLibraryScreen = ({ navigation }: OnboardingStackScreenProps<'NewLibrary
|
||||
|
||||
return (
|
||||
<OnboardingContainer>
|
||||
<Image source={Database} style={tw`h-25 w-25`} />
|
||||
<Icon name="Database" style={tw`h-25 w-25`} />
|
||||
<OnboardingTitle style={tw`mt-4`}>Create a Library</OnboardingTitle>
|
||||
<View style={tw`w-full px-4`}>
|
||||
<OnboardingDescription style={tw`mt-4`}>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ArrowRight } from 'phosphor-react-native';
|
||||
import React from 'react';
|
||||
import { Controller } from 'react-hook-form';
|
||||
import { Pressable, Text, View, ViewStyle } from 'react-native';
|
||||
import { Linking, Pressable, Text, View, ViewStyle } from 'react-native';
|
||||
import { Button } from '~/components/primitive/Button';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
import { OnboardingStackScreenProps } from '~/navigation/OnboardingNavigator';
|
||||
@@ -83,6 +84,17 @@ const PrivacyScreen = () => {
|
||||
<Button variant="accent" size="sm" onPress={form.handleSubmit(submit)} style={tw`mt-6`}>
|
||||
<Text style={tw`text-center text-base font-medium text-ink`}>Continue</Text>
|
||||
</Button>
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
Linking.openURL('https://www.spacedrive.com/docs/product/resources/privacy');
|
||||
}}
|
||||
style={tw`mt-6 flex flex-row items-center justify-center`}
|
||||
>
|
||||
<ArrowRight size={16} style={tw`mr-0.5`} color={tw.color('text-ink-faint')} />
|
||||
<Text style={tw`text-center text-sm font-medium text-ink-faint underline`}>
|
||||
Learn more about the data we collect
|
||||
</Text>
|
||||
</Pressable>
|
||||
</OnboardingContainer>
|
||||
);
|
||||
};
|
||||
|
||||
BIN
pnpm-lock.yaml
generated
BIN
pnpm-lock.yaml
generated
Binary file not shown.
Reference in New Issue
Block a user