linked-data-browser 0.0.2 → 0.0.4
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.
- package/.ldo/profile.context.ts +14 -0
- package/.ldo/profile.typings.ts +6 -4
- package/app/index.tsx +2 -1
- package/components/ResourceView.tsx +7 -2
- package/components/common/LoadingBar.tsx +27 -0
- package/components/common/ProfileAvatar.tsx +28 -0
- package/components/nav/DialogProvider.tsx +5 -8
- package/components/nav/Layout.tsx +20 -81
- package/components/nav/header/AddressBox.tsx +8 -7
- package/components/nav/header/AvatarMenu.tsx +54 -57
- package/components/nav/header/Header.tsx +18 -2
- package/components/nav/header/SignInMenu.tsx +11 -14
- package/components/nav/header/ViewMenu.tsx +4 -4
- package/components/sharing/AccessDropdown.tsx +95 -0
- package/components/sharing/CopyLink.tsx +21 -0
- package/components/sharing/PermissionRow.tsx +38 -0
- package/components/sharing/SharingModal.tsx +149 -0
- package/components/sharing/WacRuleForm.tsx +44 -0
- package/components/sharing/agentPermissions/AgentInformation.tsx +37 -0
- package/components/sharing/agentPermissions/AgentInput.tsx +126 -0
- package/components/sharing/agentPermissions/AgentPermissionRow.tsx +36 -0
- package/components/sharing/agentPermissions/AgentPermissions.tsx +56 -0
- package/components/sharing/agentPermissions/useContactFilter.ts +35 -0
- package/components/ui/button.tsx +52 -5
- package/components/ui/dialog.tsx +1 -1
- package/components/ui/input-dropdown.tsx +105 -0
- package/components/ui/input.tsx +34 -2
- package/components/ui/text.tsx +47 -0
- package/components/useViewContext.tsx +141 -0
- package/components/{nav/utilityResourceViews → utilityResourceViews}/ErrorMessageResourceView.tsx +2 -2
- package/lib/icons/Fingerprint.tsx +4 -0
- package/lib/icons/Link.tsx +4 -0
- package/lib/icons/Loader.tsx +4 -0
- package/lib/icons/LogOut.tsx +4 -0
- package/lib/icons/Plus.tsx +4 -0
- package/lib/icons/Save.tsx +4 -0
- package/lib/icons/User.tsx +4 -0
- package/lib/icons/UserPlus.tsx +4 -0
- package/lib/icons/Users.tsx +4 -0
- package/package.json +12 -7
- package/resourceViews/Container/ContainerView.tsx +5 -6
- package/resourceViews/Profile/ProfileConfig.tsx +20 -0
- package/resourceViews/Profile/ProfileKnows.tsx +65 -0
- package/resourceViews/Profile/ProfileView.tsx +59 -0
- package/resourceViews/RawCode/RawCodeView.tsx +35 -9
- package/test-server/server-config.json +1 -1
- package/components/nav/useValidView.tsx +0 -51
package/.ldo/profile.context.ts
CHANGED
|
@@ -8,12 +8,14 @@ import { LdoJsonldContext } from "@ldo/ldo";
|
|
|
8
8
|
export const profileContext: LdoJsonldContext = {
|
|
9
9
|
type: {
|
|
10
10
|
"@id": "@type",
|
|
11
|
+
"@isCollection": true,
|
|
11
12
|
},
|
|
12
13
|
Person: {
|
|
13
14
|
"@id": "http://schema.org/Person",
|
|
14
15
|
"@context": {
|
|
15
16
|
type: {
|
|
16
17
|
"@id": "@type",
|
|
18
|
+
"@isCollection": true,
|
|
17
19
|
},
|
|
18
20
|
fn: {
|
|
19
21
|
"@id": "http://www.w3.org/2006/vcard/ns#fn",
|
|
@@ -107,6 +109,7 @@ export const profileContext: LdoJsonldContext = {
|
|
|
107
109
|
"@context": {
|
|
108
110
|
type: {
|
|
109
111
|
"@id": "@type",
|
|
112
|
+
"@isCollection": true,
|
|
110
113
|
},
|
|
111
114
|
fn: {
|
|
112
115
|
"@id": "http://www.w3.org/2006/vcard/ns#fn",
|
|
@@ -238,6 +241,7 @@ export const profileContext: LdoJsonldContext = {
|
|
|
238
241
|
"@context": {
|
|
239
242
|
type: {
|
|
240
243
|
"@id": "@type",
|
|
244
|
+
"@isCollection": true,
|
|
241
245
|
},
|
|
242
246
|
value: {
|
|
243
247
|
"@id": "http://www.w3.org/2006/vcard/ns#value",
|
|
@@ -250,6 +254,7 @@ export const profileContext: LdoJsonldContext = {
|
|
|
250
254
|
"@context": {
|
|
251
255
|
type: {
|
|
252
256
|
"@id": "@type",
|
|
257
|
+
"@isCollection": true,
|
|
253
258
|
},
|
|
254
259
|
value: {
|
|
255
260
|
"@id": "http://www.w3.org/2006/vcard/ns#value",
|
|
@@ -262,6 +267,7 @@ export const profileContext: LdoJsonldContext = {
|
|
|
262
267
|
"@context": {
|
|
263
268
|
type: {
|
|
264
269
|
"@id": "@type",
|
|
270
|
+
"@isCollection": true,
|
|
265
271
|
},
|
|
266
272
|
value: {
|
|
267
273
|
"@id": "http://www.w3.org/2006/vcard/ns#value",
|
|
@@ -274,6 +280,7 @@ export const profileContext: LdoJsonldContext = {
|
|
|
274
280
|
"@context": {
|
|
275
281
|
type: {
|
|
276
282
|
"@id": "@type",
|
|
283
|
+
"@isCollection": true,
|
|
277
284
|
},
|
|
278
285
|
value: {
|
|
279
286
|
"@id": "http://www.w3.org/2006/vcard/ns#value",
|
|
@@ -286,6 +293,7 @@ export const profileContext: LdoJsonldContext = {
|
|
|
286
293
|
"@context": {
|
|
287
294
|
type: {
|
|
288
295
|
"@id": "@type",
|
|
296
|
+
"@isCollection": true,
|
|
289
297
|
},
|
|
290
298
|
value: {
|
|
291
299
|
"@id": "http://www.w3.org/2006/vcard/ns#value",
|
|
@@ -298,6 +306,7 @@ export const profileContext: LdoJsonldContext = {
|
|
|
298
306
|
"@context": {
|
|
299
307
|
type: {
|
|
300
308
|
"@id": "@type",
|
|
309
|
+
"@isCollection": true,
|
|
301
310
|
},
|
|
302
311
|
value: {
|
|
303
312
|
"@id": "http://www.w3.org/2006/vcard/ns#value",
|
|
@@ -310,6 +319,7 @@ export const profileContext: LdoJsonldContext = {
|
|
|
310
319
|
"@context": {
|
|
311
320
|
type: {
|
|
312
321
|
"@id": "@type",
|
|
322
|
+
"@isCollection": true,
|
|
313
323
|
},
|
|
314
324
|
value: {
|
|
315
325
|
"@id": "http://www.w3.org/2006/vcard/ns#value",
|
|
@@ -322,6 +332,7 @@ export const profileContext: LdoJsonldContext = {
|
|
|
322
332
|
"@context": {
|
|
323
333
|
type: {
|
|
324
334
|
"@id": "@type",
|
|
335
|
+
"@isCollection": true,
|
|
325
336
|
},
|
|
326
337
|
value: {
|
|
327
338
|
"@id": "http://www.w3.org/2006/vcard/ns#value",
|
|
@@ -334,6 +345,7 @@ export const profileContext: LdoJsonldContext = {
|
|
|
334
345
|
"@context": {
|
|
335
346
|
type: {
|
|
336
347
|
"@id": "@type",
|
|
348
|
+
"@isCollection": true,
|
|
337
349
|
},
|
|
338
350
|
value: {
|
|
339
351
|
"@id": "http://www.w3.org/2006/vcard/ns#value",
|
|
@@ -346,6 +358,7 @@ export const profileContext: LdoJsonldContext = {
|
|
|
346
358
|
"@context": {
|
|
347
359
|
type: {
|
|
348
360
|
"@id": "@type",
|
|
361
|
+
"@isCollection": true,
|
|
349
362
|
},
|
|
350
363
|
value: {
|
|
351
364
|
"@id": "http://www.w3.org/2006/vcard/ns#value",
|
|
@@ -358,6 +371,7 @@ export const profileContext: LdoJsonldContext = {
|
|
|
358
371
|
"@context": {
|
|
359
372
|
type: {
|
|
360
373
|
"@id": "@type",
|
|
374
|
+
"@isCollection": true,
|
|
361
375
|
},
|
|
362
376
|
value: {
|
|
363
377
|
"@id": "http://www.w3.org/2006/vcard/ns#value",
|
package/.ldo/profile.typings.ts
CHANGED
|
@@ -154,7 +154,7 @@ export interface EmailShape {
|
|
|
154
154
|
/**
|
|
155
155
|
* The type of email.
|
|
156
156
|
*/
|
|
157
|
-
type?:
|
|
157
|
+
type?: LdSet<
|
|
158
158
|
| {
|
|
159
159
|
"@id": "Dom";
|
|
160
160
|
}
|
|
@@ -187,7 +187,8 @@ export interface EmailShape {
|
|
|
187
187
|
}
|
|
188
188
|
| {
|
|
189
189
|
"@id": "X400";
|
|
190
|
-
}
|
|
190
|
+
}
|
|
191
|
+
>;
|
|
191
192
|
/**
|
|
192
193
|
* The value of an email as a mailto link (Example <mailto:jane@example.com>)
|
|
193
194
|
*/
|
|
@@ -205,7 +206,7 @@ export interface PhoneNumberShape {
|
|
|
205
206
|
/**
|
|
206
207
|
* They type of Phone Number
|
|
207
208
|
*/
|
|
208
|
-
type?:
|
|
209
|
+
type?: LdSet<
|
|
209
210
|
| {
|
|
210
211
|
"@id": "Dom";
|
|
211
212
|
}
|
|
@@ -238,7 +239,8 @@ export interface PhoneNumberShape {
|
|
|
238
239
|
}
|
|
239
240
|
| {
|
|
240
241
|
"@id": "X400";
|
|
241
|
-
}
|
|
242
|
+
}
|
|
243
|
+
>;
|
|
242
244
|
/**
|
|
243
245
|
* The value of a phone number as a tel link (Example <tel:555-555-5555>)
|
|
244
246
|
*/
|
package/app/index.tsx
CHANGED
|
@@ -5,6 +5,7 @@ import { RawCodeConfig } from '../resourceViews/RawCode/RawCodeConfig';
|
|
|
5
5
|
import { ContainerConfig } from '../resourceViews/Container/ContainerConfig';
|
|
6
6
|
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
|
7
7
|
import { StatusBar } from 'react-native';
|
|
8
|
+
import { ProfileConfig } from 'resourceViews/Profile/ProfileConfig';
|
|
8
9
|
|
|
9
10
|
export function Screen() {
|
|
10
11
|
const mode = process.env.EXPO_PUBLIC_IS_SERVER_HOSTED
|
|
@@ -15,7 +16,7 @@ export function Screen() {
|
|
|
15
16
|
<SafeAreaProvider>
|
|
16
17
|
<StatusBar />
|
|
17
18
|
<DataBrowser
|
|
18
|
-
views={[ContainerConfig, RawCodeConfig]}
|
|
19
|
+
views={[ProfileConfig, ContainerConfig, RawCodeConfig]}
|
|
19
20
|
mode={mode}
|
|
20
21
|
renderHomepage={() => <Text>Hopepage</Text>}
|
|
21
22
|
renderLogo={() => <Text>Logo</Text>}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
SolidConnectedPlugin,
|
|
3
|
+
SolidContainer,
|
|
4
|
+
SolidLeaf,
|
|
5
|
+
} from '@ldo/connected-solid';
|
|
6
|
+
import { ConnectedLdoDataset } from '@ldo/connected';
|
|
2
7
|
import { LucideIcon } from 'lucide-react-native';
|
|
3
8
|
import { ElementType } from 'react';
|
|
4
|
-
|
|
5
9
|
export interface ResourceViewConfig {
|
|
6
10
|
name: string;
|
|
7
11
|
displayName: string;
|
|
@@ -10,5 +14,6 @@ export interface ResourceViewConfig {
|
|
|
10
14
|
canDisplay: (
|
|
11
15
|
targetUri: string,
|
|
12
16
|
targetResource: SolidLeaf | SolidContainer,
|
|
17
|
+
dataset: ConnectedLdoDataset<SolidConnectedPlugin[]>,
|
|
13
18
|
) => boolean;
|
|
14
19
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useTheme } from '@react-navigation/native';
|
|
3
|
+
import { FunctionComponent } from 'react';
|
|
4
|
+
import { Bar } from 'react-native-progress';
|
|
5
|
+
|
|
6
|
+
interface LoadingBarProps {
|
|
7
|
+
isLoading: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const LoadingBar: FunctionComponent<LoadingBarProps> = ({
|
|
11
|
+
isLoading,
|
|
12
|
+
}) => {
|
|
13
|
+
const { colors } = useTheme();
|
|
14
|
+
|
|
15
|
+
if (!isLoading) return <></>;
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<Bar
|
|
19
|
+
color={colors.primary}
|
|
20
|
+
indeterminate
|
|
21
|
+
borderWidth={0}
|
|
22
|
+
borderRadius={0}
|
|
23
|
+
width={null}
|
|
24
|
+
className="absolute top-0 left-0 right-0"
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { SolidProfileShape } from '.ldo/profile.typings';
|
|
3
|
+
import { Avatar, AvatarFallback, AvatarImage } from '../ui/avatar';
|
|
4
|
+
import { Text } from '../ui/text';
|
|
5
|
+
import { User } from '../../lib/icons/User';
|
|
6
|
+
import { FunctionComponent } from 'react';
|
|
7
|
+
|
|
8
|
+
interface ProfileAvatarProps {
|
|
9
|
+
profile?: SolidProfileShape;
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const ProfileAvatar: FunctionComponent<ProfileAvatarProps> = ({
|
|
14
|
+
profile,
|
|
15
|
+
className,
|
|
16
|
+
}) => (
|
|
17
|
+
<Avatar
|
|
18
|
+
alt={profile?.fn ? `${profile.fn}'s Avatar` : ''}
|
|
19
|
+
className={className}
|
|
20
|
+
>
|
|
21
|
+
<AvatarImage source={{ uri: profile?.hasPhoto?.['@id'] }} />
|
|
22
|
+
<AvatarFallback>
|
|
23
|
+
<Text>
|
|
24
|
+
<User />
|
|
25
|
+
</Text>
|
|
26
|
+
</AvatarFallback>
|
|
27
|
+
</Avatar>
|
|
28
|
+
);
|
|
@@ -15,9 +15,10 @@ import {
|
|
|
15
15
|
DialogDescription,
|
|
16
16
|
DialogClose,
|
|
17
17
|
} from '../ui/dialog';
|
|
18
|
-
import {
|
|
18
|
+
import { View } from 'react-native';
|
|
19
19
|
import { Button } from '../ui/button';
|
|
20
20
|
import { Text } from '../ui/text';
|
|
21
|
+
import { Input } from '../ui/input';
|
|
21
22
|
|
|
22
23
|
type DialogOptions =
|
|
23
24
|
| { type: 'confirm'; title: string; message?: string }
|
|
@@ -112,7 +113,7 @@ export const DialogProvider: React.FC<{ children: React.ReactNode }> = ({
|
|
|
112
113
|
|
|
113
114
|
{options?.type === 'prompt' && (
|
|
114
115
|
<View className="my-2">
|
|
115
|
-
<
|
|
116
|
+
<Input
|
|
116
117
|
value={inputValue}
|
|
117
118
|
onChangeText={setInputValue}
|
|
118
119
|
placeholder="Enter text..."
|
|
@@ -125,13 +126,9 @@ export const DialogProvider: React.FC<{ children: React.ReactNode }> = ({
|
|
|
125
126
|
|
|
126
127
|
<DialogFooter>
|
|
127
128
|
<DialogClose asChild>
|
|
128
|
-
<Button variant="ghost" onPress={handleCancel}
|
|
129
|
-
<Text>Cancel</Text>
|
|
130
|
-
</Button>
|
|
129
|
+
<Button variant="ghost" onPress={handleCancel} text="Cancel" />
|
|
131
130
|
</DialogClose>
|
|
132
|
-
<Button onPress={handleConfirm}
|
|
133
|
-
<Text>Ok</Text>
|
|
134
|
-
</Button>
|
|
131
|
+
<Button onPress={handleConfirm} text="Ok" />
|
|
135
132
|
</DialogFooter>
|
|
136
133
|
</DialogContent>
|
|
137
134
|
</Dialog>
|
|
@@ -4,18 +4,14 @@ import React, {
|
|
|
4
4
|
FunctionComponent,
|
|
5
5
|
SetStateAction,
|
|
6
6
|
} from 'react';
|
|
7
|
-
import { useTargetResource } from '../TargetResourceProvider';
|
|
8
7
|
import { ResourceViewConfig } from '../ResourceView';
|
|
9
|
-
|
|
10
|
-
import { TextCursorInput } from '../../lib/icons/TextCursorInput';
|
|
11
|
-
import { CircleSlash } from '../../lib/icons/CircleSlash';
|
|
12
|
-
import { OctagonX } from '../../lib/icons/OctagonX';
|
|
13
|
-
import { ShieldX } from '../../lib/icons/ShieldX';
|
|
14
|
-
import { CircleX } from '../../lib/icons/CircleX';
|
|
8
|
+
|
|
15
9
|
import { Header } from './header/Header';
|
|
16
10
|
import { View } from 'react-native';
|
|
17
|
-
import {
|
|
11
|
+
import { useViewContext, ViewContextProvider } from '../useViewContext';
|
|
18
12
|
import { DialogProvider } from './DialogProvider';
|
|
13
|
+
import { useSolidAuth } from '@ldo/solid-react';
|
|
14
|
+
import { SharingModalProvider } from 'components/sharing/SharingModal';
|
|
19
15
|
|
|
20
16
|
export const ValidViewContext = createContext<{
|
|
21
17
|
validViews: ResourceViewConfig[];
|
|
@@ -25,14 +21,22 @@ export const ValidViewContext = createContext<{
|
|
|
25
21
|
}>({});
|
|
26
22
|
|
|
27
23
|
export const Layout: FunctionComponent = () => {
|
|
24
|
+
const { ranInitialAuthCheck } = useSolidAuth();
|
|
25
|
+
|
|
26
|
+
if (!ranInitialAuthCheck) {
|
|
27
|
+
return <></>;
|
|
28
|
+
}
|
|
29
|
+
|
|
28
30
|
return (
|
|
29
31
|
<DialogProvider>
|
|
30
|
-
<
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
<ViewContextProvider>
|
|
33
|
+
<SharingModalProvider>
|
|
34
|
+
<Header />
|
|
35
|
+
<View className="flex-1 z-0">
|
|
36
|
+
<RenderView />
|
|
37
|
+
</View>
|
|
38
|
+
</SharingModalProvider>
|
|
39
|
+
</ViewContextProvider>
|
|
36
40
|
</DialogProvider>
|
|
37
41
|
);
|
|
38
42
|
};
|
|
@@ -44,75 +48,10 @@ export const Layout: FunctionComponent = () => {
|
|
|
44
48
|
*/
|
|
45
49
|
|
|
46
50
|
export const RenderView: FunctionComponent = () => {
|
|
47
|
-
const {
|
|
51
|
+
const { curViewConfig, targetResource } = useViewContext();
|
|
48
52
|
|
|
49
|
-
|
|
53
|
+
if (targetResource?.isDoingInitialFetch()) return <></>;
|
|
50
54
|
|
|
51
|
-
// Handle Edge cases
|
|
52
|
-
if (!targetResource || !targetUri) {
|
|
53
|
-
return (
|
|
54
|
-
<ErrorMessageResourceView
|
|
55
|
-
icon={TextCursorInput}
|
|
56
|
-
message="Enter a URI in the address bar to view a resource."
|
|
57
|
-
/>
|
|
58
|
-
);
|
|
59
|
-
} else if (targetResource.type === 'InvalidIdentifierResouce') {
|
|
60
|
-
return (
|
|
61
|
-
<ErrorMessageResourceView
|
|
62
|
-
icon={CircleSlash}
|
|
63
|
-
message={`${targetResource.uri} is an invalid URI.`}
|
|
64
|
-
/>
|
|
65
|
-
);
|
|
66
|
-
} else if (targetResource?.isDoingInitialFetch()) {
|
|
67
|
-
return <></>;
|
|
68
|
-
} else if (targetResource?.isAbsent()) {
|
|
69
|
-
return (
|
|
70
|
-
<ErrorMessageResourceView
|
|
71
|
-
icon={CircleSlash}
|
|
72
|
-
message={`${targetResource.uri} either doesn't exist or you don't have read access to it.`}
|
|
73
|
-
/>
|
|
74
|
-
);
|
|
75
|
-
} else if (targetResource?.status.isError) {
|
|
76
|
-
switch (targetResource.status.type) {
|
|
77
|
-
case 'noncompliantPodError':
|
|
78
|
-
return (
|
|
79
|
-
<ErrorMessageResourceView
|
|
80
|
-
icon={OctagonX}
|
|
81
|
-
message={`${targetResource.uri} returned a response that is not compliant with the Linked Web Storage specification: ${targetResource.status.message}`}
|
|
82
|
-
/>
|
|
83
|
-
);
|
|
84
|
-
case 'serverError':
|
|
85
|
-
return (
|
|
86
|
-
<ErrorMessageResourceView
|
|
87
|
-
icon={OctagonX}
|
|
88
|
-
message={`${targetResource.uri} encountered an internal server error: ${targetResource.status.message}`}
|
|
89
|
-
/>
|
|
90
|
-
);
|
|
91
|
-
case 'unauthenticatedError':
|
|
92
|
-
return (
|
|
93
|
-
<ErrorMessageResourceView
|
|
94
|
-
icon={ShieldX}
|
|
95
|
-
message={`${targetResource.uri} requires you to log in to view.`}
|
|
96
|
-
/>
|
|
97
|
-
);
|
|
98
|
-
case 'unauthorizedError':
|
|
99
|
-
return (
|
|
100
|
-
<ErrorMessageResourceView
|
|
101
|
-
icon={ShieldX}
|
|
102
|
-
message={`You don't have access to ${targetResource.uri}.`}
|
|
103
|
-
/>
|
|
104
|
-
);
|
|
105
|
-
case 'unexpectedHttpError':
|
|
106
|
-
case 'unexpectedResourceError':
|
|
107
|
-
default:
|
|
108
|
-
return (
|
|
109
|
-
<ErrorMessageResourceView
|
|
110
|
-
icon={CircleX}
|
|
111
|
-
message={`An unexpected error occurred: ${targetResource.status.message}.`}
|
|
112
|
-
/>
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
55
|
const CurView = curViewConfig.view;
|
|
117
56
|
return <CurView />;
|
|
118
57
|
};
|
|
@@ -68,15 +68,14 @@ export const AddressBox: FunctionComponent = () => {
|
|
|
68
68
|
variant="secondary"
|
|
69
69
|
className="absolute left-0 w-10 h-10"
|
|
70
70
|
onPress={() => setIsTextMode((val) => !val)}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
{isTextMode ? (
|
|
71
|
+
iconLeft={
|
|
72
|
+
isTextMode ? (
|
|
74
73
|
<ChevronsRight size={20} />
|
|
75
74
|
) : (
|
|
76
75
|
<TextCursorInput size={20} />
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
/>
|
|
80
79
|
{(() => {
|
|
81
80
|
const shouldRefresh = targetUri === textBoxValue || !isTextMode;
|
|
82
81
|
return (
|
|
@@ -115,7 +114,9 @@ export const AddressBox: FunctionComponent = () => {
|
|
|
115
114
|
)}
|
|
116
115
|
<TouchableOpacity onPress={() => navigateTo(item.uri)}>
|
|
117
116
|
<View pointerEvents="auto">
|
|
118
|
-
<Text className="mr-0.5 underline
|
|
117
|
+
<Text className="mr-0.5 underline" size="sm">
|
|
118
|
+
{item.name}
|
|
119
|
+
</Text>
|
|
119
120
|
</View>
|
|
120
121
|
</TouchableOpacity>
|
|
121
122
|
</View>
|
|
@@ -1,62 +1,59 @@
|
|
|
1
|
+
import { useResource, useSolidAuth, useSubject } from '@ldo/solid-react';
|
|
1
2
|
import React from 'react';
|
|
2
|
-
|
|
3
3
|
import { FunctionComponent } from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import { View } from 'react-native';
|
|
5
|
+
import { SolidProfileShapeShapeType } from '../../../.ldo/profile.shapeTypes';
|
|
6
|
+
import { Text } from '../../ui/text';
|
|
7
|
+
import { Button } from '../../ui/button';
|
|
8
|
+
import { ThemeToggleMenu } from './ThemeToggleMenu';
|
|
9
|
+
import {
|
|
10
|
+
DropdownMenu,
|
|
11
|
+
DropdownMenuContent,
|
|
12
|
+
DropdownMenuItem,
|
|
13
|
+
DropdownMenuSeparator,
|
|
14
|
+
DropdownMenuTrigger,
|
|
15
|
+
} from '../../ui/dropdown-menu';
|
|
16
|
+
import { LogOut } from '../../../lib/icons/LogOut';
|
|
17
|
+
import { useTargetResource } from '../../TargetResourceProvider';
|
|
18
|
+
import { ProfileAvatar } from '../../common/ProfileAvatar';
|
|
5
19
|
|
|
6
20
|
export const AvatarMenu: FunctionComponent = () => {
|
|
7
|
-
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
// const profile = useSubject(SolidProfileShapeShapeType, session.webId);
|
|
13
|
-
// const renderAvatar = () => (
|
|
14
|
-
// <TouchableWithoutFeedback onPress={() => setMenuVisible(true)}>
|
|
15
|
-
// <Avatar
|
|
16
|
-
// source={{ uri: 'https://api.lorem.space/image/face?w=150&h=150' }}
|
|
17
|
-
// />
|
|
18
|
-
// </TouchableWithoutFeedback>
|
|
19
|
-
// );
|
|
20
|
-
// return (
|
|
21
|
-
// <Popover
|
|
22
|
-
// anchor={renderAvatar}
|
|
23
|
-
// visible={menuVisible}
|
|
24
|
-
// placement="bottom end"
|
|
25
|
-
// onBackdropPress={() => setMenuVisible(false)}
|
|
26
|
-
// style={styles.popover}
|
|
27
|
-
// >
|
|
28
|
-
// <Layout>
|
|
29
|
-
// <View style={styles.profileHeader}>
|
|
30
|
-
// <Avatar
|
|
31
|
-
// size="giant"
|
|
32
|
-
// source={{ uri: 'https://api.lorem.space/image/face?w=150&h=150' }}
|
|
33
|
-
// />
|
|
34
|
-
// <View style={styles.profileText}>
|
|
35
|
-
// <Text category="h6">{profile?.fn || ''}</Text>
|
|
36
|
-
// <Button size="tiny">Edit your profile</Button>
|
|
37
|
-
// </View>
|
|
38
|
-
// </View>
|
|
39
|
-
// <Divider />
|
|
40
|
-
// <ThemeToggleMenu />
|
|
41
|
-
// <Divider />
|
|
42
|
-
// <MenuItem
|
|
43
|
-
// onPress={logout}
|
|
44
|
-
// title="Log Out"
|
|
45
|
-
// accessoryLeft={(props) => <Icon {...props} name="log-out" />}
|
|
46
|
-
// />
|
|
47
|
-
// </Layout>
|
|
48
|
-
// </Popover>
|
|
49
|
-
// );
|
|
50
|
-
};
|
|
21
|
+
const { session, logout } = useSolidAuth();
|
|
22
|
+
// TODO: Use WebId Resource to render a skeleton loader
|
|
23
|
+
const webIdResource = useResource(session.webId);
|
|
24
|
+
const profile = useSubject(SolidProfileShapeShapeType, session.webId);
|
|
25
|
+
const { navigateTo } = useTargetResource();
|
|
51
26
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
27
|
+
if (!session.webId) return <></>;
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<DropdownMenu>
|
|
31
|
+
<DropdownMenuTrigger asChild>
|
|
32
|
+
<Button key="setMemu" variant="ghost" className="w-10">
|
|
33
|
+
<ProfileAvatar profile={profile} />
|
|
34
|
+
</Button>
|
|
35
|
+
</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">
|
|
40
|
+
<Text>{profile?.fn || ''}</Text>
|
|
41
|
+
<Button
|
|
42
|
+
size="sm"
|
|
43
|
+
onPress={() => navigateTo(session.webId ?? '')}
|
|
44
|
+
text="Edit your profile"
|
|
45
|
+
/>
|
|
46
|
+
</View>
|
|
47
|
+
</View>
|
|
48
|
+
<DropdownMenuSeparator />
|
|
49
|
+
<ThemeToggleMenu />
|
|
50
|
+
<DropdownMenuSeparator />
|
|
51
|
+
<DropdownMenuItem onPress={logout}>
|
|
52
|
+
<Text className="flex flex-row gap-1 items-center">
|
|
53
|
+
<LogOut /> Log Out
|
|
54
|
+
</Text>
|
|
55
|
+
</DropdownMenuItem>
|
|
56
|
+
</DropdownMenuContent>
|
|
57
|
+
</DropdownMenu>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
@@ -2,15 +2,19 @@ import React from 'react';
|
|
|
2
2
|
import { useSolidAuth } from '@ldo/solid-react';
|
|
3
3
|
import { FunctionComponent } from 'react';
|
|
4
4
|
import { View } from 'react-native';
|
|
5
|
-
|
|
6
5
|
import { AddressBox } from './AddressBox';
|
|
7
6
|
import { AvatarMenu } from './AvatarMenu';
|
|
8
7
|
import { SignInMenu } from './SignInMenu';
|
|
9
8
|
import { ViewMenu } from './ViewMenu';
|
|
10
|
-
import { Card } from '
|
|
9
|
+
import { Card } from '../../ui/card';
|
|
10
|
+
import { Button } from '../../ui/button';
|
|
11
|
+
import { Text } from '../../ui/text';
|
|
12
|
+
import { UserPlus } from '../../../lib/icons/UserPlus';
|
|
13
|
+
import { useSharingModal } from '../../sharing/SharingModal';
|
|
11
14
|
|
|
12
15
|
export const Header: FunctionComponent = () => {
|
|
13
16
|
const { session } = useSolidAuth();
|
|
17
|
+
const { openSharingModal } = useSharingModal();
|
|
14
18
|
|
|
15
19
|
return (
|
|
16
20
|
<Card
|
|
@@ -20,6 +24,18 @@ export const Header: FunctionComponent = () => {
|
|
|
20
24
|
>
|
|
21
25
|
<AddressBox />
|
|
22
26
|
<View className="mr-1" />
|
|
27
|
+
{session.isLoggedIn && (
|
|
28
|
+
<>
|
|
29
|
+
<Button
|
|
30
|
+
key="setMemu"
|
|
31
|
+
variant="ghost"
|
|
32
|
+
className="w-10"
|
|
33
|
+
onPress={openSharingModal}
|
|
34
|
+
iconLeft={<UserPlus />}
|
|
35
|
+
/>
|
|
36
|
+
<View className="mr-1" />
|
|
37
|
+
</>
|
|
38
|
+
)}
|
|
23
39
|
<ViewMenu />
|
|
24
40
|
<View className="mr-1" />
|
|
25
41
|
{session.isLoggedIn ? <AvatarMenu /> : <SignInMenu />}
|
|
@@ -53,24 +53,23 @@ export const SignInMenu: FunctionComponent = () => {
|
|
|
53
53
|
className="hidden sm:block"
|
|
54
54
|
onPress={() => signUp(DEFAULT_ISSUER)}
|
|
55
55
|
variant="ghost"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
</Button>
|
|
56
|
+
text="Sign Up"
|
|
57
|
+
/>
|
|
59
58
|
<Button
|
|
60
59
|
key="logIn"
|
|
61
60
|
className="hidden sm:block"
|
|
62
61
|
onPress={() => login(DEFAULT_ISSUER)}
|
|
63
62
|
variant="default"
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
</Button>
|
|
63
|
+
text="Log In"
|
|
64
|
+
/>
|
|
67
65
|
<DropdownMenu>
|
|
68
66
|
<DropdownMenuTrigger asChild>
|
|
69
|
-
<Button
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
67
|
+
<Button
|
|
68
|
+
key="setMemu"
|
|
69
|
+
variant="ghost"
|
|
70
|
+
className="w-10"
|
|
71
|
+
iconLeft={<EllipsisVertical size={20} />}
|
|
72
|
+
/>
|
|
74
73
|
</DropdownMenuTrigger>
|
|
75
74
|
<DropdownMenuContent className="w-64 native:w-72">
|
|
76
75
|
<DropdownMenuGroup>
|
|
@@ -106,9 +105,7 @@ export const SignInMenu: FunctionComponent = () => {
|
|
|
106
105
|
</DialogHeader>
|
|
107
106
|
<DialogFooter>
|
|
108
107
|
<DialogClose asChild>
|
|
109
|
-
<Button onPress={onIdpSubmit}
|
|
110
|
-
<Text>OK</Text>
|
|
111
|
-
</Button>
|
|
108
|
+
<Button onPress={onIdpSubmit} text="OK" />
|
|
112
109
|
</DialogClose>
|
|
113
110
|
</DialogFooter>
|
|
114
111
|
</DialogContent>
|