@unityevolv/ofiskit-ui-map 0.1.0
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/LICENSE +661 -0
- package/README.md +34 -0
- package/dist/Announcer.d.ts +21 -0
- package/dist/Announcer.d.ts.map +1 -0
- package/dist/Announcer.js +32 -0
- package/dist/Announcer.js.map +1 -0
- package/dist/CallAudio.d.ts +32 -0
- package/dist/CallAudio.d.ts.map +1 -0
- package/dist/CallAudio.js +72 -0
- package/dist/CallAudio.js.map +1 -0
- package/dist/CallControls.d.ts +60 -0
- package/dist/CallControls.d.ts.map +1 -0
- package/dist/CallControls.js +62 -0
- package/dist/CallControls.js.map +1 -0
- package/dist/CallTiles.d.ts +48 -0
- package/dist/CallTiles.d.ts.map +1 -0
- package/dist/CallTiles.js +134 -0
- package/dist/CallTiles.js.map +1 -0
- package/dist/DevicePanel.d.ts +32 -0
- package/dist/DevicePanel.d.ts.map +1 -0
- package/dist/DevicePanel.js +133 -0
- package/dist/DevicePanel.js.map +1 -0
- package/dist/Knocks.d.ts +46 -0
- package/dist/Knocks.d.ts.map +1 -0
- package/dist/Knocks.js +46 -0
- package/dist/Knocks.js.map +1 -0
- package/dist/OfficeMap.d.ts +69 -0
- package/dist/OfficeMap.d.ts.map +1 -0
- package/dist/OfficeMap.js +222 -0
- package/dist/OfficeMap.js.map +1 -0
- package/dist/Overflow.d.ts +41 -0
- package/dist/Overflow.d.ts.map +1 -0
- package/dist/Overflow.js +82 -0
- package/dist/Overflow.js.map +1 -0
- package/dist/PersonAvatar.d.ts +32 -0
- package/dist/PersonAvatar.d.ts.map +1 -0
- package/dist/PersonAvatar.js +80 -0
- package/dist/PersonAvatar.js.map +1 -0
- package/dist/Reactions.d.ts +47 -0
- package/dist/Reactions.d.ts.map +1 -0
- package/dist/Reactions.js +61 -0
- package/dist/Reactions.js.map +1 -0
- package/dist/RoomBar.d.ts +43 -0
- package/dist/RoomBar.d.ts.map +1 -0
- package/dist/RoomBar.js +120 -0
- package/dist/RoomBar.js.map +1 -0
- package/dist/Share.d.ts +74 -0
- package/dist/Share.d.ts.map +1 -0
- package/dist/Share.js +115 -0
- package/dist/Share.js.map +1 -0
- package/dist/StatusControl.d.ts +25 -0
- package/dist/StatusControl.d.ts.map +1 -0
- package/dist/StatusControl.js +77 -0
- package/dist/StatusControl.js.map +1 -0
- package/dist/controls.d.ts +32 -0
- package/dist/controls.d.ts.map +1 -0
- package/dist/controls.js +24 -0
- package/dist/controls.js.map +1 -0
- package/dist/hooks.d.ts +60 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +182 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -0
- package/dist/layout.d.ts +65 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +93 -0
- package/dist/layout.js.map +1 -0
- package/dist/placement.d.ts +70 -0
- package/dist/placement.d.ts.map +1 -0
- package/dist/placement.js +83 -0
- package/dist/placement.js.map +1 -0
- package/dist/sounds.d.ts +30 -0
- package/dist/sounds.d.ts.map +1 -0
- package/dist/sounds.js +125 -0
- package/dist/sounds.js.map +1 -0
- package/dist/status.d.ts +47 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +65 -0
- package/dist/status.js.map +1 -0
- package/dist/theme.d.ts +27 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +57 -0
- package/dist/theme.js.map +1 -0
- package/dist/tileLayout.d.ts +27 -0
- package/dist/tileLayout.d.ts.map +1 -0
- package/dist/tileLayout.js +41 -0
- package/dist/tileLayout.js.map +1 -0
- package/dist/useCall.d.ts +107 -0
- package/dist/useCall.d.ts.map +1 -0
- package/dist/useCall.js +238 -0
- package/dist/useCall.js.map +1 -0
- package/dist/useShare.d.ts +54 -0
- package/dist/useShare.d.ts.map +1 -0
- package/dist/useShare.js +114 -0
- package/dist/useShare.js.map +1 -0
- package/dist/useSounds.d.ts +31 -0
- package/dist/useSounds.d.ts.map +1 -0
- package/dist/useSounds.js +56 -0
- package/dist/useSounds.js.map +1 -0
- package/package.json +25 -0
package/dist/Share.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Button, Icon, Modal } from '@unityevolv/unitykit';
|
|
3
|
+
import { usableSources } from '@unityevolv/ofiskit-realtime-client';
|
|
4
|
+
import { useEffect, useRef } from 'react';
|
|
5
|
+
export function ShareStage({ sharerName, mine, stream, speakerDeviceId, onStop }) {
|
|
6
|
+
const video = useRef(null);
|
|
7
|
+
// Assigned imperatively: a MediaStream is not a URL and React has no prop for it.
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
if (video.current)
|
|
10
|
+
video.current.srcObject = stream ?? null;
|
|
11
|
+
}, [stream]);
|
|
12
|
+
/*
|
|
13
|
+
* Where a shared tab's sound comes out.
|
|
14
|
+
*
|
|
15
|
+
* The same routing the voices get, because it is part of the same conversation: a
|
|
16
|
+
* share whose sound stays on the laptop speakers while everybody is listening
|
|
17
|
+
* through a headset is both confusing and a source of echo. `setSinkId` is missing
|
|
18
|
+
* on Safari and on mobile, where the operating system decides — which is the right
|
|
19
|
+
* answer on a phone — so a failure here is silently the system default.
|
|
20
|
+
*/
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
const element = video.current;
|
|
23
|
+
if (!element || !speakerDeviceId)
|
|
24
|
+
return;
|
|
25
|
+
if (typeof element.setSinkId !== 'function')
|
|
26
|
+
return;
|
|
27
|
+
void element.setSinkId(speakerDeviceId).catch(() => { });
|
|
28
|
+
}, [speakerDeviceId, stream]);
|
|
29
|
+
/*
|
|
30
|
+
* Your own share is not played back to you.
|
|
31
|
+
*
|
|
32
|
+
* Drawing a capture of this screen on this screen is the hall of mirrors everybody
|
|
33
|
+
* has seen in a call at least once, and it tells the person nothing they do not
|
|
34
|
+
* already know. What they need is what is here instead: confirmation that it is
|
|
35
|
+
* going out, and a way to stop it.
|
|
36
|
+
*/
|
|
37
|
+
if (mine) {
|
|
38
|
+
return (_jsxs("div", { "data-testid": "share-stage",
|
|
39
|
+
// Scrolls rather than spills when the screen is short — a phone on its
|
|
40
|
+
// side leaves about a hundred pixels here, and the stop button used to
|
|
41
|
+
// land on top of the sharing banner below it.
|
|
42
|
+
className: "flex min-h-0 flex-1 flex-col items-center [justify-content:safe_center] gap-3 overflow-y-auto bg-base-200 p-6 text-center [@media(max-height:480px)]:gap-1 [@media(max-height:480px)]:p-2", children: [_jsx("span", { className: "text-primary [@media(max-height:480px)]:hidden", children: _jsx(Icon, { name: "share", size: "lg" }) }), _jsx("p", { className: "text-sm font-medium", children: "You are sharing your screen." }), _jsx("p", { className: "max-w-sm text-xs text-base-content/70 [@media(max-height:480px)]:hidden", children: "Everybody in the call can see it. Your own screen is not shown back to you, because that would be a picture of a picture." }), onStop && (_jsx(Button, { size: "sm", variant: "secondary", onClick: onStop, children: "Stop sharing" }))] }));
|
|
43
|
+
}
|
|
44
|
+
return (_jsxs("div", { "data-testid": "share-stage", className: "relative flex min-h-0 flex-1 items-center justify-center bg-base-300", children: [stream ? (_jsx("video", { ref: video, autoPlay: true, playsInline: true, "aria-label": `${sharerName}: shared screen`,
|
|
45
|
+
// Contained rather than cropped. A share is usually text, and text with its
|
|
46
|
+
// edges cut off is a share nobody can read.
|
|
47
|
+
className: "h-full w-full object-contain" })) : (
|
|
48
|
+
// Not an error: the call has said somebody is sharing and the stream is on
|
|
49
|
+
// its way. Saying whose it is makes the wait explicable.
|
|
50
|
+
_jsxs("p", { className: "p-6 text-sm text-base-content/70", children: ["Waiting for ", sharerName, "\u2019s screen\u2026"] })), _jsx("span", { className: "absolute left-2 top-2 rounded bg-base-100/85 px-2 py-0.5 text-xs backdrop-blur-sm", children: sharerName })] }));
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The sharer's own reminder that they are still sharing.
|
|
54
|
+
*
|
|
55
|
+
* Persistent, not dismissible, and present in every view including the map, because
|
|
56
|
+
* forgetting to stop is the commonest failure in any call product and the
|
|
57
|
+
* consequence of it is somebody's inbox on a projector. The stop control is here as
|
|
58
|
+
* well as in the bar so that it is one click from wherever the person has wandered.
|
|
59
|
+
*/
|
|
60
|
+
export function SharingBanner({ onStop }) {
|
|
61
|
+
return (_jsxs("div", { "data-testid": "sharing-banner", className: "flex items-center justify-center gap-2 border-t border-primary/30 bg-primary/10 px-3 py-1 text-xs", children: [_jsx("span", { className: "text-primary", children: _jsx(Icon, { name: "share", size: "xs" }) }), _jsx("span", { children: "You are sharing your screen." }), _jsx(Button, { size: "sm", variant: "ghost", onClick: onStop, children: "Stop sharing" })] }));
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Taking over somebody else's share.
|
|
65
|
+
*
|
|
66
|
+
* Asked rather than refused, because the person who wants to show something next is
|
|
67
|
+
* usually right that they do — and asked rather than done quietly, because a share
|
|
68
|
+
* that disappears mid-sentence looks like a crash to whoever was presenting.
|
|
69
|
+
*/
|
|
70
|
+
export function TakeOverDialog({ sharerName, onConfirm, onCancel, }) {
|
|
71
|
+
return (_jsx(Modal, { open: true, onOpenChange: (next) => {
|
|
72
|
+
// Escape and the overlay both mean "no". A dialog whose only safe answer is
|
|
73
|
+
// the one button on it is a dialog people press the wrong thing in.
|
|
74
|
+
if (!next)
|
|
75
|
+
onCancel();
|
|
76
|
+
}, title: "Take over sharing?", footer: _jsxs(_Fragment, { children: [_jsx(Button, { size: "sm", variant: "ghost", onClick: onCancel, children: "Cancel" }), _jsx(Button, { size: "sm", onClick: onConfirm, children: "Take over" })] }), children: _jsxs("p", { className: "text-sm", children: [sharerName, " is sharing a screen. A call shows one screen at a time, so yours would replace theirs, and they would be told that it stopped."] }) }));
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The picker for a platform whose browser has none.
|
|
80
|
+
*
|
|
81
|
+
* On the web this never appears: `getDisplayMedia` opens the browser's own picker,
|
|
82
|
+
* which people already know and which is the only one that can offer a single tab.
|
|
83
|
+
* A desktop app has no such dialog — the platform hands over a list of screens and
|
|
84
|
+
* windows and expects the app to draw it — so this is that list.
|
|
85
|
+
*
|
|
86
|
+
* Our own window is never in it. Sharing the app that is doing the sharing produces
|
|
87
|
+
* an infinite mirror, and it is dropped by `usableSources` rather than here, so the
|
|
88
|
+
* rule belongs to the list rather than to this one drawing of it.
|
|
89
|
+
*/
|
|
90
|
+
export function ScreenSourcePicker({ sources, onPick, onCancel }) {
|
|
91
|
+
const offered = usableSources(sources);
|
|
92
|
+
return (_jsx(Modal, { open: true, onOpenChange: (next) => {
|
|
93
|
+
if (!next)
|
|
94
|
+
onCancel();
|
|
95
|
+
}, title: "Choose what to share", footer: _jsx(Button, { size: "sm", variant: "ghost", onClick: onCancel, children: "Cancel" }), children: offered.length === 0 ? (
|
|
96
|
+
// Empty rather than broken, and it says what to do about it.
|
|
97
|
+
_jsx("p", { className: "text-sm text-base-content/70", children: "There is nothing available to share. Open the window you want to show, then try again." })) : (_jsx("ul", { "data-testid": "screen-sources", className: "grid grid-cols-2 gap-2 sm:grid-cols-3", children: offered.map((source) => (_jsx("li", { children: _jsxs("button", { type: "button", onClick: () => onPick(source.id), "aria-label": `Share ${source.kind}: ${source.name}`, className: "w-full rounded-lg border border-base-300 p-1 text-left hover:border-primary focus-visible:outline-2 focus-visible:outline-primary", children: [source.thumbnailUrl ? (
|
|
98
|
+
// A list of window titles is close to useless — half of them are
|
|
99
|
+
// the same word repeated — and the thumbnail is how somebody
|
|
100
|
+
// recognises the thing they meant. Empty alt: the button above is
|
|
101
|
+
// already named, and a second reading adds nothing.
|
|
102
|
+
_jsx("img", { src: source.thumbnailUrl, alt: "", className: "aspect-video w-full rounded object-cover" })) : (_jsx("span", { className: "grid aspect-video w-full place-items-center rounded bg-base-200", children: _jsx(Icon, { name: "share", size: "sm" }) })), _jsx("span", { className: "mt-1 block truncate text-xs", children: source.name })] }) }, source.id))) })) }));
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* What a live region says about a share.
|
|
106
|
+
*
|
|
107
|
+
* Politely, and named. A share is a change to what is on everybody's screen, and
|
|
108
|
+
* somebody who cannot see it change is precisely the person who needs telling.
|
|
109
|
+
*/
|
|
110
|
+
export function describeShare(sharerName, sharing) {
|
|
111
|
+
return sharing
|
|
112
|
+
? `${sharerName} is sharing their screen.`
|
|
113
|
+
: `${sharerName} stopped sharing their screen.`;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=Share.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Share.js","sourceRoot":"","sources":["../src/Share.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { CustomStatus, ManualStatus, PublicPresence } from '@unityevolv/ofiskit-realtime-client';
|
|
2
|
+
export interface StatusControlProps {
|
|
3
|
+
you: PublicPresence | null;
|
|
4
|
+
/** True when the person chose this status, rather than the office working it out. */
|
|
5
|
+
chosen: boolean;
|
|
6
|
+
/** True when do not disturb came from standing in the break room. */
|
|
7
|
+
fromBreakRoom: boolean;
|
|
8
|
+
/** Presets an org defined. The free office has none, and shows none. */
|
|
9
|
+
presets?: readonly CustomStatus[];
|
|
10
|
+
onSetStatus(manual: ManualStatus | null): void;
|
|
11
|
+
onSetCustom(custom: CustomStatus | null): void;
|
|
12
|
+
/**
|
|
13
|
+
* Whether this device plays the office's sounds — the knock at the door, the
|
|
14
|
+
* chime when somebody walks in.
|
|
15
|
+
*
|
|
16
|
+
* Here, beside do not disturb, because they answer the same question: how much
|
|
17
|
+
* the office is allowed to interrupt. Absent means the host offers no sounds.
|
|
18
|
+
*/
|
|
19
|
+
sounds?: {
|
|
20
|
+
on: boolean;
|
|
21
|
+
onChange(on: boolean): void;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export declare function StatusControl(props: StatusControlProps): import("react").JSX.Element;
|
|
25
|
+
//# sourceMappingURL=StatusControl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StatusControl.d.ts","sourceRoot":"","sources":["../src/StatusControl.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,YAAY,EACZ,YAAY,EACZ,cAAc,EACf,MAAM,qCAAqC,CAAA;AAuB5C,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,cAAc,GAAG,IAAI,CAAA;IAC1B,qFAAqF;IACrF,MAAM,EAAE,OAAO,CAAA;IACf,qEAAqE;IACrE,aAAa,EAAE,OAAO,CAAA;IACtB,wEAAwE;IACxE,OAAO,CAAC,EAAE,SAAS,YAAY,EAAE,CAAA;IACjC,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,GAAG,IAAI,CAAA;IAC9C,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,GAAG,IAAI,CAAA;IAC9C;;;;;;OAMG;IACH,MAAM,CAAC,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAA;KAAE,CAAA;CACtD;AAuCD,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,+BAyKtD"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button, Icon, Input, Popover, Select, Toggle } from '@unityevolv/unitykit';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { StatusDot, statusLabel } from './status.js';
|
|
5
|
+
/**
|
|
6
|
+
* Where a person sets and sees their own status.
|
|
7
|
+
*
|
|
8
|
+
* Two things are going on at once and the control has to keep them apart: what
|
|
9
|
+
* the product worked out (idle, in a call, in the break room) and what the
|
|
10
|
+
* person chose. A chosen status always wins and survives everything automatic,
|
|
11
|
+
* so the control says which one is in force and offers a clear way back.
|
|
12
|
+
*/
|
|
13
|
+
/** The fixed-length choices. Everything else is worked out in the viewer's zone. */
|
|
14
|
+
const DURATIONS = [
|
|
15
|
+
{ id: '30m', label: '30 minutes', ms: 30 * 60 * 1000 },
|
|
16
|
+
{ id: '1h', label: '1 hour', ms: 60 * 60 * 1000 },
|
|
17
|
+
{ id: '4h', label: '4 hours', ms: 4 * 60 * 60 * 1000 },
|
|
18
|
+
{ id: 'today', label: 'Today', ms: null },
|
|
19
|
+
{ id: 'never', label: 'Until I clear it', ms: null },
|
|
20
|
+
];
|
|
21
|
+
const CHOICES = [
|
|
22
|
+
{ value: 'available', label: 'Available' },
|
|
23
|
+
{ value: 'away', label: 'Away' },
|
|
24
|
+
{ value: 'dnd', label: 'Do not disturb' },
|
|
25
|
+
];
|
|
26
|
+
/**
|
|
27
|
+
* When a duration expires, in the viewer's zone.
|
|
28
|
+
*
|
|
29
|
+
* "Today" means the end of today where the person is, so the client works it
|
|
30
|
+
* out and sends an absolute instant. The server never computes a date in
|
|
31
|
+
* anybody's time zone, and it should not have to know where anyone is.
|
|
32
|
+
*/
|
|
33
|
+
function expiryFor(id) {
|
|
34
|
+
const choice = DURATIONS.find((duration) => duration.id === id);
|
|
35
|
+
if (!choice || choice.id === 'never')
|
|
36
|
+
return undefined;
|
|
37
|
+
if (choice.ms !== null)
|
|
38
|
+
return new Date(Date.now() + choice.ms).toISOString();
|
|
39
|
+
const endOfDay = new Date();
|
|
40
|
+
endOfDay.setHours(23, 59, 59, 999);
|
|
41
|
+
return endOfDay.toISOString();
|
|
42
|
+
}
|
|
43
|
+
/** Formats an expiry for the person looking at it, in their own zone. */
|
|
44
|
+
function formatExpiry(iso) {
|
|
45
|
+
const when = new Date(iso);
|
|
46
|
+
const today = new Date();
|
|
47
|
+
const sameDay = when.toDateString() === today.toDateString();
|
|
48
|
+
return new Intl.DateTimeFormat(undefined, {
|
|
49
|
+
hour: 'numeric',
|
|
50
|
+
minute: '2-digit',
|
|
51
|
+
...(sameDay ? {} : { weekday: 'short' }),
|
|
52
|
+
}).format(when);
|
|
53
|
+
}
|
|
54
|
+
export function StatusControl(props) {
|
|
55
|
+
const { you, chosen, fromBreakRoom, presets = [] } = props;
|
|
56
|
+
const [text, setText] = useState('');
|
|
57
|
+
const [emoji, setEmoji] = useState('');
|
|
58
|
+
const [duration, setDuration] = useState('1h');
|
|
59
|
+
const status = you?.status ?? 'offline';
|
|
60
|
+
return (_jsx(Popover, { width: "auto", trigger: _jsxs("button", { type: "button", className: "inline-flex min-w-0 max-w-52 items-center gap-1.5 rounded-md px-2 py-1 text-sm hover:bg-base-200 focus-visible:outline-2 focus-visible:outline-primary", "aria-label": `Your status: ${statusLabel(status)}${you?.custom ? `, ${you.custom.text}` : ''}. Change it.`, children: [_jsx(StatusDot, { status: status, size: 12, labelled: false }), _jsx("span", { className: "truncate", children: you?.custom
|
|
61
|
+
? `${you.custom.emoji ?? ''} ${you.custom.text}`.trim()
|
|
62
|
+
: statusLabel(status) }), _jsx(Icon, { name: "chevron-down", size: "xs" })] }), children: _jsxs("div", { className: "w-72 max-w-[calc(100vw-4rem)] space-y-3", children: [_jsxs("div", { children: [_jsx("p", { className: "mb-1 text-xs font-medium text-base-content/70", children: "Your status" }), _jsx("div", { className: "flex flex-col gap-0.5", children: CHOICES.map((choice) => (_jsxs("button", { type: "button", onClick: () => props.onSetStatus(choice.value), "aria-pressed": chosen && status === choice.value, className: [
|
|
63
|
+
'flex items-center gap-2 rounded px-2 py-1.5 text-left text-sm',
|
|
64
|
+
'hover:bg-base-200 focus-visible:outline-2 focus-visible:outline-primary',
|
|
65
|
+
chosen && status === choice.value ? 'bg-base-200 font-medium' : '',
|
|
66
|
+
].join(' '), children: [_jsx(StatusDot, { status: choice.value, size: 12, labelled: false }), choice.label] }, choice.value))) }), chosen && (_jsx("button", { type: "button", onClick: () => props.onSetStatus(null), className: "mt-1 w-full rounded px-2 py-1 text-left text-xs text-primary hover:bg-base-200 focus-visible:outline-2 focus-visible:outline-primary", children: "Go back to working it out automatically" })), !chosen && fromBreakRoom && (_jsx("p", { className: "mt-1 px-2 text-xs text-base-content/70", children: "Do not disturb, because you are in the break room. Knocks still arrive, silently." }))] }), _jsxs("div", { className: "border-t border-base-300 pt-3", children: [_jsx("p", { className: "mb-1 text-xs font-medium text-base-content/70", children: "Custom status" }), presets.length > 0 && (_jsx("div", { className: "mb-2 flex flex-wrap gap-1", children: presets.map((preset) => (_jsxs("button", { type: "button", onClick: () => props.onSetCustom(preset), className: "rounded-full bg-base-200 px-2 py-0.5 text-xs hover:bg-base-300 focus-visible:outline-2 focus-visible:outline-primary", children: [preset.emoji, " ", preset.text] }, preset.text))) })), _jsxs("div", { className: "flex gap-1", children: [_jsx(Input, { "aria-label": "Emoji", value: emoji, onChange: (event) => setEmoji(event.target.value.slice(0, 4)), placeholder: "\uD83E\uDD6A", className: "w-14 text-center" }), _jsx(Input, { "aria-label": "What is your status?", value: text, onChange: (event) => setText(event.target.value.slice(0, 100)), placeholder: "Lunch, back at two", className: "flex-1" })] }), _jsx(Select, { label: "Clear it", className: "mt-2", value: duration, onChange: (event) => setDuration(event.target.value), children: DURATIONS.map((choice) => (_jsx("option", { value: choice.id, children: choice.label }, choice.id))) }), _jsxs("div", { className: "mt-2 flex gap-2", children: [_jsx(Button, { size: "sm", disabled: text.trim().length === 0, onClick: () => {
|
|
67
|
+
const expiresAt = expiryFor(duration);
|
|
68
|
+
props.onSetCustom({
|
|
69
|
+
text: text.trim(),
|
|
70
|
+
...(emoji ? { emoji } : {}),
|
|
71
|
+
...(expiresAt ? { expiresAt } : {}),
|
|
72
|
+
});
|
|
73
|
+
setText('');
|
|
74
|
+
setEmoji('');
|
|
75
|
+
}, children: "Set status" }), you?.custom && (_jsx(Button, { size: "sm", variant: "ghost", onClick: () => props.onSetCustom(null), children: "Clear" }))] }), you?.custom?.expiresAt && (_jsxs("p", { className: "mt-1 text-xs text-base-content/70", children: ["Clears at ", formatExpiry(you.custom.expiresAt), "."] }))] }), props.sounds && (_jsx("div", { className: "border-t border-base-300 pt-3", children: _jsx(Toggle, { checked: props.sounds.on, onChange: (event) => props.sounds?.onChange(event.target.checked), label: "Sounds for knocks and arrivals" }) }))] }) }));
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=StatusControl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StatusControl.js","sourceRoot":"","sources":["../src/StatusControl.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One look, and one set of manners, for every control in the office bar.
|
|
3
|
+
*
|
|
4
|
+
* Extracted because there are now three kinds of thing in that row — a plain
|
|
5
|
+
* control, a popover trigger, and the share button with its own decisions — and a
|
|
6
|
+
* second copy of these classes is how two buttons sitting beside each other end up
|
|
7
|
+
* a pixel different from one another.
|
|
8
|
+
*/
|
|
9
|
+
export interface ControlProps {
|
|
10
|
+
/**
|
|
11
|
+
* What pressing it will do, rather than what is true now.
|
|
12
|
+
*
|
|
13
|
+
* The rule the whole bar follows: a control reading "Mute" while you are not in
|
|
14
|
+
* the call is a control that lies, and this is the label a screen reader reads.
|
|
15
|
+
*/
|
|
16
|
+
label: string;
|
|
17
|
+
icon: 'mic' | 'mic-off' | 'video' | 'video-off' | 'share' | 'raise-hand' | 'reactions' | 'call-view' | 'settings';
|
|
18
|
+
/** A keyboard shortcut, shown in the tooltip and never as a second label. */
|
|
19
|
+
hint?: string;
|
|
20
|
+
active?: boolean;
|
|
21
|
+
danger?: boolean;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
reason?: string | null;
|
|
24
|
+
onClick(): void;
|
|
25
|
+
}
|
|
26
|
+
export declare function controlClasses({ active, danger, disabled, }: {
|
|
27
|
+
active?: boolean;
|
|
28
|
+
danger?: boolean;
|
|
29
|
+
disabled?: boolean;
|
|
30
|
+
}): string;
|
|
31
|
+
export declare function Control({ label, icon, hint, active, danger, disabled, reason, onClick, }: ControlProps): import("react").JSX.Element;
|
|
32
|
+
//# sourceMappingURL=controls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controls.d.ts","sourceRoot":"","sources":["../src/controls.tsx"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EACA,KAAK,GACL,SAAS,GACT,OAAO,GACP,WAAW,GACX,OAAO,GACP,YAAY,GACZ,WAAW,GACX,WAAW,GACX,UAAU,CAAA;IACd,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,OAAO,IAAI,IAAI,CAAA;CAChB;AAED,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,MAAM,EACN,QAAQ,GACT,EAAE;IACD,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,GAAG,MAAM,CAgBT;AAED,wBAAgB,OAAO,CAAC,EACtB,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,MAAM,EACN,QAAQ,EACR,MAAM,EACN,OAAO,GACR,EAAE,YAAY,+BAmBd"}
|
package/dist/controls.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Icon, Tooltip } from '@unityevolv/unitykit';
|
|
3
|
+
export function controlClasses({ active, danger, disabled, }) {
|
|
4
|
+
return [
|
|
5
|
+
// A touch smaller on the narrowest phones, where seven of them at full size do
|
|
6
|
+
// not fit one row; 32px is still well over the 24px a tap target needs.
|
|
7
|
+
'inline-flex h-9 w-9 items-center justify-center rounded-lg max-[340px]:h-8 max-[340px]:w-8',
|
|
8
|
+
'focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-primary',
|
|
9
|
+
disabled
|
|
10
|
+
? 'cursor-not-allowed opacity-40'
|
|
11
|
+
: danger
|
|
12
|
+
? // Muted is a state worth noticing at a glance, because talking while
|
|
13
|
+
// muted is the commonest thing that happens in any call product.
|
|
14
|
+
'bg-error/15 text-error hover:bg-error/25'
|
|
15
|
+
: active
|
|
16
|
+
? 'bg-primary text-primary-content hover:bg-primary/90'
|
|
17
|
+
: 'hover:bg-base-200',
|
|
18
|
+
].join(' ');
|
|
19
|
+
}
|
|
20
|
+
export function Control({ label, icon, hint, active, danger, disabled, reason, onClick, }) {
|
|
21
|
+
const button = (_jsx("button", { type: "button", onClick: onClick, disabled: disabled, "aria-label": label, "aria-pressed": active ?? false, className: controlClasses({ active, danger, disabled }), children: _jsx(Icon, { name: icon, size: "sm" }) }));
|
|
22
|
+
return (_jsx(Tooltip, { content: disabled && reason ? reason : hint ? `${label} (${hint})` : label, children: button }));
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=controls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controls.js","sourceRoot":"","sources":["../src/controls.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAsCpD,MAAM,UAAU,cAAc,CAAC,EAC7B,MAAM,EACN,MAAM,EACN,QAAQ,GAKT;IACC,OAAO;QACL,+EAA+E;QAC/E,wEAAwE;QACxE,4FAA4F;QAC5F,sFAAsF;QACtF,QAAQ;YACN,CAAC,CAAC,+BAA+B;YACjC,CAAC,CAAC,MAAM;gBACN,CAAC,CAAC,qEAAqE;oBACrE,iEAAiE;oBACjE,0CAA0C;gBAC5C,CAAC,CAAC,MAAM;oBACN,CAAC,CAAC,qDAAqD;oBACvD,CAAC,CAAC,mBAAmB;KAC5B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACb,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,EACtB,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,MAAM,EACN,QAAQ,EACR,MAAM,EACN,OAAO,GACM;IACb,MAAM,MAAM,GAAG,CACb,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,gBACN,KAAK,kBACH,MAAM,IAAI,KAAK,EAC7B,SAAS,EAAE,cAAc,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,YAEvD,KAAC,IAAI,IAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAC,IAAI,GAAG,GACvB,CACV,CAAA;IAED,OAAO,CACL,KAAC,OAAO,IAAC,OAAO,EAAE,QAAQ,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,YAChF,MAAM,GACC,CACX,CAAA;AACH,CAAC"}
|
package/dist/hooks.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { ClientEvent, OfficeState, OfisClient } from '@unityevolv/ofiskit-realtime-client';
|
|
2
|
+
/**
|
|
3
|
+
* The React bindings.
|
|
4
|
+
*
|
|
5
|
+
* The client itself is framework-free so a React Native app can use it too.
|
|
6
|
+
* These are the few hooks that turn it into something React can render, plus the
|
|
7
|
+
* browser-only behaviour that cannot live in that package at all.
|
|
8
|
+
*/
|
|
9
|
+
/** The office, re-rendered when it changes. */
|
|
10
|
+
export declare function useOffice(client: OfisClient): OfficeState;
|
|
11
|
+
/** Things that happen to you: a refusal to announce, a template that changed. */
|
|
12
|
+
export declare function useClientEvents(client: OfisClient, handler: (event: ClientEvent) => void): void;
|
|
13
|
+
/**
|
|
14
|
+
* Whether the person has asked for less motion.
|
|
15
|
+
*
|
|
16
|
+
* Honoured throughout: somebody moving room appears in the new room rather than
|
|
17
|
+
* sliding to it. Motion is the part of this product most likely to make somebody
|
|
18
|
+
* feel unwell, and a map where everything slides is the worst case of it.
|
|
19
|
+
*/
|
|
20
|
+
export declare function useReducedMotion(): boolean;
|
|
21
|
+
/** True while the viewport matches. Used to decide the narrow-screen layout. */
|
|
22
|
+
export declare function useMediaQuery(query: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* A preference that survives a reload.
|
|
25
|
+
*
|
|
26
|
+
* Wrapped in try/catch because storage throws in private windows and with site
|
|
27
|
+
* data blocked, and a remembered preference is never worth a blank page.
|
|
28
|
+
*/
|
|
29
|
+
export declare function usePersisted<T>(key: string, fallback: T): [T, (value: T) => void];
|
|
30
|
+
/**
|
|
31
|
+
* The size of an element, measured rather than guessed.
|
|
32
|
+
*
|
|
33
|
+
* The map shares its space with the tile strip, which appears and disappears as
|
|
34
|
+
* calls start and end, and a room bar decides how much of itself to show from how
|
|
35
|
+
* wide its room actually is on screen. Neither can be worked out from a media
|
|
36
|
+
* query.
|
|
37
|
+
*/
|
|
38
|
+
export declare function useMeasured<T extends HTMLElement>(): [
|
|
39
|
+
React.RefObject<T | null>,
|
|
40
|
+
{
|
|
41
|
+
width: number;
|
|
42
|
+
height: number;
|
|
43
|
+
}
|
|
44
|
+
];
|
|
45
|
+
/**
|
|
46
|
+
* Tell the office when this device goes quiet.
|
|
47
|
+
*
|
|
48
|
+
* Keyboard and pointer activity, plus the page being hidden, which is what a
|
|
49
|
+
* screen locking or a tab going to the background looks like from here. The
|
|
50
|
+
* server resolves across every device the person has open, so typing on a phone
|
|
51
|
+
* keeps somebody available while their laptop sits idle.
|
|
52
|
+
*
|
|
53
|
+
* Suspended by the caller during a call, because somebody listening is not idle
|
|
54
|
+
* even though they have not touched anything for twenty minutes.
|
|
55
|
+
*/
|
|
56
|
+
export declare function useIdleReporting(client: OfisClient, options?: {
|
|
57
|
+
enabled?: boolean;
|
|
58
|
+
afterMs?: number;
|
|
59
|
+
}): void;
|
|
60
|
+
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAA;AAG/F;;;;;;GAMG;AAEH,+CAA+C;AAC/C,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW,CAMzD;AAED,iFAAiF;AACjF,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,IAAI,CAY/F;AA2BD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC,CAuBjF;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,WAAW,KAAK;IACpD,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC;IACzB;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;CAClC,CAiBA;AAWD;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GACpD,IAAI,CAmDN"}
|
package/dist/hooks.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* The React bindings.
|
|
4
|
+
*
|
|
5
|
+
* The client itself is framework-free so a React Native app can use it too.
|
|
6
|
+
* These are the few hooks that turn it into something React can render, plus the
|
|
7
|
+
* browser-only behaviour that cannot live in that package at all.
|
|
8
|
+
*/
|
|
9
|
+
/** The office, re-rendered when it changes. */
|
|
10
|
+
export function useOffice(client) {
|
|
11
|
+
return useSyncExternalStore(useCallback((notify) => client.subscribe(notify), [client]), useCallback(() => client.state(), [client]), useCallback(() => client.state(), [client]));
|
|
12
|
+
}
|
|
13
|
+
/** Things that happen to you: a refusal to announce, a template that changed. */
|
|
14
|
+
export function useClientEvents(client, handler) {
|
|
15
|
+
// Held in a ref so a caller can pass an inline function without
|
|
16
|
+
// re-subscribing on every render — and written in an effect rather than during
|
|
17
|
+
// render, because a render that is thrown away must not be able to change what
|
|
18
|
+
// the live subscription will call.
|
|
19
|
+
const latest = useRef(handler);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
latest.current = handler;
|
|
22
|
+
}, [handler]);
|
|
23
|
+
useEffect(() => client.on((event) => latest.current(event)), [client]);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Subscribe to a media query.
|
|
27
|
+
*
|
|
28
|
+
* `useSyncExternalStore` rather than an effect that calls setState: the browser
|
|
29
|
+
* is the source of truth, React is reading from it, and this is the primitive for
|
|
30
|
+
* exactly that. It also gets the first paint right, which an effect cannot.
|
|
31
|
+
*/
|
|
32
|
+
function useMatchMedia(query) {
|
|
33
|
+
const subscribe = useCallback((notify) => {
|
|
34
|
+
const list = globalThis.matchMedia?.(query);
|
|
35
|
+
if (!list)
|
|
36
|
+
return () => { };
|
|
37
|
+
list.addEventListener('change', notify);
|
|
38
|
+
return () => list.removeEventListener('change', notify);
|
|
39
|
+
}, [query]);
|
|
40
|
+
const read = useCallback(() => globalThis.matchMedia?.(query).matches ?? false, [query]);
|
|
41
|
+
// The server snapshot is false: there is no viewport to ask on a server, and
|
|
42
|
+
// guessing would mean the first client render disagreed with the markup.
|
|
43
|
+
return useSyncExternalStore(subscribe, read, () => false);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Whether the person has asked for less motion.
|
|
47
|
+
*
|
|
48
|
+
* Honoured throughout: somebody moving room appears in the new room rather than
|
|
49
|
+
* sliding to it. Motion is the part of this product most likely to make somebody
|
|
50
|
+
* feel unwell, and a map where everything slides is the worst case of it.
|
|
51
|
+
*/
|
|
52
|
+
export function useReducedMotion() {
|
|
53
|
+
return useMatchMedia('(prefers-reduced-motion: reduce)');
|
|
54
|
+
}
|
|
55
|
+
/** True while the viewport matches. Used to decide the narrow-screen layout. */
|
|
56
|
+
export function useMediaQuery(query) {
|
|
57
|
+
return useMatchMedia(query);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* A preference that survives a reload.
|
|
61
|
+
*
|
|
62
|
+
* Wrapped in try/catch because storage throws in private windows and with site
|
|
63
|
+
* data blocked, and a remembered preference is never worth a blank page.
|
|
64
|
+
*/
|
|
65
|
+
export function usePersisted(key, fallback) {
|
|
66
|
+
const [value, setValue] = useState(() => {
|
|
67
|
+
try {
|
|
68
|
+
const stored = localStorage.getItem(key);
|
|
69
|
+
return stored === null ? fallback : JSON.parse(stored);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return fallback;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
const store = useCallback((next) => {
|
|
76
|
+
setValue(next);
|
|
77
|
+
try {
|
|
78
|
+
localStorage.setItem(key, JSON.stringify(next));
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// Not worth telling anybody about.
|
|
82
|
+
}
|
|
83
|
+
}, [key]);
|
|
84
|
+
return [value, store];
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The size of an element, measured rather than guessed.
|
|
88
|
+
*
|
|
89
|
+
* The map shares its space with the tile strip, which appears and disappears as
|
|
90
|
+
* calls start and end, and a room bar decides how much of itself to show from how
|
|
91
|
+
* wide its room actually is on screen. Neither can be worked out from a media
|
|
92
|
+
* query.
|
|
93
|
+
*/
|
|
94
|
+
export function useMeasured() {
|
|
95
|
+
const ref = useRef(null);
|
|
96
|
+
const [size, setSize] = useState({ width: 0, height: 0 });
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
const element = ref.current;
|
|
99
|
+
if (!element || typeof ResizeObserver === 'undefined')
|
|
100
|
+
return;
|
|
101
|
+
const observer = new ResizeObserver(([entry]) => {
|
|
102
|
+
const rect = entry?.contentRect;
|
|
103
|
+
if (rect)
|
|
104
|
+
setSize({ width: rect.width, height: rect.height });
|
|
105
|
+
});
|
|
106
|
+
observer.observe(element);
|
|
107
|
+
return () => observer.disconnect();
|
|
108
|
+
}, []);
|
|
109
|
+
return [ref, size];
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* How long without keyboard or pointer before this device counts as idle.
|
|
113
|
+
*
|
|
114
|
+
* Ten minutes, the same number the presence store resolves against. It is here as
|
|
115
|
+
* well because the device is what notices, and the server is what decides — this
|
|
116
|
+
* reports one device's signal and never a conclusion about the person.
|
|
117
|
+
*/
|
|
118
|
+
const IDLE_AFTER_MS = 10 * 60 * 1000;
|
|
119
|
+
/**
|
|
120
|
+
* Tell the office when this device goes quiet.
|
|
121
|
+
*
|
|
122
|
+
* Keyboard and pointer activity, plus the page being hidden, which is what a
|
|
123
|
+
* screen locking or a tab going to the background looks like from here. The
|
|
124
|
+
* server resolves across every device the person has open, so typing on a phone
|
|
125
|
+
* keeps somebody available while their laptop sits idle.
|
|
126
|
+
*
|
|
127
|
+
* Suspended by the caller during a call, because somebody listening is not idle
|
|
128
|
+
* even though they have not touched anything for twenty minutes.
|
|
129
|
+
*/
|
|
130
|
+
export function useIdleReporting(client, options = {}) {
|
|
131
|
+
const { enabled = true, afterMs = IDLE_AFTER_MS } = options;
|
|
132
|
+
const idle = useRef(false);
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
if (!enabled) {
|
|
135
|
+
// Leaving the feature has to leave the person active, or somebody who
|
|
136
|
+
// joined a call while idle stays away for the length of it.
|
|
137
|
+
if (idle.current) {
|
|
138
|
+
idle.current = false;
|
|
139
|
+
client.setActivity({ idle: false, foreground: true });
|
|
140
|
+
}
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
let timer = null;
|
|
144
|
+
const report = (next) => {
|
|
145
|
+
// Only on a change. These events arrive by the hundred and almost none of
|
|
146
|
+
// them mean anything to anybody else.
|
|
147
|
+
if (idle.current === next)
|
|
148
|
+
return;
|
|
149
|
+
idle.current = next;
|
|
150
|
+
client.setActivity({ idle: next, foreground: !document.hidden });
|
|
151
|
+
};
|
|
152
|
+
const restart = () => {
|
|
153
|
+
report(false);
|
|
154
|
+
if (timer)
|
|
155
|
+
clearTimeout(timer);
|
|
156
|
+
timer = setTimeout(() => report(true), afterMs);
|
|
157
|
+
};
|
|
158
|
+
const onVisibility = () => {
|
|
159
|
+
// A hidden tab is not necessarily an idle person on a laptop, but it is the
|
|
160
|
+
// only signal a browser gives for a locked screen.
|
|
161
|
+
if (document.hidden)
|
|
162
|
+
report(true);
|
|
163
|
+
else
|
|
164
|
+
restart();
|
|
165
|
+
};
|
|
166
|
+
// Passive: none of these ever calls preventDefault, and saying so keeps
|
|
167
|
+
// scrolling smooth on a touch screen.
|
|
168
|
+
const events = ['pointerdown', 'pointermove', 'keydown', 'wheel', 'touchstart'];
|
|
169
|
+
for (const event of events)
|
|
170
|
+
globalThis.addEventListener(event, restart, { passive: true });
|
|
171
|
+
document.addEventListener('visibilitychange', onVisibility);
|
|
172
|
+
restart();
|
|
173
|
+
return () => {
|
|
174
|
+
for (const event of events)
|
|
175
|
+
globalThis.removeEventListener(event, restart);
|
|
176
|
+
document.removeEventListener('visibilitychange', onVisibility);
|
|
177
|
+
if (timer)
|
|
178
|
+
clearTimeout(timer);
|
|
179
|
+
};
|
|
180
|
+
}, [client, enabled, afterMs]);
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The office you can see.
|
|
3
|
+
*
|
|
4
|
+
* React DOM, built on unitykit, and deliberately not platform-agnostic: this is
|
|
5
|
+
* the package that holds the web-only half, so everything below it can run on a
|
|
6
|
+
* phone. In this app the map fills the window; in unityofis the same components
|
|
7
|
+
* render inside the app shell, which adds the header and the switchers and
|
|
8
|
+
* changes nothing here.
|
|
9
|
+
*
|
|
10
|
+
* Nothing here chooses a colour. Every surface is a kit component with a
|
|
11
|
+
* theme-aware background, because the office background is whatever an author
|
|
12
|
+
* generated and nothing legible can be guaranteed on top of it.
|
|
13
|
+
*/
|
|
14
|
+
export { ThemeProvider, useTheme, type Theme, type ThemeChoice } from './theme.js';
|
|
15
|
+
export { AnnouncerProvider, useAnnounce } from './Announcer.js';
|
|
16
|
+
export { KnockDock, OutgoingKnock, type IncomingKnock, type KnockDockProps, type KnockOutcome, type OutgoingKnockProps, } from './Knocks.js';
|
|
17
|
+
export { MaximiseButton, OfficeMap, RoomListView, ViewToggle, useMaximised, type OfficeMapProps, type OfficeView, } from './OfficeMap.js';
|
|
18
|
+
export { CallAudio, type CallAudioProps } from './CallAudio.js';
|
|
19
|
+
export { CallControls, type CallControlsProps } from './CallControls.js';
|
|
20
|
+
export { CallTiles, TILES_VISIBLE, type CallTilesProps } from './CallTiles.js';
|
|
21
|
+
export { REACTION_VISIBLE_MS, speakerOrder, useCallMedia, useReactions, useSpeakerOrder, type CallMedia, type LiveReaction, type PeerMedia, type PeerQuality, } from './useCall.js';
|
|
22
|
+
export { ReactionFloat, ReactionPicker, describeReaction, type ReactionFloatProps, type ReactionPickerProps, } from './Reactions.js';
|
|
23
|
+
/**
|
|
24
|
+
* Showing a screen, and the two questions that sometimes come first.
|
|
25
|
+
*
|
|
26
|
+
* The pickers are a seam rather than a feature: on the web the browser asks, and a
|
|
27
|
+
* desktop host supplies its own list of screens and windows because its browser
|
|
28
|
+
* cannot. Nothing here knows Electron exists.
|
|
29
|
+
*/
|
|
30
|
+
export { ScreenSourcePicker, ShareStage, SharingBanner, TakeOverDialog, describeShare, type ScreenSourcePickerProps, type ShareStageProps, } from './Share.js';
|
|
31
|
+
export { useShare, useShareView, type Share, type UseShareOptions } from './useShare.js';
|
|
32
|
+
export { createSounds, type Sounds } from './sounds.js';
|
|
33
|
+
export { useSounds, type UseSoundsOptions } from './useSounds.js';
|
|
34
|
+
export { tileLayout, VIDEO_ASPECT, type TileLayout } from './tileLayout.js';
|
|
35
|
+
export { Control, controlClasses, type ControlProps } from './controls.js';
|
|
36
|
+
export { DevicePanel, PermissionPrimer, type DevicePanelProps } from './DevicePanel.js';
|
|
37
|
+
export { RoomBar, type RoomBarProps } from './RoomBar.js';
|
|
38
|
+
export { StatusControl, type StatusControlProps } from './StatusControl.js';
|
|
39
|
+
export { PersonAvatar, type PersonAvatarProps } from './PersonAvatar.js';
|
|
40
|
+
export { OVERFLOW_COLUMNS, OVERFLOW_ROWS, OverflowAvatar, type OverflowAvatarProps, } from './Overflow.js';
|
|
41
|
+
export { StatusDot, describeStatus, statusLabel, STATUS_LOOKS, type StatusDotProps } from './status.js';
|
|
42
|
+
export { useClientEvents, useIdleReporting, useMeasured, useMediaQuery, useOffice, usePersisted, useReducedMotion, } from './hooks.js';
|
|
43
|
+
export { fitCanvas, readingOrder, roomInDirection, tilePlacement, toPixels, type CanvasBox, } from './layout.js';
|
|
44
|
+
export { placeInRoom, tokensFor, type DevicesToDraw, type PlacedAvatar, type Placement, type Token, } from './placement.js';
|
|
45
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|