@protobase/ui 0.59.2 → 0.59.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/dist/app/app.js +11 -3
- package/dist/app-shell/app-shell.js +2 -1
- package/dist/app-shell/profile-menu.d.ts +1 -0
- package/dist/app-shell/profile-menu.js +25 -3
- package/dist/app-shell/sidebar.js +3 -2
- package/dist/data/api-provider.d.ts +12 -0
- package/dist/data/meta-data.d.ts +2 -0
- package/dist/data/query-keys.js +8 -0
- package/dist/data/use-meta.js +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +3 -1
- package/dist/organizations/members-page.js +7 -3
- package/dist/package.js +1 -1
- package/dist/people/person-row.js +8 -2
- package/dist/people/use-members.d.ts +9 -4
- package/dist/people/use-members.js +19 -3
- package/dist/shell/assets/app-CA2weCDB.js +9 -0
- package/dist/shell/assets/{index-mZM1MZhj.css → index-D0sx7n9i.css} +1 -1
- package/dist/shell/assets/{index-C-4shB1O.js → index-Dto4tygi.js} +2 -2
- package/dist/shell/assets/{records-map-CnJCsPQi.js → records-map-lppqdLea.js} +2 -2
- package/dist/shell/assets/shared-protobase-ui-C99fw_w0.js +2 -0
- package/dist/shell/index.html +4 -4
- package/dist/shell/protobase.shell.json +2 -2
- package/dist/shell/protobase.sw.js +2 -2
- package/dist/shell-source-maps/assets/app-CA2weCDB.js.map +1 -0
- package/dist/shell-source-maps/assets/{index-C-4shB1O.js.map → index-Dto4tygi.js.map} +1 -1
- package/dist/shell-source-maps/assets/{records-map-CnJCsPQi.js.map → records-map-lppqdLea.js.map} +1 -1
- package/dist/shell-source-maps/assets/shared-protobase-ui-C99fw_w0.js.map +1 -0
- package/dist/status/clear-time.js +40 -0
- package/dist/status/messages.js +58 -0
- package/dist/status/status-dialog.js +190 -0
- package/dist/status/status-draft.js +40 -0
- package/dist/status/status-mark.d.ts +24 -0
- package/dist/status/status-mark.js +51 -0
- package/dist/status/use-status-menu.js +55 -0
- package/dist/status/use-statuses.d.ts +15 -0
- package/dist/status/use-statuses.js +43 -0
- package/package.json +4 -4
- package/dist/shell/assets/app-b2Amc-iC.js +0 -9
- package/dist/shell/assets/shared-protobase-ui-CQPxMGrM.js +0 -2
- package/dist/shell-source-maps/assets/app-b2Amc-iC.js.map +0 -1
- package/dist/shell-source-maps/assets/shared-protobase-ui-CQPxMGrM.js.map +0 -1
package/dist/app/app.js
CHANGED
|
@@ -10,6 +10,7 @@ import { humanize } from "../live/naming.js";
|
|
|
10
10
|
import { text } from "./messages.js";
|
|
11
11
|
import { Router, useRouter } from "./router.js";
|
|
12
12
|
import { useAppAddress } from "./use-app-address.js";
|
|
13
|
+
import { useStatuses } from "../status/use-statuses.js";
|
|
13
14
|
import { ProjectUiContext, useProjectUi } from "./pages/project-ui.js";
|
|
14
15
|
import { RecordTitles, useRecordTitle } from "./titles/record-titles.js";
|
|
15
16
|
import { isAdmin, userToShell } from "../auth/user-to-shell.js";
|
|
@@ -62,6 +63,7 @@ import { RuntimeUpdateButton } from "./runtime/runtime-update.js";
|
|
|
62
63
|
import { StaffBanner } from "./staff-banner.js";
|
|
63
64
|
import { useNavRecent } from "./use-nav-recent.js";
|
|
64
65
|
import { LiveUpdateGate } from "./live-update/live-update-gate.js";
|
|
66
|
+
import { useStatusMenu } from "../status/use-status-menu.js";
|
|
65
67
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
66
68
|
import { viewOfList } from "@protobase/schema/internal";
|
|
67
69
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -114,6 +116,8 @@ const Routes = ({ workspace, sidebarMode, assistant, runtime }) => {
|
|
|
114
116
|
const Actions = shell?.actions;
|
|
115
117
|
const RightPanel = shell?.rightPanel;
|
|
116
118
|
const bell = meta.bell && user && !staff && /* @__PURE__ */ jsx(ShellBell, { own: shell?.bell });
|
|
119
|
+
const status = useStatusMenu(Boolean(meta.status && user && !staff));
|
|
120
|
+
const statuses = useStatuses().data;
|
|
117
121
|
const [assistantOpen, setAssistantOpen] = useState(false);
|
|
118
122
|
const assistantUrl = meta.assistant?.url;
|
|
119
123
|
const assistantButton = assistantUrl && /* @__PURE__ */ jsx(AssistantButton, {
|
|
@@ -187,7 +191,10 @@ const Routes = ({ workspace, sidebarMode, assistant, runtime }) => {
|
|
|
187
191
|
}) : organizationPage && (path === "/-/organization/members" || path === "/-/organization/invitations") ? !managesOrganization(current) ? /* @__PURE__ */ jsx(Notice, {
|
|
188
192
|
title: text("ownersOnly"),
|
|
189
193
|
children: text("onlyOwners")
|
|
190
|
-
}) : path === "/-/organization/members" ? /* @__PURE__ */ jsx(MembersPage, {
|
|
194
|
+
}) : path === "/-/organization/members" ? /* @__PURE__ */ jsx(MembersPage, {
|
|
195
|
+
current,
|
|
196
|
+
statuses
|
|
197
|
+
}) : /* @__PURE__ */ jsx(InvitationsPage, { current }) : organizationPage && path === "/-/organizations" || outsideOrganizations ? /* @__PURE__ */ jsx(OrganizationsPage, {
|
|
191
198
|
state: organizations,
|
|
192
199
|
basePath
|
|
193
200
|
}) : !address ? path === "/" ? /* @__PURE__ */ jsx(Notice, {
|
|
@@ -210,7 +217,7 @@ const Routes = ({ workspace, sidebarMode, assistant, runtime }) => {
|
|
|
210
217
|
recordKey: address.key
|
|
211
218
|
}, `${address.resource}/${address.key}`) : /* @__PURE__ */ jsx(ListPage, { address }, navItemId(address.resource, address.list));
|
|
212
219
|
const pluginNotice = admin && pluginsOff.length > 0;
|
|
213
|
-
return /* @__PURE__ */
|
|
220
|
+
return /* @__PURE__ */ jsxs(AppShell, {
|
|
214
221
|
pageScroll: true,
|
|
215
222
|
sidebarMode: sidebarMode ?? "text-small",
|
|
216
223
|
activeItem: place ?? "",
|
|
@@ -242,6 +249,7 @@ const Routes = ({ workspace, sidebarMode, assistant, runtime }) => {
|
|
|
242
249
|
preference: theme.preference,
|
|
243
250
|
onChange: theme.choose
|
|
244
251
|
},
|
|
252
|
+
...status.menu && { status: status.menu },
|
|
245
253
|
roles: role.current?.active ? {
|
|
246
254
|
active: role.current.active,
|
|
247
255
|
roles: role.current.roles,
|
|
@@ -279,7 +287,7 @@ const Routes = ({ workspace, sidebarMode, assistant, runtime }) => {
|
|
|
279
287
|
fallback: null,
|
|
280
288
|
children: /* @__PURE__ */ jsx(RightPanel, {})
|
|
281
289
|
}), assistantPanel] }),
|
|
282
|
-
children: content
|
|
290
|
+
children: [content, status.dialog]
|
|
283
291
|
});
|
|
284
292
|
};
|
|
285
293
|
const Authenticated = ({ baseUrl, client, sources, workspace, sidebarMode, ui, assistant, runtime }) => {
|
|
@@ -11,7 +11,7 @@ import { useEffect, useRef, useState } from "react";
|
|
|
11
11
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
12
|
//#region src/app-shell/app-shell.tsx
|
|
13
13
|
const drawerId = "app-drawer";
|
|
14
|
-
const AppShell = ({ sidebarMode, activeItem, breadcrumb, user, workspace, appName, appIcon, logo, actions, rightPanel, sidebarNotice, banner, forceExpanded, profileMenuOpen, navGroups, userMenu, account, bottomBar, onNavigate, onBack, onSignOut, signIn, organizations, roles, theme, search, pageScroll: scrollsByDefault = false, className, children }) => {
|
|
14
|
+
const AppShell = ({ sidebarMode, activeItem, breadcrumb, user, workspace, appName, appIcon, logo, actions, rightPanel, sidebarNotice, banner, forceExpanded, profileMenuOpen, navGroups, userMenu, account, bottomBar, onNavigate, onBack, onSignOut, signIn, organizations, roles, theme, status, search, pageScroll: scrollsByDefault = false, className, children }) => {
|
|
15
15
|
const desktop = useIsDesktop();
|
|
16
16
|
const noticeShown = useSidebarNoticeShown(sidebarMode);
|
|
17
17
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
@@ -46,6 +46,7 @@ const AppShell = ({ sidebarMode, activeItem, breadcrumb, user, workspace, appNam
|
|
|
46
46
|
organizations,
|
|
47
47
|
roles,
|
|
48
48
|
theme,
|
|
49
|
+
status,
|
|
49
50
|
logo
|
|
50
51
|
});
|
|
51
52
|
return /* @__PURE__ */ jsx(PageScrollContext.Provider, {
|
|
@@ -5,12 +5,12 @@ import { Badge } from "../primitives/badge.js";
|
|
|
5
5
|
import { SwitchGroup } from "./switch-group.js";
|
|
6
6
|
import { ThemeSwitch } from "./theme-switch.js";
|
|
7
7
|
import { useEffect, useRef, useState } from "react";
|
|
8
|
-
import { ChevronsUpDown, LogOut } from "lucide-react";
|
|
8
|
+
import { ChevronsUpDown, LogOut, SmilePlus } from "lucide-react";
|
|
9
9
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
10
10
|
//#region src/app-shell/profile-menu.tsx
|
|
11
11
|
const menuRow = "flex min-h-11 w-full items-center gap-2.5 px-3 py-1.5 text-left text-muted-foreground hover:bg-muted md:min-h-0";
|
|
12
12
|
/** The person at the bottom of the sidebar; the menu is fixed to the viewport so a narrow sidebar cannot clip it. */
|
|
13
|
-
const ProfileMenu = ({ user, compact, open: controlled, items = [], account, onNavigate, onSignOut, organizations, roles, theme }) => {
|
|
13
|
+
const ProfileMenu = ({ user, compact, open: controlled, items = [], account, onNavigate, onSignOut, organizations, roles, theme, status }) => {
|
|
14
14
|
const activeRole = roles?.roles.find((role) => role.name === roles.active);
|
|
15
15
|
const [local, setLocal] = useState(false);
|
|
16
16
|
const open = controlled ?? local;
|
|
@@ -60,7 +60,13 @@ const ProfileMenu = ({ user, compact, open: controlled, items = [], account, onN
|
|
|
60
60
|
className: "ring-0"
|
|
61
61
|
}), !compact && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
|
|
62
62
|
className: "min-w-0 flex-1 leading-tight",
|
|
63
|
-
children: [/* @__PURE__ */
|
|
63
|
+
children: [status?.mark ? /* @__PURE__ */ jsxs("span", {
|
|
64
|
+
className: "flex min-w-0 items-center gap-1",
|
|
65
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
66
|
+
className: "truncate font-medium",
|
|
67
|
+
children: user.name
|
|
68
|
+
}), status.mark]
|
|
69
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
64
70
|
className: "block truncate font-medium",
|
|
65
71
|
children: user.name
|
|
66
72
|
}), /* @__PURE__ */ jsx(Badge, {
|
|
@@ -100,6 +106,22 @@ const ProfileMenu = ({ user, compact, open: controlled, items = [], account, onN
|
|
|
100
106
|
children: user.email
|
|
101
107
|
})]
|
|
102
108
|
}),
|
|
109
|
+
status && /* @__PURE__ */ jsx("div", {
|
|
110
|
+
className: "border-b py-1",
|
|
111
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
112
|
+
type: "button",
|
|
113
|
+
role: "menuitem",
|
|
114
|
+
className: menuRow,
|
|
115
|
+
onClick: () => {
|
|
116
|
+
setLocal(false);
|
|
117
|
+
status.onOpen();
|
|
118
|
+
},
|
|
119
|
+
children: [/* @__PURE__ */ jsx(SmilePlus, { className: "size-4 shrink-0" }), /* @__PURE__ */ jsx("span", {
|
|
120
|
+
className: "min-w-0 flex-1 truncate",
|
|
121
|
+
children: status.label
|
|
122
|
+
})]
|
|
123
|
+
})
|
|
124
|
+
}),
|
|
103
125
|
organizations && (organizations.current || organizations.others.length > 0) && /* @__PURE__ */ jsx(SwitchGroup, {
|
|
104
126
|
label: text("organizations"),
|
|
105
127
|
...organizations.current && { current: {
|
|
@@ -13,7 +13,7 @@ import { Fragment } from "react";
|
|
|
13
13
|
import { LogIn } from "lucide-react";
|
|
14
14
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
15
15
|
//#region src/app-shell/sidebar.tsx
|
|
16
|
-
const Sidebar = ({ mode, activeItem, user, workspace, appName, appIcon, forceExpanded, profileMenuOpen, groups = navGroups, userMenu, account, onNavigate, onSignOut, signIn, organizations, roles, theme, sticky, logo: SidebarLogo = Logo, notice }) => {
|
|
16
|
+
const Sidebar = ({ mode, activeItem, user, workspace, appName, appIcon, forceExpanded, profileMenuOpen, groups = navGroups, userMenu, account, onNavigate, onSignOut, signIn, organizations, roles, theme, status, sticky, logo: SidebarLogo = Logo, notice }) => {
|
|
17
17
|
const expanding = mode === "icon-expand";
|
|
18
18
|
const expanded = expanding && forceExpanded;
|
|
19
19
|
const inline = isTextMode(mode) || expanding;
|
|
@@ -70,7 +70,8 @@ const Sidebar = ({ mode, activeItem, user, workspace, appName, appIcon, forceExp
|
|
|
70
70
|
onSignOut,
|
|
71
71
|
organizations,
|
|
72
72
|
roles,
|
|
73
|
-
theme
|
|
73
|
+
theme,
|
|
74
|
+
status
|
|
74
75
|
}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [theme && inline && /* @__PURE__ */ jsx(ThemeSwitch, {
|
|
75
76
|
theme,
|
|
76
77
|
className: "flex min-h-11 w-full items-center gap-2.5 whitespace-nowrap p-1.5 text-muted-foreground md:min-h-0"
|
|
@@ -71,6 +71,12 @@ export declare const useClient: () => {
|
|
|
71
71
|
markRead: (id: string) => Promise<void>;
|
|
72
72
|
markAllRead: (upTo: string) => Promise<void>;
|
|
73
73
|
};
|
|
74
|
+
status: {
|
|
75
|
+
own: () => Promise<import("@protobase/client").PersonStatus | null>;
|
|
76
|
+
set: (status: import("@protobase/client").StatusInput) => Promise<import("@protobase/client").PersonStatus | null>;
|
|
77
|
+
clear: () => Promise<void>;
|
|
78
|
+
people: () => Promise<import("@protobase/client").PersonStatus[]>;
|
|
79
|
+
};
|
|
74
80
|
};
|
|
75
81
|
/**
|
|
76
82
|
* The client of the API that serves `resource`: a source's for a resource the app draws from one, the app's own
|
|
@@ -144,6 +150,12 @@ export declare const useResourceClient: (resource: string) => {
|
|
|
144
150
|
markRead: (id: string) => Promise<void>;
|
|
145
151
|
markAllRead: (upTo: string) => Promise<void>;
|
|
146
152
|
};
|
|
153
|
+
status: {
|
|
154
|
+
own: () => Promise<import("@protobase/client").PersonStatus | null>;
|
|
155
|
+
set: (status: import("@protobase/client").StatusInput) => Promise<import("@protobase/client").PersonStatus | null>;
|
|
156
|
+
clear: () => Promise<void>;
|
|
157
|
+
people: () => Promise<import("@protobase/client").PersonStatus[]>;
|
|
158
|
+
};
|
|
147
159
|
};
|
|
148
160
|
export type ApiProviderProps = {
|
|
149
161
|
/** Defaults to `/api/v1` on the current origin. */
|
package/dist/data/meta-data.d.ts
CHANGED
|
@@ -50,6 +50,8 @@ export type MetaData = {
|
|
|
50
50
|
};
|
|
51
51
|
/** Whether the top bar draws the notification bell: the app has notifications and their `ui` is not false (see `ShellBell`). */
|
|
52
52
|
bell?: boolean;
|
|
53
|
+
/** Whether people set their own status and see those they share an organization with (see `useStatuses`). */
|
|
54
|
+
status?: boolean;
|
|
53
55
|
/** The language the app's screens are in for everyone, when it sets one (see `AppLocale`). */
|
|
54
56
|
locale?: Locale;
|
|
55
57
|
/** The hosts besides the app's own whose images are drawn with the page's referrer (see `FileImage`). */
|
package/dist/data/query-keys.js
CHANGED
|
@@ -164,6 +164,14 @@ const keys = {
|
|
|
164
164
|
inboxPages: ["inbox", "pages"],
|
|
165
165
|
/** The people of the organization the session of this user works in (`useMembers`). */
|
|
166
166
|
members: (userId) => ["members", userId],
|
|
167
|
+
/** The statuses of everyone who shares an organization with this user (`useStatuses`). */
|
|
168
|
+
statuses: (userId) => ["statuses", userId],
|
|
169
|
+
/** This user's own status (`useOwnStatus`). */
|
|
170
|
+
ownStatus: (userId) => [
|
|
171
|
+
"statuses",
|
|
172
|
+
userId,
|
|
173
|
+
"own"
|
|
174
|
+
],
|
|
167
175
|
/** A public form, as `/-/forms/<name>` draws it. */
|
|
168
176
|
publicForm: (name) => ["public-form", name]
|
|
169
177
|
};
|
package/dist/data/use-meta.js
CHANGED
|
@@ -45,6 +45,7 @@ const useMeta = () => {
|
|
|
45
45
|
...result.meta.errorReporting && { errorReporting: result.meta.errorReporting },
|
|
46
46
|
...result.meta.maps && { maps: result.meta.maps },
|
|
47
47
|
...result.meta.notifications?.bell && { bell: true },
|
|
48
|
+
...result.meta.status && { status: true },
|
|
48
49
|
...result.meta.locale && { locale: result.meta.locale },
|
|
49
50
|
...result.meta.files && { files: result.meta.files },
|
|
50
51
|
...result.meta.plugins && { plugins: result.meta.plugins },
|
package/dist/index.d.ts
CHANGED
|
@@ -30,7 +30,9 @@ import { RecordLink } from "./app/displays/record-link.js";
|
|
|
30
30
|
import { useApiToken } from "./auth/use-api-token.js";
|
|
31
31
|
import { PushState } from "./notifications/push/web-push.js";
|
|
32
32
|
import { SignedInUser, useSignedInUser } from "./auth/use-signed-in-user.js";
|
|
33
|
-
import { MembersResult, useMembers } from "./people/use-members.js";
|
|
33
|
+
import { Member, MembersResult, useMembers } from "./people/use-members.js";
|
|
34
|
+
import { AvailabilityMark, StatusMark, StatusMarkProps } from "./status/status-mark.js";
|
|
35
|
+
import { statusRefreshMs, useOwnStatus, useStatuses } from "./status/use-statuses.js";
|
|
34
36
|
import { usePushNotifications } from "./notifications/push/use-push-notifications.js";
|
|
35
37
|
import { useInbox, useMarkNotificationsRead, useUnreadNotifications } from "./notifications/bell/use-inbox.js";
|
|
36
38
|
import { SignInPrompt, SignInPromptProps } from "./auth/sign-in-prompt.js";
|
|
@@ -84,4 +86,4 @@ import { Section } from "./record-view/section.js";
|
|
|
84
86
|
import { Form, FormProps } from "./app/create/form.js";
|
|
85
87
|
import { RecordPicker, RecordPickerProps } from "./app/record-picker.js";
|
|
86
88
|
import { Spinner } from "./primitives/spinner.js";
|
|
87
|
-
export { ActionCard, type ActionCardProps, type ActionContext, type ActionHandler, ActionInputError, type ActionInputProps, ApiProvider, type ApiProviderProps, App, AppLink, type AppProps, AssistantDock, type AssistantDockProps, AuthLayout, type AuthLayoutProps, type BackendData, type BackendRefresh, type BeforeSignInProps, type BoardDrop, type BoardDropContext, type BoardDropUndo, type BoardDropWrite, Button, type ButtonProps, type CardAction, type CardTone, Carousel, type CarouselImage, type CarouselProps, type ChatAuthor, ChatDivider, type ChatDividerProps, ChatMessage, type ChatMessageProps, ChatThread, type ChatThreadProps, type ChoiceOption, Choices, type ChoicesProps, CodeForm, type CodeFormProps, CompactTable, type CompactTableProps, Composer, type ComposerProps, type CurrentAddress, Dialog, type DialogProps, type DiffLine, DiffView, type DiffViewProps, type Drop, type DropRule, Field, FieldList, type FieldProps, type FieldWidget, type FieldWidgetEditorProps, type FieldWidgetProps, Form, type FormProps, Input, type InputProps, type LayoutRecord, type ListAddress, type ListChange, type ListDisplay, type ListDisplayRow, LogoMark, Markdown, type MarkdownContentProps as MarkdownProps, type MarksHook, type MembersResult, MentionBox, type MentionBoxProps, type MentionDraft, MentionedText, MenuSelect, type MenuSelectProps, type MetaData, OtpInput, type OtpInputProps, PartView, type PeopleSearch, type PickedUser, type PluginInfo, type ProjectUi, type PushState, type RecordCardProps, RecordLink, type RecordMark, RecordPicker, type RecordPickerProps, ResendCodeButton, type ResendCodeButtonProps, ResponsiveDialog, type ResponsiveDialogProps, Section, Segmented, type SegmentedOption, type SegmentedProps, type ShellLogoProps, SidePanel, type SidePanelProps, SignInPrompt, type SignInPromptProps, type SignedInUser, Spinner, type Step, StepList, type StepState, Textarea, type TextareaProps, type TitleReader, type TitleRule, allowDelayMs, buttonClass, chatDayLabel, definePlugin, defineUi, diffStats, emptyDraft, isConflict, isFramed, mentionedIds, mergeUi, parseDiff, pluginApi, protobaseVersion, sameChatDay, uiFromModule, useApiToken, useAppAddress, useArmedAfterFocus, useAssistant, useAssistantClient, useBackendData, useClient, useFacets, useFieldControl, useGuardedPress, useHistogram, useInbox, useLayoutRecord, useList, useListDisplay, useMarkNotificationsRead, useMembers, useMeta, usePlugin, usePushNotifications, useRecord, useResourceClient, useSeries, useSignedInUser, useUnreadNotifications, useUpdateRecord };
|
|
89
|
+
export { ActionCard, type ActionCardProps, type ActionContext, type ActionHandler, ActionInputError, type ActionInputProps, ApiProvider, type ApiProviderProps, App, AppLink, type AppProps, AssistantDock, type AssistantDockProps, AuthLayout, type AuthLayoutProps, AvailabilityMark, type BackendData, type BackendRefresh, type BeforeSignInProps, type BoardDrop, type BoardDropContext, type BoardDropUndo, type BoardDropWrite, Button, type ButtonProps, type CardAction, type CardTone, Carousel, type CarouselImage, type CarouselProps, type ChatAuthor, ChatDivider, type ChatDividerProps, ChatMessage, type ChatMessageProps, ChatThread, type ChatThreadProps, type ChoiceOption, Choices, type ChoicesProps, CodeForm, type CodeFormProps, CompactTable, type CompactTableProps, Composer, type ComposerProps, type CurrentAddress, Dialog, type DialogProps, type DiffLine, DiffView, type DiffViewProps, type Drop, type DropRule, Field, FieldList, type FieldProps, type FieldWidget, type FieldWidgetEditorProps, type FieldWidgetProps, Form, type FormProps, Input, type InputProps, type LayoutRecord, type ListAddress, type ListChange, type ListDisplay, type ListDisplayRow, LogoMark, Markdown, type MarkdownContentProps as MarkdownProps, type MarksHook, type Member, type MembersResult, MentionBox, type MentionBoxProps, type MentionDraft, MentionedText, MenuSelect, type MenuSelectProps, type MetaData, OtpInput, type OtpInputProps, PartView, type PeopleSearch, type PickedUser, type PluginInfo, type ProjectUi, type PushState, type RecordCardProps, RecordLink, type RecordMark, RecordPicker, type RecordPickerProps, ResendCodeButton, type ResendCodeButtonProps, ResponsiveDialog, type ResponsiveDialogProps, Section, Segmented, type SegmentedOption, type SegmentedProps, type ShellLogoProps, SidePanel, type SidePanelProps, SignInPrompt, type SignInPromptProps, type SignedInUser, Spinner, StatusMark, type StatusMarkProps, type Step, StepList, type StepState, Textarea, type TextareaProps, type TitleReader, type TitleRule, allowDelayMs, buttonClass, chatDayLabel, definePlugin, defineUi, diffStats, emptyDraft, isConflict, isFramed, mentionedIds, mergeUi, parseDiff, pluginApi, protobaseVersion, sameChatDay, statusRefreshMs, uiFromModule, useApiToken, useAppAddress, useArmedAfterFocus, useAssistant, useAssistantClient, useBackendData, useClient, useFacets, useFieldControl, useGuardedPress, useHistogram, useInbox, useLayoutRecord, useList, useListDisplay, useMarkNotificationsRead, useMembers, useMeta, useOwnStatus, usePlugin, usePushNotifications, useRecord, useResourceClient, useSeries, useSignedInUser, useStatuses, useUnreadNotifications, useUpdateRecord };
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import { AppLink, useAppAddress } from "./app/use-app-address.js";
|
|
|
12
12
|
import { Segmented } from "./primitives/segmented.js";
|
|
13
13
|
import { Textarea } from "./primitives/textarea.js";
|
|
14
14
|
import { useList } from "./data/use-list.js";
|
|
15
|
+
import { statusRefreshMs, useOwnStatus, useStatuses } from "./status/use-statuses.js";
|
|
15
16
|
import { useMembers } from "./people/use-members.js";
|
|
16
17
|
import { definePlugin, defineUi } from "./app/pages/project-ui.js";
|
|
17
18
|
import { OtpInput } from "./primitives/otp-input.js";
|
|
@@ -54,6 +55,7 @@ import { AssistantDock } from "./assistant-dock/assistant-dock.js";
|
|
|
54
55
|
import { useAssistant } from "./assistant-dock/use-assistant.js";
|
|
55
56
|
import { useAssistantClient } from "./assistant-dock/use-assistant-client.js";
|
|
56
57
|
import { Form } from "./app/create/form.js";
|
|
58
|
+
import { AvailabilityMark, StatusMark } from "./status/status-mark.js";
|
|
57
59
|
import { usePushNotifications } from "./notifications/push/use-push-notifications.js";
|
|
58
60
|
import { useInbox, useMarkNotificationsRead, useUnreadNotifications } from "./notifications/bell/use-inbox.js";
|
|
59
61
|
import { isConflict, useUpdateRecord } from "./data/use-update-record.js";
|
|
@@ -63,4 +65,4 @@ import { useSignedInUser } from "./auth/use-signed-in-user.js";
|
|
|
63
65
|
import { Carousel } from "./carousel/carousel.js";
|
|
64
66
|
import { Choices } from "./primitives/choices.js";
|
|
65
67
|
import { RecordPicker } from "./app/record-picker.js";
|
|
66
|
-
export { ActionCard, ActionInputError, ApiProvider, App, AppLink, AssistantDock, AuthLayout, Button, Carousel, ChatDivider, ChatMessage, ChatThread, Choices, CodeForm, CompactTable, Composer, Dialog, DiffView, Field, FieldList, Form, Input, LogoMark, Markdown, MentionBox, MentionedText, MenuSelect, OtpInput, PartView, RecordLink, RecordPicker, ResendCodeButton, ResponsiveDialog, Section, Segmented, SidePanel, SignInPrompt, Spinner, StepList, Textarea, allowDelayMs, buttonClass, chatDayLabel, definePlugin, defineUi, diffStats, emptyDraft, isConflict, isFramed, mentionedIds, mergeUi, parseDiff, pluginApi, protobaseVersion, sameChatDay, uiFromModule, useApiToken, useAppAddress, useArmedAfterFocus, useAssistant, useAssistantClient, useBackendData, useClient, useFacets, useFieldControl, useGuardedPress, useHistogram, useInbox, useLayoutRecord, useList, useListDisplay, useMarkNotificationsRead, useMembers, useMeta, usePlugin, usePushNotifications, useRecord, useResourceClient, useSeries, useSignedInUser, useUnreadNotifications, useUpdateRecord };
|
|
68
|
+
export { ActionCard, ActionInputError, ApiProvider, App, AppLink, AssistantDock, AuthLayout, AvailabilityMark, Button, Carousel, ChatDivider, ChatMessage, ChatThread, Choices, CodeForm, CompactTable, Composer, Dialog, DiffView, Field, FieldList, Form, Input, LogoMark, Markdown, MentionBox, MentionedText, MenuSelect, OtpInput, PartView, RecordLink, RecordPicker, ResendCodeButton, ResponsiveDialog, Section, Segmented, SidePanel, SignInPrompt, Spinner, StatusMark, StepList, Textarea, allowDelayMs, buttonClass, chatDayLabel, definePlugin, defineUi, diffStats, emptyDraft, isConflict, isFramed, mentionedIds, mergeUi, parseDiff, pluginApi, protobaseVersion, sameChatDay, statusRefreshMs, uiFromModule, useApiToken, useAppAddress, useArmedAfterFocus, useAssistant, useAssistantClient, useBackendData, useClient, useFacets, useFieldControl, useGuardedPress, useHistogram, useInbox, useLayoutRecord, useList, useListDisplay, useMarkNotificationsRead, useMembers, useMeta, useOwnStatus, usePlugin, usePushNotifications, useRecord, useResourceClient, useSeries, useSignedInUser, useStatuses, useUnreadNotifications, useUpdateRecord };
|
|
@@ -9,6 +9,7 @@ import { text } from "./messages.js";
|
|
|
9
9
|
import { AppRolesPicker } from "./app-roles-picker.js";
|
|
10
10
|
import { grantableRoles, organizationRoleLabel } from "./organization-paths.js";
|
|
11
11
|
import { PersonRow } from "../people/person-row.js";
|
|
12
|
+
import { StatusMark } from "../status/status-mark.js";
|
|
12
13
|
import { useCallback, useEffect, useState } from "react";
|
|
13
14
|
import { AuthError } from "@protobase/client";
|
|
14
15
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -19,7 +20,7 @@ const assignableOrganizationRoles = (current) => current?.role === "owner" ? [
|
|
|
19
20
|
"admin",
|
|
20
21
|
"member"
|
|
21
22
|
] : ["admin", "member"];
|
|
22
|
-
const MemberRow = ({ member, current, onChanged }) => {
|
|
23
|
+
const MemberRow = ({ member, current, status, onChanged }) => {
|
|
23
24
|
const { session, organizations } = useAuth();
|
|
24
25
|
const organizationId = current.organization.id;
|
|
25
26
|
const { busy, error, run } = useAction();
|
|
@@ -33,6 +34,7 @@ const MemberRow = ({ member, current, onChanged }) => {
|
|
|
33
34
|
email: member.email,
|
|
34
35
|
self,
|
|
35
36
|
error,
|
|
37
|
+
...status && { status: /* @__PURE__ */ jsx(StatusMark, { status }) },
|
|
36
38
|
actions: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("select", {
|
|
37
39
|
"aria-label": text("roleOf", member.name),
|
|
38
40
|
value: member.role,
|
|
@@ -71,9 +73,10 @@ const MemberRow = ({ member, current, onChanged }) => {
|
|
|
71
73
|
};
|
|
72
74
|
/**
|
|
73
75
|
* The members of the organization one works in, for its owners and admins: organization roles, which decide who
|
|
74
|
-
* manages it, and app roles, which decide what each sees and does; only roles the person may give can change.
|
|
76
|
+
* manages it, and app roles, which decide what each sees and does; only roles the person may give can change. In an
|
|
77
|
+
* app with status, `statuses` puts each member's beside their name (see `useStatuses`).
|
|
75
78
|
*/
|
|
76
|
-
const MembersPage = ({ current }) => {
|
|
79
|
+
const MembersPage = ({ current, statuses }) => {
|
|
77
80
|
const { session } = useAuth();
|
|
78
81
|
const [members, setMembers] = useState();
|
|
79
82
|
const [error, setError] = useState();
|
|
@@ -96,6 +99,7 @@ const MembersPage = ({ current }) => {
|
|
|
96
99
|
children: members.map((member) => /* @__PURE__ */ jsx(MemberRow, {
|
|
97
100
|
member,
|
|
98
101
|
current,
|
|
102
|
+
status: statuses?.find((status) => status.userId === member.userId),
|
|
99
103
|
onChanged: load
|
|
100
104
|
}, `${member.id}/${member.role}/${member.appRoles.join()}`))
|
|
101
105
|
}) : !error && /* @__PURE__ */ jsxs("div", {
|
package/dist/package.js
CHANGED
|
@@ -2,14 +2,20 @@ import { SectionError } from "../account/account-section.js";
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
//#region src/people/person-row.tsx
|
|
4
4
|
/** Someone in a list of people: their name, " (you)" on one's own row, their address, what can be done and their roles. */
|
|
5
|
-
const PersonRow = ({ name, email, self, actions, children, error }) => /* @__PURE__ */ jsxs("li", {
|
|
5
|
+
const PersonRow = ({ name, email, self, actions, children, error, status }) => /* @__PURE__ */ jsxs("li", {
|
|
6
6
|
className: "flex flex-col gap-2 px-3 py-3",
|
|
7
7
|
children: [
|
|
8
8
|
/* @__PURE__ */ jsxs("div", {
|
|
9
9
|
className: "flex flex-wrap items-center gap-2",
|
|
10
10
|
children: [/* @__PURE__ */ jsxs("span", {
|
|
11
11
|
className: "min-w-0 flex-1",
|
|
12
|
-
children: [/* @__PURE__ */ jsxs("span", {
|
|
12
|
+
children: [status ? /* @__PURE__ */ jsxs("span", {
|
|
13
|
+
className: "flex min-w-0 items-center gap-1.5",
|
|
14
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
15
|
+
className: "truncate font-medium",
|
|
16
|
+
children: [name, self && " (you)"]
|
|
17
|
+
}), status]
|
|
18
|
+
}) : /* @__PURE__ */ jsxs("span", {
|
|
13
19
|
className: "block truncate font-medium",
|
|
14
20
|
children: [name, self && " (you)"]
|
|
15
21
|
}), /* @__PURE__ */ jsx("span", {
|
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import { OrganizationPerson } from "@protobase/client";
|
|
1
|
+
import { OrganizationPerson, PersonStatus } from "@protobase/client";
|
|
2
2
|
import { UseQueryResult } from "@tanstack/react-query";
|
|
3
3
|
//#region src/people/use-members.d.ts
|
|
4
|
-
|
|
4
|
+
/** A member, with their status in an app with `status: {}` when they have one that holds (see `useStatuses`). */
|
|
5
|
+
export type Member = OrganizationPerson & {
|
|
6
|
+
status?: PersonStatus;
|
|
7
|
+
};
|
|
8
|
+
export type MembersResult = UseQueryResult<Member[]> & {
|
|
5
9
|
/** The member with this user id; an unknown one reads the list again (see `asksAgainFor`). */
|
|
6
|
-
member: (id: string) =>
|
|
10
|
+
member: (id: string) => Member | undefined;
|
|
7
11
|
};
|
|
8
12
|
/**
|
|
9
13
|
* The members of the organization the session works in, as `{ id, name, image? }` with `id` the user id that
|
|
10
14
|
* `.author()` stores and mentions hold, by name, readable by every member and without addresses. Read again on focus
|
|
11
|
-
* once 30 s old, and when `member` is asked for an id it lacks.
|
|
15
|
+
* once 30 s old, and when `member` is asked for an id it lacks. In an app with `status: {}`, each carries their
|
|
16
|
+
* status, which is read again every 15 seconds (see `useStatuses`). Without organizations or the app's auth it fails
|
|
12
17
|
* saying so.
|
|
13
18
|
*/
|
|
14
19
|
export declare const useMembers: () => MembersResult;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useOptionalAuth } from "../auth/auth-provider.js";
|
|
2
2
|
import { keys } from "../data/query-keys.js";
|
|
3
|
+
import { useStatuses } from "../status/use-statuses.js";
|
|
3
4
|
import { useCallback, useMemo } from "react";
|
|
4
5
|
import { useQuery } from "@tanstack/react-query";
|
|
5
6
|
//#region src/people/use-members.ts
|
|
@@ -7,10 +8,22 @@ import { useQuery } from "@tanstack/react-query";
|
|
|
7
8
|
const unknownIdRefetchMs = 1e4;
|
|
8
9
|
/** Whether an id the list lacks reads it again: once it has loaded, is not being read, and is old enough. */
|
|
9
10
|
const asksAgainFor = (query, now = Date.now()) => query.isSuccess && !query.isFetching && now - query.dataUpdatedAt >= 1e4;
|
|
11
|
+
/** The members, each with their status when they have one. */
|
|
12
|
+
const withStatuses = (people, statuses) => {
|
|
13
|
+
const byUser = new Map(statuses.map((status) => [status.userId, status]));
|
|
14
|
+
return people.map((person) => {
|
|
15
|
+
const status = byUser.get(person.id);
|
|
16
|
+
return status ? {
|
|
17
|
+
...person,
|
|
18
|
+
status
|
|
19
|
+
} : person;
|
|
20
|
+
});
|
|
21
|
+
};
|
|
10
22
|
/**
|
|
11
23
|
* The members of the organization the session works in, as `{ id, name, image? }` with `id` the user id that
|
|
12
24
|
* `.author()` stores and mentions hold, by name, readable by every member and without addresses. Read again on focus
|
|
13
|
-
* once 30 s old, and when `member` is asked for an id it lacks.
|
|
25
|
+
* once 30 s old, and when `member` is asked for an id it lacks. In an app with `status: {}`, each carries their
|
|
26
|
+
* status, which is read again every 15 seconds (see `useStatuses`). Without organizations or the app's auth it fails
|
|
14
27
|
* saying so.
|
|
15
28
|
*/
|
|
16
29
|
const useMembers = () => {
|
|
@@ -24,7 +37,9 @@ const useMembers = () => {
|
|
|
24
37
|
return auth.session.organizations.people();
|
|
25
38
|
}
|
|
26
39
|
});
|
|
27
|
-
const
|
|
40
|
+
const statuses = useStatuses().data;
|
|
41
|
+
const data = useMemo(() => query.data && (statuses ? withStatuses(query.data, statuses) : query.data), [query.data, statuses]);
|
|
42
|
+
const byId = useMemo(() => new Map((data ?? []).map((person) => [person.id, person])), [data]);
|
|
28
43
|
const { refetch } = query;
|
|
29
44
|
const member = useCallback((id) => {
|
|
30
45
|
const found = byId.get(id);
|
|
@@ -37,8 +52,9 @@ const useMembers = () => {
|
|
|
37
52
|
]);
|
|
38
53
|
return {
|
|
39
54
|
...query,
|
|
55
|
+
data,
|
|
40
56
|
member
|
|
41
57
|
};
|
|
42
58
|
};
|
|
43
59
|
//#endregion
|
|
44
|
-
export { asksAgainFor, unknownIdRefetchMs, useMembers };
|
|
60
|
+
export { asksAgainFor, unknownIdRefetchMs, useMembers, withStatuses };
|