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
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Input } from '../../components/ui/input';
|
|
2
|
+
import React, { FunctionComponent, useCallback, useState } from 'react';
|
|
3
|
+
import { Plus } from '../../lib/icons/Plus';
|
|
4
|
+
import { SolidProfileShape } from '.ldo/profile.typings';
|
|
5
|
+
import { AgentInformation } from '../../components/sharing/agentPermissions/AgentInformation';
|
|
6
|
+
import { View } from 'react-native';
|
|
7
|
+
import { useChangeSetData } from '@ldo/react';
|
|
8
|
+
import { SolidLeaf } from '@ldo/connected-solid';
|
|
9
|
+
import { Button } from '../../components/ui/button';
|
|
10
|
+
import { Trash } from '../../lib/icons/Trash';
|
|
11
|
+
|
|
12
|
+
export interface ProfileKnowsProps {
|
|
13
|
+
resource: SolidLeaf;
|
|
14
|
+
profile: SolidProfileShape;
|
|
15
|
+
setProfile: useChangeSetData<SolidProfileShape, any>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const ProfileKnows: FunctionComponent<ProfileKnowsProps> = ({
|
|
19
|
+
resource,
|
|
20
|
+
profile,
|
|
21
|
+
setProfile,
|
|
22
|
+
}) => {
|
|
23
|
+
console.log(profile.knows?.size);
|
|
24
|
+
|
|
25
|
+
const [newContact, setNewContact] = useState('');
|
|
26
|
+
const addNewContact = useCallback(() => {
|
|
27
|
+
setProfile(resource, (cProfile) => {
|
|
28
|
+
cProfile.knows?.add({ '@id': newContact });
|
|
29
|
+
});
|
|
30
|
+
setNewContact('');
|
|
31
|
+
}, [newContact, resource, setProfile]);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<View className="gap-4">
|
|
35
|
+
<Input
|
|
36
|
+
placeholder="https://example.pod/john/profile/card#me"
|
|
37
|
+
label="New Contact WebId"
|
|
38
|
+
buttonRight={{
|
|
39
|
+
iconRight: <Plus />,
|
|
40
|
+
onPress: addNewContact,
|
|
41
|
+
variant: 'secondary',
|
|
42
|
+
}}
|
|
43
|
+
value={newContact}
|
|
44
|
+
onChangeText={setNewContact}
|
|
45
|
+
onSubmitEditing={addNewContact}
|
|
46
|
+
/>
|
|
47
|
+
{profile.knows?.map((friend) => (
|
|
48
|
+
<AgentInformation
|
|
49
|
+
webId={friend['@id']}
|
|
50
|
+
accessoryRight={
|
|
51
|
+
<Button
|
|
52
|
+
variant="ghost"
|
|
53
|
+
iconRight={<Trash />}
|
|
54
|
+
onPress={() => {
|
|
55
|
+
setProfile(resource, (cProfile) => {
|
|
56
|
+
cProfile.knows?.delete(friend);
|
|
57
|
+
});
|
|
58
|
+
}}
|
|
59
|
+
/>
|
|
60
|
+
}
|
|
61
|
+
/>
|
|
62
|
+
))}
|
|
63
|
+
</View>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ScrollView, View } from 'react-native';
|
|
2
|
+
import { Text } from '../../components/ui/text';
|
|
3
|
+
import React, { FunctionComponent } from 'react';
|
|
4
|
+
import { Input } from '../../components/ui/input';
|
|
5
|
+
import { Separator } from '../../components/ui/separator';
|
|
6
|
+
import { ProfileKnows } from './ProfileKnows';
|
|
7
|
+
import { useViewContext } from 'components/useViewContext';
|
|
8
|
+
import { SolidProfileShapeShapeType } from '.ldo/profile.shapeTypes';
|
|
9
|
+
import { useChangeSubject, useResource } from '@ldo/solid-react';
|
|
10
|
+
import { Button } from '../../components/ui/button';
|
|
11
|
+
import { SolidLeaf } from '@ldo/connected-solid';
|
|
12
|
+
|
|
13
|
+
export const ProfileView: FunctionComponent = () => {
|
|
14
|
+
const { targetUri } = useViewContext();
|
|
15
|
+
|
|
16
|
+
const profileResource = useResource(targetUri);
|
|
17
|
+
const [profile, setProfile, commitProfile, transactionDataset] =
|
|
18
|
+
useChangeSubject(SolidProfileShapeShapeType, targetUri);
|
|
19
|
+
|
|
20
|
+
if (
|
|
21
|
+
!targetUri ||
|
|
22
|
+
!profileResource ||
|
|
23
|
+
!profile ||
|
|
24
|
+
profileResource.type === 'InvalidIdentifierResource'
|
|
25
|
+
)
|
|
26
|
+
return <></>;
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<ScrollView contentContainerClassName="flex-row justify-center p-4">
|
|
30
|
+
<View className="max-w-[600px] flex-1 padding gap-4">
|
|
31
|
+
<Text variant="h1">Profile</Text>
|
|
32
|
+
<Input
|
|
33
|
+
placeholder="John Doe"
|
|
34
|
+
label="Name"
|
|
35
|
+
value={profile?.fn}
|
|
36
|
+
onChangeText={(text) => {
|
|
37
|
+
setProfile(profileResource, (cProfile) => {
|
|
38
|
+
cProfile!.fn = text;
|
|
39
|
+
});
|
|
40
|
+
}}
|
|
41
|
+
/>
|
|
42
|
+
<Separator />
|
|
43
|
+
<Text variant="h2">Contacts</Text>
|
|
44
|
+
<ProfileKnows
|
|
45
|
+
profile={profile}
|
|
46
|
+
setProfile={setProfile}
|
|
47
|
+
resource={profileResource as SolidLeaf}
|
|
48
|
+
/>
|
|
49
|
+
<Button
|
|
50
|
+
disabled={!transactionDataset.hasChanges()}
|
|
51
|
+
text="Update Profile"
|
|
52
|
+
className="self-end"
|
|
53
|
+
onPress={commitProfile}
|
|
54
|
+
isLoading={profileResource.isLoading()}
|
|
55
|
+
/>
|
|
56
|
+
</View>
|
|
57
|
+
</ScrollView>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
@@ -5,21 +5,30 @@ import React, {
|
|
|
5
5
|
useEffect,
|
|
6
6
|
useState,
|
|
7
7
|
} from 'react';
|
|
8
|
-
import { useTargetResource } from '../../components/TargetResourceProvider';
|
|
9
8
|
import { RawCodeEditor } from './RawCodeEditor';
|
|
10
9
|
import { View } from 'react-native';
|
|
11
10
|
import { Button } from '../../components/ui/button';
|
|
12
11
|
import { Text } from '../../components/ui/text';
|
|
13
12
|
import { Notifier } from 'react-native-notifier';
|
|
13
|
+
import { useViewContext } from '../../components/useViewContext';
|
|
14
|
+
import { LoadingBar } from '../../components/common/LoadingBar';
|
|
15
|
+
import { Save } from "../../lib/icons/Save";
|
|
14
16
|
|
|
15
17
|
export const RawCodeView: FunctionComponent = () => {
|
|
16
|
-
const { targetUri } = useTargetResource();
|
|
17
18
|
const { fetch } = useSolidAuth();
|
|
18
19
|
const [content, setContent] = useState<string>('');
|
|
20
|
+
const [contentType, setContentType] = useState<string>('');
|
|
21
|
+
const [didEdit, setDidEdit] = useState(false);
|
|
22
|
+
const { curViewConfig, targetResource } = useViewContext();
|
|
23
|
+
const [isFetching, setIsFetching] = useState(false);
|
|
24
|
+
const [isSaving, setIsSaving] = useState(false);
|
|
25
|
+
|
|
26
|
+
const targetUri = targetResource?.uri;
|
|
19
27
|
|
|
20
28
|
// Independently fetch the target resource, so we have the raw turtle
|
|
21
29
|
const fetchContent = useCallback(async () => {
|
|
22
|
-
if (!targetUri) return;
|
|
30
|
+
if (!targetUri || curViewConfig.name !== 'rawCode') return;
|
|
31
|
+
setIsFetching(true);
|
|
23
32
|
const response = await fetch(targetUri);
|
|
24
33
|
if (response.status !== 200) {
|
|
25
34
|
Notifier.showNotification({
|
|
@@ -27,24 +36,35 @@ export const RawCodeView: FunctionComponent = () => {
|
|
|
27
36
|
});
|
|
28
37
|
}
|
|
29
38
|
setContent(await response.text());
|
|
30
|
-
|
|
39
|
+
setIsFetching(false);
|
|
40
|
+
setDidEdit(false);
|
|
41
|
+
setContentType(response.headers.get('content-type') ?? '');
|
|
42
|
+
}, [curViewConfig.name, fetch, targetUri]);
|
|
31
43
|
|
|
32
44
|
const submitChanges = useCallback(async () => {
|
|
33
45
|
if (!targetUri) return;
|
|
46
|
+
setIsSaving(true);
|
|
34
47
|
const response = await fetch(targetUri, {
|
|
35
48
|
method: 'put',
|
|
49
|
+
headers: {
|
|
50
|
+
'content-type': contentType,
|
|
51
|
+
},
|
|
36
52
|
body: content,
|
|
37
53
|
});
|
|
38
54
|
if (response.status !== 205) {
|
|
39
55
|
Notifier.showNotification({
|
|
40
56
|
title: `Could save document. Recieved ${response.status}`,
|
|
41
57
|
});
|
|
58
|
+
setIsSaving(false);
|
|
59
|
+
return;
|
|
42
60
|
}
|
|
43
61
|
Notifier.showNotification({
|
|
44
62
|
title: `Document Saved`,
|
|
45
63
|
});
|
|
46
64
|
await fetchContent();
|
|
47
|
-
|
|
65
|
+
setIsSaving(false);
|
|
66
|
+
setDidEdit(false);
|
|
67
|
+
}, [content, contentType, fetch, fetchContent, targetUri]);
|
|
48
68
|
|
|
49
69
|
useEffect(() => {
|
|
50
70
|
fetchContent();
|
|
@@ -52,16 +72,22 @@ export const RawCodeView: FunctionComponent = () => {
|
|
|
52
72
|
|
|
53
73
|
return (
|
|
54
74
|
<View className="flex-1 relative">
|
|
75
|
+
<LoadingBar isLoading={isFetching || isSaving} />
|
|
55
76
|
<RawCodeEditor
|
|
56
77
|
value={content}
|
|
57
|
-
onChange={(value) =>
|
|
78
|
+
onChange={(value) => {
|
|
79
|
+
setDidEdit(true);
|
|
80
|
+
setContent(value ?? '');
|
|
81
|
+
}}
|
|
58
82
|
/>
|
|
59
83
|
<Button
|
|
60
84
|
className="absolute bottom-2 right-2 z-10"
|
|
61
85
|
onPress={submitChanges}
|
|
62
|
-
|
|
63
|
-
<
|
|
64
|
-
|
|
86
|
+
text="Save Changes"
|
|
87
|
+
iconLeft={<Save />}
|
|
88
|
+
isLoading={isSaving}
|
|
89
|
+
disabled={!didEdit || isSaving}
|
|
90
|
+
/>
|
|
65
91
|
</View>
|
|
66
92
|
);
|
|
67
93
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"@context": [
|
|
3
|
-
"https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^
|
|
3
|
+
"https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^8.0.0/components/context.jsonld"
|
|
4
4
|
],
|
|
5
5
|
"import": [
|
|
6
6
|
"css:config/app/init/default.json",
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import createContainer from 'constate';
|
|
2
|
-
import React, { useEffect, useMemo, useState } from 'react';
|
|
3
|
-
import { useDataBrowserConfig } from '../DataBrowser';
|
|
4
|
-
import { useTargetResource } from '../TargetResourceProvider';
|
|
5
|
-
import { ResourceViewConfig } from '../ResourceView';
|
|
6
|
-
import { EyeOff } from '../../lib/icons/EyeOff';
|
|
7
|
-
import { ErrorMessageResourceView } from './utilityResourceViews/ErrorMessageResourceView';
|
|
8
|
-
|
|
9
|
-
export const [ValidViewProvider, useValidView] = createContainer(() => {
|
|
10
|
-
const { targetUri, targetResource } = useTargetResource();
|
|
11
|
-
const { views } = useDataBrowserConfig();
|
|
12
|
-
|
|
13
|
-
const validViews = useMemo(() => {
|
|
14
|
-
const noValidView: ResourceViewConfig = {
|
|
15
|
-
name: 'noValid',
|
|
16
|
-
displayName: 'No Valid View',
|
|
17
|
-
displayIcon: EyeOff,
|
|
18
|
-
view: () => (
|
|
19
|
-
<ErrorMessageResourceView
|
|
20
|
-
icon={EyeOff}
|
|
21
|
-
message="No Views are available to display this resource."
|
|
22
|
-
/>
|
|
23
|
-
),
|
|
24
|
-
canDisplay: () => false,
|
|
25
|
-
};
|
|
26
|
-
if (
|
|
27
|
-
!targetResource ||
|
|
28
|
-
!targetUri ||
|
|
29
|
-
targetResource.type === 'InvalidIdentifierResouce'
|
|
30
|
-
) {
|
|
31
|
-
return [noValidView];
|
|
32
|
-
}
|
|
33
|
-
const potentialViews = views.filter((view) =>
|
|
34
|
-
view.canDisplay(targetUri, targetResource),
|
|
35
|
-
);
|
|
36
|
-
return potentialViews.length > 0 ? potentialViews : [noValidView];
|
|
37
|
-
}, [targetResource, targetUri, views]);
|
|
38
|
-
|
|
39
|
-
const [curViewConfig, setCurViewConfig] = useState<ResourceViewConfig>(
|
|
40
|
-
validViews[0],
|
|
41
|
-
);
|
|
42
|
-
useEffect(() => {
|
|
43
|
-
setCurViewConfig(validViews[0]);
|
|
44
|
-
}, [targetUri, validViews]);
|
|
45
|
-
|
|
46
|
-
return {
|
|
47
|
-
validViews,
|
|
48
|
-
curViewConfig,
|
|
49
|
-
setCurViewConfig,
|
|
50
|
-
};
|
|
51
|
-
});
|