@basictech/react 0.12.0-beta.0 → 0.12.0-beta.2
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/CHANGELOG.md +603 -0
- package/README.md +125 -29
- package/dist/index.d.mts +120 -11
- package/dist/index.d.ts +120 -11
- package/dist/index.js +1168 -111
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1165 -110
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +557 -0
- package/dist/styles.css.map +1 -0
- package/dist/styles.d.mts +2 -0
- package/dist/styles.d.ts +2 -0
- package/package.json +12 -6
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
"use strict";
|
|
2
3
|
var __create = Object.create;
|
|
3
4
|
var __defProp = Object.defineProperty;
|
|
@@ -30,10 +31,21 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
31
|
// src/index.ts
|
|
31
32
|
var index_exports = {};
|
|
32
33
|
__export(index_exports, {
|
|
34
|
+
AccountSettingsModal: () => AccountSettingsModal,
|
|
35
|
+
AuthStatus: () => AuthStatus,
|
|
36
|
+
BasicButton: () => BasicButton,
|
|
33
37
|
BasicProvider: () => BasicProvider,
|
|
38
|
+
BasicUIProvider: () => BasicUIProvider,
|
|
34
39
|
BrowserKeyValueStorage: () => BrowserKeyValueStorage,
|
|
35
40
|
BrowserTokenStore: () => BrowserTokenStore,
|
|
36
41
|
PersistenceStore: () => PersistenceStore,
|
|
42
|
+
SharesModal: () => SharesModal,
|
|
43
|
+
SignInButton: () => SignInButton,
|
|
44
|
+
SignOutButton: () => SignOutButton,
|
|
45
|
+
SyncStatus: () => SyncStatus,
|
|
46
|
+
UserAvatar: () => UserAvatar,
|
|
47
|
+
UserButton: () => UserButton,
|
|
48
|
+
UserMenu: () => UserMenu,
|
|
37
49
|
browserCurrentUrl: () => browserCurrentUrl,
|
|
38
50
|
browserNavigate: () => browserNavigate,
|
|
39
51
|
browserReplaceUrl: () => browserReplaceUrl,
|
|
@@ -51,6 +63,7 @@ __export(index_exports, {
|
|
|
51
63
|
useFiles: () => useFiles,
|
|
52
64
|
useMounts: () => useMounts,
|
|
53
65
|
useOutgoingShares: () => useOutgoingShares,
|
|
66
|
+
useProjectProfile: () => useProjectProfile,
|
|
54
67
|
useQuery: () => useQuery,
|
|
55
68
|
useRepos: () => useRepos,
|
|
56
69
|
useSchemaStatus: () => useSchemaStatus,
|
|
@@ -59,10 +72,21 @@ __export(index_exports, {
|
|
|
59
72
|
});
|
|
60
73
|
module.exports = __toCommonJS(index_exports);
|
|
61
74
|
|
|
75
|
+
// src/create-basic.tsx
|
|
76
|
+
var import_react12 = require("react");
|
|
77
|
+
|
|
78
|
+
// src/context.ts
|
|
79
|
+
var import_react = require("react");
|
|
80
|
+
var BasicClientContext = (0, import_react.createContext)(null);
|
|
81
|
+
|
|
62
82
|
// src/create-client.ts
|
|
63
83
|
var import_core4 = require("@basictech/core");
|
|
64
84
|
|
|
65
85
|
// src/adapters/broadcast-channel.ts
|
|
86
|
+
function supportsAccessTokenAdoption(store) {
|
|
87
|
+
const candidate = store;
|
|
88
|
+
return typeof candidate?.accessToken === "function" && typeof candidate?.adoptAccessToken === "function";
|
|
89
|
+
}
|
|
66
90
|
function createBrowserMessageChannel(name) {
|
|
67
91
|
const channel = new BroadcastChannel(name);
|
|
68
92
|
const adapter = {
|
|
@@ -73,12 +97,11 @@ function createBrowserMessageChannel(name) {
|
|
|
73
97
|
channel.onmessage = (event) => adapter.onmessage?.({ data: event.data });
|
|
74
98
|
return adapter;
|
|
75
99
|
}
|
|
76
|
-
function createBrowserAuthChannelFactory(
|
|
77
|
-
|
|
78
|
-
return (name) => {
|
|
100
|
+
function createBrowserAuthChannelFactory(tokenStore) {
|
|
101
|
+
return (name, context) => {
|
|
79
102
|
const channel = createBrowserMessageChannel(name);
|
|
80
|
-
|
|
81
|
-
const tokenKey =
|
|
103
|
+
if (!context) return channel;
|
|
104
|
+
const { tokenKey } = context;
|
|
82
105
|
const adapter = {
|
|
83
106
|
postMessage: (message) => {
|
|
84
107
|
const outgoing = message;
|
|
@@ -620,32 +643,27 @@ function browserReconcileTrigger(listener) {
|
|
|
620
643
|
function createBasicClient(config) {
|
|
621
644
|
const local = browserStorage("localStorage") ?? new import_core4.MemoryKeyValueStorage();
|
|
622
645
|
const session = browserStorage("sessionStorage") ?? new import_core4.MemoryKeyValueStorage();
|
|
623
|
-
const
|
|
646
|
+
const tokenStore = config.tokenStore ?? new BrowserTokenStore(local);
|
|
624
647
|
const canBroadcast = typeof BroadcastChannel !== "undefined";
|
|
625
648
|
const canPersistReplica = typeof indexedDB !== "undefined";
|
|
649
|
+
const messageChannel = !canBroadcast ? void 0 : supportsAccessTokenAdoption(tokenStore) ? createBrowserAuthChannelFactory(tokenStore) : createBrowserMessageChannel;
|
|
626
650
|
return (0, import_core4.createBasicClient)({
|
|
627
651
|
...config,
|
|
628
652
|
kv: config.kv ?? local,
|
|
629
|
-
tokenStore
|
|
653
|
+
tokenStore,
|
|
630
654
|
sessionStorage: config.sessionStorage ?? session,
|
|
631
655
|
replicaStore: config.replicaStore ?? (canPersistReplica ? new PersistenceStore(config.clientId) : new import_core4.MemoryReplicaStoreFactory()),
|
|
632
656
|
navigate: config.navigate ?? browserNavigate,
|
|
633
657
|
currentUrl: config.currentUrl ?? browserCurrentUrl,
|
|
634
658
|
replaceUrl: config.replaceUrl ?? browserReplaceUrl,
|
|
635
659
|
reconcileTrigger: config.reconcileTrigger ?? browserReconcileTrigger,
|
|
636
|
-
createMessageChannel: config.createMessageChannel ??
|
|
660
|
+
createMessageChannel: config.createMessageChannel ?? messageChannel,
|
|
637
661
|
uploadTransport: config.uploadTransport ?? (typeof XMLHttpRequest === "undefined" ? void 0 : browserUploadTransport)
|
|
638
662
|
});
|
|
639
663
|
}
|
|
640
664
|
|
|
641
665
|
// src/provider.tsx
|
|
642
666
|
var import_react2 = require("react");
|
|
643
|
-
|
|
644
|
-
// src/context.ts
|
|
645
|
-
var import_react = require("react");
|
|
646
|
-
var BasicClientContext = (0, import_react.createContext)(null);
|
|
647
|
-
|
|
648
|
-
// src/provider.tsx
|
|
649
667
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
650
668
|
var clientLifecycles = /* @__PURE__ */ new WeakMap();
|
|
651
669
|
function retainClient(client) {
|
|
@@ -727,6 +745,10 @@ function useRequiredClient() {
|
|
|
727
745
|
function useClientSnapshot(client) {
|
|
728
746
|
return (0, import_react3.useSyncExternalStore)(client.subscribe, client.getSnapshot, client.getSnapshot);
|
|
729
747
|
}
|
|
748
|
+
function useSignInGate(snapshot) {
|
|
749
|
+
const code = snapshot.isReady && !snapshot.isSignedIn ? snapshot.readOnlyReason ?? "AUTHORIZATION_REQUIRED" : null;
|
|
750
|
+
return (0, import_react3.useMemo)(() => code === null ? null : new import_core5.BasicError(code, code === "AUTH_EXPIRED" ? "Session expired. Sign in again to load this list." : "Sign in to load this list."), [code]);
|
|
751
|
+
}
|
|
730
752
|
function toBasicError(error, fallback = "UNKNOWN_ERROR") {
|
|
731
753
|
if (error instanceof import_core5.BasicError) return error;
|
|
732
754
|
return new import_core5.BasicError(fallback, error instanceof Error ? error.message : String(error));
|
|
@@ -775,15 +797,15 @@ function useAsyncResult(load, initialData, enabled, settled, dependencies) {
|
|
|
775
797
|
function useAccountsFor(client) {
|
|
776
798
|
const snapshot = useClientSnapshot(client);
|
|
777
799
|
const switchAccount = (0, import_react4.useCallback)((id) => client.switchAccount(id), [client]);
|
|
778
|
-
const addAccount = (0, import_react4.useCallback)(() => client.addAccount(), [client]);
|
|
800
|
+
const addAccount = (0, import_react4.useCallback)((options) => client.addAccount(options), [client]);
|
|
779
801
|
const removeAccount = (0, import_react4.useCallback)((id) => client.removeAccount(id), [client]);
|
|
780
802
|
return (0, import_react4.useMemo)(() => ({
|
|
781
803
|
accounts: snapshot.accounts,
|
|
782
|
-
|
|
804
|
+
activeAccount: snapshot.activeAccount,
|
|
783
805
|
switchAccount,
|
|
784
806
|
addAccount,
|
|
785
807
|
removeAccount
|
|
786
|
-
}), [snapshot.accounts, snapshot.
|
|
808
|
+
}), [snapshot.accounts, snapshot.activeAccount, switchAccount, addAccount, removeAccount]);
|
|
787
809
|
}
|
|
788
810
|
function useAccounts() {
|
|
789
811
|
return useAccountsFor(useRequiredClient());
|
|
@@ -791,7 +813,6 @@ function useAccounts() {
|
|
|
791
813
|
|
|
792
814
|
// src/hooks/auth.ts
|
|
793
815
|
var import_react5 = require("react");
|
|
794
|
-
var import_core6 = require("@basictech/core");
|
|
795
816
|
function useAuthFor(client) {
|
|
796
817
|
const snapshot = useClientSnapshot(client);
|
|
797
818
|
const signIn = (0, import_react5.useCallback)((input) => client.signIn(input), [client]);
|
|
@@ -804,7 +825,7 @@ function useAuthFor(client) {
|
|
|
804
825
|
canWrite: snapshot.canWrite,
|
|
805
826
|
readOnlyReason: snapshot.readOnlyReason,
|
|
806
827
|
status: snapshot.authStatus,
|
|
807
|
-
error: snapshot.
|
|
828
|
+
error: snapshot.authError,
|
|
808
829
|
user: snapshot.user,
|
|
809
830
|
did: snapshot.did,
|
|
810
831
|
handle: snapshot.handle,
|
|
@@ -817,6 +838,9 @@ function useAuth() {
|
|
|
817
838
|
return useAuthFor(useRequiredClient());
|
|
818
839
|
}
|
|
819
840
|
|
|
841
|
+
// src/hooks/basic.ts
|
|
842
|
+
var import_react8 = require("react");
|
|
843
|
+
|
|
820
844
|
// src/hooks/db.ts
|
|
821
845
|
function useDbFor(client, source = "default") {
|
|
822
846
|
return client.db(source);
|
|
@@ -893,51 +917,41 @@ function useBasicFor(client) {
|
|
|
893
917
|
const auth = useAuthFor(client);
|
|
894
918
|
const accounts = useAccountsFor(client);
|
|
895
919
|
const sync = useSyncStatusFor(client);
|
|
896
|
-
const repos = useReposFor(client);
|
|
897
|
-
|
|
920
|
+
const repos = useReposFor(client).repos;
|
|
921
|
+
const db = useDbFor(client);
|
|
922
|
+
return (0, import_react8.useMemo)(() => ({
|
|
898
923
|
...auth,
|
|
899
924
|
client,
|
|
900
|
-
db
|
|
925
|
+
db,
|
|
901
926
|
accounts,
|
|
902
927
|
sync,
|
|
903
|
-
repos
|
|
904
|
-
};
|
|
928
|
+
repos
|
|
929
|
+
}), [auth, client, db, accounts, sync, repos]);
|
|
905
930
|
}
|
|
906
931
|
function useBasic() {
|
|
907
932
|
return useBasicFor(useRequiredClient());
|
|
908
933
|
}
|
|
909
934
|
|
|
910
935
|
// src/hooks/files.ts
|
|
911
|
-
var
|
|
936
|
+
var import_react9 = require("react");
|
|
912
937
|
var zero = () => 0;
|
|
913
938
|
function useFilesFor(client, query = {}, options = {}) {
|
|
914
939
|
const source = options.source ?? "default";
|
|
915
940
|
const sourceKey = JSON.stringify(source);
|
|
916
941
|
const queryKey = JSON.stringify(query);
|
|
917
942
|
const snapshot = useClientSnapshot(client);
|
|
918
|
-
const subscription = (0,
|
|
943
|
+
const subscription = (0, import_react9.useMemo)(
|
|
919
944
|
() => client.queryStore.collection(source, "_files"),
|
|
920
945
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
921
946
|
[client, sourceKey]
|
|
922
947
|
);
|
|
923
|
-
const version = (0,
|
|
948
|
+
const version = (0, import_react9.useSyncExternalStore)(subscription.subscribe, subscription.getSnapshot, zero);
|
|
924
949
|
return useAsyncResult(
|
|
925
|
-
async () =>
|
|
926
|
-
const files = client.db(source).files;
|
|
927
|
-
if (source !== "default" && "mountId" in source) {
|
|
928
|
-
const page = await files.list({
|
|
929
|
-
...query.prefix !== void 0 ? { prefix: query.prefix } : {},
|
|
930
|
-
...query.limit !== void 0 ? { limit: query.limit } : {},
|
|
931
|
-
...query.cursor !== void 0 ? { cursor: query.cursor } : {}
|
|
932
|
-
});
|
|
933
|
-
return page.data;
|
|
934
|
-
}
|
|
935
|
-
return (await files.list(query)).data;
|
|
936
|
-
},
|
|
950
|
+
async () => (await client.db(source).files.list(query)).data,
|
|
937
951
|
[],
|
|
938
952
|
snapshot.isReady,
|
|
939
953
|
snapshot.isReady,
|
|
940
|
-
[sourceKey, queryKey, version, snapshot.
|
|
954
|
+
[sourceKey, queryKey, version, snapshot.activeAccount?.id]
|
|
941
955
|
);
|
|
942
956
|
}
|
|
943
957
|
function useFiles(query = {}, options = {}) {
|
|
@@ -945,61 +959,62 @@ function useFiles(query = {}, options = {}) {
|
|
|
945
959
|
}
|
|
946
960
|
function useStorageInfoFor(client) {
|
|
947
961
|
const snapshot = useClientSnapshot(client);
|
|
948
|
-
|
|
962
|
+
const gate = useSignInGate(snapshot);
|
|
963
|
+
const result = useAsyncResult(
|
|
949
964
|
() => client.storageInfo(client.resolveRepoId()),
|
|
950
965
|
null,
|
|
951
966
|
snapshot.isReady && snapshot.isSignedIn,
|
|
952
967
|
snapshot.isReady,
|
|
953
|
-
[snapshot.
|
|
968
|
+
[snapshot.activeAccount?.id]
|
|
954
969
|
);
|
|
970
|
+
return { ...result, error: result.error ?? gate };
|
|
955
971
|
}
|
|
956
972
|
function useStorageInfo() {
|
|
957
973
|
return useStorageInfoFor(useRequiredClient());
|
|
958
974
|
}
|
|
959
975
|
|
|
960
976
|
// src/hooks/mounts.ts
|
|
961
|
-
var
|
|
977
|
+
var import_react10 = require("react");
|
|
962
978
|
function useMountsFor(client, query = {}) {
|
|
963
979
|
const snapshot = useClientSnapshot(client);
|
|
980
|
+
const gate = useSignInGate(snapshot);
|
|
964
981
|
const queryKey = JSON.stringify(query);
|
|
965
982
|
const result = useAsyncResult(
|
|
966
983
|
() => client.shares.mounts(query),
|
|
967
984
|
[],
|
|
968
985
|
snapshot.isReady && snapshot.isSignedIn,
|
|
969
986
|
snapshot.isReady,
|
|
970
|
-
[queryKey, snapshot.
|
|
987
|
+
[queryKey, snapshot.activeAccount?.id]
|
|
971
988
|
);
|
|
972
|
-
const open = (0,
|
|
989
|
+
const open = (0, import_react10.useCallback)(
|
|
973
990
|
(mountId) => client.shares.mount(mountId),
|
|
974
991
|
[client]
|
|
975
992
|
);
|
|
976
|
-
const manageUrl = (0,
|
|
977
|
-
|
|
978
|
-
return { data: mounts, mounts, ...state, open, manageUrl };
|
|
993
|
+
const manageUrl = (0, import_react10.useCallback)(() => client.shares.manageUrl(), [client]);
|
|
994
|
+
return { ...result, error: result.error ?? gate, open, manageUrl };
|
|
979
995
|
}
|
|
980
996
|
function useMounts(query = {}) {
|
|
981
997
|
return useMountsFor(useRequiredClient(), query);
|
|
982
998
|
}
|
|
983
999
|
function useOutgoingSharesFor(client) {
|
|
984
1000
|
const snapshot = useClientSnapshot(client);
|
|
1001
|
+
const gate = useSignInGate(snapshot);
|
|
985
1002
|
const result = useAsyncResult(
|
|
986
1003
|
() => client.shares.listOutgoing(),
|
|
987
1004
|
[],
|
|
988
1005
|
snapshot.isReady && snapshot.isSignedIn,
|
|
989
1006
|
snapshot.isReady,
|
|
990
|
-
[snapshot.
|
|
1007
|
+
[snapshot.activeAccount?.id]
|
|
991
1008
|
);
|
|
992
|
-
const create = (0,
|
|
993
|
-
const get = (0,
|
|
994
|
-
const cancel = (0,
|
|
995
|
-
const revoke = (0,
|
|
996
|
-
const getContactHandle = (0,
|
|
997
|
-
const manageUrl = (0,
|
|
998
|
-
const { data: outgoingShares, ...state } = result;
|
|
1009
|
+
const create = (0, import_react10.useCallback)((input) => client.shares.createOutgoing(input), [client]);
|
|
1010
|
+
const get = (0, import_react10.useCallback)((id) => client.shares.getOutgoing(id), [client]);
|
|
1011
|
+
const cancel = (0, import_react10.useCallback)((id) => client.shares.cancel(id), [client]);
|
|
1012
|
+
const revoke = (0, import_react10.useCallback)((id) => client.shares.revoke(id), [client]);
|
|
1013
|
+
const getContactHandle = (0, import_react10.useCallback)((did) => client.shares.getContactHandle(did), [client]);
|
|
1014
|
+
const manageUrl = (0, import_react10.useCallback)(() => client.shares.manageUrl(), [client]);
|
|
999
1015
|
return {
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
...state,
|
|
1016
|
+
...result,
|
|
1017
|
+
error: result.error ?? gate,
|
|
1003
1018
|
create,
|
|
1004
1019
|
get,
|
|
1005
1020
|
cancel,
|
|
@@ -1013,58 +1028,35 @@ function useOutgoingShares() {
|
|
|
1013
1028
|
}
|
|
1014
1029
|
|
|
1015
1030
|
// src/hooks/query.ts
|
|
1016
|
-
var
|
|
1017
|
-
var subscribeNever = () => () => {
|
|
1018
|
-
};
|
|
1019
|
-
var zero2 = () => 0;
|
|
1031
|
+
var import_react11 = require("react");
|
|
1020
1032
|
function useQueryValueFor(client, collection, query = {}, options = {}) {
|
|
1021
1033
|
const source = options.source ?? "default";
|
|
1022
1034
|
const sourceKey = JSON.stringify(source);
|
|
1023
1035
|
const queryKey = JSON.stringify(query);
|
|
1024
1036
|
const snapshot = useClientSnapshot(client);
|
|
1025
|
-
const
|
|
1026
|
-
() => client.queryStore.collection(source, collection),
|
|
1027
|
-
// sourceKey captures semantic source identity rather than object identity.
|
|
1028
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1029
|
-
[client, sourceKey, collection]
|
|
1030
|
-
);
|
|
1031
|
-
const version = (0, import_react10.useSyncExternalStore)(
|
|
1032
|
-
subscription?.subscribe ?? subscribeNever,
|
|
1033
|
-
subscription?.getSnapshot ?? zero2,
|
|
1034
|
-
zero2
|
|
1035
|
-
);
|
|
1036
|
-
const [result, setResult] = (0, import_react10.useState)({
|
|
1037
|
+
const [result, setResult] = (0, import_react11.useState)({
|
|
1037
1038
|
data: [],
|
|
1038
1039
|
isLoading: true,
|
|
1039
1040
|
error: null
|
|
1040
1041
|
});
|
|
1041
|
-
(0,
|
|
1042
|
-
let active = true;
|
|
1043
|
-
if (!snapshot.isReady) {
|
|
1044
|
-
setResult((current) => ({ ...current, isLoading: true, error: null }));
|
|
1045
|
-
return () => {
|
|
1046
|
-
active = false;
|
|
1047
|
-
};
|
|
1048
|
-
}
|
|
1042
|
+
(0, import_react11.useEffect)(() => {
|
|
1049
1043
|
setResult((current) => ({ ...current, isLoading: true, error: null }));
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
(error) => {
|
|
1057
|
-
if (active) setResult((current) => ({
|
|
1044
|
+
if (!snapshot.isReady) return;
|
|
1045
|
+
try {
|
|
1046
|
+
return client.db(source).collection(collection).watch(
|
|
1047
|
+
query,
|
|
1048
|
+
(page) => setResult({ data: page.data, isLoading: false, error: null }),
|
|
1049
|
+
(error) => setResult((current) => ({
|
|
1058
1050
|
...current,
|
|
1059
1051
|
isLoading: false,
|
|
1060
1052
|
error: toBasicError(error, "QUERY_FAILED")
|
|
1061
|
-
}))
|
|
1062
|
-
|
|
1063
|
-
)
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
}
|
|
1067
|
-
}, [client, collection, sourceKey, queryKey,
|
|
1053
|
+
}))
|
|
1054
|
+
);
|
|
1055
|
+
} catch (error) {
|
|
1056
|
+
setResult((current) => ({ ...current, isLoading: false, error: toBasicError(error, "QUERY_FAILED") }));
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
}, [client, collection, sourceKey, queryKey, snapshot.isReady]);
|
|
1068
1060
|
return result;
|
|
1069
1061
|
}
|
|
1070
1062
|
function useQueryFor(client, collection, query = {}, options = {}) {
|
|
@@ -1074,6 +1066,45 @@ function useQuery(collection, query = {}, options = {}) {
|
|
|
1074
1066
|
return useQueryValueFor(useRequiredClient(), collection, query, options);
|
|
1075
1067
|
}
|
|
1076
1068
|
|
|
1069
|
+
// src/hooks/profile.ts
|
|
1070
|
+
var import_core6 = require("@basictech/core");
|
|
1071
|
+
function useProjectProfileFor(client, enabled = true) {
|
|
1072
|
+
const snapshot = useClientSnapshot(client);
|
|
1073
|
+
const accountId = snapshot.activeAccount?.id;
|
|
1074
|
+
const result = useAsyncResult(
|
|
1075
|
+
async () => ({ accountId, profile: await client.getProjectProfile() }),
|
|
1076
|
+
null,
|
|
1077
|
+
enabled && snapshot.isReady && snapshot.isSignedIn,
|
|
1078
|
+
snapshot.isReady,
|
|
1079
|
+
[snapshot.activeAccount?.id]
|
|
1080
|
+
);
|
|
1081
|
+
const checkAccount = () => {
|
|
1082
|
+
if (client.getSnapshot().activeAccount?.id !== accountId)
|
|
1083
|
+
throw new import_core6.BasicError("ACCOUNT_CHANGED");
|
|
1084
|
+
};
|
|
1085
|
+
return {
|
|
1086
|
+
...result,
|
|
1087
|
+
data: enabled && snapshot.isSignedIn && result.data && result.data.accountId === accountId ? result.data.profile : null,
|
|
1088
|
+
async update(patch) {
|
|
1089
|
+
checkAccount();
|
|
1090
|
+
const profile = await client.updateProjectProfile(patch);
|
|
1091
|
+
checkAccount();
|
|
1092
|
+
result.refresh();
|
|
1093
|
+
return profile;
|
|
1094
|
+
},
|
|
1095
|
+
async reset() {
|
|
1096
|
+
checkAccount();
|
|
1097
|
+
const profile = await client.resetProjectProfile();
|
|
1098
|
+
checkAccount();
|
|
1099
|
+
result.refresh();
|
|
1100
|
+
return profile;
|
|
1101
|
+
}
|
|
1102
|
+
};
|
|
1103
|
+
}
|
|
1104
|
+
function useProjectProfile(enabled = true) {
|
|
1105
|
+
return useProjectProfileFor(useRequiredClient(), enabled);
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1077
1108
|
// src/create-basic.tsx
|
|
1078
1109
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1079
1110
|
function createBasic(config) {
|
|
@@ -1081,33 +1112,1058 @@ function createBasic(config) {
|
|
|
1081
1112
|
function Provider({ children, renderWhileLoading }) {
|
|
1082
1113
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BasicProvider, { client, renderWhileLoading, children });
|
|
1083
1114
|
}
|
|
1115
|
+
function useBound() {
|
|
1116
|
+
if ((0, import_react12.useContext)(BasicClientContext) !== client) {
|
|
1117
|
+
throw new Error("Bound Basic hooks must be used within their own <basic.Provider>");
|
|
1118
|
+
}
|
|
1119
|
+
return client;
|
|
1120
|
+
}
|
|
1084
1121
|
function useBoundDb(source = "default") {
|
|
1085
|
-
return
|
|
1122
|
+
return useBound().db(source);
|
|
1086
1123
|
}
|
|
1087
1124
|
return {
|
|
1088
1125
|
client,
|
|
1089
1126
|
Provider,
|
|
1090
|
-
useBasic: () => useBasicFor(
|
|
1091
|
-
useAuth: () => useAuthFor(
|
|
1092
|
-
useAccounts: () => useAccountsFor(
|
|
1127
|
+
useBasic: () => useBasicFor(useBound()),
|
|
1128
|
+
useAuth: () => useAuthFor(useBound()),
|
|
1129
|
+
useAccounts: () => useAccountsFor(useBound()),
|
|
1093
1130
|
useDb: useBoundDb,
|
|
1094
|
-
useCollection: (name, options = {}) => useCollectionFor(
|
|
1095
|
-
useQuery: (collection, query = {}, options = {}) => useQueryFor(
|
|
1096
|
-
useSyncStatus: (source) => useSyncStatusFor(
|
|
1097
|
-
useSchemaStatus: (source = "default") => useSchemaStatusFor(
|
|
1098
|
-
useRepos: () => useReposFor(
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1131
|
+
useCollection: (name, options = {}) => useCollectionFor(useBound(), name, options),
|
|
1132
|
+
useQuery: (collection, query = {}, options = {}) => useQueryFor(useBound(), collection, query, options),
|
|
1133
|
+
useSyncStatus: (source) => useSyncStatusFor(useBound(), source),
|
|
1134
|
+
useSchemaStatus: (source = "default") => useSchemaStatusFor(useBound(), source),
|
|
1135
|
+
useRepos: () => useReposFor(useBound()),
|
|
1136
|
+
useProjectProfile: (enabled) => useProjectProfileFor(useBound(), enabled),
|
|
1137
|
+
useFiles: (query = {}, options = {}) => useFilesFor(useBound(), query, options),
|
|
1138
|
+
useStorageInfo: () => useStorageInfoFor(useBound()),
|
|
1139
|
+
useMounts: (query = {}) => useMountsFor(useBound(), query),
|
|
1140
|
+
useOutgoingShares: () => useOutgoingSharesFor(useBound())
|
|
1103
1141
|
};
|
|
1104
1142
|
}
|
|
1143
|
+
|
|
1144
|
+
// src/components.tsx
|
|
1145
|
+
var import_react13 = require("react");
|
|
1146
|
+
var import_avatar = require("@base-ui/react/avatar");
|
|
1147
|
+
var import_dialog = require("@base-ui/react/dialog");
|
|
1148
|
+
var import_menu = require("@base-ui/react/menu");
|
|
1149
|
+
var import_tabs = require("@base-ui/react/tabs");
|
|
1150
|
+
var import_core7 = require("@basictech/core");
|
|
1151
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1152
|
+
var AppearanceContext = (0, import_react13.createContext)({});
|
|
1153
|
+
function appearanceStyle(appearance) {
|
|
1154
|
+
return {
|
|
1155
|
+
...appearance.accent ? { "--basic-accent": appearance.accent } : {},
|
|
1156
|
+
...appearance.radius ? { "--basic-radius": appearance.radius } : {}
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
function BasicUIProvider({
|
|
1160
|
+
appearance = {},
|
|
1161
|
+
children
|
|
1162
|
+
}) {
|
|
1163
|
+
const parent = (0, import_react13.useContext)(AppearanceContext);
|
|
1164
|
+
const value = { ...parent, ...appearance };
|
|
1165
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AppearanceContext.Provider, { value, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1166
|
+
"div",
|
|
1167
|
+
{
|
|
1168
|
+
className: "basic-ui",
|
|
1169
|
+
"data-theme": value.theme ?? "light",
|
|
1170
|
+
"data-density": value.density ?? "comfortable",
|
|
1171
|
+
style: appearanceStyle(value),
|
|
1172
|
+
children
|
|
1173
|
+
}
|
|
1174
|
+
) });
|
|
1175
|
+
}
|
|
1176
|
+
function useAppearanceProps() {
|
|
1177
|
+
const appearance = (0, import_react13.useContext)(AppearanceContext);
|
|
1178
|
+
return {
|
|
1179
|
+
"data-theme": appearance.theme ?? "light",
|
|
1180
|
+
"data-density": appearance.density ?? "comfortable",
|
|
1181
|
+
style: appearanceStyle(appearance)
|
|
1182
|
+
};
|
|
1183
|
+
}
|
|
1184
|
+
var BasicButton = (0, import_react13.forwardRef)(
|
|
1185
|
+
function BasicButton2({ variant = "outline", className = "", type = "button", ...props }, ref) {
|
|
1186
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1187
|
+
"button",
|
|
1188
|
+
{
|
|
1189
|
+
...props,
|
|
1190
|
+
ref,
|
|
1191
|
+
type,
|
|
1192
|
+
className: `basic-button ${className}`,
|
|
1193
|
+
"data-variant": variant
|
|
1194
|
+
}
|
|
1195
|
+
);
|
|
1196
|
+
}
|
|
1197
|
+
);
|
|
1198
|
+
function useAction() {
|
|
1199
|
+
const [pending, setPending] = (0, import_react13.useState)(false);
|
|
1200
|
+
const [error, setError] = (0, import_react13.useState)(null);
|
|
1201
|
+
const busy = (0, import_react13.useRef)(false);
|
|
1202
|
+
const mounted = (0, import_react13.useRef)(true);
|
|
1203
|
+
(0, import_react13.useEffect)(() => {
|
|
1204
|
+
mounted.current = true;
|
|
1205
|
+
return () => {
|
|
1206
|
+
mounted.current = false;
|
|
1207
|
+
};
|
|
1208
|
+
}, []);
|
|
1209
|
+
async function run(action) {
|
|
1210
|
+
if (busy.current) return;
|
|
1211
|
+
busy.current = true;
|
|
1212
|
+
setPending(true);
|
|
1213
|
+
setError(null);
|
|
1214
|
+
try {
|
|
1215
|
+
await action();
|
|
1216
|
+
} catch (cause) {
|
|
1217
|
+
if (mounted.current)
|
|
1218
|
+
setError(
|
|
1219
|
+
cause instanceof import_core7.BasicError && cause.code === "REDIRECT_URI_NOT_REGISTERED" ? "Sign-in is unavailable on this URL. Ask the app owner to register its callback URL in project settings." : cause instanceof Error ? cause.message : "The action failed. Please try again."
|
|
1220
|
+
);
|
|
1221
|
+
} finally {
|
|
1222
|
+
busy.current = false;
|
|
1223
|
+
if (mounted.current) setPending(false);
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
return { pending, error, run, clearError: () => setError(null) };
|
|
1227
|
+
}
|
|
1228
|
+
function AuthButton({
|
|
1229
|
+
auth: supplied,
|
|
1230
|
+
input,
|
|
1231
|
+
signOut = false,
|
|
1232
|
+
children,
|
|
1233
|
+
disabled,
|
|
1234
|
+
onClick,
|
|
1235
|
+
...props
|
|
1236
|
+
}) {
|
|
1237
|
+
const live = useAuth();
|
|
1238
|
+
const auth = supplied ?? live;
|
|
1239
|
+
const action = useAction();
|
|
1240
|
+
const expired = auth.status === "expired";
|
|
1241
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "basic-action", children: [
|
|
1242
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1243
|
+
BasicButton,
|
|
1244
|
+
{
|
|
1245
|
+
...props,
|
|
1246
|
+
disabled: disabled || !auth.isReady || action.pending || (signOut ? !auth.isSignedIn && !expired : auth.isSignedIn && !expired),
|
|
1247
|
+
"aria-busy": action.pending,
|
|
1248
|
+
onClick: (event) => {
|
|
1249
|
+
onClick?.(event);
|
|
1250
|
+
if (!event.defaultPrevented)
|
|
1251
|
+
void action.run(
|
|
1252
|
+
() => signOut ? auth.signOut() : auth.signIn(input)
|
|
1253
|
+
);
|
|
1254
|
+
},
|
|
1255
|
+
children: action.pending ? "Please wait\u2026" : children ?? (signOut ? "Sign out" : expired ? "Sign in again" : "Sign in")
|
|
1256
|
+
}
|
|
1257
|
+
),
|
|
1258
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ActionError, { error: action.error, onClose: action.clearError })
|
|
1259
|
+
] });
|
|
1260
|
+
}
|
|
1261
|
+
function SignInButton(props) {
|
|
1262
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AuthButton, { ...props });
|
|
1263
|
+
}
|
|
1264
|
+
function SignOutButton(props) {
|
|
1265
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AuthButton, { ...props, signOut: true });
|
|
1266
|
+
}
|
|
1267
|
+
function UserAvatar({
|
|
1268
|
+
accounts,
|
|
1269
|
+
auth,
|
|
1270
|
+
projectProfile,
|
|
1271
|
+
allowAddAccount,
|
|
1272
|
+
...avatarProps
|
|
1273
|
+
}) {
|
|
1274
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1275
|
+
UserMenu,
|
|
1276
|
+
{
|
|
1277
|
+
accounts,
|
|
1278
|
+
auth,
|
|
1279
|
+
projectProfile,
|
|
1280
|
+
allowAddAccount,
|
|
1281
|
+
sync: avatarProps.sync,
|
|
1282
|
+
showSyncBadge: avatarProps.showSyncBadge,
|
|
1283
|
+
avatarProps
|
|
1284
|
+
}
|
|
1285
|
+
);
|
|
1286
|
+
}
|
|
1287
|
+
function AccountAvatar({
|
|
1288
|
+
profile: supplied,
|
|
1289
|
+
size = 32,
|
|
1290
|
+
showSyncBadge,
|
|
1291
|
+
sync,
|
|
1292
|
+
className = "",
|
|
1293
|
+
style,
|
|
1294
|
+
...props
|
|
1295
|
+
}) {
|
|
1296
|
+
const accounts = useAccounts();
|
|
1297
|
+
const client = useRequiredClient();
|
|
1298
|
+
const profile = supplied === void 0 ? accounts.activeAccount : supplied;
|
|
1299
|
+
const name = profile?.name || (profile?.kind === "anon" ? "Local account" : profile?.handle || profile?.email || "Guest");
|
|
1300
|
+
const initials = name.trim().split(/\s+/).slice(0, 2).map((part) => Array.from(part)[0]).join("").toUpperCase();
|
|
1301
|
+
const avatar = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1302
|
+
import_avatar.Avatar.Root,
|
|
1303
|
+
{
|
|
1304
|
+
...props,
|
|
1305
|
+
className: `basic-avatar ${className}`,
|
|
1306
|
+
style: { width: size, height: size, ...style },
|
|
1307
|
+
role: "img",
|
|
1308
|
+
"aria-label": props["aria-label"] ?? name,
|
|
1309
|
+
children: [
|
|
1310
|
+
profile?.picture && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1311
|
+
import_avatar.Avatar.Image,
|
|
1312
|
+
{
|
|
1313
|
+
src: profile.picture,
|
|
1314
|
+
alt: "",
|
|
1315
|
+
className: "basic-avatar-image",
|
|
1316
|
+
referrerPolicy: "no-referrer"
|
|
1317
|
+
}
|
|
1318
|
+
),
|
|
1319
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_avatar.Avatar.Fallback, { className: "basic-avatar-fallback", children: initials || "?" })
|
|
1320
|
+
]
|
|
1321
|
+
}
|
|
1322
|
+
);
|
|
1323
|
+
return showSyncBadge ?? client.mode === "sync" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "basic-avatar-container", children: [
|
|
1324
|
+
avatar,
|
|
1325
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SyncStatus, { sync, className: "basic-avatar-sync" })
|
|
1326
|
+
] }) : avatar;
|
|
1327
|
+
}
|
|
1328
|
+
function AuthStatus({
|
|
1329
|
+
auth: supplied,
|
|
1330
|
+
className = "",
|
|
1331
|
+
...props
|
|
1332
|
+
}) {
|
|
1333
|
+
const live = useAuth();
|
|
1334
|
+
const auth = supplied ?? live;
|
|
1335
|
+
const expired = auth.status === "expired";
|
|
1336
|
+
const label = !auth.isReady || auth.status === "bootstrapping" ? "Checking session" : expired ? "Session expired" : auth.status === "recovering" ? "Reconnecting session" : auth.isSignedIn ? "Signed in" : auth.isAnonymous ? "Guest \xB7 local only" : "Signed out";
|
|
1337
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1338
|
+
"span",
|
|
1339
|
+
{
|
|
1340
|
+
...props,
|
|
1341
|
+
role: "status",
|
|
1342
|
+
className: `basic-status ${className}`,
|
|
1343
|
+
"data-tone": expired || auth.error ? "warning" : auth.isSignedIn ? "success" : "neutral",
|
|
1344
|
+
children: [
|
|
1345
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "basic-dot" }),
|
|
1346
|
+
label
|
|
1347
|
+
]
|
|
1348
|
+
}
|
|
1349
|
+
);
|
|
1350
|
+
}
|
|
1351
|
+
function SyncStatus({
|
|
1352
|
+
sync: supplied,
|
|
1353
|
+
source,
|
|
1354
|
+
className = "",
|
|
1355
|
+
...props
|
|
1356
|
+
}) {
|
|
1357
|
+
const live = useSyncStatus(source);
|
|
1358
|
+
const client = useRequiredClient();
|
|
1359
|
+
const sync = supplied ?? live;
|
|
1360
|
+
if (client.mode === "rest") return null;
|
|
1361
|
+
const issues = sync.rejected.length + sync.conflicts.length;
|
|
1362
|
+
const labels = {
|
|
1363
|
+
idle: "Idle",
|
|
1364
|
+
bootstrapping: "Starting sync",
|
|
1365
|
+
connecting: "Connecting",
|
|
1366
|
+
online: "Synced",
|
|
1367
|
+
offline: "Offline",
|
|
1368
|
+
stopped: "Sync stopped",
|
|
1369
|
+
error: "Sync error",
|
|
1370
|
+
local: "Local only",
|
|
1371
|
+
stale: "Sync stale",
|
|
1372
|
+
ended: "Access ended"
|
|
1373
|
+
};
|
|
1374
|
+
const label = issues ? `${issues} sync ${issues === 1 ? "issue" : "issues"}` : sync.status === "online" && sync.pendingCount ? "Syncing" : labels[sync.status];
|
|
1375
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1376
|
+
"span",
|
|
1377
|
+
{
|
|
1378
|
+
...props,
|
|
1379
|
+
role: "status",
|
|
1380
|
+
title: props.title ?? `${label}${sync.pendingCount > 0 ? ` \xB7 ${sync.pendingCount} pending` : ""}`,
|
|
1381
|
+
className: `basic-status ${className}`,
|
|
1382
|
+
"data-tone": issues || ["error", "stale", "ended"].includes(sync.status) ? "warning" : sync.status === "online" && !sync.pendingCount ? "success" : ["bootstrapping", "connecting"].includes(sync.status) || sync.status === "online" && sync.pendingCount > 0 ? "progress" : "neutral",
|
|
1383
|
+
children: [
|
|
1384
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "basic-dot" }),
|
|
1385
|
+
label,
|
|
1386
|
+
sync.pendingCount > 0 && ` \xB7 ${sync.pendingCount} pending`
|
|
1387
|
+
]
|
|
1388
|
+
}
|
|
1389
|
+
);
|
|
1390
|
+
}
|
|
1391
|
+
function displayHandle(handle) {
|
|
1392
|
+
return handle ? `@${handle.replace(/^@+/, "")}` : "";
|
|
1393
|
+
}
|
|
1394
|
+
function UserButton(props) {
|
|
1395
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(UserMenu, { ...props, trigger: "button" });
|
|
1396
|
+
}
|
|
1397
|
+
function UserMenu({
|
|
1398
|
+
accounts: supplied,
|
|
1399
|
+
auth: suppliedAuth,
|
|
1400
|
+
sync,
|
|
1401
|
+
showSyncStatus = true,
|
|
1402
|
+
showSyncBadge,
|
|
1403
|
+
shape = "rounded",
|
|
1404
|
+
trigger = "avatar",
|
|
1405
|
+
avatarProps,
|
|
1406
|
+
projectProfile,
|
|
1407
|
+
allowAddAccount = true,
|
|
1408
|
+
className = "",
|
|
1409
|
+
accountSettingsUrl
|
|
1410
|
+
}) {
|
|
1411
|
+
const liveAccounts = useAccounts();
|
|
1412
|
+
const liveAuth = useAuth();
|
|
1413
|
+
const accounts = supplied ?? liveAccounts;
|
|
1414
|
+
const auth = suppliedAuth ?? liveAuth;
|
|
1415
|
+
const action = useAction();
|
|
1416
|
+
const [settingsOpen, setSettingsOpen] = (0, import_react13.useState)(false);
|
|
1417
|
+
const [clearAccountId, setClearAccountId] = (0, import_react13.useState)(null);
|
|
1418
|
+
const triggerRef = (0, import_react13.useRef)(null);
|
|
1419
|
+
const appearance = useAppearanceProps();
|
|
1420
|
+
const expired = auth.status === "expired";
|
|
1421
|
+
const active = accounts.activeAccount;
|
|
1422
|
+
const otherAccounts = accounts.accounts.filter(
|
|
1423
|
+
(account) => account.id !== active?.id
|
|
1424
|
+
);
|
|
1425
|
+
const name = active?.name || active?.kind !== "anon" && displayHandle(active?.handle) || "Local account";
|
|
1426
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: `basic-action ${className}`, children: [
|
|
1427
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_menu.Menu.Root, { children: [
|
|
1428
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1429
|
+
import_menu.Menu.Trigger,
|
|
1430
|
+
{
|
|
1431
|
+
ref: triggerRef,
|
|
1432
|
+
className: `basic-button basic-account-trigger basic-trigger-${trigger}`,
|
|
1433
|
+
"data-shape": shape,
|
|
1434
|
+
disabled: !auth.isReady || action.pending,
|
|
1435
|
+
"aria-label": "Open user menu",
|
|
1436
|
+
children: [
|
|
1437
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1438
|
+
AccountAvatar,
|
|
1439
|
+
{
|
|
1440
|
+
profile: accounts.activeAccount,
|
|
1441
|
+
sync,
|
|
1442
|
+
showSyncBadge,
|
|
1443
|
+
...avatarProps
|
|
1444
|
+
}
|
|
1445
|
+
),
|
|
1446
|
+
trigger === "button" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
1447
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "basic-identity", children: [
|
|
1448
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: name }),
|
|
1449
|
+
active?.kind !== "anon" && active?.handle && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("small", { children: displayHandle(active.handle) })
|
|
1450
|
+
] }),
|
|
1451
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1452
|
+
"svg",
|
|
1453
|
+
{
|
|
1454
|
+
className: "basic-chevron",
|
|
1455
|
+
width: "16",
|
|
1456
|
+
height: "16",
|
|
1457
|
+
viewBox: "0 0 24 24",
|
|
1458
|
+
fill: "none",
|
|
1459
|
+
stroke: "currentColor",
|
|
1460
|
+
strokeWidth: "2",
|
|
1461
|
+
strokeLinecap: "round",
|
|
1462
|
+
strokeLinejoin: "round",
|
|
1463
|
+
"aria-hidden": "true",
|
|
1464
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "m6 9 6 6 6-6" })
|
|
1465
|
+
}
|
|
1466
|
+
)
|
|
1467
|
+
] })
|
|
1468
|
+
]
|
|
1469
|
+
}
|
|
1470
|
+
),
|
|
1471
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_menu.Menu.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_menu.Menu.Positioner, { sideOffset: 8, className: "basic-positioner", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_menu.Menu.Popup, { ...appearance, className: "basic-ui basic-menu", children: [
|
|
1472
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "basic-menu-profile", children: [
|
|
1473
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1474
|
+
AccountAvatar,
|
|
1475
|
+
{
|
|
1476
|
+
profile: active,
|
|
1477
|
+
size: 48,
|
|
1478
|
+
sync,
|
|
1479
|
+
showSyncBadge: false
|
|
1480
|
+
}
|
|
1481
|
+
),
|
|
1482
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "basic-identity", children: [
|
|
1483
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: active ? name : "No active account" }),
|
|
1484
|
+
active?.kind !== "anon" && active?.handle && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("small", { children: displayHandle(active.handle) }),
|
|
1485
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "basic-account-statuses", children: [
|
|
1486
|
+
expired && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "basic-status", "data-tone": "warning", children: "Expired" }),
|
|
1487
|
+
showSyncStatus && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SyncStatus, { sync })
|
|
1488
|
+
] })
|
|
1489
|
+
] })
|
|
1490
|
+
] }),
|
|
1491
|
+
expired && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1492
|
+
import_menu.Menu.Item,
|
|
1493
|
+
{
|
|
1494
|
+
className: "basic-menu-item basic-menu-reauth",
|
|
1495
|
+
disabled: action.pending,
|
|
1496
|
+
onClick: () => void action.run(() => auth.signIn()),
|
|
1497
|
+
children: "Sign in again"
|
|
1498
|
+
}
|
|
1499
|
+
),
|
|
1500
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1501
|
+
import_menu.Menu.Item,
|
|
1502
|
+
{
|
|
1503
|
+
className: "basic-menu-item basic-manage-account",
|
|
1504
|
+
onClick: () => setSettingsOpen(true),
|
|
1505
|
+
children: [
|
|
1506
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1507
|
+
"svg",
|
|
1508
|
+
{
|
|
1509
|
+
className: "basic-menu-icon",
|
|
1510
|
+
width: "16",
|
|
1511
|
+
height: "16",
|
|
1512
|
+
viewBox: "0 0 24 24",
|
|
1513
|
+
fill: "none",
|
|
1514
|
+
stroke: "currentColor",
|
|
1515
|
+
strokeWidth: "1.75",
|
|
1516
|
+
strokeLinecap: "round",
|
|
1517
|
+
strokeLinejoin: "round",
|
|
1518
|
+
"aria-hidden": "true",
|
|
1519
|
+
children: [
|
|
1520
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "12", cy: "7", r: "4" }),
|
|
1521
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" })
|
|
1522
|
+
]
|
|
1523
|
+
}
|
|
1524
|
+
),
|
|
1525
|
+
"Manage account"
|
|
1526
|
+
]
|
|
1527
|
+
}
|
|
1528
|
+
),
|
|
1529
|
+
(auth.isSignedIn || expired || active?.kind === "anon") && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1530
|
+
import_menu.Menu.Item,
|
|
1531
|
+
{
|
|
1532
|
+
className: "basic-menu-item basic-menu-signout",
|
|
1533
|
+
disabled: action.pending,
|
|
1534
|
+
onClick: () => active?.kind === "anon" ? setClearAccountId(active.id) : void action.run(() => auth.signOut()),
|
|
1535
|
+
children: [
|
|
1536
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1537
|
+
"svg",
|
|
1538
|
+
{
|
|
1539
|
+
className: "basic-menu-icon",
|
|
1540
|
+
width: "16",
|
|
1541
|
+
height: "16",
|
|
1542
|
+
viewBox: "0 0 24 24",
|
|
1543
|
+
fill: "none",
|
|
1544
|
+
stroke: "currentColor",
|
|
1545
|
+
strokeWidth: "1.75",
|
|
1546
|
+
strokeLinecap: "round",
|
|
1547
|
+
strokeLinejoin: "round",
|
|
1548
|
+
"aria-hidden": "true",
|
|
1549
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4M16 17l5-5-5-5M21 12H9" })
|
|
1550
|
+
}
|
|
1551
|
+
),
|
|
1552
|
+
active?.kind === "anon" ? "Clear account" : "Sign out"
|
|
1553
|
+
]
|
|
1554
|
+
}
|
|
1555
|
+
),
|
|
1556
|
+
otherAccounts.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
1557
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_menu.Menu.Separator, { className: "basic-separator" }),
|
|
1558
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "basic-menu-heading", children: "Switch accounts" })
|
|
1559
|
+
] }),
|
|
1560
|
+
otherAccounts.map((account) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1561
|
+
import_menu.Menu.Item,
|
|
1562
|
+
{
|
|
1563
|
+
className: "basic-menu-item",
|
|
1564
|
+
disabled: action.pending,
|
|
1565
|
+
onClick: () => void action.run(() => accounts.switchAccount(account.id)),
|
|
1566
|
+
children: [
|
|
1567
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1568
|
+
AccountAvatar,
|
|
1569
|
+
{
|
|
1570
|
+
profile: account,
|
|
1571
|
+
size: 32,
|
|
1572
|
+
sync,
|
|
1573
|
+
showSyncBadge: false
|
|
1574
|
+
}
|
|
1575
|
+
),
|
|
1576
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "basic-identity", children: [
|
|
1577
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: account.name || account.kind !== "anon" && displayHandle(account.handle) || (account.kind === "anon" ? "Local account" : "Account") }),
|
|
1578
|
+
account.kind !== "anon" && account.handle && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("small", { children: displayHandle(account.handle) })
|
|
1579
|
+
] }),
|
|
1580
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "basic-account-statuses", children: account.auth.status === "expired" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "basic-status", "data-tone": "warning", children: "Expired" }) : null })
|
|
1581
|
+
]
|
|
1582
|
+
},
|
|
1583
|
+
account.id
|
|
1584
|
+
)),
|
|
1585
|
+
(allowAddAccount || !auth.isSignedIn && !expired) && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1586
|
+
import_menu.Menu.Item,
|
|
1587
|
+
{
|
|
1588
|
+
className: "basic-menu-item",
|
|
1589
|
+
disabled: action.pending,
|
|
1590
|
+
onClick: () => void action.run(
|
|
1591
|
+
() => allowAddAccount ? accounts.addAccount({ signIn: true }) : auth.signIn()
|
|
1592
|
+
),
|
|
1593
|
+
children: "\uFF0B Add account"
|
|
1594
|
+
}
|
|
1595
|
+
)
|
|
1596
|
+
] }) }) })
|
|
1597
|
+
] }),
|
|
1598
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1599
|
+
ActionError,
|
|
1600
|
+
{
|
|
1601
|
+
error: action.error,
|
|
1602
|
+
onClose: action.clearError,
|
|
1603
|
+
finalFocus: triggerRef
|
|
1604
|
+
}
|
|
1605
|
+
),
|
|
1606
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1607
|
+
Modal,
|
|
1608
|
+
{
|
|
1609
|
+
open: clearAccountId !== null,
|
|
1610
|
+
onOpenChange: (open) => {
|
|
1611
|
+
if (!open) setClearAccountId(null);
|
|
1612
|
+
},
|
|
1613
|
+
trigger: null,
|
|
1614
|
+
finalFocus: triggerRef,
|
|
1615
|
+
title: "Clear local account?",
|
|
1616
|
+
description: "This deletes this account\u2019s local data, including unsynced changes. This cannot be undone.",
|
|
1617
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "basic-actions", children: [
|
|
1618
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1619
|
+
BasicButton,
|
|
1620
|
+
{
|
|
1621
|
+
disabled: action.pending,
|
|
1622
|
+
onClick: () => setClearAccountId(null),
|
|
1623
|
+
children: "Cancel"
|
|
1624
|
+
}
|
|
1625
|
+
),
|
|
1626
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1627
|
+
BasicButton,
|
|
1628
|
+
{
|
|
1629
|
+
disabled: action.pending,
|
|
1630
|
+
onClick: () => {
|
|
1631
|
+
const id = clearAccountId;
|
|
1632
|
+
if (!id) return;
|
|
1633
|
+
setClearAccountId(null);
|
|
1634
|
+
void action.run(() => accounts.removeAccount(id));
|
|
1635
|
+
},
|
|
1636
|
+
children: "Clear account"
|
|
1637
|
+
}
|
|
1638
|
+
)
|
|
1639
|
+
] })
|
|
1640
|
+
}
|
|
1641
|
+
),
|
|
1642
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1643
|
+
AccountSettingsModal,
|
|
1644
|
+
{
|
|
1645
|
+
auth,
|
|
1646
|
+
accounts,
|
|
1647
|
+
open: settingsOpen,
|
|
1648
|
+
onOpenChange: setSettingsOpen,
|
|
1649
|
+
trigger: null,
|
|
1650
|
+
finalFocus: triggerRef,
|
|
1651
|
+
accountSettingsUrl,
|
|
1652
|
+
projectProfile,
|
|
1653
|
+
sync
|
|
1654
|
+
}
|
|
1655
|
+
)
|
|
1656
|
+
] });
|
|
1657
|
+
}
|
|
1658
|
+
function ActionError({
|
|
1659
|
+
error,
|
|
1660
|
+
onClose,
|
|
1661
|
+
finalFocus
|
|
1662
|
+
}) {
|
|
1663
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1664
|
+
Modal,
|
|
1665
|
+
{
|
|
1666
|
+
open: !!error,
|
|
1667
|
+
onOpenChange: (open) => {
|
|
1668
|
+
if (!open) onClose();
|
|
1669
|
+
},
|
|
1670
|
+
trigger: null,
|
|
1671
|
+
finalFocus,
|
|
1672
|
+
title: "Unable to complete action",
|
|
1673
|
+
description: "Your request could not be completed.",
|
|
1674
|
+
children: [
|
|
1675
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { role: "alert", className: "basic-error", children: error }),
|
|
1676
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(BasicButton, { onClick: onClose, children: "Dismiss" })
|
|
1677
|
+
]
|
|
1678
|
+
}
|
|
1679
|
+
);
|
|
1680
|
+
}
|
|
1681
|
+
function Modal({
|
|
1682
|
+
open,
|
|
1683
|
+
onOpenChange,
|
|
1684
|
+
trigger,
|
|
1685
|
+
finalFocus,
|
|
1686
|
+
title,
|
|
1687
|
+
description,
|
|
1688
|
+
header,
|
|
1689
|
+
sectionLabel,
|
|
1690
|
+
children
|
|
1691
|
+
}) {
|
|
1692
|
+
const appearance = useAppearanceProps();
|
|
1693
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_dialog.Dialog.Root, { open, onOpenChange, children: [
|
|
1694
|
+
trigger !== null && ((0, import_react13.isValidElement)(trigger) ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dialog.Dialog.Trigger, { render: trigger }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dialog.Dialog.Trigger, { render: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(BasicButton, {}), children: trigger ?? title })),
|
|
1695
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_dialog.Dialog.Portal, { children: [
|
|
1696
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dialog.Dialog.Backdrop, { className: "basic-backdrop" }),
|
|
1697
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1698
|
+
import_dialog.Dialog.Popup,
|
|
1699
|
+
{
|
|
1700
|
+
...appearance,
|
|
1701
|
+
finalFocus,
|
|
1702
|
+
className: "basic-ui basic-dialog",
|
|
1703
|
+
children: [
|
|
1704
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1705
|
+
"div",
|
|
1706
|
+
{
|
|
1707
|
+
className: `basic-dialog-heading${header ? " basic-dialog-heading-overlay" : ""}`,
|
|
1708
|
+
children: [
|
|
1709
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dialog.Dialog.Title, { className: header ? "basic-sr-only" : "basic-title", children: title }),
|
|
1710
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1711
|
+
import_dialog.Dialog.Close,
|
|
1712
|
+
{
|
|
1713
|
+
className: "basic-button",
|
|
1714
|
+
"aria-label": `Close ${title.toLowerCase()}`,
|
|
1715
|
+
children: "\u2715"
|
|
1716
|
+
}
|
|
1717
|
+
)
|
|
1718
|
+
]
|
|
1719
|
+
}
|
|
1720
|
+
),
|
|
1721
|
+
header,
|
|
1722
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1723
|
+
import_dialog.Dialog.Description,
|
|
1724
|
+
{
|
|
1725
|
+
className: header ? "basic-sr-only" : "basic-muted",
|
|
1726
|
+
children: description
|
|
1727
|
+
}
|
|
1728
|
+
),
|
|
1729
|
+
sectionLabel && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "basic-modal-section", children: sectionLabel }),
|
|
1730
|
+
children
|
|
1731
|
+
]
|
|
1732
|
+
}
|
|
1733
|
+
)
|
|
1734
|
+
] })
|
|
1735
|
+
] });
|
|
1736
|
+
}
|
|
1737
|
+
function AccountSettingsModal({
|
|
1738
|
+
auth: suppliedAuth,
|
|
1739
|
+
accounts: suppliedAccounts,
|
|
1740
|
+
accountSettingsUrl,
|
|
1741
|
+
projectProfile,
|
|
1742
|
+
sync,
|
|
1743
|
+
children,
|
|
1744
|
+
...props
|
|
1745
|
+
}) {
|
|
1746
|
+
const liveAuth = useAuth();
|
|
1747
|
+
const liveAccounts = useAccounts();
|
|
1748
|
+
const auth = suppliedAuth ?? liveAuth;
|
|
1749
|
+
const accounts = suppliedAccounts ?? liveAccounts;
|
|
1750
|
+
const liveSync = useSyncStatus();
|
|
1751
|
+
const syncState = sync ?? liveSync;
|
|
1752
|
+
const client = useRequiredClient();
|
|
1753
|
+
const account = accounts.activeAccount;
|
|
1754
|
+
const copyAction = useAction();
|
|
1755
|
+
const [copied, setCopied] = (0, import_react13.useState)(null);
|
|
1756
|
+
const details = [
|
|
1757
|
+
[
|
|
1758
|
+
"Account DID",
|
|
1759
|
+
account?.kind === "anon" ? "Not assigned (local account)" : account?.did ?? auth.did ?? "Unavailable"
|
|
1760
|
+
],
|
|
1761
|
+
[
|
|
1762
|
+
"PDS URL",
|
|
1763
|
+
account?.kind === "anon" ? "Not connected (local account)" : auth.user?.pds_url ?? "Unavailable"
|
|
1764
|
+
],
|
|
1765
|
+
["Local account ID", account?.id ?? "None"],
|
|
1766
|
+
["Account kind", account?.kind ?? "None"],
|
|
1767
|
+
["Storage prefix", account?.storagePrefix ?? "None"],
|
|
1768
|
+
["Client mode", client.mode],
|
|
1769
|
+
["Auth state", auth.status],
|
|
1770
|
+
["Ready", auth.isReady ? "Yes" : "No"],
|
|
1771
|
+
["Can write", auth.canWrite ? "Yes" : "No"],
|
|
1772
|
+
["Read-only reason", auth.readOnlyReason ?? "None"],
|
|
1773
|
+
["Auth error code", auth.error?.code ?? "None"],
|
|
1774
|
+
["Sync state", syncState.status],
|
|
1775
|
+
["Pending writes", String(syncState.pendingCount)],
|
|
1776
|
+
["Rejected writes", String(syncState.rejected.length)],
|
|
1777
|
+
["Conflicts", String(syncState.conflicts.length)]
|
|
1778
|
+
];
|
|
1779
|
+
const diagnostics = JSON.stringify(Object.fromEntries(details), null, 2);
|
|
1780
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1781
|
+
Modal,
|
|
1782
|
+
{
|
|
1783
|
+
...props,
|
|
1784
|
+
title: "Account settings",
|
|
1785
|
+
description: "Your identity and session on this device.",
|
|
1786
|
+
header: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AccountHeader, { account: accounts.activeAccount, sync }),
|
|
1787
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_tabs.Tabs.Root, { defaultValue: "profile", children: [
|
|
1788
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1789
|
+
import_tabs.Tabs.List,
|
|
1790
|
+
{
|
|
1791
|
+
className: "basic-tabs",
|
|
1792
|
+
"aria-label": "Account settings sections",
|
|
1793
|
+
children: [
|
|
1794
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_tabs.Tabs.Tab, { value: "profile", children: "Profile" }),
|
|
1795
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_tabs.Tabs.Tab, { value: "advanced", children: "Advanced" })
|
|
1796
|
+
]
|
|
1797
|
+
}
|
|
1798
|
+
),
|
|
1799
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_tabs.Tabs.Panel, { value: "profile", keepMounted: true, children: [
|
|
1800
|
+
auth.isSignedIn && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1801
|
+
ProjectProfileEditor,
|
|
1802
|
+
{
|
|
1803
|
+
profile: projectProfile,
|
|
1804
|
+
canWrite: auth.canWrite
|
|
1805
|
+
},
|
|
1806
|
+
accounts.activeAccount?.id ?? auth.did
|
|
1807
|
+
),
|
|
1808
|
+
accountSettingsUrl && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("a", { className: "basic-link", href: accountSettingsUrl, children: "Manage profile and security \u2197" }),
|
|
1809
|
+
children,
|
|
1810
|
+
!auth.isSignedIn && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "basic-actions", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SignInButton, { auth }) })
|
|
1811
|
+
] }),
|
|
1812
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_tabs.Tabs.Panel, { value: "advanced", children: [
|
|
1813
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "basic-muted", children: "Read-only diagnostics for this account." }),
|
|
1814
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("dl", { className: "basic-details", children: details.map(([label, value]) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react13.Fragment, { children: [
|
|
1815
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("dt", { children: label }),
|
|
1816
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("dd", { children: value })
|
|
1817
|
+
] }, label)) }),
|
|
1818
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "basic-actions", children: [
|
|
1819
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1820
|
+
BasicButton,
|
|
1821
|
+
{
|
|
1822
|
+
disabled: copyAction.pending,
|
|
1823
|
+
onClick: () => {
|
|
1824
|
+
setCopied(null);
|
|
1825
|
+
void copyAction.run(async () => {
|
|
1826
|
+
if (!navigator.clipboard?.writeText)
|
|
1827
|
+
throw new Error("Clipboard is unavailable in this browser.");
|
|
1828
|
+
await navigator.clipboard.writeText(diagnostics);
|
|
1829
|
+
setCopied(diagnostics);
|
|
1830
|
+
});
|
|
1831
|
+
},
|
|
1832
|
+
children: "Copy to clipboard"
|
|
1833
|
+
}
|
|
1834
|
+
),
|
|
1835
|
+
copied === diagnostics && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { role: "status", className: "basic-muted", children: "Copied" })
|
|
1836
|
+
] }),
|
|
1837
|
+
copyAction.error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { role: "alert", className: "basic-error", children: copyAction.error })
|
|
1838
|
+
] })
|
|
1839
|
+
] })
|
|
1840
|
+
}
|
|
1841
|
+
);
|
|
1842
|
+
}
|
|
1843
|
+
function AccountHeader({
|
|
1844
|
+
account,
|
|
1845
|
+
sync
|
|
1846
|
+
}) {
|
|
1847
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "basic-profile", children: [
|
|
1848
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1849
|
+
AccountAvatar,
|
|
1850
|
+
{
|
|
1851
|
+
profile: account,
|
|
1852
|
+
size: 72,
|
|
1853
|
+
sync,
|
|
1854
|
+
showSyncBadge: false
|
|
1855
|
+
}
|
|
1856
|
+
),
|
|
1857
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "basic-identity", children: [
|
|
1858
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: account?.name || account?.kind !== "anon" && displayHandle(account?.handle) || "Local account" }),
|
|
1859
|
+
account?.kind !== "anon" && account?.handle && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("small", { children: displayHandle(account.handle) }),
|
|
1860
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "basic-account-statuses", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SyncStatus, { sync }) })
|
|
1861
|
+
] })
|
|
1862
|
+
] });
|
|
1863
|
+
}
|
|
1864
|
+
function ProjectProfileEditor({
|
|
1865
|
+
profile: supplied,
|
|
1866
|
+
canWrite
|
|
1867
|
+
}) {
|
|
1868
|
+
const live = useProjectProfile(supplied === void 0);
|
|
1869
|
+
const profile = supplied ?? live;
|
|
1870
|
+
const action = useAction();
|
|
1871
|
+
const [draft, setDraft] = (0, import_react13.useState)({});
|
|
1872
|
+
const [saved, setSaved] = (0, import_react13.useState)(false);
|
|
1873
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1874
|
+
"form",
|
|
1875
|
+
{
|
|
1876
|
+
className: "basic-profile-editor",
|
|
1877
|
+
onSubmit: (event) => {
|
|
1878
|
+
event.preventDefault();
|
|
1879
|
+
if (!canWrite || !Object.keys(draft).length) return;
|
|
1880
|
+
void action.run(async () => {
|
|
1881
|
+
await profile.update(draft);
|
|
1882
|
+
setDraft({});
|
|
1883
|
+
setSaved(true);
|
|
1884
|
+
});
|
|
1885
|
+
},
|
|
1886
|
+
children: [
|
|
1887
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { className: "basic-section-label", children: "Personal information" }),
|
|
1888
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "basic-muted", children: "Only this app sees these overrides. Clear a field to use your universal profile value." }),
|
|
1889
|
+
profile.isLoading ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { role: "status", children: "Loading project profile\u2026" }) : profile.error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
1890
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { role: "alert", className: "basic-error", children: profile.error.message }),
|
|
1891
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(BasicButton, { onClick: profile.refresh, children: "Retry profile" })
|
|
1892
|
+
] }) : profile.data && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("fieldset", { disabled: !canWrite || action.pending, children: [
|
|
1893
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "basic-field", children: [
|
|
1894
|
+
"Display name",
|
|
1895
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1896
|
+
"input",
|
|
1897
|
+
{
|
|
1898
|
+
className: "basic-input",
|
|
1899
|
+
type: "text",
|
|
1900
|
+
maxLength: 64,
|
|
1901
|
+
value: ("name" in draft ? draft.name : profile.data?.profile.name) ?? "",
|
|
1902
|
+
onChange: (event) => {
|
|
1903
|
+
setSaved(false);
|
|
1904
|
+
setDraft((current) => ({
|
|
1905
|
+
...current,
|
|
1906
|
+
name: event.target.value || null
|
|
1907
|
+
}));
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
)
|
|
1911
|
+
] }),
|
|
1912
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "basic-actions basic-modal-footer", children: [
|
|
1913
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1914
|
+
BasicButton,
|
|
1915
|
+
{
|
|
1916
|
+
variant: "ghost",
|
|
1917
|
+
onClick: () => {
|
|
1918
|
+
setDraft({});
|
|
1919
|
+
setSaved(false);
|
|
1920
|
+
},
|
|
1921
|
+
children: "Cancel"
|
|
1922
|
+
}
|
|
1923
|
+
),
|
|
1924
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1925
|
+
BasicButton,
|
|
1926
|
+
{
|
|
1927
|
+
type: "submit",
|
|
1928
|
+
variant: "solid",
|
|
1929
|
+
disabled: !Object.keys(draft).length,
|
|
1930
|
+
children: "Save changes"
|
|
1931
|
+
}
|
|
1932
|
+
)
|
|
1933
|
+
] })
|
|
1934
|
+
] }),
|
|
1935
|
+
action.error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { role: "alert", className: "basic-error", children: action.error }),
|
|
1936
|
+
saved && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { role: "status", children: "Project profile saved." })
|
|
1937
|
+
]
|
|
1938
|
+
}
|
|
1939
|
+
);
|
|
1940
|
+
}
|
|
1941
|
+
function SharesModal({
|
|
1942
|
+
auth: supplied,
|
|
1943
|
+
accounts: suppliedAccounts,
|
|
1944
|
+
sync,
|
|
1945
|
+
shares,
|
|
1946
|
+
scope,
|
|
1947
|
+
repo = "default",
|
|
1948
|
+
...props
|
|
1949
|
+
}) {
|
|
1950
|
+
const live = useAuth();
|
|
1951
|
+
const liveAccounts = useAccounts();
|
|
1952
|
+
const auth = supplied ?? live;
|
|
1953
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1954
|
+
Modal,
|
|
1955
|
+
{
|
|
1956
|
+
...props,
|
|
1957
|
+
title: "Shares",
|
|
1958
|
+
description: "Manage incoming invitations and share access to selected data.",
|
|
1959
|
+
header: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1960
|
+
AccountHeader,
|
|
1961
|
+
{
|
|
1962
|
+
account: (suppliedAccounts ?? liveAccounts).activeAccount,
|
|
1963
|
+
sync
|
|
1964
|
+
}
|
|
1965
|
+
),
|
|
1966
|
+
children: !auth.isReady ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { role: "status", children: "Checking session\u2026" }) : !auth.isSignedIn ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
1967
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "basic-muted", children: "Sign in to share data." }),
|
|
1968
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SignInButton, { auth })
|
|
1969
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1970
|
+
SharesContent,
|
|
1971
|
+
{
|
|
1972
|
+
shares,
|
|
1973
|
+
scope,
|
|
1974
|
+
repo,
|
|
1975
|
+
canWrite: auth.canWrite
|
|
1976
|
+
},
|
|
1977
|
+
auth.did ?? "account"
|
|
1978
|
+
)
|
|
1979
|
+
}
|
|
1980
|
+
);
|
|
1981
|
+
}
|
|
1982
|
+
function SharesContent({
|
|
1983
|
+
shares: supplied,
|
|
1984
|
+
scope,
|
|
1985
|
+
repo,
|
|
1986
|
+
canWrite
|
|
1987
|
+
}) {
|
|
1988
|
+
const live = useOutgoingShares();
|
|
1989
|
+
const shares = supplied ?? live;
|
|
1990
|
+
const action = useAction();
|
|
1991
|
+
const [recipient, setRecipient] = (0, import_react13.useState)("");
|
|
1992
|
+
const [role, setRole] = (0, import_react13.useState)("viewer");
|
|
1993
|
+
const [confirmation, setConfirmation] = (0, import_react13.useState)(null);
|
|
1994
|
+
const [message, setMessage] = (0, import_react13.useState)("");
|
|
1995
|
+
const scopeValid = scope.length > 0 && scope.every(
|
|
1996
|
+
(item) => item.table.trim() && (item.recordIds === void 0 || item.recordIds.length > 0)
|
|
1997
|
+
);
|
|
1998
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_tabs.Tabs.Root, { defaultValue: "outgoing", className: "basic-shares", children: [
|
|
1999
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_tabs.Tabs.List, { className: "basic-tabs", "aria-label": "Share direction", children: [
|
|
2000
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_tabs.Tabs.Tab, { value: "outgoing", children: "Outgoing" }),
|
|
2001
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_tabs.Tabs.Tab, { value: "incoming", children: "Incoming invitations" })
|
|
2002
|
+
] }),
|
|
2003
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_tabs.Tabs.Panel, { value: "incoming", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { "aria-label": "Incoming invitations", children: [
|
|
2004
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { className: "basic-title", children: "Incoming invitations" }),
|
|
2005
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "basic-muted", children: "View, accept, or decline incoming invitations in Basic ID. Your app session cannot access the account-wide inbox." }),
|
|
2006
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("a", { className: "basic-link", href: shares.manageUrl(), children: "Open incoming invitations in Basic ID \u2197" })
|
|
2007
|
+
] }) }),
|
|
2008
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_tabs.Tabs.Panel, { value: "outgoing", children: [
|
|
2009
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "basic-muted", children: "Revoking access stops future requests. It cannot retract data the recipient already downloaded." }),
|
|
2010
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "basic-scope", children: [
|
|
2011
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: "New invitation permissions" }),
|
|
2012
|
+
scope.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [
|
|
2013
|
+
item.table,
|
|
2014
|
+
" \xB7",
|
|
2015
|
+
" ",
|
|
2016
|
+
item.recordIds ? `${item.recordIds.length} selected records` : "all records"
|
|
2017
|
+
] }, index))
|
|
2018
|
+
] }),
|
|
2019
|
+
!canWrite && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "basic-error", children: "This session is read only. Sharing changes are disabled." }),
|
|
2020
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
2021
|
+
"form",
|
|
2022
|
+
{
|
|
2023
|
+
className: "basic-share-form",
|
|
2024
|
+
onSubmit: (event) => {
|
|
2025
|
+
event.preventDefault();
|
|
2026
|
+
if (!recipient.trim() || !scopeValid || !canWrite) return;
|
|
2027
|
+
void action.run(async () => {
|
|
2028
|
+
await shares.create({
|
|
2029
|
+
repo,
|
|
2030
|
+
scope,
|
|
2031
|
+
role,
|
|
2032
|
+
...recipient.trim().startsWith("did:") ? { recipientDid: recipient.trim() } : { recipientHandle: recipient.trim().replace(/^@+/, "") }
|
|
2033
|
+
});
|
|
2034
|
+
setRecipient("");
|
|
2035
|
+
setMessage("Invitation sent.");
|
|
2036
|
+
shares.refresh();
|
|
2037
|
+
});
|
|
2038
|
+
},
|
|
2039
|
+
children: [
|
|
2040
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "basic-field", children: [
|
|
2041
|
+
"Recipient handle or DID",
|
|
2042
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2043
|
+
"input",
|
|
2044
|
+
{
|
|
2045
|
+
className: "basic-input",
|
|
2046
|
+
value: recipient,
|
|
2047
|
+
onChange: (event) => setRecipient(event.target.value),
|
|
2048
|
+
placeholder: "@alex.basic.id",
|
|
2049
|
+
required: true,
|
|
2050
|
+
disabled: action.pending || !canWrite
|
|
2051
|
+
}
|
|
2052
|
+
)
|
|
2053
|
+
] }),
|
|
2054
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "basic-field", children: [
|
|
2055
|
+
"Permission",
|
|
2056
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
2057
|
+
"select",
|
|
2058
|
+
{
|
|
2059
|
+
className: "basic-input",
|
|
2060
|
+
value: role,
|
|
2061
|
+
onChange: (event) => setRole(event.target.value),
|
|
2062
|
+
disabled: action.pending || !canWrite,
|
|
2063
|
+
children: [
|
|
2064
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "viewer", children: "Can view" }),
|
|
2065
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "editor", children: "Can edit" })
|
|
2066
|
+
]
|
|
2067
|
+
}
|
|
2068
|
+
)
|
|
2069
|
+
] }),
|
|
2070
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2071
|
+
BasicButton,
|
|
2072
|
+
{
|
|
2073
|
+
type: "submit",
|
|
2074
|
+
variant: "solid",
|
|
2075
|
+
disabled: action.pending || !canWrite || !scopeValid || !recipient.trim(),
|
|
2076
|
+
children: action.pending ? "Please wait\u2026" : "Send invitation"
|
|
2077
|
+
}
|
|
2078
|
+
)
|
|
2079
|
+
]
|
|
2080
|
+
}
|
|
2081
|
+
),
|
|
2082
|
+
!scopeValid && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { role: "alert", className: "basic-error", children: "Select at least one table or record to share." }),
|
|
2083
|
+
action.error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { role: "alert", className: "basic-error", children: action.error }),
|
|
2084
|
+
message && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { role: "status", className: "basic-muted", children: message }),
|
|
2085
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "basic-section-heading", children: [
|
|
2086
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { className: "basic-title", children: "Outgoing shares \xB7 all permissions" }),
|
|
2087
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2088
|
+
BasicButton,
|
|
2089
|
+
{
|
|
2090
|
+
disabled: action.pending || shares.isLoading,
|
|
2091
|
+
onClick: shares.refresh,
|
|
2092
|
+
children: "Refresh"
|
|
2093
|
+
}
|
|
2094
|
+
)
|
|
2095
|
+
] }),
|
|
2096
|
+
shares.isLoading ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { role: "status", children: "Loading shares\u2026" }) : shares.error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { role: "alert", className: "basic-error", children: shares.error.message }) : !shares.data.length ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "basic-empty", children: "No outgoing shares yet." }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "basic-share-list", children: shares.data.map((share) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("li", { children: [
|
|
2097
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "basic-identity", children: [
|
|
2098
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: share.display.shareName || share.recipientDid }),
|
|
2099
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("small", { children: [
|
|
2100
|
+
share.role === "viewer" ? "Can view" : "Can edit",
|
|
2101
|
+
" \xB7",
|
|
2102
|
+
" ",
|
|
2103
|
+
share.effectiveState,
|
|
2104
|
+
" \xB7",
|
|
2105
|
+
" ",
|
|
2106
|
+
share.scope.map((item) => item.table).join(", ")
|
|
2107
|
+
] })
|
|
2108
|
+
] }),
|
|
2109
|
+
share.state !== "ended" && (confirmation === share.id ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "basic-actions", children: [
|
|
2110
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Remove access?" }),
|
|
2111
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
2112
|
+
BasicButton,
|
|
2113
|
+
{
|
|
2114
|
+
disabled: action.pending || !canWrite,
|
|
2115
|
+
onClick: () => void action.run(async () => {
|
|
2116
|
+
await (share.state === "pending" ? shares.cancel(share.id) : shares.revoke(share.id));
|
|
2117
|
+
setConfirmation(null);
|
|
2118
|
+
setMessage(
|
|
2119
|
+
share.state === "pending" ? "Invitation canceled." : "Access revoked."
|
|
2120
|
+
);
|
|
2121
|
+
shares.refresh();
|
|
2122
|
+
}),
|
|
2123
|
+
children: [
|
|
2124
|
+
"Confirm",
|
|
2125
|
+
" ",
|
|
2126
|
+
share.state === "pending" ? "cancel" : "revoke"
|
|
2127
|
+
]
|
|
2128
|
+
}
|
|
2129
|
+
),
|
|
2130
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2131
|
+
BasicButton,
|
|
2132
|
+
{
|
|
2133
|
+
disabled: action.pending,
|
|
2134
|
+
onClick: () => setConfirmation(null),
|
|
2135
|
+
children: "Keep access"
|
|
2136
|
+
}
|
|
2137
|
+
)
|
|
2138
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2139
|
+
BasicButton,
|
|
2140
|
+
{
|
|
2141
|
+
disabled: action.pending || !canWrite,
|
|
2142
|
+
onClick: () => setConfirmation(share.id),
|
|
2143
|
+
children: share.state === "pending" ? "Cancel invitation" : "Revoke access"
|
|
2144
|
+
}
|
|
2145
|
+
))
|
|
2146
|
+
] }, share.id)) })
|
|
2147
|
+
] })
|
|
2148
|
+
] });
|
|
2149
|
+
}
|
|
1105
2150
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1106
2151
|
0 && (module.exports = {
|
|
2152
|
+
AccountSettingsModal,
|
|
2153
|
+
AuthStatus,
|
|
2154
|
+
BasicButton,
|
|
1107
2155
|
BasicProvider,
|
|
2156
|
+
BasicUIProvider,
|
|
1108
2157
|
BrowserKeyValueStorage,
|
|
1109
2158
|
BrowserTokenStore,
|
|
1110
2159
|
PersistenceStore,
|
|
2160
|
+
SharesModal,
|
|
2161
|
+
SignInButton,
|
|
2162
|
+
SignOutButton,
|
|
2163
|
+
SyncStatus,
|
|
2164
|
+
UserAvatar,
|
|
2165
|
+
UserButton,
|
|
2166
|
+
UserMenu,
|
|
1111
2167
|
browserCurrentUrl,
|
|
1112
2168
|
browserNavigate,
|
|
1113
2169
|
browserReplaceUrl,
|
|
@@ -1125,6 +2181,7 @@ function createBasic(config) {
|
|
|
1125
2181
|
useFiles,
|
|
1126
2182
|
useMounts,
|
|
1127
2183
|
useOutgoingShares,
|
|
2184
|
+
useProjectProfile,
|
|
1128
2185
|
useQuery,
|
|
1129
2186
|
useRepos,
|
|
1130
2187
|
useSchemaStatus,
|