linked-data-browser 0.0.7 → 0.0.8-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/.env +2 -0
  2. package/.eslintrc.js +1 -0
  3. package/app/index.tsx +3 -0
  4. package/babel.config.js +7 -8
  5. package/components/DataBrowser.tsx +9 -19
  6. package/components/DataBrowserContext.ts +24 -0
  7. package/components/TargetResourceProvider.tsx +1 -1
  8. package/components/ThemeProvider.tsx +2 -4
  9. package/components/common/LoadingBar.tsx +11 -1
  10. package/components/common/ProfileAvatar.tsx +4 -7
  11. package/components/nav/Layout.tsx +20 -4
  12. package/components/nav/header/AddressBox.tsx +104 -48
  13. package/components/nav/header/AvatarMenu.tsx +42 -10
  14. package/components/nav/header/Header.tsx +26 -19
  15. package/components/nav/header/SignInMenu.tsx +63 -36
  16. package/components/nav/header/ThemeToggleMenu.tsx +21 -9
  17. package/components/nav/header/ViewMenu.tsx +79 -71
  18. package/components/sharing/AccessDropdown.tsx +18 -7
  19. package/components/sharing/CopyLink.tsx +15 -3
  20. package/components/sharing/PermissionRow.tsx +17 -2
  21. package/components/sharing/SharingModal.tsx +30 -3
  22. package/components/sharing/WacRuleForm.tsx +12 -3
  23. package/components/sharing/agentPermissions/AgentInformation.tsx +16 -3
  24. package/components/sharing/agentPermissions/AgentInput.tsx +19 -4
  25. package/components/sharing/agentPermissions/AgentPermissionRow.tsx +24 -6
  26. package/components/ui/accordion.tsx +1 -1
  27. package/components/ui/alert.tsx +62 -46
  28. package/components/ui/avatar.tsx +38 -13
  29. package/components/ui/badge.tsx +63 -48
  30. package/components/ui/button.tsx +226 -108
  31. package/components/ui/card.tsx +53 -38
  32. package/components/ui/checkbox.tsx +53 -16
  33. package/components/ui/context-menu.tsx +4 -4
  34. package/components/ui/dialog.tsx +116 -65
  35. package/components/ui/dropdown-menu.tsx +304 -105
  36. package/components/ui/icon.tsx +23 -0
  37. package/components/ui/input-dropdown.tsx +42 -5
  38. package/components/ui/input.tsx +85 -22
  39. package/components/ui/label.tsx +16 -7
  40. package/components/ui/menubar.tsx +4 -4
  41. package/components/ui/navigation-menu.tsx +157 -90
  42. package/components/ui/progress.tsx +38 -24
  43. package/components/ui/select.tsx +139 -67
  44. package/components/ui/separator.tsx +22 -7
  45. package/components/ui/skeleton.tsx +14 -11
  46. package/components/ui/switch.tsx +82 -37
  47. package/components/ui/table.tsx +57 -35
  48. package/components/ui/tabs.tsx +66 -35
  49. package/components/ui/text.tsx +221 -30
  50. package/components/ui/textarea.tsx +34 -10
  51. package/components/ui/typography.tsx +94 -65
  52. package/components/useViewContext.tsx +8 -8
  53. package/global.css +93 -3
  54. package/metro.config.js +1 -3
  55. package/package.json +3 -7
  56. package/resourceViews/Container/ContainerConfig.tsx +1 -1
  57. package/resourceViews/Container/ContainerView.tsx +63 -25
  58. package/resourceViews/Profile/ProfileConfig.tsx +1 -1
  59. package/resourceViews/Profile/ProfileKnows.tsx +17 -9
  60. package/resourceViews/Profile/ProfileView.tsx +21 -4
  61. package/resourceViews/RawCode/RawCodeConfig.tsx +1 -1
  62. package/resourceViews/RawCode/RawCodeView.tsx +20 -6
  63. package/components.json +0 -7
  64. package/lib/icons/ArrowRight.tsx +0 -4
  65. package/lib/icons/Check.tsx +0 -4
  66. package/lib/icons/ChevronDown.tsx +0 -4
  67. package/lib/icons/ChevronRight.tsx +0 -4
  68. package/lib/icons/ChevronUp.tsx +0 -4
  69. package/lib/icons/ChevronsRight.tsx +0 -4
  70. package/lib/icons/CircleSlash.tsx +0 -4
  71. package/lib/icons/CircleX.tsx +0 -4
  72. package/lib/icons/Code.tsx +0 -4
  73. package/lib/icons/EllipsisVertical.tsx +0 -4
  74. package/lib/icons/EyeOff.tsx +0 -4
  75. package/lib/icons/File.tsx +0 -4
  76. package/lib/icons/Fingerprint.tsx +0 -4
  77. package/lib/icons/Folder.tsx +0 -4
  78. package/lib/icons/Folders.tsx +0 -4
  79. package/lib/icons/Info.tsx +0 -4
  80. package/lib/icons/Link.tsx +0 -4
  81. package/lib/icons/Loader.tsx +0 -4
  82. package/lib/icons/LogOut.tsx +0 -4
  83. package/lib/icons/MonitorSmartphone.tsx +0 -4
  84. package/lib/icons/MoonStar.tsx +0 -4
  85. package/lib/icons/OctagonX.tsx +0 -4
  86. package/lib/icons/Plus.tsx +0 -4
  87. package/lib/icons/RefreshCw.tsx +0 -4
  88. package/lib/icons/Save.tsx +0 -4
  89. package/lib/icons/ShieldX.tsx +0 -4
  90. package/lib/icons/Sun.tsx +0 -4
  91. package/lib/icons/TextCursorInput.tsx +0 -4
  92. package/lib/icons/Trash.tsx +0 -4
  93. package/lib/icons/User.tsx +0 -4
  94. package/lib/icons/UserPlus.tsx +0 -4
  95. package/lib/icons/Users.tsx +0 -4
  96. package/lib/icons/ViewIcon.tsx +0 -4
  97. package/lib/icons/X.tsx +0 -4
  98. package/lib/icons/iconWithClassName.ts +0 -14
  99. package/lib/utils.ts +0 -6
  100. package/nativewind-env.d.ts +0 -1
  101. package/tailwind.config.js +0 -69
