@basictech/react 0.12.0-beta.1 → 0.12.0-beta.3
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 +31 -0
- package/README.md +145 -9
- package/dist/index.d.mts +126 -5
- package/dist/index.d.ts +126 -5
- package/dist/index.js +1122 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1122 -1
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +564 -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 +10 -5
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
1
3
|
// src/create-basic.tsx
|
|
2
4
|
import { useContext as useContext2 } from "react";
|
|
3
5
|
|
|
@@ -734,7 +736,7 @@ function useAsyncResult(load, initialData, enabled, settled, dependencies) {
|
|
|
734
736
|
function useAccountsFor(client) {
|
|
735
737
|
const snapshot = useClientSnapshot(client);
|
|
736
738
|
const switchAccount = useCallback((id) => client.switchAccount(id), [client]);
|
|
737
|
-
const addAccount = useCallback(() => client.addAccount(), [client]);
|
|
739
|
+
const addAccount = useCallback((options) => client.addAccount(options), [client]);
|
|
738
740
|
const removeAccount = useCallback((id) => client.removeAccount(id), [client]);
|
|
739
741
|
return useMemo2(() => ({
|
|
740
742
|
accounts: snapshot.accounts,
|
|
@@ -1003,6 +1005,47 @@ function useQuery(collection, query = {}, options = {}) {
|
|
|
1003
1005
|
return useQueryValueFor(useRequiredClient(), collection, query, options);
|
|
1004
1006
|
}
|
|
1005
1007
|
|
|
1008
|
+
// src/hooks/profile.ts
|
|
1009
|
+
import {
|
|
1010
|
+
BasicError as BasicError5
|
|
1011
|
+
} from "@basictech/core";
|
|
1012
|
+
function useProjectProfileFor(client, enabled = true) {
|
|
1013
|
+
const snapshot = useClientSnapshot(client);
|
|
1014
|
+
const accountId = snapshot.activeAccount?.id;
|
|
1015
|
+
const result = useAsyncResult(
|
|
1016
|
+
async () => ({ accountId, profile: await client.getProjectProfile() }),
|
|
1017
|
+
null,
|
|
1018
|
+
enabled && snapshot.isReady && snapshot.isSignedIn,
|
|
1019
|
+
snapshot.isReady,
|
|
1020
|
+
[snapshot.activeAccount?.id]
|
|
1021
|
+
);
|
|
1022
|
+
const checkAccount = () => {
|
|
1023
|
+
if (client.getSnapshot().activeAccount?.id !== accountId)
|
|
1024
|
+
throw new BasicError5("ACCOUNT_CHANGED");
|
|
1025
|
+
};
|
|
1026
|
+
return {
|
|
1027
|
+
...result,
|
|
1028
|
+
data: enabled && snapshot.isSignedIn && result.data && result.data.accountId === accountId ? result.data.profile : null,
|
|
1029
|
+
async update(patch) {
|
|
1030
|
+
checkAccount();
|
|
1031
|
+
const profile = await client.updateProjectProfile(patch);
|
|
1032
|
+
checkAccount();
|
|
1033
|
+
result.refresh();
|
|
1034
|
+
return profile;
|
|
1035
|
+
},
|
|
1036
|
+
async reset() {
|
|
1037
|
+
checkAccount();
|
|
1038
|
+
const profile = await client.resetProjectProfile();
|
|
1039
|
+
checkAccount();
|
|
1040
|
+
result.refresh();
|
|
1041
|
+
return profile;
|
|
1042
|
+
}
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
function useProjectProfile(enabled = true) {
|
|
1046
|
+
return useProjectProfileFor(useRequiredClient(), enabled);
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1006
1049
|
// src/create-basic.tsx
|
|
1007
1050
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
1008
1051
|
function createBasic(config) {
|
|
@@ -1031,17 +1074,1094 @@ function createBasic(config) {
|
|
|
1031
1074
|
useSyncStatus: (source) => useSyncStatusFor(useBound(), source),
|
|
1032
1075
|
useSchemaStatus: (source = "default") => useSchemaStatusFor(useBound(), source),
|
|
1033
1076
|
useRepos: () => useReposFor(useBound()),
|
|
1077
|
+
useProjectProfile: (enabled) => useProjectProfileFor(useBound(), enabled),
|
|
1034
1078
|
useFiles: (query = {}, options = {}) => useFilesFor(useBound(), query, options),
|
|
1035
1079
|
useStorageInfo: () => useStorageInfoFor(useBound()),
|
|
1036
1080
|
useMounts: (query = {}) => useMountsFor(useBound(), query),
|
|
1037
1081
|
useOutgoingShares: () => useOutgoingSharesFor(useBound())
|
|
1038
1082
|
};
|
|
1039
1083
|
}
|
|
1084
|
+
|
|
1085
|
+
// src/components.tsx
|
|
1086
|
+
import {
|
|
1087
|
+
createContext as createContext2,
|
|
1088
|
+
Fragment,
|
|
1089
|
+
forwardRef,
|
|
1090
|
+
isValidElement,
|
|
1091
|
+
useContext as useContext3,
|
|
1092
|
+
useEffect as useEffect4,
|
|
1093
|
+
useRef as useRef3,
|
|
1094
|
+
useState as useState4
|
|
1095
|
+
} from "react";
|
|
1096
|
+
import { Avatar } from "@base-ui/react/avatar";
|
|
1097
|
+
import { Dialog } from "@base-ui/react/dialog";
|
|
1098
|
+
import { Menu } from "@base-ui/react/menu";
|
|
1099
|
+
import { Tabs } from "@base-ui/react/tabs";
|
|
1100
|
+
import { BasicError as BasicError6 } from "@basictech/core";
|
|
1101
|
+
import { Fragment as Fragment2, jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
1102
|
+
var AppearanceContext = createContext2({});
|
|
1103
|
+
function accentText(accent) {
|
|
1104
|
+
if (!/^#([\da-f]{3}|[\da-f]{6})$/i.test(accent)) return "#fff";
|
|
1105
|
+
const hex = accent.length === 4 ? accent.slice(1).split("").map((digit) => digit + digit).join("") : accent.slice(1);
|
|
1106
|
+
const [red, green, blue] = [0, 2, 4].map((offset) => {
|
|
1107
|
+
const channel = parseInt(hex.slice(offset, offset + 2), 16) / 255;
|
|
1108
|
+
return channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4;
|
|
1109
|
+
});
|
|
1110
|
+
return 0.2126 * red + 0.7152 * green + 0.0722 * blue > 0.179 ? "#000" : "#fff";
|
|
1111
|
+
}
|
|
1112
|
+
function appearanceStyle(appearance) {
|
|
1113
|
+
return {
|
|
1114
|
+
...appearance.base ? { "--basic-surface": appearance.base } : {},
|
|
1115
|
+
...appearance.accent ? {
|
|
1116
|
+
"--basic-accent": appearance.accent,
|
|
1117
|
+
"--basic-accent-text": accentText(appearance.accent)
|
|
1118
|
+
} : {},
|
|
1119
|
+
...appearance.radius ? { "--basic-radius": appearance.radius } : {}
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
function BasicUIProvider({
|
|
1123
|
+
appearance = {},
|
|
1124
|
+
children
|
|
1125
|
+
}) {
|
|
1126
|
+
const parent = useContext3(AppearanceContext);
|
|
1127
|
+
const value = { ...parent, ...appearance };
|
|
1128
|
+
return /* @__PURE__ */ jsx3(AppearanceContext.Provider, { value, children: /* @__PURE__ */ jsx3(
|
|
1129
|
+
"div",
|
|
1130
|
+
{
|
|
1131
|
+
className: "basic-ui",
|
|
1132
|
+
"data-theme": value.theme ?? "light",
|
|
1133
|
+
"data-density": value.density ?? "comfortable",
|
|
1134
|
+
style: appearanceStyle(value),
|
|
1135
|
+
children
|
|
1136
|
+
}
|
|
1137
|
+
) });
|
|
1138
|
+
}
|
|
1139
|
+
function useAppearanceProps() {
|
|
1140
|
+
const appearance = useContext3(AppearanceContext);
|
|
1141
|
+
return {
|
|
1142
|
+
"data-theme": appearance.theme ?? "light",
|
|
1143
|
+
"data-density": appearance.density ?? "comfortable",
|
|
1144
|
+
style: appearanceStyle(appearance)
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1147
|
+
var BasicButton = forwardRef(
|
|
1148
|
+
function BasicButton2({ variant = "outline", className = "", type = "button", ...props }, ref) {
|
|
1149
|
+
return /* @__PURE__ */ jsx3(
|
|
1150
|
+
"button",
|
|
1151
|
+
{
|
|
1152
|
+
...props,
|
|
1153
|
+
ref,
|
|
1154
|
+
type,
|
|
1155
|
+
className: `basic-button ${className}`,
|
|
1156
|
+
"data-variant": variant
|
|
1157
|
+
}
|
|
1158
|
+
);
|
|
1159
|
+
}
|
|
1160
|
+
);
|
|
1161
|
+
function useAction() {
|
|
1162
|
+
const [pending, setPending] = useState4(false);
|
|
1163
|
+
const [error, setError] = useState4(null);
|
|
1164
|
+
const busy = useRef3(false);
|
|
1165
|
+
const mounted = useRef3(true);
|
|
1166
|
+
useEffect4(() => {
|
|
1167
|
+
mounted.current = true;
|
|
1168
|
+
return () => {
|
|
1169
|
+
mounted.current = false;
|
|
1170
|
+
};
|
|
1171
|
+
}, []);
|
|
1172
|
+
async function run(action) {
|
|
1173
|
+
if (busy.current) return;
|
|
1174
|
+
busy.current = true;
|
|
1175
|
+
setPending(true);
|
|
1176
|
+
setError(null);
|
|
1177
|
+
try {
|
|
1178
|
+
await action();
|
|
1179
|
+
} catch (cause) {
|
|
1180
|
+
if (mounted.current)
|
|
1181
|
+
setError(
|
|
1182
|
+
cause instanceof BasicError6 && 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."
|
|
1183
|
+
);
|
|
1184
|
+
} finally {
|
|
1185
|
+
busy.current = false;
|
|
1186
|
+
if (mounted.current) setPending(false);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
return { pending, error, run, clearError: () => setError(null) };
|
|
1190
|
+
}
|
|
1191
|
+
function AuthButton({
|
|
1192
|
+
auth: supplied,
|
|
1193
|
+
input,
|
|
1194
|
+
signOut = false,
|
|
1195
|
+
children,
|
|
1196
|
+
disabled,
|
|
1197
|
+
onClick,
|
|
1198
|
+
...props
|
|
1199
|
+
}) {
|
|
1200
|
+
const live = useAuth();
|
|
1201
|
+
const auth = supplied ?? live;
|
|
1202
|
+
const action = useAction();
|
|
1203
|
+
const expired = auth.status === "expired";
|
|
1204
|
+
return /* @__PURE__ */ jsxs("span", { className: "basic-action", children: [
|
|
1205
|
+
/* @__PURE__ */ jsx3(
|
|
1206
|
+
BasicButton,
|
|
1207
|
+
{
|
|
1208
|
+
...props,
|
|
1209
|
+
disabled: disabled || !auth.isReady || action.pending || (signOut ? !auth.isSignedIn && !expired : auth.isSignedIn && !expired),
|
|
1210
|
+
"aria-busy": action.pending,
|
|
1211
|
+
onClick: (event) => {
|
|
1212
|
+
onClick?.(event);
|
|
1213
|
+
if (!event.defaultPrevented)
|
|
1214
|
+
void action.run(
|
|
1215
|
+
() => signOut ? auth.signOut() : auth.signIn(input)
|
|
1216
|
+
);
|
|
1217
|
+
},
|
|
1218
|
+
children: action.pending ? "Please wait\u2026" : children ?? (signOut ? "Sign out" : expired ? "Sign in again" : "Sign in")
|
|
1219
|
+
}
|
|
1220
|
+
),
|
|
1221
|
+
/* @__PURE__ */ jsx3(ActionError, { error: action.error, onClose: action.clearError })
|
|
1222
|
+
] });
|
|
1223
|
+
}
|
|
1224
|
+
function SignInButton(props) {
|
|
1225
|
+
return /* @__PURE__ */ jsx3(AuthButton, { ...props });
|
|
1226
|
+
}
|
|
1227
|
+
function SignOutButton(props) {
|
|
1228
|
+
return /* @__PURE__ */ jsx3(AuthButton, { ...props, signOut: true });
|
|
1229
|
+
}
|
|
1230
|
+
function UserAvatar({
|
|
1231
|
+
accounts,
|
|
1232
|
+
auth,
|
|
1233
|
+
projectProfile,
|
|
1234
|
+
allowAddAccount,
|
|
1235
|
+
menuItems,
|
|
1236
|
+
...avatarProps
|
|
1237
|
+
}) {
|
|
1238
|
+
return /* @__PURE__ */ jsx3(
|
|
1239
|
+
UserMenu,
|
|
1240
|
+
{
|
|
1241
|
+
accounts,
|
|
1242
|
+
auth,
|
|
1243
|
+
projectProfile,
|
|
1244
|
+
allowAddAccount,
|
|
1245
|
+
menuItems,
|
|
1246
|
+
sync: avatarProps.sync,
|
|
1247
|
+
showSyncBadge: avatarProps.showSyncBadge,
|
|
1248
|
+
avatarProps
|
|
1249
|
+
}
|
|
1250
|
+
);
|
|
1251
|
+
}
|
|
1252
|
+
function AccountAvatar({
|
|
1253
|
+
profile: supplied,
|
|
1254
|
+
size = 32,
|
|
1255
|
+
showSyncBadge,
|
|
1256
|
+
sync,
|
|
1257
|
+
className = "",
|
|
1258
|
+
style,
|
|
1259
|
+
...props
|
|
1260
|
+
}) {
|
|
1261
|
+
const accounts = useAccounts();
|
|
1262
|
+
const client = useRequiredClient();
|
|
1263
|
+
const profile = supplied === void 0 ? accounts.activeAccount : supplied;
|
|
1264
|
+
const name = profile?.name || (profile?.kind === "anon" ? "Local account" : profile?.handle || profile?.email || "Guest");
|
|
1265
|
+
const initials = name.trim().split(/\s+/).slice(0, 2).map((part) => Array.from(part)[0]).join("").toUpperCase();
|
|
1266
|
+
const avatar = /* @__PURE__ */ jsxs(
|
|
1267
|
+
Avatar.Root,
|
|
1268
|
+
{
|
|
1269
|
+
...props,
|
|
1270
|
+
className: `basic-avatar ${className}`,
|
|
1271
|
+
style: { width: size, height: size, ...style },
|
|
1272
|
+
role: "img",
|
|
1273
|
+
"aria-label": props["aria-label"] ?? name,
|
|
1274
|
+
children: [
|
|
1275
|
+
profile?.picture && /* @__PURE__ */ jsx3(
|
|
1276
|
+
Avatar.Image,
|
|
1277
|
+
{
|
|
1278
|
+
src: profile.picture,
|
|
1279
|
+
alt: "",
|
|
1280
|
+
className: "basic-avatar-image",
|
|
1281
|
+
referrerPolicy: "no-referrer"
|
|
1282
|
+
}
|
|
1283
|
+
),
|
|
1284
|
+
/* @__PURE__ */ jsx3(Avatar.Fallback, { className: "basic-avatar-fallback", children: initials || "?" })
|
|
1285
|
+
]
|
|
1286
|
+
}
|
|
1287
|
+
);
|
|
1288
|
+
return showSyncBadge ?? client.mode === "sync" ? /* @__PURE__ */ jsxs("span", { className: "basic-avatar-container", children: [
|
|
1289
|
+
avatar,
|
|
1290
|
+
/* @__PURE__ */ jsx3(SyncStatus, { sync, className: "basic-avatar-sync" })
|
|
1291
|
+
] }) : avatar;
|
|
1292
|
+
}
|
|
1293
|
+
function AuthStatus({
|
|
1294
|
+
auth: supplied,
|
|
1295
|
+
className = "",
|
|
1296
|
+
...props
|
|
1297
|
+
}) {
|
|
1298
|
+
const live = useAuth();
|
|
1299
|
+
const auth = supplied ?? live;
|
|
1300
|
+
const expired = auth.status === "expired";
|
|
1301
|
+
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";
|
|
1302
|
+
return /* @__PURE__ */ jsxs(
|
|
1303
|
+
"span",
|
|
1304
|
+
{
|
|
1305
|
+
...props,
|
|
1306
|
+
role: "status",
|
|
1307
|
+
className: `basic-status ${className}`,
|
|
1308
|
+
"data-tone": expired || auth.error ? "warning" : auth.isSignedIn ? "success" : "neutral",
|
|
1309
|
+
children: [
|
|
1310
|
+
/* @__PURE__ */ jsx3("span", { className: "basic-dot" }),
|
|
1311
|
+
label
|
|
1312
|
+
]
|
|
1313
|
+
}
|
|
1314
|
+
);
|
|
1315
|
+
}
|
|
1316
|
+
function SyncStatus({
|
|
1317
|
+
sync: supplied,
|
|
1318
|
+
source,
|
|
1319
|
+
className = "",
|
|
1320
|
+
...props
|
|
1321
|
+
}) {
|
|
1322
|
+
const live = useSyncStatus(source);
|
|
1323
|
+
const client = useRequiredClient();
|
|
1324
|
+
const sync = supplied ?? live;
|
|
1325
|
+
if (client.mode === "rest") return null;
|
|
1326
|
+
const issues = sync.rejected.length + sync.conflicts.length;
|
|
1327
|
+
const labels = {
|
|
1328
|
+
idle: "Idle",
|
|
1329
|
+
bootstrapping: "Starting sync",
|
|
1330
|
+
connecting: "Connecting",
|
|
1331
|
+
online: "Synced",
|
|
1332
|
+
offline: "Offline",
|
|
1333
|
+
stopped: "Sync stopped",
|
|
1334
|
+
error: "Sync error",
|
|
1335
|
+
local: "Local only",
|
|
1336
|
+
stale: "Sync stale",
|
|
1337
|
+
ended: "Access ended"
|
|
1338
|
+
};
|
|
1339
|
+
const label = issues ? `${issues} sync ${issues === 1 ? "issue" : "issues"}` : sync.status === "online" && sync.pendingCount ? "Syncing" : labels[sync.status];
|
|
1340
|
+
return /* @__PURE__ */ jsxs(
|
|
1341
|
+
"span",
|
|
1342
|
+
{
|
|
1343
|
+
...props,
|
|
1344
|
+
role: "status",
|
|
1345
|
+
title: props.title ?? `${label}${sync.pendingCount > 0 ? ` \xB7 ${sync.pendingCount} pending` : ""}`,
|
|
1346
|
+
className: `basic-status ${className}`,
|
|
1347
|
+
"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",
|
|
1348
|
+
children: [
|
|
1349
|
+
/* @__PURE__ */ jsx3("span", { className: "basic-dot" }),
|
|
1350
|
+
label,
|
|
1351
|
+
sync.pendingCount > 0 && ` \xB7 ${sync.pendingCount} pending`
|
|
1352
|
+
]
|
|
1353
|
+
}
|
|
1354
|
+
);
|
|
1355
|
+
}
|
|
1356
|
+
function displayHandle(handle) {
|
|
1357
|
+
return handle ? `@${handle.replace(/^@+/, "")}` : "";
|
|
1358
|
+
}
|
|
1359
|
+
function UserButton(props) {
|
|
1360
|
+
return /* @__PURE__ */ jsx3(UserMenu, { ...props, trigger: "button" });
|
|
1361
|
+
}
|
|
1362
|
+
function UserMenu({
|
|
1363
|
+
accounts: supplied,
|
|
1364
|
+
auth: suppliedAuth,
|
|
1365
|
+
sync,
|
|
1366
|
+
showSyncStatus = true,
|
|
1367
|
+
showSyncBadge,
|
|
1368
|
+
shape = "rounded",
|
|
1369
|
+
trigger = "avatar",
|
|
1370
|
+
avatarProps,
|
|
1371
|
+
projectProfile,
|
|
1372
|
+
allowAddAccount = true,
|
|
1373
|
+
className = "",
|
|
1374
|
+
accountSettingsUrl,
|
|
1375
|
+
menuItems = []
|
|
1376
|
+
}) {
|
|
1377
|
+
const liveAccounts = useAccounts();
|
|
1378
|
+
const liveAuth = useAuth();
|
|
1379
|
+
const accounts = supplied ?? liveAccounts;
|
|
1380
|
+
const auth = suppliedAuth ?? liveAuth;
|
|
1381
|
+
const action = useAction();
|
|
1382
|
+
const [settingsOpen, setSettingsOpen] = useState4(false);
|
|
1383
|
+
const [clearAccountId, setClearAccountId] = useState4(null);
|
|
1384
|
+
const triggerRef = useRef3(null);
|
|
1385
|
+
const appearance = useAppearanceProps();
|
|
1386
|
+
const expired = auth.status === "expired";
|
|
1387
|
+
const active = accounts.activeAccount;
|
|
1388
|
+
const otherAccounts = accounts.accounts.filter(
|
|
1389
|
+
(account) => account.id !== active?.id
|
|
1390
|
+
);
|
|
1391
|
+
const name = active?.name || active?.kind !== "anon" && displayHandle(active?.handle) || "Local account";
|
|
1392
|
+
return /* @__PURE__ */ jsxs("span", { className: `basic-action ${className}`, children: [
|
|
1393
|
+
/* @__PURE__ */ jsxs(Menu.Root, { children: [
|
|
1394
|
+
/* @__PURE__ */ jsxs(
|
|
1395
|
+
Menu.Trigger,
|
|
1396
|
+
{
|
|
1397
|
+
ref: triggerRef,
|
|
1398
|
+
className: `basic-button basic-account-trigger basic-trigger-${trigger}`,
|
|
1399
|
+
"data-shape": shape,
|
|
1400
|
+
disabled: !auth.isReady || action.pending,
|
|
1401
|
+
"aria-label": "Open user menu",
|
|
1402
|
+
children: [
|
|
1403
|
+
/* @__PURE__ */ jsx3(
|
|
1404
|
+
AccountAvatar,
|
|
1405
|
+
{
|
|
1406
|
+
profile: accounts.activeAccount,
|
|
1407
|
+
sync,
|
|
1408
|
+
showSyncBadge,
|
|
1409
|
+
...avatarProps
|
|
1410
|
+
}
|
|
1411
|
+
),
|
|
1412
|
+
trigger === "button" && /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
1413
|
+
/* @__PURE__ */ jsxs("span", { className: "basic-identity", children: [
|
|
1414
|
+
/* @__PURE__ */ jsx3("strong", { children: name }),
|
|
1415
|
+
active?.kind !== "anon" && active?.handle && /* @__PURE__ */ jsx3("small", { children: displayHandle(active.handle) })
|
|
1416
|
+
] }),
|
|
1417
|
+
/* @__PURE__ */ jsx3(
|
|
1418
|
+
"svg",
|
|
1419
|
+
{
|
|
1420
|
+
className: "basic-chevron",
|
|
1421
|
+
width: "16",
|
|
1422
|
+
height: "16",
|
|
1423
|
+
viewBox: "0 0 24 24",
|
|
1424
|
+
fill: "none",
|
|
1425
|
+
stroke: "currentColor",
|
|
1426
|
+
strokeWidth: "2",
|
|
1427
|
+
strokeLinecap: "round",
|
|
1428
|
+
strokeLinejoin: "round",
|
|
1429
|
+
"aria-hidden": "true",
|
|
1430
|
+
children: /* @__PURE__ */ jsx3("path", { d: "m6 9 6 6 6-6" })
|
|
1431
|
+
}
|
|
1432
|
+
)
|
|
1433
|
+
] })
|
|
1434
|
+
]
|
|
1435
|
+
}
|
|
1436
|
+
),
|
|
1437
|
+
/* @__PURE__ */ jsx3(Menu.Portal, { children: /* @__PURE__ */ jsx3(Menu.Positioner, { sideOffset: 8, className: "basic-positioner", children: /* @__PURE__ */ jsxs(Menu.Popup, { ...appearance, className: "basic-ui basic-menu", children: [
|
|
1438
|
+
/* @__PURE__ */ jsxs("div", { className: "basic-menu-profile", children: [
|
|
1439
|
+
/* @__PURE__ */ jsx3(
|
|
1440
|
+
AccountAvatar,
|
|
1441
|
+
{
|
|
1442
|
+
profile: active,
|
|
1443
|
+
size: 48,
|
|
1444
|
+
sync,
|
|
1445
|
+
showSyncBadge: false
|
|
1446
|
+
}
|
|
1447
|
+
),
|
|
1448
|
+
/* @__PURE__ */ jsxs("span", { className: "basic-identity", children: [
|
|
1449
|
+
/* @__PURE__ */ jsx3("strong", { children: active ? name : "No active account" }),
|
|
1450
|
+
active?.kind !== "anon" && active?.handle && /* @__PURE__ */ jsx3("small", { children: displayHandle(active.handle) }),
|
|
1451
|
+
/* @__PURE__ */ jsxs("span", { className: "basic-account-statuses", children: [
|
|
1452
|
+
expired && /* @__PURE__ */ jsx3("span", { className: "basic-status", "data-tone": "warning", children: "Expired" }),
|
|
1453
|
+
showSyncStatus && /* @__PURE__ */ jsx3(SyncStatus, { sync })
|
|
1454
|
+
] })
|
|
1455
|
+
] })
|
|
1456
|
+
] }),
|
|
1457
|
+
expired && /* @__PURE__ */ jsx3(
|
|
1458
|
+
Menu.Item,
|
|
1459
|
+
{
|
|
1460
|
+
className: "basic-menu-item basic-menu-reauth",
|
|
1461
|
+
disabled: action.pending,
|
|
1462
|
+
onClick: () => void action.run(() => auth.signIn()),
|
|
1463
|
+
children: "Sign in again"
|
|
1464
|
+
}
|
|
1465
|
+
),
|
|
1466
|
+
/* @__PURE__ */ jsxs(
|
|
1467
|
+
Menu.Item,
|
|
1468
|
+
{
|
|
1469
|
+
className: "basic-menu-item basic-manage-account",
|
|
1470
|
+
onClick: () => setSettingsOpen(true),
|
|
1471
|
+
children: [
|
|
1472
|
+
/* @__PURE__ */ jsxs(
|
|
1473
|
+
"svg",
|
|
1474
|
+
{
|
|
1475
|
+
className: "basic-menu-icon",
|
|
1476
|
+
width: "16",
|
|
1477
|
+
height: "16",
|
|
1478
|
+
viewBox: "0 0 24 24",
|
|
1479
|
+
fill: "none",
|
|
1480
|
+
stroke: "currentColor",
|
|
1481
|
+
strokeWidth: "1.75",
|
|
1482
|
+
strokeLinecap: "round",
|
|
1483
|
+
strokeLinejoin: "round",
|
|
1484
|
+
"aria-hidden": "true",
|
|
1485
|
+
children: [
|
|
1486
|
+
/* @__PURE__ */ jsx3("circle", { cx: "12", cy: "7", r: "4" }),
|
|
1487
|
+
/* @__PURE__ */ jsx3("path", { d: "M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" })
|
|
1488
|
+
]
|
|
1489
|
+
}
|
|
1490
|
+
),
|
|
1491
|
+
"Manage account"
|
|
1492
|
+
]
|
|
1493
|
+
}
|
|
1494
|
+
),
|
|
1495
|
+
menuItems.map((item) => /* @__PURE__ */ jsxs(
|
|
1496
|
+
Menu.Item,
|
|
1497
|
+
{
|
|
1498
|
+
className: "basic-menu-item",
|
|
1499
|
+
render: item.href !== void 0 ? /* @__PURE__ */ jsx3("a", { href: item.href }) : void 0,
|
|
1500
|
+
disabled: item.disabled || action.pending,
|
|
1501
|
+
onClick: () => {
|
|
1502
|
+
if (item.onClick) void action.run(item.onClick);
|
|
1503
|
+
},
|
|
1504
|
+
children: [
|
|
1505
|
+
/* @__PURE__ */ jsx3("span", { className: "basic-menu-icon", "aria-hidden": "true", children: item.icon }),
|
|
1506
|
+
item.label
|
|
1507
|
+
]
|
|
1508
|
+
},
|
|
1509
|
+
item.id
|
|
1510
|
+
)),
|
|
1511
|
+
(auth.isSignedIn || expired || active?.kind === "anon") && /* @__PURE__ */ jsxs(
|
|
1512
|
+
Menu.Item,
|
|
1513
|
+
{
|
|
1514
|
+
className: "basic-menu-item basic-menu-signout",
|
|
1515
|
+
disabled: action.pending,
|
|
1516
|
+
onClick: () => active?.kind === "anon" ? setClearAccountId(active.id) : void action.run(() => auth.signOut()),
|
|
1517
|
+
children: [
|
|
1518
|
+
/* @__PURE__ */ jsx3(
|
|
1519
|
+
"svg",
|
|
1520
|
+
{
|
|
1521
|
+
className: "basic-menu-icon",
|
|
1522
|
+
width: "16",
|
|
1523
|
+
height: "16",
|
|
1524
|
+
viewBox: "0 0 24 24",
|
|
1525
|
+
fill: "none",
|
|
1526
|
+
stroke: "currentColor",
|
|
1527
|
+
strokeWidth: "1.75",
|
|
1528
|
+
strokeLinecap: "round",
|
|
1529
|
+
strokeLinejoin: "round",
|
|
1530
|
+
"aria-hidden": "true",
|
|
1531
|
+
children: /* @__PURE__ */ jsx3("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4M16 17l5-5-5-5M21 12H9" })
|
|
1532
|
+
}
|
|
1533
|
+
),
|
|
1534
|
+
active?.kind === "anon" ? "Clear account" : "Sign out"
|
|
1535
|
+
]
|
|
1536
|
+
}
|
|
1537
|
+
),
|
|
1538
|
+
otherAccounts.length > 0 && /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
1539
|
+
/* @__PURE__ */ jsx3(Menu.Separator, { className: "basic-separator" }),
|
|
1540
|
+
/* @__PURE__ */ jsx3("div", { className: "basic-menu-heading", children: "Switch accounts" })
|
|
1541
|
+
] }),
|
|
1542
|
+
otherAccounts.map((account) => /* @__PURE__ */ jsxs(
|
|
1543
|
+
Menu.Item,
|
|
1544
|
+
{
|
|
1545
|
+
className: "basic-menu-item",
|
|
1546
|
+
disabled: action.pending,
|
|
1547
|
+
onClick: () => void action.run(() => accounts.switchAccount(account.id)),
|
|
1548
|
+
children: [
|
|
1549
|
+
/* @__PURE__ */ jsx3(
|
|
1550
|
+
AccountAvatar,
|
|
1551
|
+
{
|
|
1552
|
+
profile: account,
|
|
1553
|
+
size: 32,
|
|
1554
|
+
sync,
|
|
1555
|
+
showSyncBadge: false
|
|
1556
|
+
}
|
|
1557
|
+
),
|
|
1558
|
+
/* @__PURE__ */ jsxs("span", { className: "basic-identity", children: [
|
|
1559
|
+
/* @__PURE__ */ jsx3("strong", { children: account.name || account.kind !== "anon" && displayHandle(account.handle) || (account.kind === "anon" ? "Local account" : "Account") }),
|
|
1560
|
+
account.kind !== "anon" && account.handle && /* @__PURE__ */ jsx3("small", { children: displayHandle(account.handle) })
|
|
1561
|
+
] }),
|
|
1562
|
+
/* @__PURE__ */ jsx3("span", { className: "basic-account-statuses", children: account.auth.status === "expired" ? /* @__PURE__ */ jsx3("span", { className: "basic-status", "data-tone": "warning", children: "Expired" }) : null })
|
|
1563
|
+
]
|
|
1564
|
+
},
|
|
1565
|
+
account.id
|
|
1566
|
+
)),
|
|
1567
|
+
(allowAddAccount || !auth.isSignedIn && !expired) && /* @__PURE__ */ jsxs(
|
|
1568
|
+
Menu.Item,
|
|
1569
|
+
{
|
|
1570
|
+
className: "basic-menu-item",
|
|
1571
|
+
disabled: action.pending,
|
|
1572
|
+
onClick: () => void action.run(
|
|
1573
|
+
() => allowAddAccount ? accounts.addAccount({ signIn: true }) : auth.signIn()
|
|
1574
|
+
),
|
|
1575
|
+
children: [
|
|
1576
|
+
/* @__PURE__ */ jsx3(
|
|
1577
|
+
"svg",
|
|
1578
|
+
{
|
|
1579
|
+
className: "basic-menu-icon",
|
|
1580
|
+
width: "16",
|
|
1581
|
+
height: "16",
|
|
1582
|
+
viewBox: "0 0 24 24",
|
|
1583
|
+
fill: "none",
|
|
1584
|
+
stroke: "currentColor",
|
|
1585
|
+
strokeWidth: "1.75",
|
|
1586
|
+
strokeLinecap: "round",
|
|
1587
|
+
"aria-hidden": "true",
|
|
1588
|
+
children: /* @__PURE__ */ jsx3("path", { d: "M12 5v14M5 12h14" })
|
|
1589
|
+
}
|
|
1590
|
+
),
|
|
1591
|
+
"Add account"
|
|
1592
|
+
]
|
|
1593
|
+
}
|
|
1594
|
+
)
|
|
1595
|
+
] }) }) })
|
|
1596
|
+
] }),
|
|
1597
|
+
/* @__PURE__ */ jsx3(
|
|
1598
|
+
ActionError,
|
|
1599
|
+
{
|
|
1600
|
+
error: action.error,
|
|
1601
|
+
onClose: action.clearError,
|
|
1602
|
+
finalFocus: triggerRef
|
|
1603
|
+
}
|
|
1604
|
+
),
|
|
1605
|
+
/* @__PURE__ */ jsx3(
|
|
1606
|
+
Modal,
|
|
1607
|
+
{
|
|
1608
|
+
open: clearAccountId !== null,
|
|
1609
|
+
onOpenChange: (open) => {
|
|
1610
|
+
if (!open) setClearAccountId(null);
|
|
1611
|
+
},
|
|
1612
|
+
trigger: null,
|
|
1613
|
+
finalFocus: triggerRef,
|
|
1614
|
+
title: "Clear local account?",
|
|
1615
|
+
description: "This deletes this account\u2019s local data, including unsynced changes. This cannot be undone.",
|
|
1616
|
+
children: /* @__PURE__ */ jsxs("div", { className: "basic-actions", children: [
|
|
1617
|
+
/* @__PURE__ */ jsx3(
|
|
1618
|
+
BasicButton,
|
|
1619
|
+
{
|
|
1620
|
+
disabled: action.pending,
|
|
1621
|
+
onClick: () => setClearAccountId(null),
|
|
1622
|
+
children: "Cancel"
|
|
1623
|
+
}
|
|
1624
|
+
),
|
|
1625
|
+
/* @__PURE__ */ jsx3(
|
|
1626
|
+
BasicButton,
|
|
1627
|
+
{
|
|
1628
|
+
disabled: action.pending,
|
|
1629
|
+
onClick: () => {
|
|
1630
|
+
const id = clearAccountId;
|
|
1631
|
+
if (!id) return;
|
|
1632
|
+
setClearAccountId(null);
|
|
1633
|
+
void action.run(() => accounts.removeAccount(id));
|
|
1634
|
+
},
|
|
1635
|
+
children: "Clear account"
|
|
1636
|
+
}
|
|
1637
|
+
)
|
|
1638
|
+
] })
|
|
1639
|
+
}
|
|
1640
|
+
),
|
|
1641
|
+
/* @__PURE__ */ jsx3(
|
|
1642
|
+
AccountSettingsModal,
|
|
1643
|
+
{
|
|
1644
|
+
auth,
|
|
1645
|
+
accounts,
|
|
1646
|
+
open: settingsOpen,
|
|
1647
|
+
onOpenChange: setSettingsOpen,
|
|
1648
|
+
trigger: null,
|
|
1649
|
+
finalFocus: triggerRef,
|
|
1650
|
+
accountSettingsUrl,
|
|
1651
|
+
projectProfile,
|
|
1652
|
+
sync
|
|
1653
|
+
}
|
|
1654
|
+
)
|
|
1655
|
+
] });
|
|
1656
|
+
}
|
|
1657
|
+
function ActionError({
|
|
1658
|
+
error,
|
|
1659
|
+
onClose,
|
|
1660
|
+
finalFocus
|
|
1661
|
+
}) {
|
|
1662
|
+
return /* @__PURE__ */ jsxs(
|
|
1663
|
+
Modal,
|
|
1664
|
+
{
|
|
1665
|
+
open: !!error,
|
|
1666
|
+
onOpenChange: (open) => {
|
|
1667
|
+
if (!open) onClose();
|
|
1668
|
+
},
|
|
1669
|
+
trigger: null,
|
|
1670
|
+
finalFocus,
|
|
1671
|
+
title: "Unable to complete action",
|
|
1672
|
+
description: "Your request could not be completed.",
|
|
1673
|
+
children: [
|
|
1674
|
+
/* @__PURE__ */ jsx3("p", { role: "alert", className: "basic-error", children: error }),
|
|
1675
|
+
/* @__PURE__ */ jsx3(BasicButton, { onClick: onClose, children: "Dismiss" })
|
|
1676
|
+
]
|
|
1677
|
+
}
|
|
1678
|
+
);
|
|
1679
|
+
}
|
|
1680
|
+
function Modal({
|
|
1681
|
+
open,
|
|
1682
|
+
onOpenChange,
|
|
1683
|
+
trigger,
|
|
1684
|
+
finalFocus,
|
|
1685
|
+
title,
|
|
1686
|
+
description,
|
|
1687
|
+
header,
|
|
1688
|
+
sectionLabel,
|
|
1689
|
+
children
|
|
1690
|
+
}) {
|
|
1691
|
+
const appearance = useAppearanceProps();
|
|
1692
|
+
return /* @__PURE__ */ jsxs(Dialog.Root, { open, onOpenChange, children: [
|
|
1693
|
+
trigger !== null && (isValidElement(trigger) ? /* @__PURE__ */ jsx3(Dialog.Trigger, { render: trigger }) : /* @__PURE__ */ jsx3(Dialog.Trigger, { render: /* @__PURE__ */ jsx3(BasicButton, {}), children: trigger ?? title })),
|
|
1694
|
+
/* @__PURE__ */ jsxs(Dialog.Portal, { children: [
|
|
1695
|
+
/* @__PURE__ */ jsx3(Dialog.Backdrop, { className: "basic-backdrop" }),
|
|
1696
|
+
/* @__PURE__ */ jsxs(
|
|
1697
|
+
Dialog.Popup,
|
|
1698
|
+
{
|
|
1699
|
+
...appearance,
|
|
1700
|
+
finalFocus,
|
|
1701
|
+
className: "basic-ui basic-dialog",
|
|
1702
|
+
children: [
|
|
1703
|
+
/* @__PURE__ */ jsxs(
|
|
1704
|
+
"div",
|
|
1705
|
+
{
|
|
1706
|
+
className: `basic-dialog-heading${header ? " basic-dialog-heading-overlay" : ""}`,
|
|
1707
|
+
children: [
|
|
1708
|
+
/* @__PURE__ */ jsx3(Dialog.Title, { className: header ? "basic-sr-only" : "basic-title", children: title }),
|
|
1709
|
+
/* @__PURE__ */ jsx3(
|
|
1710
|
+
Dialog.Close,
|
|
1711
|
+
{
|
|
1712
|
+
className: "basic-button",
|
|
1713
|
+
"aria-label": `Close ${title.toLowerCase()}`,
|
|
1714
|
+
children: "\u2715"
|
|
1715
|
+
}
|
|
1716
|
+
)
|
|
1717
|
+
]
|
|
1718
|
+
}
|
|
1719
|
+
),
|
|
1720
|
+
header,
|
|
1721
|
+
/* @__PURE__ */ jsx3(
|
|
1722
|
+
Dialog.Description,
|
|
1723
|
+
{
|
|
1724
|
+
className: header ? "basic-sr-only" : "basic-muted",
|
|
1725
|
+
children: description
|
|
1726
|
+
}
|
|
1727
|
+
),
|
|
1728
|
+
sectionLabel && /* @__PURE__ */ jsx3("div", { className: "basic-modal-section", children: sectionLabel }),
|
|
1729
|
+
children
|
|
1730
|
+
]
|
|
1731
|
+
}
|
|
1732
|
+
)
|
|
1733
|
+
] })
|
|
1734
|
+
] });
|
|
1735
|
+
}
|
|
1736
|
+
function AccountSettingsModal({
|
|
1737
|
+
auth: suppliedAuth,
|
|
1738
|
+
accounts: suppliedAccounts,
|
|
1739
|
+
accountSettingsUrl,
|
|
1740
|
+
projectProfile,
|
|
1741
|
+
sync,
|
|
1742
|
+
children,
|
|
1743
|
+
...props
|
|
1744
|
+
}) {
|
|
1745
|
+
const liveAuth = useAuth();
|
|
1746
|
+
const liveAccounts = useAccounts();
|
|
1747
|
+
const auth = suppliedAuth ?? liveAuth;
|
|
1748
|
+
const accounts = suppliedAccounts ?? liveAccounts;
|
|
1749
|
+
const liveSync = useSyncStatus();
|
|
1750
|
+
const syncState = sync ?? liveSync;
|
|
1751
|
+
const client = useRequiredClient();
|
|
1752
|
+
const account = accounts.activeAccount;
|
|
1753
|
+
const copyAction = useAction();
|
|
1754
|
+
const [copied, setCopied] = useState4(null);
|
|
1755
|
+
const details = [
|
|
1756
|
+
[
|
|
1757
|
+
"Account DID",
|
|
1758
|
+
account?.kind === "anon" ? "Not assigned (local account)" : account?.did ?? auth.did ?? "Unavailable"
|
|
1759
|
+
],
|
|
1760
|
+
[
|
|
1761
|
+
"PDS URL",
|
|
1762
|
+
account?.kind === "anon" ? "Not connected (local account)" : auth.user?.pds_url ?? "Unavailable"
|
|
1763
|
+
],
|
|
1764
|
+
["Local account ID", account?.id ?? "None"],
|
|
1765
|
+
["Account kind", account?.kind ?? "None"],
|
|
1766
|
+
["Storage prefix", account?.storagePrefix ?? "None"],
|
|
1767
|
+
["Client mode", client.mode],
|
|
1768
|
+
["Auth state", auth.status],
|
|
1769
|
+
["Ready", auth.isReady ? "Yes" : "No"],
|
|
1770
|
+
["Can write", auth.canWrite ? "Yes" : "No"],
|
|
1771
|
+
["Read-only reason", auth.readOnlyReason ?? "None"],
|
|
1772
|
+
["Auth error code", auth.error?.code ?? "None"],
|
|
1773
|
+
["Sync state", syncState.status],
|
|
1774
|
+
["Pending writes", String(syncState.pendingCount)],
|
|
1775
|
+
["Rejected writes", String(syncState.rejected.length)],
|
|
1776
|
+
["Conflicts", String(syncState.conflicts.length)]
|
|
1777
|
+
];
|
|
1778
|
+
const diagnostics = JSON.stringify(Object.fromEntries(details), null, 2);
|
|
1779
|
+
return /* @__PURE__ */ jsx3(
|
|
1780
|
+
Modal,
|
|
1781
|
+
{
|
|
1782
|
+
...props,
|
|
1783
|
+
title: "Account settings",
|
|
1784
|
+
description: "Your identity and session on this device.",
|
|
1785
|
+
header: /* @__PURE__ */ jsx3(AccountHeader, { account: accounts.activeAccount, sync }),
|
|
1786
|
+
children: /* @__PURE__ */ jsxs(Tabs.Root, { defaultValue: "profile", children: [
|
|
1787
|
+
/* @__PURE__ */ jsxs(
|
|
1788
|
+
Tabs.List,
|
|
1789
|
+
{
|
|
1790
|
+
className: "basic-tabs",
|
|
1791
|
+
"aria-label": "Account settings sections",
|
|
1792
|
+
children: [
|
|
1793
|
+
/* @__PURE__ */ jsx3(Tabs.Tab, { value: "profile", children: "Profile" }),
|
|
1794
|
+
/* @__PURE__ */ jsx3(Tabs.Tab, { value: "advanced", children: "Advanced" })
|
|
1795
|
+
]
|
|
1796
|
+
}
|
|
1797
|
+
),
|
|
1798
|
+
/* @__PURE__ */ jsxs(Tabs.Panel, { value: "profile", keepMounted: true, children: [
|
|
1799
|
+
auth.isSignedIn && /* @__PURE__ */ jsx3(
|
|
1800
|
+
ProjectProfileEditor,
|
|
1801
|
+
{
|
|
1802
|
+
profile: projectProfile,
|
|
1803
|
+
canWrite: auth.canWrite
|
|
1804
|
+
},
|
|
1805
|
+
accounts.activeAccount?.id ?? auth.did
|
|
1806
|
+
),
|
|
1807
|
+
accountSettingsUrl && /* @__PURE__ */ jsx3("a", { className: "basic-link", href: accountSettingsUrl, children: "Manage profile and security \u2197" }),
|
|
1808
|
+
children,
|
|
1809
|
+
!auth.isSignedIn && /* @__PURE__ */ jsx3("div", { className: "basic-actions", children: /* @__PURE__ */ jsx3(SignInButton, { auth }) })
|
|
1810
|
+
] }),
|
|
1811
|
+
/* @__PURE__ */ jsxs(Tabs.Panel, { value: "advanced", children: [
|
|
1812
|
+
/* @__PURE__ */ jsx3("p", { className: "basic-muted", children: "Read-only diagnostics for this account." }),
|
|
1813
|
+
/* @__PURE__ */ jsx3("dl", { className: "basic-details", children: details.map(([label, value]) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1814
|
+
/* @__PURE__ */ jsx3("dt", { children: label }),
|
|
1815
|
+
/* @__PURE__ */ jsx3("dd", { children: value })
|
|
1816
|
+
] }, label)) }),
|
|
1817
|
+
/* @__PURE__ */ jsxs("div", { className: "basic-actions", children: [
|
|
1818
|
+
/* @__PURE__ */ jsx3(
|
|
1819
|
+
BasicButton,
|
|
1820
|
+
{
|
|
1821
|
+
disabled: copyAction.pending,
|
|
1822
|
+
onClick: () => {
|
|
1823
|
+
setCopied(null);
|
|
1824
|
+
void copyAction.run(async () => {
|
|
1825
|
+
if (!navigator.clipboard?.writeText)
|
|
1826
|
+
throw new Error("Clipboard is unavailable in this browser.");
|
|
1827
|
+
await navigator.clipboard.writeText(diagnostics);
|
|
1828
|
+
setCopied(diagnostics);
|
|
1829
|
+
});
|
|
1830
|
+
},
|
|
1831
|
+
children: "Copy to clipboard"
|
|
1832
|
+
}
|
|
1833
|
+
),
|
|
1834
|
+
copied === diagnostics && /* @__PURE__ */ jsx3("span", { role: "status", className: "basic-muted", children: "Copied" })
|
|
1835
|
+
] }),
|
|
1836
|
+
copyAction.error && /* @__PURE__ */ jsx3("p", { role: "alert", className: "basic-error", children: copyAction.error })
|
|
1837
|
+
] })
|
|
1838
|
+
] })
|
|
1839
|
+
}
|
|
1840
|
+
);
|
|
1841
|
+
}
|
|
1842
|
+
function AccountHeader({
|
|
1843
|
+
account,
|
|
1844
|
+
sync
|
|
1845
|
+
}) {
|
|
1846
|
+
return /* @__PURE__ */ jsxs("div", { className: "basic-profile", children: [
|
|
1847
|
+
/* @__PURE__ */ jsx3(
|
|
1848
|
+
AccountAvatar,
|
|
1849
|
+
{
|
|
1850
|
+
profile: account,
|
|
1851
|
+
size: 72,
|
|
1852
|
+
sync,
|
|
1853
|
+
showSyncBadge: false
|
|
1854
|
+
}
|
|
1855
|
+
),
|
|
1856
|
+
/* @__PURE__ */ jsxs("span", { className: "basic-identity", children: [
|
|
1857
|
+
/* @__PURE__ */ jsx3("strong", { children: account?.name || account?.kind !== "anon" && displayHandle(account?.handle) || "Local account" }),
|
|
1858
|
+
account?.kind !== "anon" && account?.handle && /* @__PURE__ */ jsx3("small", { children: displayHandle(account.handle) }),
|
|
1859
|
+
/* @__PURE__ */ jsx3("span", { className: "basic-account-statuses", children: /* @__PURE__ */ jsx3(SyncStatus, { sync }) })
|
|
1860
|
+
] })
|
|
1861
|
+
] });
|
|
1862
|
+
}
|
|
1863
|
+
function ProjectProfileEditor({
|
|
1864
|
+
profile: supplied,
|
|
1865
|
+
canWrite
|
|
1866
|
+
}) {
|
|
1867
|
+
const live = useProjectProfile(supplied === void 0);
|
|
1868
|
+
const profile = supplied ?? live;
|
|
1869
|
+
const action = useAction();
|
|
1870
|
+
const [draft, setDraft] = useState4({});
|
|
1871
|
+
const [saved, setSaved] = useState4(false);
|
|
1872
|
+
return /* @__PURE__ */ jsxs(
|
|
1873
|
+
"form",
|
|
1874
|
+
{
|
|
1875
|
+
className: "basic-profile-editor",
|
|
1876
|
+
onSubmit: (event) => {
|
|
1877
|
+
event.preventDefault();
|
|
1878
|
+
if (!canWrite || !Object.keys(draft).length) return;
|
|
1879
|
+
void action.run(async () => {
|
|
1880
|
+
await profile.update(draft);
|
|
1881
|
+
setDraft({});
|
|
1882
|
+
setSaved(true);
|
|
1883
|
+
});
|
|
1884
|
+
},
|
|
1885
|
+
children: [
|
|
1886
|
+
/* @__PURE__ */ jsx3("h3", { className: "basic-section-label", children: "Personal information" }),
|
|
1887
|
+
/* @__PURE__ */ jsx3("p", { className: "basic-muted", children: "Only this app sees these overrides. Clear a field to use your universal profile value." }),
|
|
1888
|
+
profile.isLoading ? /* @__PURE__ */ jsx3("p", { role: "status", children: "Loading project profile\u2026" }) : profile.error ? /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
1889
|
+
/* @__PURE__ */ jsx3("p", { role: "alert", className: "basic-error", children: profile.error.message }),
|
|
1890
|
+
/* @__PURE__ */ jsx3(BasicButton, { onClick: profile.refresh, children: "Retry profile" })
|
|
1891
|
+
] }) : profile.data && /* @__PURE__ */ jsxs("fieldset", { disabled: !canWrite || action.pending, children: [
|
|
1892
|
+
/* @__PURE__ */ jsxs("label", { className: "basic-field", children: [
|
|
1893
|
+
"Display name",
|
|
1894
|
+
/* @__PURE__ */ jsx3(
|
|
1895
|
+
"input",
|
|
1896
|
+
{
|
|
1897
|
+
className: "basic-input",
|
|
1898
|
+
type: "text",
|
|
1899
|
+
maxLength: 64,
|
|
1900
|
+
value: ("name" in draft ? draft.name : profile.data?.profile.name) ?? "",
|
|
1901
|
+
onChange: (event) => {
|
|
1902
|
+
setSaved(false);
|
|
1903
|
+
setDraft((current) => ({
|
|
1904
|
+
...current,
|
|
1905
|
+
name: event.target.value || null
|
|
1906
|
+
}));
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
)
|
|
1910
|
+
] }),
|
|
1911
|
+
/* @__PURE__ */ jsxs("div", { className: "basic-actions basic-modal-footer", children: [
|
|
1912
|
+
/* @__PURE__ */ jsx3(
|
|
1913
|
+
BasicButton,
|
|
1914
|
+
{
|
|
1915
|
+
variant: "ghost",
|
|
1916
|
+
onClick: () => {
|
|
1917
|
+
setDraft({});
|
|
1918
|
+
setSaved(false);
|
|
1919
|
+
},
|
|
1920
|
+
children: "Cancel"
|
|
1921
|
+
}
|
|
1922
|
+
),
|
|
1923
|
+
/* @__PURE__ */ jsx3(
|
|
1924
|
+
BasicButton,
|
|
1925
|
+
{
|
|
1926
|
+
type: "submit",
|
|
1927
|
+
variant: "solid",
|
|
1928
|
+
disabled: !Object.keys(draft).length,
|
|
1929
|
+
children: "Save changes"
|
|
1930
|
+
}
|
|
1931
|
+
)
|
|
1932
|
+
] })
|
|
1933
|
+
] }),
|
|
1934
|
+
action.error && /* @__PURE__ */ jsx3("p", { role: "alert", className: "basic-error", children: action.error }),
|
|
1935
|
+
saved && /* @__PURE__ */ jsx3("p", { role: "status", children: "Project profile saved." })
|
|
1936
|
+
]
|
|
1937
|
+
}
|
|
1938
|
+
);
|
|
1939
|
+
}
|
|
1940
|
+
function SharesModal({
|
|
1941
|
+
auth: supplied,
|
|
1942
|
+
accounts: suppliedAccounts,
|
|
1943
|
+
sync,
|
|
1944
|
+
shares,
|
|
1945
|
+
scope,
|
|
1946
|
+
repo = "default",
|
|
1947
|
+
...props
|
|
1948
|
+
}) {
|
|
1949
|
+
const live = useAuth();
|
|
1950
|
+
const liveAccounts = useAccounts();
|
|
1951
|
+
const auth = supplied ?? live;
|
|
1952
|
+
return /* @__PURE__ */ jsx3(
|
|
1953
|
+
Modal,
|
|
1954
|
+
{
|
|
1955
|
+
...props,
|
|
1956
|
+
title: "Shares",
|
|
1957
|
+
description: "Manage incoming invitations and share access to selected data.",
|
|
1958
|
+
header: /* @__PURE__ */ jsx3(
|
|
1959
|
+
AccountHeader,
|
|
1960
|
+
{
|
|
1961
|
+
account: (suppliedAccounts ?? liveAccounts).activeAccount,
|
|
1962
|
+
sync
|
|
1963
|
+
}
|
|
1964
|
+
),
|
|
1965
|
+
children: !auth.isReady ? /* @__PURE__ */ jsx3("p", { role: "status", children: "Checking session\u2026" }) : !auth.isSignedIn ? /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
1966
|
+
/* @__PURE__ */ jsx3("p", { className: "basic-muted", children: "Sign in to share data." }),
|
|
1967
|
+
/* @__PURE__ */ jsx3(SignInButton, { auth })
|
|
1968
|
+
] }) : /* @__PURE__ */ jsx3(
|
|
1969
|
+
SharesContent,
|
|
1970
|
+
{
|
|
1971
|
+
shares,
|
|
1972
|
+
scope,
|
|
1973
|
+
repo,
|
|
1974
|
+
canWrite: auth.canWrite
|
|
1975
|
+
},
|
|
1976
|
+
auth.did ?? "account"
|
|
1977
|
+
)
|
|
1978
|
+
}
|
|
1979
|
+
);
|
|
1980
|
+
}
|
|
1981
|
+
function SharesContent({
|
|
1982
|
+
shares: supplied,
|
|
1983
|
+
scope,
|
|
1984
|
+
repo,
|
|
1985
|
+
canWrite
|
|
1986
|
+
}) {
|
|
1987
|
+
const live = useOutgoingShares();
|
|
1988
|
+
const shares = supplied ?? live;
|
|
1989
|
+
const action = useAction();
|
|
1990
|
+
const [recipient, setRecipient] = useState4("");
|
|
1991
|
+
const [role, setRole] = useState4("viewer");
|
|
1992
|
+
const [confirmation, setConfirmation] = useState4(null);
|
|
1993
|
+
const [message, setMessage] = useState4("");
|
|
1994
|
+
const scopeValid = scope.length > 0 && scope.every(
|
|
1995
|
+
(item) => item.table.trim() && (item.recordIds === void 0 || item.recordIds.length > 0)
|
|
1996
|
+
);
|
|
1997
|
+
return /* @__PURE__ */ jsxs(Tabs.Root, { defaultValue: "outgoing", className: "basic-shares", children: [
|
|
1998
|
+
/* @__PURE__ */ jsxs(Tabs.List, { className: "basic-tabs", "aria-label": "Share direction", children: [
|
|
1999
|
+
/* @__PURE__ */ jsx3(Tabs.Tab, { value: "outgoing", children: "Outgoing" }),
|
|
2000
|
+
/* @__PURE__ */ jsx3(Tabs.Tab, { value: "incoming", children: "Incoming invitations" })
|
|
2001
|
+
] }),
|
|
2002
|
+
/* @__PURE__ */ jsx3(Tabs.Panel, { value: "incoming", children: /* @__PURE__ */ jsxs("section", { "aria-label": "Incoming invitations", children: [
|
|
2003
|
+
/* @__PURE__ */ jsx3("h3", { className: "basic-title", children: "Incoming invitations" }),
|
|
2004
|
+
/* @__PURE__ */ jsx3("p", { className: "basic-muted", children: "View, accept, or decline incoming invitations in Basic ID. Your app session cannot access the account-wide inbox." }),
|
|
2005
|
+
/* @__PURE__ */ jsx3("a", { className: "basic-link", href: shares.manageUrl(), children: "Open incoming invitations in Basic ID \u2197" })
|
|
2006
|
+
] }) }),
|
|
2007
|
+
/* @__PURE__ */ jsxs(Tabs.Panel, { value: "outgoing", children: [
|
|
2008
|
+
/* @__PURE__ */ jsx3("p", { className: "basic-muted", children: "Revoking access stops future requests. It cannot retract data the recipient already downloaded." }),
|
|
2009
|
+
/* @__PURE__ */ jsxs("div", { className: "basic-scope", children: [
|
|
2010
|
+
/* @__PURE__ */ jsx3("strong", { children: "New invitation permissions" }),
|
|
2011
|
+
scope.map((item, index) => /* @__PURE__ */ jsxs("span", { children: [
|
|
2012
|
+
item.table,
|
|
2013
|
+
" \xB7",
|
|
2014
|
+
" ",
|
|
2015
|
+
item.recordIds ? `${item.recordIds.length} selected records` : "all records"
|
|
2016
|
+
] }, index))
|
|
2017
|
+
] }),
|
|
2018
|
+
!canWrite && /* @__PURE__ */ jsx3("p", { className: "basic-error", children: "This session is read only. Sharing changes are disabled." }),
|
|
2019
|
+
/* @__PURE__ */ jsxs(
|
|
2020
|
+
"form",
|
|
2021
|
+
{
|
|
2022
|
+
className: "basic-share-form",
|
|
2023
|
+
onSubmit: (event) => {
|
|
2024
|
+
event.preventDefault();
|
|
2025
|
+
if (!recipient.trim() || !scopeValid || !canWrite) return;
|
|
2026
|
+
void action.run(async () => {
|
|
2027
|
+
await shares.create({
|
|
2028
|
+
repo,
|
|
2029
|
+
scope,
|
|
2030
|
+
role,
|
|
2031
|
+
...recipient.trim().startsWith("did:") ? { recipientDid: recipient.trim() } : { recipientHandle: recipient.trim().replace(/^@+/, "") }
|
|
2032
|
+
});
|
|
2033
|
+
setRecipient("");
|
|
2034
|
+
setMessage("Invitation sent.");
|
|
2035
|
+
shares.refresh();
|
|
2036
|
+
});
|
|
2037
|
+
},
|
|
2038
|
+
children: [
|
|
2039
|
+
/* @__PURE__ */ jsxs("label", { className: "basic-field", children: [
|
|
2040
|
+
"Recipient handle or DID",
|
|
2041
|
+
/* @__PURE__ */ jsx3(
|
|
2042
|
+
"input",
|
|
2043
|
+
{
|
|
2044
|
+
className: "basic-input",
|
|
2045
|
+
value: recipient,
|
|
2046
|
+
onChange: (event) => setRecipient(event.target.value),
|
|
2047
|
+
placeholder: "@alex.basic.id",
|
|
2048
|
+
required: true,
|
|
2049
|
+
disabled: action.pending || !canWrite
|
|
2050
|
+
}
|
|
2051
|
+
)
|
|
2052
|
+
] }),
|
|
2053
|
+
/* @__PURE__ */ jsxs("label", { className: "basic-field", children: [
|
|
2054
|
+
"Permission",
|
|
2055
|
+
/* @__PURE__ */ jsxs(
|
|
2056
|
+
"select",
|
|
2057
|
+
{
|
|
2058
|
+
className: "basic-input",
|
|
2059
|
+
value: role,
|
|
2060
|
+
onChange: (event) => setRole(event.target.value),
|
|
2061
|
+
disabled: action.pending || !canWrite,
|
|
2062
|
+
children: [
|
|
2063
|
+
/* @__PURE__ */ jsx3("option", { value: "viewer", children: "Can view" }),
|
|
2064
|
+
/* @__PURE__ */ jsx3("option", { value: "editor", children: "Can edit" })
|
|
2065
|
+
]
|
|
2066
|
+
}
|
|
2067
|
+
)
|
|
2068
|
+
] }),
|
|
2069
|
+
/* @__PURE__ */ jsx3(
|
|
2070
|
+
BasicButton,
|
|
2071
|
+
{
|
|
2072
|
+
type: "submit",
|
|
2073
|
+
variant: "solid",
|
|
2074
|
+
disabled: action.pending || !canWrite || !scopeValid || !recipient.trim(),
|
|
2075
|
+
children: action.pending ? "Please wait\u2026" : "Send invitation"
|
|
2076
|
+
}
|
|
2077
|
+
)
|
|
2078
|
+
]
|
|
2079
|
+
}
|
|
2080
|
+
),
|
|
2081
|
+
!scopeValid && /* @__PURE__ */ jsx3("p", { role: "alert", className: "basic-error", children: "Select at least one table or record to share." }),
|
|
2082
|
+
action.error && /* @__PURE__ */ jsx3("p", { role: "alert", className: "basic-error", children: action.error }),
|
|
2083
|
+
message && /* @__PURE__ */ jsx3("p", { role: "status", className: "basic-muted", children: message }),
|
|
2084
|
+
/* @__PURE__ */ jsxs("div", { className: "basic-section-heading", children: [
|
|
2085
|
+
/* @__PURE__ */ jsx3("h3", { className: "basic-title", children: "Outgoing shares \xB7 all permissions" }),
|
|
2086
|
+
/* @__PURE__ */ jsx3(
|
|
2087
|
+
BasicButton,
|
|
2088
|
+
{
|
|
2089
|
+
disabled: action.pending || shares.isLoading,
|
|
2090
|
+
onClick: shares.refresh,
|
|
2091
|
+
children: "Refresh"
|
|
2092
|
+
}
|
|
2093
|
+
)
|
|
2094
|
+
] }),
|
|
2095
|
+
shares.isLoading ? /* @__PURE__ */ jsx3("p", { role: "status", children: "Loading shares\u2026" }) : shares.error ? /* @__PURE__ */ jsx3("p", { role: "alert", className: "basic-error", children: shares.error.message }) : !shares.data.length ? /* @__PURE__ */ jsx3("p", { className: "basic-empty", children: "No outgoing shares yet." }) : /* @__PURE__ */ jsx3("ul", { className: "basic-share-list", children: shares.data.map((share) => /* @__PURE__ */ jsxs("li", { children: [
|
|
2096
|
+
/* @__PURE__ */ jsxs("div", { className: "basic-identity", children: [
|
|
2097
|
+
/* @__PURE__ */ jsx3("strong", { children: share.display.shareName || share.recipientDid }),
|
|
2098
|
+
/* @__PURE__ */ jsxs("small", { children: [
|
|
2099
|
+
share.role === "viewer" ? "Can view" : "Can edit",
|
|
2100
|
+
" \xB7",
|
|
2101
|
+
" ",
|
|
2102
|
+
share.effectiveState,
|
|
2103
|
+
" \xB7",
|
|
2104
|
+
" ",
|
|
2105
|
+
share.scope.map((item) => item.table).join(", ")
|
|
2106
|
+
] })
|
|
2107
|
+
] }),
|
|
2108
|
+
share.state !== "ended" && (confirmation === share.id ? /* @__PURE__ */ jsxs("div", { className: "basic-actions", children: [
|
|
2109
|
+
/* @__PURE__ */ jsx3("span", { children: "Remove access?" }),
|
|
2110
|
+
/* @__PURE__ */ jsxs(
|
|
2111
|
+
BasicButton,
|
|
2112
|
+
{
|
|
2113
|
+
disabled: action.pending || !canWrite,
|
|
2114
|
+
onClick: () => void action.run(async () => {
|
|
2115
|
+
await (share.state === "pending" ? shares.cancel(share.id) : shares.revoke(share.id));
|
|
2116
|
+
setConfirmation(null);
|
|
2117
|
+
setMessage(
|
|
2118
|
+
share.state === "pending" ? "Invitation canceled." : "Access revoked."
|
|
2119
|
+
);
|
|
2120
|
+
shares.refresh();
|
|
2121
|
+
}),
|
|
2122
|
+
children: [
|
|
2123
|
+
"Confirm",
|
|
2124
|
+
" ",
|
|
2125
|
+
share.state === "pending" ? "cancel" : "revoke"
|
|
2126
|
+
]
|
|
2127
|
+
}
|
|
2128
|
+
),
|
|
2129
|
+
/* @__PURE__ */ jsx3(
|
|
2130
|
+
BasicButton,
|
|
2131
|
+
{
|
|
2132
|
+
disabled: action.pending,
|
|
2133
|
+
onClick: () => setConfirmation(null),
|
|
2134
|
+
children: "Keep access"
|
|
2135
|
+
}
|
|
2136
|
+
)
|
|
2137
|
+
] }) : /* @__PURE__ */ jsx3(
|
|
2138
|
+
BasicButton,
|
|
2139
|
+
{
|
|
2140
|
+
disabled: action.pending || !canWrite,
|
|
2141
|
+
onClick: () => setConfirmation(share.id),
|
|
2142
|
+
children: share.state === "pending" ? "Cancel invitation" : "Revoke access"
|
|
2143
|
+
}
|
|
2144
|
+
))
|
|
2145
|
+
] }, share.id)) })
|
|
2146
|
+
] })
|
|
2147
|
+
] });
|
|
2148
|
+
}
|
|
1040
2149
|
export {
|
|
2150
|
+
AccountSettingsModal,
|
|
2151
|
+
AuthStatus,
|
|
2152
|
+
BasicButton,
|
|
1041
2153
|
BasicProvider,
|
|
2154
|
+
BasicUIProvider,
|
|
1042
2155
|
BrowserKeyValueStorage,
|
|
1043
2156
|
BrowserTokenStore,
|
|
1044
2157
|
PersistenceStore,
|
|
2158
|
+
SharesModal,
|
|
2159
|
+
SignInButton,
|
|
2160
|
+
SignOutButton,
|
|
2161
|
+
SyncStatus,
|
|
2162
|
+
UserAvatar,
|
|
2163
|
+
UserButton,
|
|
2164
|
+
UserMenu,
|
|
1045
2165
|
browserCurrentUrl,
|
|
1046
2166
|
browserNavigate,
|
|
1047
2167
|
browserReplaceUrl,
|
|
@@ -1059,6 +2179,7 @@ export {
|
|
|
1059
2179
|
useFiles,
|
|
1060
2180
|
useMounts,
|
|
1061
2181
|
useOutgoingShares,
|
|
2182
|
+
useProjectProfile,
|
|
1062
2183
|
useQuery,
|
|
1063
2184
|
useRepos,
|
|
1064
2185
|
useSchemaStatus,
|