@cairntale/chimeling-react 1.0.0-alpha.0 → 1.0.0-alpha.1
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 +8 -0
- package/LICENSE +1 -0
- package/dist/components/provider.js +1 -3
- package/dist/components/provider.js.map +1 -1
- package/dist/components/root.d.ts +1 -1
- package/dist/hooks/index.d.ts +3 -0
- package/dist/hooks/use-resolved-user-presence.js +1 -2
- package/dist/hooks/use-resolved-user-presence.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/lib/config.js +1 -1
- package/dist/lib/showable-presence.d.ts +1 -1
- package/dist/lib/widget-context.d.ts +1 -0
- package/dist/package.js.map +1 -0
- package/dist/types.d.ts +1 -1
- package/package.json +16 -4
- package/dist/react/package.js.map +0 -1
- package/dist/sdk/src/client/client/client.gen.js +0 -174
- package/dist/sdk/src/client/client/client.gen.js.map +0 -1
- package/dist/sdk/src/client/client/index.js +0 -5
- package/dist/sdk/src/client/client/utils.gen.js +0 -177
- package/dist/sdk/src/client/client/utils.gen.js.map +0 -1
- package/dist/sdk/src/client/core/auth.gen.js +0 -12
- package/dist/sdk/src/client/core/auth.gen.js.map +0 -1
- package/dist/sdk/src/client/core/bodySerializer.gen.js +0 -6
- package/dist/sdk/src/client/core/bodySerializer.gen.js.map +0 -1
- package/dist/sdk/src/client/core/params.gen.js +0 -9
- package/dist/sdk/src/client/core/params.gen.js.map +0 -1
- package/dist/sdk/src/client/core/pathSerializer.gen.js +0 -82
- package/dist/sdk/src/client/core/pathSerializer.gen.js.map +0 -1
- package/dist/sdk/src/client/core/serverSentEvents.gen.js +0 -96
- package/dist/sdk/src/client/core/serverSentEvents.gen.js.map +0 -1
- package/dist/sdk/src/client/core/utils.gen.js +0 -78
- package/dist/sdk/src/client/core/utils.gen.js.map +0 -1
- package/src/components/chimeling.ts +0 -9
- package/src/components/index.ts +0 -8
- package/src/components/logo.tsx +0 -18
- package/src/components/popover.tsx +0 -44
- package/src/components/provider.tsx +0 -26
- package/src/components/root.tsx +0 -91
- package/src/components/trigger.tsx +0 -33
- package/src/hooks/index.ts +0 -4
- package/src/hooks/use-client.ts +0 -8
- package/src/hooks/use-resolved-user-presence.ts +0 -27
- package/src/index.ts +0 -4
- package/src/lib/config.ts +0 -9
- package/src/lib/context.ts +0 -24
- package/src/lib/format-activity-duration.ts +0 -21
- package/src/lib/icon-url.ts +0 -5
- package/src/lib/presence-key.ts +0 -5
- package/src/lib/showable-presence.ts +0 -22
- package/src/lib/widget-context.ts +0 -33
- package/src/types.ts +0 -3
- package/tsconfig.json +0 -14
- package/tsdown.config.ts +0 -13
- package/turbo.json +0 -10
- /package/dist/{react/package.js → package.js} +0 -0
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
//#region ../sdk/src/client/core/pathSerializer.gen.ts
|
|
2
|
-
const separatorArrayExplode = (style) => {
|
|
3
|
-
switch (style) {
|
|
4
|
-
case "label": return ".";
|
|
5
|
-
case "matrix": return ";";
|
|
6
|
-
case "simple": return ",";
|
|
7
|
-
default: return "&";
|
|
8
|
-
}
|
|
9
|
-
};
|
|
10
|
-
const separatorArrayNoExplode = (style) => {
|
|
11
|
-
switch (style) {
|
|
12
|
-
case "form": return ",";
|
|
13
|
-
case "pipeDelimited": return "|";
|
|
14
|
-
case "spaceDelimited": return "%20";
|
|
15
|
-
default: return ",";
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
const separatorObjectExplode = (style) => {
|
|
19
|
-
switch (style) {
|
|
20
|
-
case "label": return ".";
|
|
21
|
-
case "matrix": return ";";
|
|
22
|
-
case "simple": return ",";
|
|
23
|
-
default: return "&";
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
const serializeArrayParam = ({ allowReserved, explode, name, style, value }) => {
|
|
27
|
-
if (!explode) {
|
|
28
|
-
const joinedValues = (allowReserved ? value : value.map((v) => encodeURIComponent(v))).join(separatorArrayNoExplode(style));
|
|
29
|
-
switch (style) {
|
|
30
|
-
case "label": return `.${joinedValues}`;
|
|
31
|
-
case "matrix": return `;${name}=${joinedValues}`;
|
|
32
|
-
case "simple": return joinedValues;
|
|
33
|
-
default: return `${name}=${joinedValues}`;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
const separator = separatorArrayExplode(style);
|
|
37
|
-
const joinedValues = value.map((v) => {
|
|
38
|
-
if (style === "label" || style === "simple") return allowReserved ? v : encodeURIComponent(v);
|
|
39
|
-
return serializePrimitiveParam({
|
|
40
|
-
allowReserved,
|
|
41
|
-
name,
|
|
42
|
-
value: v
|
|
43
|
-
});
|
|
44
|
-
}).join(separator);
|
|
45
|
-
return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
|
|
46
|
-
};
|
|
47
|
-
const serializePrimitiveParam = ({ allowReserved, name, value }) => {
|
|
48
|
-
if (value === void 0 || value === null) return "";
|
|
49
|
-
if (typeof value === "object") throw new Error("Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.");
|
|
50
|
-
return `${name}=${allowReserved ? value : encodeURIComponent(value)}`;
|
|
51
|
-
};
|
|
52
|
-
const serializeObjectParam = ({ allowReserved, explode, name, style, value, valueOnly }) => {
|
|
53
|
-
if (value instanceof Date) return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`;
|
|
54
|
-
if (style !== "deepObject" && !explode) {
|
|
55
|
-
let values = [];
|
|
56
|
-
Object.entries(value).forEach(([key, v]) => {
|
|
57
|
-
values = [
|
|
58
|
-
...values,
|
|
59
|
-
key,
|
|
60
|
-
allowReserved ? v : encodeURIComponent(v)
|
|
61
|
-
];
|
|
62
|
-
});
|
|
63
|
-
const joinedValues = values.join(",");
|
|
64
|
-
switch (style) {
|
|
65
|
-
case "form": return `${name}=${joinedValues}`;
|
|
66
|
-
case "label": return `.${joinedValues}`;
|
|
67
|
-
case "matrix": return `;${name}=${joinedValues}`;
|
|
68
|
-
default: return joinedValues;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
const separator = separatorObjectExplode(style);
|
|
72
|
-
const joinedValues = Object.entries(value).map(([key, v]) => serializePrimitiveParam({
|
|
73
|
-
allowReserved,
|
|
74
|
-
name: style === "deepObject" ? `${name}[${key}]` : key,
|
|
75
|
-
value: v
|
|
76
|
-
})).join(separator);
|
|
77
|
-
return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
|
|
78
|
-
};
|
|
79
|
-
//#endregion
|
|
80
|
-
export { separatorArrayExplode, separatorArrayNoExplode, separatorObjectExplode, serializeArrayParam, serializeObjectParam, serializePrimitiveParam };
|
|
81
|
-
|
|
82
|
-
//# sourceMappingURL=pathSerializer.gen.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pathSerializer.gen.js","names":[],"sources":["../../../../../../sdk/src/client/core/pathSerializer.gen.ts"],"sourcesContent":["// This file is auto-generated by @hey-api/openapi-ts\n\ninterface SerializeOptions<T> extends SerializePrimitiveOptions, SerializerOptions<T> {}\n\ninterface SerializePrimitiveOptions {\n allowReserved?: boolean\n name: string\n}\n\nexport interface SerializerOptions<T> {\n /**\n * @default true\n */\n explode: boolean\n style: T\n}\n\nexport type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'\nexport type ArraySeparatorStyle = ArrayStyle | MatrixStyle\ntype MatrixStyle = 'label' | 'matrix' | 'simple'\nexport type ObjectStyle = 'form' | 'deepObject'\ntype ObjectSeparatorStyle = ObjectStyle | MatrixStyle\n\ninterface SerializePrimitiveParam extends SerializePrimitiveOptions {\n value: string\n}\n\nexport const separatorArrayExplode = (style: ArraySeparatorStyle): '.' | ';' | ',' | '&' => {\n switch (style) {\n case 'label':\n return '.'\n case 'matrix':\n return ';'\n case 'simple':\n return ','\n default:\n return '&'\n }\n}\n\nexport const separatorArrayNoExplode = (style: ArraySeparatorStyle): ',' | '|' | '%20' => {\n switch (style) {\n case 'form':\n return ','\n case 'pipeDelimited':\n return '|'\n case 'spaceDelimited':\n return '%20'\n default:\n return ','\n }\n}\n\nexport const separatorObjectExplode = (style: ObjectSeparatorStyle): '.' | ';' | ',' | '&' => {\n switch (style) {\n case 'label':\n return '.'\n case 'matrix':\n return ';'\n case 'simple':\n return ','\n default:\n return '&'\n }\n}\n\nexport const serializeArrayParam = ({\n allowReserved,\n explode,\n name,\n style,\n value,\n}: SerializeOptions<ArraySeparatorStyle> & {\n value: unknown[]\n}): string => {\n if (!explode) {\n const joinedValues = (\n allowReserved ? value : value.map(v => encodeURIComponent(v as string))\n ).join(separatorArrayNoExplode(style))\n switch (style) {\n case 'label':\n return `.${joinedValues}`\n case 'matrix':\n return `;${name}=${joinedValues}`\n case 'simple':\n return joinedValues\n default:\n return `${name}=${joinedValues}`\n }\n }\n\n const separator = separatorArrayExplode(style)\n const joinedValues = value\n .map(v => {\n if (style === 'label' || style === 'simple') {\n return allowReserved ? v : encodeURIComponent(v as string)\n }\n\n return serializePrimitiveParam({\n allowReserved,\n name,\n value: v as string,\n })\n })\n .join(separator)\n return style === 'label' || style === 'matrix' ? separator + joinedValues : joinedValues\n}\n\nexport const serializePrimitiveParam = ({\n allowReserved,\n name,\n value,\n}: SerializePrimitiveParam): string => {\n if (value === undefined || value === null) {\n return ''\n }\n\n if (typeof value === 'object') {\n throw new Error(\n 'Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.',\n )\n }\n\n return `${name}=${allowReserved ? value : encodeURIComponent(value)}`\n}\n\nexport const serializeObjectParam = ({\n allowReserved,\n explode,\n name,\n style,\n value,\n valueOnly,\n}: SerializeOptions<ObjectSeparatorStyle> & {\n value: Record<string, unknown> | Date\n valueOnly?: boolean\n}): string => {\n if (value instanceof Date) {\n return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`\n }\n\n if (style !== 'deepObject' && !explode) {\n let values: string[] = []\n Object.entries(value).forEach(([key, v]) => {\n values = [...values, key, allowReserved ? (v as string) : encodeURIComponent(v as string)]\n })\n const joinedValues = values.join(',')\n switch (style) {\n case 'form':\n return `${name}=${joinedValues}`\n case 'label':\n return `.${joinedValues}`\n case 'matrix':\n return `;${name}=${joinedValues}`\n default:\n return joinedValues\n }\n }\n\n const separator = separatorObjectExplode(style)\n const joinedValues = Object.entries(value)\n .map(([key, v]) =>\n serializePrimitiveParam({\n allowReserved,\n name: style === 'deepObject' ? `${name}[${key}]` : key,\n value: v as string,\n }),\n )\n .join(separator)\n return style === 'label' || style === 'matrix' ? separator + joinedValues : joinedValues\n}\n"],"mappings":";AA2BA,MAAa,yBAAyB,UAAsD;CAC1F,QAAQ,OAAR;EACE,KAAK,SACH,OAAO;EACT,KAAK,UACH,OAAO;EACT,KAAK,UACH,OAAO;EACT,SACE,OAAO;CACX;AACF;AAEA,MAAa,2BAA2B,UAAkD;CACxF,QAAQ,OAAR;EACE,KAAK,QACH,OAAO;EACT,KAAK,iBACH,OAAO;EACT,KAAK,kBACH,OAAO;EACT,SACE,OAAO;CACX;AACF;AAEA,MAAa,0BAA0B,UAAuD;CAC5F,QAAQ,OAAR;EACE,KAAK,SACH,OAAO;EACT,KAAK,UACH,OAAO;EACT,KAAK,UACH,OAAO;EACT,SACE,OAAO;CACX;AACF;AAEA,MAAa,uBAAuB,EAClC,eACA,SACA,MACA,OACA,YAGY;CACZ,IAAI,CAAC,SAAS;EACZ,MAAM,gBACJ,gBAAgB,QAAQ,MAAM,KAAI,MAAK,mBAAmB,CAAW,CAAC,EAAA,CACtE,KAAK,wBAAwB,KAAK,CAAC;EACrC,QAAQ,OAAR;GACE,KAAK,SACH,OAAO,IAAI;GACb,KAAK,UACH,OAAO,IAAI,KAAK,GAAG;GACrB,KAAK,UACH,OAAO;GACT,SACE,OAAO,GAAG,KAAK,GAAG;EACtB;CACF;CAEA,MAAM,YAAY,sBAAsB,KAAK;CAC7C,MAAM,eAAe,MAClB,KAAI,MAAK;EACR,IAAI,UAAU,WAAW,UAAU,UACjC,OAAO,gBAAgB,IAAI,mBAAmB,CAAW;EAG3D,OAAO,wBAAwB;GAC7B;GACA;GACA,OAAO;EACT,CAAC;CACH,CAAC,CAAC,CACD,KAAK,SAAS;CACjB,OAAO,UAAU,WAAW,UAAU,WAAW,YAAY,eAAe;AAC9E;AAEA,MAAa,2BAA2B,EACtC,eACA,MACA,YACqC;CACrC,IAAI,UAAU,KAAA,KAAa,UAAU,MACnC,OAAO;CAGT,IAAI,OAAO,UAAU,UACnB,MAAM,IAAI,MACR,sGACF;CAGF,OAAO,GAAG,KAAK,GAAG,gBAAgB,QAAQ,mBAAmB,KAAK;AACpE;AAEA,MAAa,wBAAwB,EACnC,eACA,SACA,MACA,OACA,OACA,gBAIY;CACZ,IAAI,iBAAiB,MACnB,OAAO,YAAY,MAAM,YAAY,IAAI,GAAG,KAAK,GAAG,MAAM,YAAY;CAGxE,IAAI,UAAU,gBAAgB,CAAC,SAAS;EACtC,IAAI,SAAmB,CAAC;EACxB,OAAO,QAAQ,KAAK,CAAC,CAAC,SAAS,CAAC,KAAK,OAAO;GAC1C,SAAS;IAAC,GAAG;IAAQ;IAAK,gBAAiB,IAAe,mBAAmB,CAAW;GAAC;EAC3F,CAAC;EACD,MAAM,eAAe,OAAO,KAAK,GAAG;EACpC,QAAQ,OAAR;GACE,KAAK,QACH,OAAO,GAAG,KAAK,GAAG;GACpB,KAAK,SACH,OAAO,IAAI;GACb,KAAK,UACH,OAAO,IAAI,KAAK,GAAG;GACrB,SACE,OAAO;EACX;CACF;CAEA,MAAM,YAAY,uBAAuB,KAAK;CAC9C,MAAM,eAAe,OAAO,QAAQ,KAAK,CAAC,CACvC,KAAK,CAAC,KAAK,OACV,wBAAwB;EACtB;EACA,MAAM,UAAU,eAAe,GAAG,KAAK,GAAG,IAAI,KAAK;EACnD,OAAO;CACT,CAAC,CACH,CAAC,CACA,KAAK,SAAS;CACjB,OAAO,UAAU,WAAW,UAAU,WAAW,YAAY,eAAe;AAC9E"}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
//#region ../sdk/src/client/core/serverSentEvents.gen.ts
|
|
2
|
-
function createSseClient({ onRequest, onSseError, onSseEvent, responseTransformer, responseValidator, sseDefaultRetryDelay, sseMaxRetryAttempts, sseMaxRetryDelay, sseSleepFn, url, ...options }) {
|
|
3
|
-
let lastEventId;
|
|
4
|
-
const sleep = sseSleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
5
|
-
const createStream = async function* () {
|
|
6
|
-
let retryDelay = sseDefaultRetryDelay ?? 3e3;
|
|
7
|
-
let attempt = 0;
|
|
8
|
-
const signal = options.signal ?? new AbortController().signal;
|
|
9
|
-
while (true) {
|
|
10
|
-
if (signal.aborted) break;
|
|
11
|
-
attempt++;
|
|
12
|
-
const headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers);
|
|
13
|
-
if (lastEventId !== void 0) headers.set("Last-Event-ID", lastEventId);
|
|
14
|
-
try {
|
|
15
|
-
const requestInit = {
|
|
16
|
-
redirect: "follow",
|
|
17
|
-
...options,
|
|
18
|
-
body: options.serializedBody,
|
|
19
|
-
headers,
|
|
20
|
-
signal
|
|
21
|
-
};
|
|
22
|
-
let request = new Request(url, requestInit);
|
|
23
|
-
if (onRequest) request = await onRequest(url, requestInit);
|
|
24
|
-
const response = await (options.fetch ?? globalThis.fetch)(request);
|
|
25
|
-
if (!response.ok) throw new Error(`SSE failed: ${response.status} ${response.statusText}`);
|
|
26
|
-
if (!response.body) throw new Error("No body in SSE response");
|
|
27
|
-
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
|
|
28
|
-
let buffer = "";
|
|
29
|
-
const abortHandler = () => {
|
|
30
|
-
try {
|
|
31
|
-
reader.cancel();
|
|
32
|
-
} catch {}
|
|
33
|
-
};
|
|
34
|
-
signal.addEventListener("abort", abortHandler);
|
|
35
|
-
try {
|
|
36
|
-
while (true) {
|
|
37
|
-
const { done, value } = await reader.read();
|
|
38
|
-
if (done) break;
|
|
39
|
-
buffer += value;
|
|
40
|
-
buffer = buffer.replace(/\r\n?/g, "\n");
|
|
41
|
-
const chunks = buffer.split("\n\n");
|
|
42
|
-
buffer = chunks.pop() ?? "";
|
|
43
|
-
for (const chunk of chunks) {
|
|
44
|
-
const lines = chunk.split("\n");
|
|
45
|
-
const dataLines = [];
|
|
46
|
-
let eventName;
|
|
47
|
-
for (const line of lines) if (line.startsWith("data:")) dataLines.push(line.replace(/^data:\s*/, ""));
|
|
48
|
-
else if (line.startsWith("event:")) eventName = line.replace(/^event:\s*/, "");
|
|
49
|
-
else if (line.startsWith("id:")) lastEventId = line.replace(/^id:\s*/, "");
|
|
50
|
-
else if (line.startsWith("retry:")) {
|
|
51
|
-
const parsed = Number.parseInt(line.replace(/^retry:\s*/, ""), 10);
|
|
52
|
-
if (!Number.isNaN(parsed)) retryDelay = parsed;
|
|
53
|
-
}
|
|
54
|
-
let data;
|
|
55
|
-
let parsedJson = false;
|
|
56
|
-
if (dataLines.length) {
|
|
57
|
-
const rawData = dataLines.join("\n");
|
|
58
|
-
try {
|
|
59
|
-
data = JSON.parse(rawData);
|
|
60
|
-
parsedJson = true;
|
|
61
|
-
} catch {
|
|
62
|
-
data = rawData;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
if (parsedJson) {
|
|
66
|
-
if (responseValidator) await responseValidator(data);
|
|
67
|
-
if (responseTransformer) data = await responseTransformer(data);
|
|
68
|
-
}
|
|
69
|
-
onSseEvent?.({
|
|
70
|
-
data,
|
|
71
|
-
event: eventName,
|
|
72
|
-
id: lastEventId,
|
|
73
|
-
retry: retryDelay
|
|
74
|
-
});
|
|
75
|
-
if (dataLines.length) yield data;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
} finally {
|
|
79
|
-
signal.removeEventListener("abort", abortHandler);
|
|
80
|
-
reader.releaseLock();
|
|
81
|
-
}
|
|
82
|
-
break;
|
|
83
|
-
} catch (error) {
|
|
84
|
-
onSseError?.(error);
|
|
85
|
-
if (sseMaxRetryAttempts !== void 0 && attempt >= sseMaxRetryAttempts) break;
|
|
86
|
-
const backoff = Math.min(retryDelay * 2 ** (attempt - 1), sseMaxRetryDelay ?? 3e4);
|
|
87
|
-
await sleep(backoff);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
return { stream: createStream() };
|
|
92
|
-
}
|
|
93
|
-
//#endregion
|
|
94
|
-
export { createSseClient };
|
|
95
|
-
|
|
96
|
-
//# sourceMappingURL=serverSentEvents.gen.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"serverSentEvents.gen.js","names":[],"sources":["../../../../../../sdk/src/client/core/serverSentEvents.gen.ts"],"sourcesContent":["// This file is auto-generated by @hey-api/openapi-ts\n\nimport type { Config } from './types.gen'\n\nexport type ServerSentEventsOptions<TData = unknown> = Omit<RequestInit, 'method'> &\n Pick<Config, 'method' | 'responseTransformer' | 'responseValidator'> & {\n /**\n * Fetch API implementation. You can use this option to provide a custom\n * fetch instance.\n *\n * @default globalThis.fetch\n */\n fetch?: typeof fetch\n /**\n * Implementing clients can call request interceptors inside this hook.\n */\n onRequest?: (url: string, init: RequestInit) => Promise<Request>\n /**\n * Callback invoked when a network or parsing error occurs during streaming.\n *\n * This option applies only if the endpoint returns a stream of events.\n *\n * @param error The error that occurred.\n */\n onSseError?: (error: unknown) => void\n /**\n * Callback invoked when an event is streamed from the server.\n *\n * This option applies only if the endpoint returns a stream of events.\n *\n * @param event Event streamed from the server.\n * @returns Nothing (void).\n */\n onSseEvent?: (event: StreamEvent<TData>) => void\n serializedBody?: RequestInit['body']\n /**\n * Default retry delay in milliseconds.\n *\n * This option applies only if the endpoint returns a stream of events.\n *\n * @default 3000\n */\n sseDefaultRetryDelay?: number\n /**\n * Maximum number of retry attempts before giving up.\n */\n sseMaxRetryAttempts?: number\n /**\n * Maximum retry delay in milliseconds.\n *\n * Applies only when exponential backoff is used.\n *\n * This option applies only if the endpoint returns a stream of events.\n *\n * @default 30000\n */\n sseMaxRetryDelay?: number\n /**\n * Optional sleep function for retry backoff.\n *\n * Defaults to using `setTimeout`.\n */\n sseSleepFn?: (ms: number) => Promise<void>\n url: string\n }\n\nexport interface StreamEvent<TData = unknown> {\n data: TData\n event?: string\n id?: string\n retry?: number\n}\n\nexport type ServerSentEventsResult<TData = unknown, TReturn = void, TNext = unknown> = {\n stream: AsyncGenerator<\n TData extends Record<string, unknown> ? TData[keyof TData] : TData,\n TReturn,\n TNext\n >\n}\n\nexport function createSseClient<TData = unknown>({\n onRequest,\n onSseError,\n onSseEvent,\n responseTransformer,\n responseValidator,\n sseDefaultRetryDelay,\n sseMaxRetryAttempts,\n sseMaxRetryDelay,\n sseSleepFn,\n url,\n ...options\n}: ServerSentEventsOptions): ServerSentEventsResult<TData> {\n let lastEventId: string | undefined\n\n const sleep = sseSleepFn ?? ((ms: number) => new Promise(resolve => setTimeout(resolve, ms)))\n\n const createStream = async function* () {\n let retryDelay: number = sseDefaultRetryDelay ?? 3000\n let attempt = 0\n const signal = options.signal ?? new AbortController().signal\n\n while (true) {\n if (signal.aborted) break\n\n attempt++\n\n const headers =\n options.headers instanceof Headers\n ? options.headers\n : new Headers(options.headers as Record<string, string> | undefined)\n\n if (lastEventId !== undefined) {\n headers.set('Last-Event-ID', lastEventId)\n }\n\n try {\n const requestInit: RequestInit = {\n redirect: 'follow',\n ...options,\n body: options.serializedBody,\n headers,\n signal,\n }\n let request = new Request(url, requestInit)\n if (onRequest) {\n request = await onRequest(url, requestInit)\n }\n // fetch must be assigned here, otherwise it would throw the error:\n // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation\n const _fetch = options.fetch ?? globalThis.fetch\n const response = await _fetch(request)\n\n if (!response.ok) throw new Error(`SSE failed: ${response.status} ${response.statusText}`)\n\n if (!response.body) throw new Error('No body in SSE response')\n\n const reader = response.body.pipeThrough(new TextDecoderStream()).getReader()\n\n let buffer = ''\n\n const abortHandler = () => {\n try {\n reader.cancel()\n } catch {\n // noop\n }\n }\n\n signal.addEventListener('abort', abortHandler)\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n buffer += value\n buffer = buffer.replace(/\\r\\n?/g, '\\n') // normalize line endings\n\n const chunks = buffer.split('\\n\\n')\n buffer = chunks.pop() ?? ''\n\n for (const chunk of chunks) {\n const lines = chunk.split('\\n')\n const dataLines: Array<string> = []\n let eventName: string | undefined\n\n for (const line of lines) {\n if (line.startsWith('data:')) {\n dataLines.push(line.replace(/^data:\\s*/, ''))\n } else if (line.startsWith('event:')) {\n eventName = line.replace(/^event:\\s*/, '')\n } else if (line.startsWith('id:')) {\n lastEventId = line.replace(/^id:\\s*/, '')\n } else if (line.startsWith('retry:')) {\n const parsed = Number.parseInt(line.replace(/^retry:\\s*/, ''), 10)\n if (!Number.isNaN(parsed)) {\n retryDelay = parsed\n }\n }\n }\n\n let data: unknown\n let parsedJson = false\n\n if (dataLines.length) {\n const rawData = dataLines.join('\\n')\n try {\n data = JSON.parse(rawData)\n parsedJson = true\n } catch {\n data = rawData\n }\n }\n\n if (parsedJson) {\n if (responseValidator) {\n await responseValidator(data)\n }\n\n if (responseTransformer) {\n data = await responseTransformer(data)\n }\n }\n\n onSseEvent?.({\n data,\n event: eventName,\n id: lastEventId,\n retry: retryDelay,\n })\n\n if (dataLines.length) {\n yield data as any\n }\n }\n }\n } finally {\n signal.removeEventListener('abort', abortHandler)\n reader.releaseLock()\n }\n\n break // exit loop on normal completion\n } catch (error) {\n // connection failed or aborted; retry after delay\n onSseError?.(error)\n\n if (sseMaxRetryAttempts !== undefined && attempt >= sseMaxRetryAttempts) {\n break // stop after firing error\n }\n\n // exponential backoff: double retry each attempt, cap at 30s\n const backoff = Math.min(retryDelay * 2 ** (attempt - 1), sseMaxRetryDelay ?? 30000)\n await sleep(backoff)\n }\n }\n }\n\n const stream = createStream()\n\n return { stream }\n}\n"],"mappings":"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"}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { serializeArrayParam, serializeObjectParam, serializePrimitiveParam } from "./pathSerializer.gen.js";
|
|
2
|
-
//#region ../sdk/src/client/core/utils.gen.ts
|
|
3
|
-
const PATH_PARAM_RE = /\{[^{}]+\}/g;
|
|
4
|
-
const defaultPathSerializer = ({ path, url: _url }) => {
|
|
5
|
-
let url = _url;
|
|
6
|
-
const matches = _url.match(PATH_PARAM_RE);
|
|
7
|
-
if (matches) for (const match of matches) {
|
|
8
|
-
let explode = false;
|
|
9
|
-
let name = match.substring(1, match.length - 1);
|
|
10
|
-
let style = "simple";
|
|
11
|
-
if (name.endsWith("*")) {
|
|
12
|
-
explode = true;
|
|
13
|
-
name = name.substring(0, name.length - 1);
|
|
14
|
-
}
|
|
15
|
-
if (name.startsWith(".")) {
|
|
16
|
-
name = name.substring(1);
|
|
17
|
-
style = "label";
|
|
18
|
-
} else if (name.startsWith(";")) {
|
|
19
|
-
name = name.substring(1);
|
|
20
|
-
style = "matrix";
|
|
21
|
-
}
|
|
22
|
-
const value = path[name];
|
|
23
|
-
if (value === void 0 || value === null) continue;
|
|
24
|
-
if (Array.isArray(value)) {
|
|
25
|
-
url = url.replace(match, serializeArrayParam({
|
|
26
|
-
explode,
|
|
27
|
-
name,
|
|
28
|
-
style,
|
|
29
|
-
value
|
|
30
|
-
}));
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
if (typeof value === "object") {
|
|
34
|
-
url = url.replace(match, serializeObjectParam({
|
|
35
|
-
explode,
|
|
36
|
-
name,
|
|
37
|
-
style,
|
|
38
|
-
value,
|
|
39
|
-
valueOnly: true
|
|
40
|
-
}));
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
if (style === "matrix") {
|
|
44
|
-
url = url.replace(match, `;${serializePrimitiveParam({
|
|
45
|
-
name,
|
|
46
|
-
value
|
|
47
|
-
})}`);
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
const replaceValue = encodeURIComponent(style === "label" ? `.${value}` : value);
|
|
51
|
-
url = url.replace(match, replaceValue);
|
|
52
|
-
}
|
|
53
|
-
return url;
|
|
54
|
-
};
|
|
55
|
-
const getUrl = ({ baseUrl, path, query, querySerializer, url: _url }) => {
|
|
56
|
-
const pathUrl = _url.startsWith("/") ? _url : `/${_url}`;
|
|
57
|
-
let url = (baseUrl ?? "") + pathUrl;
|
|
58
|
-
if (path) url = defaultPathSerializer({
|
|
59
|
-
path,
|
|
60
|
-
url
|
|
61
|
-
});
|
|
62
|
-
let search = query ? querySerializer(query) : "";
|
|
63
|
-
if (search.startsWith("?")) search = search.substring(1);
|
|
64
|
-
if (search) url += `?${search}`;
|
|
65
|
-
return url;
|
|
66
|
-
};
|
|
67
|
-
function getValidRequestBody(options) {
|
|
68
|
-
const hasBody = options.body !== void 0;
|
|
69
|
-
if (hasBody && options.bodySerializer) {
|
|
70
|
-
if ("serializedBody" in options) return options.serializedBody !== void 0 && options.serializedBody !== "" ? options.serializedBody : null;
|
|
71
|
-
return options.body !== "" ? options.body : null;
|
|
72
|
-
}
|
|
73
|
-
if (hasBody) return options.body;
|
|
74
|
-
}
|
|
75
|
-
//#endregion
|
|
76
|
-
export { PATH_PARAM_RE, defaultPathSerializer, getUrl, getValidRequestBody };
|
|
77
|
-
|
|
78
|
-
//# sourceMappingURL=utils.gen.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils.gen.js","names":[],"sources":["../../../../../../sdk/src/client/core/utils.gen.ts"],"sourcesContent":["// This file is auto-generated by @hey-api/openapi-ts\n\nimport type { BodySerializer, QuerySerializer } from './bodySerializer.gen'\nimport {\n type ArraySeparatorStyle,\n serializeArrayParam,\n serializeObjectParam,\n serializePrimitiveParam,\n} from './pathSerializer.gen'\n\nexport interface PathSerializer {\n path: Record<string, unknown>\n url: string\n}\n\nexport const PATH_PARAM_RE: RegExp = /\\{[^{}]+\\}/g\n\nexport const defaultPathSerializer = ({ path, url: _url }: PathSerializer): string => {\n let url = _url\n const matches = _url.match(PATH_PARAM_RE)\n if (matches) {\n for (const match of matches) {\n let explode = false\n let name = match.substring(1, match.length - 1)\n let style: ArraySeparatorStyle = 'simple'\n\n if (name.endsWith('*')) {\n explode = true\n name = name.substring(0, name.length - 1)\n }\n\n if (name.startsWith('.')) {\n name = name.substring(1)\n style = 'label'\n } else if (name.startsWith(';')) {\n name = name.substring(1)\n style = 'matrix'\n }\n\n const value = path[name]\n\n if (value === undefined || value === null) {\n continue\n }\n\n if (Array.isArray(value)) {\n url = url.replace(match, serializeArrayParam({ explode, name, style, value }))\n continue\n }\n\n if (typeof value === 'object') {\n url = url.replace(\n match,\n serializeObjectParam({\n explode,\n name,\n style,\n value: value as Record<string, unknown>,\n valueOnly: true,\n }),\n )\n continue\n }\n\n if (style === 'matrix') {\n url = url.replace(\n match,\n `;${serializePrimitiveParam({\n name,\n value: value as string,\n })}`,\n )\n continue\n }\n\n const replaceValue = encodeURIComponent(\n style === 'label' ? `.${value as string}` : (value as string),\n )\n url = url.replace(match, replaceValue)\n }\n }\n return url\n}\n\nexport const getUrl = ({\n baseUrl,\n path,\n query,\n querySerializer,\n url: _url,\n}: {\n baseUrl?: string\n path?: Record<string, unknown>\n query?: Record<string, unknown>\n querySerializer: QuerySerializer\n url: string\n}): string => {\n const pathUrl = _url.startsWith('/') ? _url : `/${_url}`\n let url = (baseUrl ?? '') + pathUrl\n if (path) {\n url = defaultPathSerializer({ path, url })\n }\n let search = query ? querySerializer(query) : ''\n if (search.startsWith('?')) {\n search = search.substring(1)\n }\n if (search) {\n url += `?${search}`\n }\n return url\n}\n\nexport function getValidRequestBody(options: {\n body?: unknown\n bodySerializer?: BodySerializer | null\n serializedBody?: unknown\n}): unknown {\n const hasBody = options.body !== undefined\n const isSerializedBody = hasBody && options.bodySerializer\n\n if (isSerializedBody) {\n if ('serializedBody' in options) {\n const hasSerializedBody =\n options.serializedBody !== undefined && options.serializedBody !== ''\n\n return hasSerializedBody ? options.serializedBody : null\n }\n\n // not all clients implement a serializedBody property (i.e., client-axios)\n return options.body !== '' ? options.body : null\n }\n\n // plain/text body\n if (hasBody) {\n return options.body\n }\n\n // no body was provided\n return undefined\n}\n"],"mappings":";;AAeA,MAAa,gBAAwB;AAErC,MAAa,yBAAyB,EAAE,MAAM,KAAK,WAAmC;CACpF,IAAI,MAAM;CACV,MAAM,UAAU,KAAK,MAAM,aAAa;CACxC,IAAI,SACF,KAAK,MAAM,SAAS,SAAS;EAC3B,IAAI,UAAU;EACd,IAAI,OAAO,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC;EAC9C,IAAI,QAA6B;EAEjC,IAAI,KAAK,SAAS,GAAG,GAAG;GACtB,UAAU;GACV,OAAO,KAAK,UAAU,GAAG,KAAK,SAAS,CAAC;EAC1C;EAEA,IAAI,KAAK,WAAW,GAAG,GAAG;GACxB,OAAO,KAAK,UAAU,CAAC;GACvB,QAAQ;EACV,OAAO,IAAI,KAAK,WAAW,GAAG,GAAG;GAC/B,OAAO,KAAK,UAAU,CAAC;GACvB,QAAQ;EACV;EAEA,MAAM,QAAQ,KAAK;EAEnB,IAAI,UAAU,KAAA,KAAa,UAAU,MACnC;EAGF,IAAI,MAAM,QAAQ,KAAK,GAAG;GACxB,MAAM,IAAI,QAAQ,OAAO,oBAAoB;IAAE;IAAS;IAAM;IAAO;GAAM,CAAC,CAAC;GAC7E;EACF;EAEA,IAAI,OAAO,UAAU,UAAU;GAC7B,MAAM,IAAI,QACR,OACA,qBAAqB;IACnB;IACA;IACA;IACO;IACP,WAAW;GACb,CAAC,CACH;GACA;EACF;EAEA,IAAI,UAAU,UAAU;GACtB,MAAM,IAAI,QACR,OACA,IAAI,wBAAwB;IAC1B;IACO;GACT,CAAC,GACH;GACA;EACF;EAEA,MAAM,eAAe,mBACnB,UAAU,UAAU,IAAI,UAAqB,KAC/C;EACA,MAAM,IAAI,QAAQ,OAAO,YAAY;CACvC;CAEF,OAAO;AACT;AAEA,MAAa,UAAU,EACrB,SACA,MACA,OACA,iBACA,KAAK,WAOO;CACZ,MAAM,UAAU,KAAK,WAAW,GAAG,IAAI,OAAO,IAAI;CAClD,IAAI,OAAO,WAAW,MAAM;CAC5B,IAAI,MACF,MAAM,sBAAsB;EAAE;EAAM;CAAI,CAAC;CAE3C,IAAI,SAAS,QAAQ,gBAAgB,KAAK,IAAI;CAC9C,IAAI,OAAO,WAAW,GAAG,GACvB,SAAS,OAAO,UAAU,CAAC;CAE7B,IAAI,QACF,OAAO,IAAI;CAEb,OAAO;AACT;AAEA,SAAgB,oBAAoB,SAIxB;CACV,MAAM,UAAU,QAAQ,SAAS,KAAA;CAGjC,IAFyB,WAAW,QAAQ,gBAEtB;EACpB,IAAI,oBAAoB,SAItB,OAFE,QAAQ,mBAAmB,KAAA,KAAa,QAAQ,mBAAmB,KAE1C,QAAQ,iBAAiB;EAItD,OAAO,QAAQ,SAAS,KAAK,QAAQ,OAAO;CAC9C;CAGA,IAAI,SACF,OAAO,QAAQ;AAKnB"}
|
package/src/components/index.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
export * from './provider'
|
|
4
|
-
export { Chimeling } from './chimeling'
|
|
5
|
-
export { ChimelingRoot, type ChimelingRootProps } from './root'
|
|
6
|
-
export { ChimelingTrigger, type ChimelingTriggerProps } from './trigger'
|
|
7
|
-
export { ChimelingPopover, type ChimelingPopoverProps } from './popover'
|
|
8
|
-
export type { ShowablePresence, PresenceActivity } from '../lib/showable-presence'
|
package/src/components/logo.tsx
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
function Logo() {
|
|
2
|
-
return (
|
|
3
|
-
<svg
|
|
4
|
-
width="1em"
|
|
5
|
-
height="1em"
|
|
6
|
-
viewBox="0 0 804 804"
|
|
7
|
-
fill="none"
|
|
8
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
9
|
-
>
|
|
10
|
-
<path
|
|
11
|
-
d="M402 0C600.823 0 762 161.177 762 360C762 525.32 650.565 664.61 498.711 706.86C489.497 709.423 484.89 710.705 482.445 713.923C480 717.141 480 721.695 480 730.802V774.682C480 790.69 480 798.695 474.745 802.266C469.489 805.837 462.047 802.89 447.163 796.996L340.163 754.62C332.821 751.712 329.149 750.259 327.075 747.205C325 744.152 325 740.204 325 732.306V731.08C325 721.947 325 717.381 322.545 714.159C320.09 710.937 315.467 709.664 306.221 707.119C153.891 665.181 42 525.658 42 360C42 161.177 203.177 0 402 0ZM402 243C245.151 243 118 345.975 118 473C118 569.681 191.658 652.427 296.005 686.445C308.733 690.594 315.097 692.669 320.049 689.075C325 685.482 325 678.444 325 664.368V475.318C325 459.31 325 451.305 330.255 447.734C335.511 444.163 342.953 447.11 357.837 453.004L464.837 495.38C472.179 498.288 475.851 499.741 477.925 502.795C480 505.848 480 509.796 480 517.694V664.044C480 678.16 480 685.217 484.968 688.81C489.936 692.403 496.311 690.3 509.059 686.095C612.843 651.863 686 569.351 686 473C686 345.975 558.849 243 402 243Z"
|
|
12
|
-
fill="currentColor"
|
|
13
|
-
/>
|
|
14
|
-
</svg>
|
|
15
|
-
)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export default Logo
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { Popover } from '@base-ui/react/popover'
|
|
4
|
-
import type { PopoverPopupProps } from '@base-ui/react/popover'
|
|
5
|
-
import { formatActivityDuration } from '../lib/format-activity-duration'
|
|
6
|
-
import { iconImageUrl } from '../lib/icon-url'
|
|
7
|
-
import { useWidgetContext } from '../lib/widget-context'
|
|
8
|
-
import Logo from './logo'
|
|
9
|
-
|
|
10
|
-
// TODO: decide whether to expose the rest of Base UI Popup props.
|
|
11
|
-
export type ChimelingPopoverProps = Pick<PopoverPopupProps, 'className' | 'style'>
|
|
12
|
-
|
|
13
|
-
export function ChimelingPopover({ className, style }: ChimelingPopoverProps) {
|
|
14
|
-
const { showable, now, poweredBy, placement, baseUrl } = useWidgetContext()
|
|
15
|
-
|
|
16
|
-
if (!showable) {
|
|
17
|
-
return null
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const { gerund, appName, iconHash, startedAt } = showable.activity
|
|
21
|
-
|
|
22
|
-
return (
|
|
23
|
-
<Popover.Portal>
|
|
24
|
-
{/* TODO: decide whether to expose the rest of Base UI Positioner props. */}
|
|
25
|
-
<Popover.Positioner
|
|
26
|
-
side={placement.side}
|
|
27
|
-
align={placement.align}
|
|
28
|
-
sideOffset={placement.sideOffset}
|
|
29
|
-
>
|
|
30
|
-
<Popover.Popup className={className} style={style}>
|
|
31
|
-
<Popover.Description>{gerund}</Popover.Description>
|
|
32
|
-
<Popover.Title>{appName}</Popover.Title>
|
|
33
|
-
<span>{formatActivityDuration(startedAt, now)}</span>
|
|
34
|
-
{iconHash ? <img src={iconImageUrl(baseUrl, iconHash)} alt="" /> : null}
|
|
35
|
-
{poweredBy ? (
|
|
36
|
-
<div>
|
|
37
|
-
Powered by <Logo />
|
|
38
|
-
</div>
|
|
39
|
-
) : null}
|
|
40
|
-
</Popover.Popup>
|
|
41
|
-
</Popover.Positioner>
|
|
42
|
-
</Popover.Portal>
|
|
43
|
-
)
|
|
44
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { useMemo, type ReactNode } from 'react'
|
|
4
|
-
import { createClient, createConfig } from '../../../sdk/src/client/client'
|
|
5
|
-
import { DEFAULT_PRESENCE_REFRESH_INTERVAL } from '../lib/config'
|
|
6
|
-
import { ChimelingContext } from '../lib/context'
|
|
7
|
-
|
|
8
|
-
type ChimelingProviderProps = {
|
|
9
|
-
baseUrl?: string
|
|
10
|
-
refreshInterval?: number
|
|
11
|
-
children: ReactNode
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function ChimelingProvider({
|
|
15
|
-
baseUrl,
|
|
16
|
-
refreshInterval = DEFAULT_PRESENCE_REFRESH_INTERVAL,
|
|
17
|
-
children,
|
|
18
|
-
}: ChimelingProviderProps) {
|
|
19
|
-
const client = useMemo(
|
|
20
|
-
() => createClient(createConfig({ baseUrl: baseUrl ?? 'https://chimeling.app' })),
|
|
21
|
-
[baseUrl],
|
|
22
|
-
)
|
|
23
|
-
const value = useMemo(() => ({ client, refreshInterval }), [client, refreshInterval])
|
|
24
|
-
|
|
25
|
-
return <ChimelingContext.Provider value={value}>{children}</ChimelingContext.Provider>
|
|
26
|
-
}
|
package/src/components/root.tsx
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react'
|
|
4
|
-
import { Popover } from '@base-ui/react/popover'
|
|
5
|
-
import type { GetResolvedUserPresenceResponse } from '@cairntale/chimeling-js'
|
|
6
|
-
import { useResolvedUserPresence } from '../hooks/use-resolved-user-presence'
|
|
7
|
-
import { useChimelingContext } from '../lib/context'
|
|
8
|
-
import { getShowablePresence, type ShowablePresence } from '../lib/showable-presence'
|
|
9
|
-
import { WidgetContext, type WidgetPlacement } from '../lib/widget-context'
|
|
10
|
-
|
|
11
|
-
const DEFAULT_HOVER_OPEN_DELAY = 300
|
|
12
|
-
const DEFAULT_HOVER_CLOSE_DELAY = 0
|
|
13
|
-
|
|
14
|
-
export type ChimelingRootProps = WidgetPlacement & {
|
|
15
|
-
userId: string
|
|
16
|
-
children: ReactNode
|
|
17
|
-
poweredBy?: boolean
|
|
18
|
-
openOnHover?: boolean
|
|
19
|
-
hoverOpenDelay?: number
|
|
20
|
-
hoverCloseDelay?: number
|
|
21
|
-
onReady?: (presence: ShowablePresence) => void
|
|
22
|
-
presence?: GetResolvedUserPresenceResponse
|
|
23
|
-
refreshInterval?: number
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function ChimelingRoot({
|
|
27
|
-
userId,
|
|
28
|
-
children,
|
|
29
|
-
poweredBy = true,
|
|
30
|
-
openOnHover = false,
|
|
31
|
-
hoverOpenDelay = DEFAULT_HOVER_OPEN_DELAY,
|
|
32
|
-
hoverCloseDelay = DEFAULT_HOVER_CLOSE_DELAY,
|
|
33
|
-
onReady,
|
|
34
|
-
presence: presenceOverride,
|
|
35
|
-
refreshInterval,
|
|
36
|
-
side,
|
|
37
|
-
align,
|
|
38
|
-
sideOffset,
|
|
39
|
-
}: ChimelingRootProps) {
|
|
40
|
-
const { client } = useChimelingContext()
|
|
41
|
-
const fetched = useResolvedUserPresence(presenceOverride === undefined ? userId : '', {
|
|
42
|
-
refreshInterval,
|
|
43
|
-
})
|
|
44
|
-
const presence = presenceOverride ?? fetched.data
|
|
45
|
-
const showable = useMemo(() => getShowablePresence(presence), [presence])
|
|
46
|
-
|
|
47
|
-
const [now, setNow] = useState(() => new Date())
|
|
48
|
-
const readyRef = useRef(false)
|
|
49
|
-
|
|
50
|
-
useEffect(() => {
|
|
51
|
-
if (!showable) {
|
|
52
|
-
return
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const id = setInterval(() => setNow(new Date()), 1000)
|
|
56
|
-
return () => clearInterval(id)
|
|
57
|
-
}, [showable])
|
|
58
|
-
|
|
59
|
-
useEffect(() => {
|
|
60
|
-
if (readyRef.current || !showable) {
|
|
61
|
-
return
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
readyRef.current = true
|
|
65
|
-
onReady?.(showable)
|
|
66
|
-
}, [showable, onReady])
|
|
67
|
-
|
|
68
|
-
const baseUrl = client.getConfig().baseUrl ?? ''
|
|
69
|
-
const placement = useMemo(() => ({ side, align, sideOffset }), [side, align, sideOffset])
|
|
70
|
-
|
|
71
|
-
const value = useMemo(
|
|
72
|
-
() => ({
|
|
73
|
-
showable,
|
|
74
|
-
now,
|
|
75
|
-
poweredBy,
|
|
76
|
-
openOnHover,
|
|
77
|
-
hoverOpenDelay,
|
|
78
|
-
hoverCloseDelay,
|
|
79
|
-
placement,
|
|
80
|
-
baseUrl,
|
|
81
|
-
}),
|
|
82
|
-
[showable, now, poweredBy, openOnHover, hoverOpenDelay, hoverCloseDelay, placement, baseUrl],
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
return (
|
|
86
|
-
<WidgetContext.Provider value={value}>
|
|
87
|
-
{/* TODO: decide whether to expose Base UI Root props (open, onOpenChange, …). */}
|
|
88
|
-
{showable ? <Popover.Root>{children}</Popover.Root> : children}
|
|
89
|
-
</WidgetContext.Provider>
|
|
90
|
-
)
|
|
91
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { Popover } from '@base-ui/react/popover'
|
|
4
|
-
import type { PopoverTriggerProps } from '@base-ui/react/popover'
|
|
5
|
-
import { useWidgetContext } from '../lib/widget-context'
|
|
6
|
-
|
|
7
|
-
// TODO: decide whether to expose the rest of Base UI Trigger props.
|
|
8
|
-
export type ChimelingTriggerProps = Pick<
|
|
9
|
-
PopoverTriggerProps,
|
|
10
|
-
'className' | 'style' | 'nativeButton' | 'id'
|
|
11
|
-
>
|
|
12
|
-
|
|
13
|
-
export function ChimelingTrigger({ className, style, nativeButton, id }: ChimelingTriggerProps) {
|
|
14
|
-
const { showable, openOnHover, hoverOpenDelay, hoverCloseDelay } = useWidgetContext()
|
|
15
|
-
|
|
16
|
-
if (!showable) {
|
|
17
|
-
return null
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<Popover.Trigger
|
|
22
|
-
className={className}
|
|
23
|
-
style={style}
|
|
24
|
-
nativeButton={nativeButton}
|
|
25
|
-
id={id}
|
|
26
|
-
openOnHover={openOnHover}
|
|
27
|
-
delay={hoverOpenDelay}
|
|
28
|
-
closeDelay={hoverCloseDelay}
|
|
29
|
-
>
|
|
30
|
-
{showable.activity.gerund}
|
|
31
|
-
</Popover.Trigger>
|
|
32
|
-
)
|
|
33
|
-
}
|
package/src/hooks/index.ts
DELETED
package/src/hooks/use-client.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import useSWR from 'swr'
|
|
4
|
-
import { getResolvedUserPresence } from '@cairntale/chimeling-js'
|
|
5
|
-
import { useChimelingContext } from '../lib/context'
|
|
6
|
-
import { resolvedUserPresenceKey } from '../lib/presence-key'
|
|
7
|
-
|
|
8
|
-
export function useResolvedUserPresence(
|
|
9
|
-
userId: string,
|
|
10
|
-
options?: {
|
|
11
|
-
refreshInterval?: number
|
|
12
|
-
},
|
|
13
|
-
) {
|
|
14
|
-
const { client, refreshInterval } = useChimelingContext()
|
|
15
|
-
const baseUrl = client.getConfig().baseUrl ?? ''
|
|
16
|
-
|
|
17
|
-
return useSWR(
|
|
18
|
-
userId ? resolvedUserPresenceKey(userId, baseUrl) : null,
|
|
19
|
-
async ([, , , id]) => {
|
|
20
|
-
const { data } = await getResolvedUserPresence({ userId: id }, { client, throwOnError: true })
|
|
21
|
-
return data
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
refreshInterval: options?.refreshInterval ?? refreshInterval,
|
|
25
|
-
},
|
|
26
|
-
)
|
|
27
|
-
}
|
package/src/index.ts
DELETED
package/src/lib/config.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import packageJson from '../../package.json' with { type: 'json' }
|
|
2
|
-
|
|
3
|
-
export const PACKAGE_NAME = packageJson.name
|
|
4
|
-
|
|
5
|
-
export const DEFAULT_PRESENCE_REFRESH_INTERVAL = 5_000
|
|
6
|
-
|
|
7
|
-
export function packageErrorMessage(message: string): string {
|
|
8
|
-
return `${PACKAGE_NAME}: ${message}`
|
|
9
|
-
}
|
package/src/lib/context.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { createContext, useContext } from 'react'
|
|
4
|
-
import type { ChimelingClient } from '../types'
|
|
5
|
-
import { packageErrorMessage } from './config'
|
|
6
|
-
|
|
7
|
-
type ChimelingContextValue = {
|
|
8
|
-
client: ChimelingClient
|
|
9
|
-
refreshInterval: number
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const ChimelingContext = createContext<ChimelingContextValue | null>(null)
|
|
13
|
-
|
|
14
|
-
export function useChimelingContext(): ChimelingContextValue {
|
|
15
|
-
const context = useContext(ChimelingContext)
|
|
16
|
-
if (!context) {
|
|
17
|
-
throw new Error(
|
|
18
|
-
packageErrorMessage(
|
|
19
|
-
'This hook or component can only be used within the <ChimelingProvider /> component.',
|
|
20
|
-
),
|
|
21
|
-
)
|
|
22
|
-
}
|
|
23
|
-
return context
|
|
24
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { formatDuration, intervalToDuration } from 'date-fns'
|
|
2
|
-
|
|
3
|
-
const UNITS = ['years', 'months', 'weeks', 'days', 'hours', 'minutes'] as const
|
|
4
|
-
|
|
5
|
-
const JUST_NOW_MS = 60_000
|
|
6
|
-
|
|
7
|
-
export function formatActivityDuration(startedAt: Date, now: Date): string {
|
|
8
|
-
const start = startedAt instanceof Date ? startedAt : new Date(startedAt)
|
|
9
|
-
const elapsedMs = now.getTime() - start.getTime()
|
|
10
|
-
if (elapsedMs < JUST_NOW_MS) {
|
|
11
|
-
return 'just now'
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const duration = intervalToDuration({ start, end: now })
|
|
15
|
-
const unit = UNITS.find(name => (duration[name] ?? 0) > 0)
|
|
16
|
-
if (!unit) {
|
|
17
|
-
return 'just now'
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return `for ${formatDuration({ [unit]: duration[unit] }, { format: [unit] })}`
|
|
21
|
-
}
|