package/.env ADDED
@@ -0,0 +1,2 @@
1
+ EXPO_PUBLIC_IS_SERVER_HOSTED=false
2
+ EXPO_PUBLIC_DEFAULT_ISSUER=http://localhost:3000
package/.eslintrc.js CHANGED
@@ -8,6 +8,7 @@ module.exports = {
8
8
  rules: {
9
9
  'prettier/prettier': ['error'],
10
10
  'react/no-unstable-nested-components': 0,
11
+ 'react-native/no-inline-styles': 0,
11
12
  '@typescript-eslint/no-unused-vars': 1,
12
13
  'no-restricted-imports': [
13
14
  'error',
package/app/index.tsx CHANGED
@@ -12,6 +12,8 @@ export function Screen() {
12
12
  ? 'server-ui'
13
13
  : 'standalone-app';
14
14
 
15
+ const defaultIssuer = process.env.EXPO_PUBLIC_DEFAULT_ISSUER;
16
+
15
17
  return (
16
18
  <SafeAreaProvider>
17
19
  <StatusBar />
@@ -20,6 +22,7 @@ export function Screen() {
20
22
  mode={mode}
21
23
  renderHomepage={() => <Text>Hopepage</Text>}
22
24
  renderLogo={() => <Text>Logo</Text>}
25
+ defaultIssuer={defaultIssuer}
23
26
  />
24
27
  </SafeAreaProvider>
25
28
  );
package/babel.config.js CHANGED
@@ -2,14 +2,13 @@ module.exports = function (api) {
2
2
  api.cache(true);
3
3
  return {
4
4
  presets: [
5
- ['babel-preset-expo', {
6
- jsxImportSource: 'nativewind',
7
- unstable_transformProfile: 'hermes-canary'
8
- }],
9
- 'nativewind/babel'
5
+ [
6
+ 'babel-preset-expo',
7
+ {
8
+ unstable_transformProfile: 'hermes-canary',
9
+ },
10
+ ],
10
11
  ],
11
- plugins: [
12
- '@babel/plugin-syntax-import-meta'
13
- ]
12
+ plugins: ['@babel/plugin-syntax-import-meta'],
14
13
  };
15
14
  };
@@ -1,31 +1,21 @@
1
- import { createContext, ReactNode, useContext, useMemo } from 'react';
1
+ import { useMemo } from 'react';
2
2
  import { BrowserSolidLdoProvider } from '@ldo/solid-react';
3
3
  import React, { FunctionComponent } from 'react';
4
4
  import { Layout } from './nav/Layout';
5
5
  import { PortalHost } from '@rn-primitives/portal';
6
6
  import { TargetResourceProvider } from './TargetResourceProvider';
7
- import { ResourceViewConfig } from './ResourceView';
8
7
  import { NotifierWrapper } from 'react-native-notifier';
9
8
  import { Platform } from 'react-native';
10
9
  import { ThemeProvider } from './ThemeProvider';
10
+ import {
11
+ DataBrowserConfig,
12
+ DataBrowserConfigContext,
13
+ DataBrowserConfigProps,
14
+ } from './DataBrowserContext';
11
15
 
12
- export interface DataBrowserConfig {
13
- views: ResourceViewConfig[];
14
- mode: 'standalone-app' | 'server-ui';
15
- defaultIssuer?: string;
16
- origin?: string;
17
- renderHomepage?: () => ReactNode;
18
- renderLogo?: () => ReactNode;
19
- }
20
-
21
- // @ts-ignore This will be filled in once the component mounts
22
- export const DataBrowserConfigContext = createContext<DataBrowserConfig>({});
23
-
24
- export function useDataBrowserConfig() {
25
- return useContext(DataBrowserConfigContext);
26
- }
27
-
28
- export const DataBrowser: FunctionComponent<DataBrowserConfig> = (props) => {
16
+ export const DataBrowser: FunctionComponent<DataBrowserConfigProps> = (
17
+ props,
18
+ ) => {
29
19
  const providerProps = useMemo<DataBrowserConfig>(() => {
30
20
  return {
31
21
  origin:
@@ -0,0 +1,24 @@
1
+ import { createContext, ReactNode, useContext } from 'react';
2
+ import { ResourceViewConfig } from './ResourceView';
3
+
4
+ export interface DataBrowserConfigProps {
5
+ views: ResourceViewConfig[];
6
+ mode: 'standalone-app' | 'server-ui';
7
+ defaultIssuer?: string;
8
+ origin?: string;
9
+ renderHomepage?: () => ReactNode;
10
+ renderLogo?: () => ReactNode;
11
+ }
12
+
13
+ export interface DataBrowserConfig extends DataBrowserConfigProps {
14
+ defaultIssuer: string;
15
+ }
16
+
17
+ export const DataBrowserConfigContext = createContext<DataBrowserConfig>(
18
+ // @ts-ignore This will be filled in once the component mounts
19
+ {},
20
+ );
21
+
22
+ export function useDataBrowserConfig() {
23
+ return useContext(DataBrowserConfigContext);
24
+ }
@@ -7,7 +7,7 @@ import {
7
7
  } from 'react';
8
8
  import { SolidLeaf, SolidContainer } from '@ldo/connected-solid';
9
9
  import { InvalidIdentifierResource } from '@ldo/connected';
10
- import { useDataBrowserConfig } from './DataBrowser';
10
+ import { useDataBrowserConfig } from './DataBrowserContext';
11
11
  import { useResource } from '@ldo/solid-react';
12
12
  import { Platform } from 'react-native';
13
13
 
@@ -17,7 +17,6 @@ import {
17
17
  Theme,
18
18
  } from '@react-navigation/native';
19
19
  import { NAV_THEME } from '../lib/constants';
20
- import { useColorScheme } from 'nativewind';
21
20
  import { setAndroidNavigationBar } from '../lib/android-navigation-bar';
22
21
 
23
22
  const COLOR_SCHEME_KEY = 'colorScheme';
@@ -57,14 +56,14 @@ const usePlatformSpecificSetup = Platform.select({
57
56
  export const ThemeProvider: FunctionComponent<PropsWithChildren> = ({
58
57
  children,
59
58
  }) => {
60
- const { colorScheme, setColorScheme } = useColorScheme();
59
+ const [colorScheme, setColorScheme] = useState<ColorSchemeName>();
61
60
  const [loadingColorScheme, setLoadingColorScheme] = useState(true);
62
61
 
63
62
  usePlatformSpecificSetup();
64
63
 
65
64
  useEffect(() => {
66
65
  const lookupCurColorScheme = async () => {
67
- setColorScheme(Appearance.getColorScheme() ?? 'system');
66
+ setColorScheme(Appearance.getColorScheme() ?? 'light');
68
67
  const storedColorSchemeName: ColorSchemeName =
69
68
  ((await AsyncStorage.getItem(COLOR_SCHEME_KEY)) as ColorSchemeName) ||
70
69
  Appearance.getColorScheme();
@@ -74,7 +73,6 @@ export const ThemeProvider: FunctionComponent<PropsWithChildren> = ({
74
73
  setLoadingColorScheme(false);
75
74
  };
76
75
  lookupCurColorScheme();
77
- // eslint-disable-next-line react-hooks/exhaustive-deps
78
76
  }, []);
79
77
 
80
78
  const context = useMemo(
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import { useTheme } from '@react-navigation/native';
3
3
  import { FunctionComponent } from 'react';
4
4
  import { Bar } from 'react-native-progress';
5
+ import { StyleSheet } from 'react-native';
5
6
 
6
7
  interface LoadingBarProps {
7
8
  isLoading: boolean;
@@ -21,7 +22,16 @@ export const LoadingBar: FunctionComponent<LoadingBarProps> = ({
21
22
  borderWidth={0}
22
23
  borderRadius={0}
23
24
  width={null}
24
- className="absolute top-0 left-0 right-0"
25
+ style={styles.bar}
25
26
  />
26
27
  );
27
28
  };
29
+
30
+ const styles = StyleSheet.create({
31
+ bar: {
32
+ position: 'absolute',
33
+ top: 0,
34
+ left: 0,
35
+ right: 0,
36
+ },
37
+ });
@@ -2,22 +2,19 @@ import React from 'react';
2
2
  import { SolidProfile } from '../../.ldo/profile.typings';
3
3
  import { Avatar, AvatarFallback, AvatarImage } from '../ui/avatar';
4
4
  import { Text } from '../ui/text';
5
- import { User } from '../../lib/icons/User';
5
+ import { User } from 'lucide-react-native';
6
6
  import { FunctionComponent } from 'react';
7
7
 
8
8
  interface ProfileAvatarProps {
9
9
  profile?: SolidProfile;
10
- className?: string;
10
+ style?: any;
11
11
  }
12
12
 
13
13
  export const ProfileAvatar: FunctionComponent<ProfileAvatarProps> = ({
14
14
  profile,
15
- className,
15
+ style,
16
16
  }) => (
17
- <Avatar
18
- alt={profile?.fn ? `${profile.fn}'s Avatar` : ''}
19
- className={className}
20
- >
17
+ <Avatar alt={profile?.fn ? `${profile.fn}'s Avatar` : ''} style={style}>
21
18
  <AvatarImage source={{ uri: profile?.hasPhoto?.['@id'] }} />
22
19
  <AvatarFallback>
23
20
  <Text>
@@ -7,11 +7,12 @@ import React, {
7
7
  import { ResourceViewConfig } from '../ResourceView';
8
8
 
9
9
  import { Header } from './header/Header';
10
- import { View } from 'react-native';
10
+ import { View, StyleSheet } from 'react-native';
11
11
  import { useViewContext, ViewContextProvider } from '../useViewContext';
12
12
  import { DialogProvider } from './DialogProvider';
13
13
  import { useSolidAuth } from '@ldo/solid-react';
14
14
  import { SharingModalProvider } from '../sharing/SharingModal';
15
+ import { useTheme } from '@react-navigation/native';
15
16
 
16
17
  export const ValidViewContext = createContext<{
17
18
  validViews: ResourceViewConfig[];
@@ -22,6 +23,7 @@ export const ValidViewContext = createContext<{
22
23
 
23
24
  export const Layout: FunctionComponent = () => {
24
25
  const { ranInitialAuthCheck } = useSolidAuth();
26
+ const { colors } = useTheme();
25
27
 
26
28
  if (!ranInitialAuthCheck) {
27
29
  return <></>;
@@ -31,9 +33,13 @@ export const Layout: FunctionComponent = () => {
31
33
  <DialogProvider>
32
34
  <ViewContextProvider>
33
35
  <SharingModalProvider>
34
- <Header />
35
- <View className="flex-1 z-0">
36
- <RenderView />
36
+ <View
37
+ style={[styles.container, { backgroundColor: colors.background }]}
38
+ >
39
+ <Header />
40
+ <View style={styles.contentView}>
41
+ <RenderView />
42
+ </View>
37
43
  </View>
38
44
  </SharingModalProvider>
39
45
  </ViewContextProvider>
@@ -55,3 +61,13 @@ export const RenderView: FunctionComponent = () => {
55
61
  const CurView = curViewConfig.view;
56
62
  return <CurView />;
57
63
  };
64
+
65
+ const styles = StyleSheet.create({
66
+ container: {
67
+ flex: 1,
68
+ },
69
+ contentView: {
70
+ flex: 1,
71
+ zIndex: 0,
72
+ },
73
+ });
@@ -1,20 +1,23 @@
1
1
  import React, { useEffect, useMemo } from 'react';
2
2
  import { FunctionComponent, useState } from 'react';
3
- import { TouchableOpacity, View } from 'react-native';
3
+ import { TouchableOpacity, View, StyleSheet, ScrollView } from 'react-native';
4
4
  import { Input } from '../../ui/input';
5
- import { ChevronRight } from '../../../lib/icons/ChevronRight';
6
- import { ChevronsRight } from '../../../lib/icons/ChevronsRight';
7
- import { TextCursorInput } from '../../../lib/icons/TextCursorInput';
8
- import { RefreshCw } from '../../../lib/icons/RefreshCw';
9
- import { ArrowRight } from '../../../lib/icons/ArrowRight';
5
+ import { ChevronRight } from 'lucide-react-native';
6
+ import { ChevronsRight } from 'lucide-react-native';
7
+ import { TextCursorInput } from 'lucide-react-native';
8
+ import { RefreshCw } from 'lucide-react-native';
9
+ import { ArrowRight } from 'lucide-react-native';
10
10
  import { Button } from '../../../components/ui/button';
11
11
  import { Text } from '../../ui/text';
12
12
  import { useTargetResource } from '../../TargetResourceProvider';
13
+ import { useTheme } from '@react-navigation/native';
14
+ import { Icon } from '../../../components/ui/icon';
13
15
 
14
16
  export const AddressBox: FunctionComponent = () => {
15
17
  const [isTextMode, setIsTextMode] = useState(false);
16
18
  const { targetUri, refresh, navigateTo, targetResource } =
17
19
  useTargetResource();
20
+ const { colors } = useTheme();
18
21
 
19
22
  const [textBoxValue, setTextBoxValue] = useState(targetUri ?? '');
20
23
  useEffect(() => {
@@ -51,9 +54,9 @@ export const AddressBox: FunctionComponent = () => {
51
54
  }, [targetUri]);
52
55
 
53
56
  return (
54
- <View className="flex-1">
57
+ <View style={styles.container}>
55
58
  <Input
56
- className="flex-1 bg-secondary web:py-2.5 border-none pl-10 pr-10 h-[40px] text-sm web:focus-visible:ring-0 web:focus-visible:ring-transparent web:focus-visible:ring-offset-0 web:focus:outline-none web:outline-none"
59
+ style={[styles.input, { backgroundColor: colors.border }]}
57
60
  onFocus={() => setIsTextMode(true)}
58
61
  onBlur={() => setTimeout(() => setIsTextMode(false), 100)}
59
62
  onChangeText={setTextBoxValue}
@@ -66,22 +69,41 @@ export const AddressBox: FunctionComponent = () => {
66
69
  />
67
70
  <Button
68
71
  variant="secondary"
69
- className="absolute left-0 w-10 h-10"
72
+ style={styles.leftButton}
70
73
  onPress={() => setIsTextMode((val) => !val)}
71
- iconLeft={
72
- isTextMode ? (
73
- <ChevronsRight size={20} />
74
- ) : (
75
- <TextCursorInput size={20} />
76
- )
77
- }
74
+ iconLeft={isTextMode ? ChevronsRight : TextCursorInput}
75
+ textStyle={styles.buttonText}
78
76
  />
77
+ <ScrollView
78
+ style={styles.breadcrumbContainer}
79
+ contentContainerStyle={styles.breadcrumbContentContainer}
80
+ pointerEvents="none"
81
+ horizontal
82
+ >
83
+ {!isTextMode &&
84
+ breadcrumbInfo.map((item, index) => (
85
+ <View style={styles.breadcrumbItem} key={item.uri}>
86
+ <TouchableOpacity onPress={() => navigateTo(item.uri)}>
87
+ <View pointerEvents="auto">
88
+ <Text style={styles.breadcrumbText} size="sm">
89
+ {item.name}
90
+ </Text>
91
+ </View>
92
+ </TouchableOpacity>
93
+ {index !== breadcrumbInfo.length - 1 ? (
94
+ <Icon icon={ChevronRight} style={styles.chevron} />
95
+ ) : (
96
+ <View style={styles.spacer} />
97
+ )}
98
+ </View>
99
+ ))}
100
+ </ScrollView>
79
101
  {(() => {
80
102
  const shouldRefresh = targetUri === textBoxValue || !isTextMode;
81
103
  return (
82
104
  <Button
83
105
  variant="secondary"
84
- className="absolute right-0 w-10 h-10"
106
+ style={styles.rightButton}
85
107
  onPressIn={() => {
86
108
  if (shouldRefresh) {
87
109
  refresh();
@@ -89,39 +111,73 @@ export const AddressBox: FunctionComponent = () => {
89
111
  navigateTo(textBoxValue);
90
112
  }
91
113
  }}
92
- >
93
- <Text className={targetResource?.isLoading() ? 'animate-spin' : ''}>
94
- {shouldRefresh || targetResource?.isLoading() ? (
95
- <RefreshCw size={20} />
96
- ) : (
97
- <ArrowRight size={20} />
98
- )}
99
- </Text>
100
- </Button>
114
+ iconLeft={
115
+ shouldRefresh || targetResource?.isLoading()
116
+ ? RefreshCw
117
+ : ArrowRight
118
+ }
119
+ textStyle={styles.buttonText}
120
+ />
101
121
  );
102
122
  })()}
103
- <View
104
- className="absolute top-0 left-0 right-0 bottom-0 flex-row-reverse items-center ml-10 mr-10 overflow-x-auto scrollbar-hide [direction:rtl]"
105
- pointerEvents="none"
106
- >
107
- {!isTextMode &&
108
- breadcrumbInfo.map((item, index) => (
109
- <View className="flex-row" key={item.uri}>
110
- {index !== breadcrumbInfo.length - 1 ? (
111
- <ChevronRight className="w-4 h-4 mr-0.5 mt-0.5 text-gray-500" />
112
- ) : (
113
- <View className="w-2" />
114
- )}
115
- <TouchableOpacity onPress={() => navigateTo(item.uri)}>
116
- <View pointerEvents="auto">
117
- <Text className="mr-0.5 underline" size="sm">
118
- {item.name}
119
- </Text>
120
- </View>
121
- </TouchableOpacity>
122
- </View>
123
- ))}
124
- </View>
125
123
  </View>
126
124
  );
127
125
  };
126
+
127
+ const styles = StyleSheet.create({
128
+ container: {
129
+ flex: 1,
130
+ height: 40,
131
+ },
132
+ input: {
133
+ flex: 1,
134
+ borderWidth: 0,
135
+ paddingHorizontal: 40,
136
+ height: 40,
137
+ fontSize: 14,
138
+ },
139
+ leftButton: {
140
+ position: 'absolute',
141
+ left: 0,
142
+ width: 40,
143
+ height: 40,
144
+ },
145
+ rightButton: {
146
+ position: 'absolute',
147
+ right: 0,
148
+ width: 40,
149
+ height: 40,
150
+ },
151
+ breadcrumbContainer: {
152
+ position: 'absolute',
153
+ top: 0,
154
+ left: 0,
155
+ right: 0,
156
+ bottom: 0,
157
+ marginLeft: 40,
158
+ marginRight: 40,
159
+ },
160
+ breadcrumbContentContainer: {
161
+ flexDirection: 'row',
162
+ alignItems: 'center',
163
+ },
164
+ breadcrumbItem: {
165
+ flexDirection: 'row',
166
+ },
167
+ chevron: {
168
+ width: 16,
169
+ height: 16,
170
+ marginRight: 2,
171
+ color: 'hsl(var(--muted-foreground))',
172
+ },
173
+ spacer: {
174
+ width: 8,
175
+ },
176
+ breadcrumbText: {
177
+ marginRight: 2,
178
+ textDecorationLine: 'underline',
179
+ },
180
+ buttonText: {
181
+ fontSize: 20,
182
+ },
183
+ });
@@ -1,7 +1,7 @@
1
1
  import { useResource, useSolidAuth, useSubject } from '@ldo/solid-react';
2
2
  import React from 'react';
3
3
  import { FunctionComponent } from 'react';
4
- import { View } from 'react-native';
4
+ import { View, StyleSheet } from 'react-native';
5
5
  import { SolidProfileShapeType } from '../../../.ldo/profile.shapeTypes';
6
6
  import { Text } from '../../ui/text';
7
7
  import { Button } from '../../ui/button';
@@ -13,9 +13,10 @@ import {
13
13
  DropdownMenuSeparator,
14
14
  DropdownMenuTrigger,
15
15
  } from '../../ui/dropdown-menu';
16
- import { LogOut } from '../../../lib/icons/LogOut';
16
+ import { LogOut } from 'lucide-react-native';
17
17
  import { useTargetResource } from '../../TargetResourceProvider';
18
18
  import { ProfileAvatar } from '../../common/ProfileAvatar';
19
+ import { Icon } from '../../ui/icon';
19
20
 
20
21
  export const AvatarMenu: FunctionComponent = () => {
21
22
  const { session, logout } = useSolidAuth();
@@ -29,14 +30,14 @@ export const AvatarMenu: FunctionComponent = () => {
29
30
  return (
30
31
  <DropdownMenu>
31
32
  <DropdownMenuTrigger asChild>
32
- <Button key="setMemu" variant="ghost" className="w-10">
33
+ <Button key="setMemu" variant="ghost" style={styles.button}>
33
34
  <ProfileAvatar profile={profile} />
34
35
  </Button>
35
36
  </DropdownMenuTrigger>
36
- <DropdownMenuContent className="w-64 native:w-72 mr-2 mt-2">
37
- <View className="p-2 flex-row items-center">
38
- <ProfileAvatar profile={profile} className="w-20 h-20" />
39
- <View className="ml-2">
37
+ <DropdownMenuContent style={styles.dropdownContent}>
38
+ <View style={styles.profileContainer}>
39
+ <ProfileAvatar profile={profile} style={styles.profileAvatar} />
40
+ <View style={styles.profileInfo}>
40
41
  <Text>{profile?.fn || ''}</Text>
41
42
  <Button
42
43
  size="sm"
@@ -49,11 +50,42 @@ export const AvatarMenu: FunctionComponent = () => {
49
50
  <ThemeToggleMenu />
50
51
  <DropdownMenuSeparator />
51
52
  <DropdownMenuItem onPress={logout}>
52
- <Text className="flex flex-row gap-1 items-center">
53
- <LogOut /> Log Out
54
- </Text>
53
+ <View style={styles.logoutText}>
54
+ <Icon icon={LogOut} />
55
+ <Text>Log Out</Text>
56
+ </View>
55
57
  </DropdownMenuItem>
56
58
  </DropdownMenuContent>
57
59
  </DropdownMenu>
58
60
  );
59
61
  };
62
+
63
+ const styles = StyleSheet.create({
64
+ button: {
65
+ width: 40,
66
+ },
67
+ dropdownContent: {
68
+ width: 256,
69
+ marginRight: 8,
70
+ marginTop: 8,
71
+ },
72
+ profileContainer: {
73
+ padding: 8,
74
+ flexDirection: 'row',
75
+ alignItems: 'center',
76
+ },
77
+ profileAvatar: {
78
+ width: 80,
79
+ height: 80,
80
+ borderRadius: 40,
81
+ },
82
+ profileInfo: {
83
+ marginLeft: 8,
84
+ gap: 8,
85
+ },
86
+ logoutText: {
87
+ flexDirection: 'row',
88
+ gap: 4,
89
+ alignItems: 'center',
90
+ },
91
+ });
@@ -1,14 +1,14 @@
1
1
  import React from 'react';
2
2
  import { useSolidAuth } from '@ldo/solid-react';
3
3
  import { FunctionComponent } from 'react';
4
- import { View } from 'react-native';
4
+ import { StyleSheet } from 'react-native';
5
5
  import { AddressBox } from './AddressBox';
6
6
  import { AvatarMenu } from './AvatarMenu';
7
7
  import { SignInMenu } from './SignInMenu';
8
8
  import { ViewMenu } from './ViewMenu';
9
9
  import { Card } from '../../ui/card';
10
10
  import { Button } from '../../ui/button';
11
- import { UserPlus } from '../../../lib/icons/UserPlus';
11
+ import { UserPlus } from 'lucide-react-native';
12
12
  import { useSharingModal } from '../../sharing/SharingModal';
13
13
 
14
14
  export const Header: FunctionComponent = () => {
@@ -16,28 +16,35 @@ export const Header: FunctionComponent = () => {
16
16
  const { openSharingModal } = useSharingModal();
17
17
 
18
18
  return (
19
- <Card
20
- className={
21
- 'h-12 flex-row justify-between items-center rounded-none border-0 sm:p-2 p-1 z-[1]'
22
- }
23
- >
19
+ <Card style={styles.card}>
24
20
  <AddressBox />
25
- <View className="mr-1" />
26
21
  {session.isLoggedIn && (
27
- <>
28
- <Button
29
- key="setMemu"
30
- variant="ghost"
31
- className="w-10"
32
- onPress={openSharingModal}
33
- iconLeft={<UserPlus />}
34
- />
35
- <View className="mr-1" />
36
- </>
22
+ <Button
23
+ key="setMemu"
24
+ variant="ghost"
25
+ style={styles.shareButton}
26
+ onPress={openSharingModal}
27
+ iconLeft={UserPlus}
28
+ />
37
29
  )}
38
30
  <ViewMenu />
39
- <View className="mr-1" />
40
31
  {session.isLoggedIn ? <AvatarMenu /> : <SignInMenu />}
41
32
  </Card>
42
33
  );
43
34
  };
35
+
36
+ const styles = StyleSheet.create({
37
+ card: {
38
+ height: 48, // h-12 = 3rem = 48px
39
+ flexDirection: 'row',
40
+ justifyContent: 'space-between',
41
+ alignItems: 'center',
42
+ borderRadius: 0, // rounded-none
43
+ borderWidth: 0, // border-0
44
+ padding: 8,
45
+ zIndex: 1, // z-[1]
46
+ },
47
+ shareButton: {
48
+ width: 40, // w-10 = 2.5rem = 40px
49
+ },
50
+ });