@ethora/chat-component 26.8.2 → 26.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/{ChatProfileModal-DJcii-VO.js → ChatProfileModal-DJu41Pd7.js} +6 -3
- package/dist/{FilePreviewModal-CZtgkIHl.js → FilePreviewModal-C9LtTRwf.js} +63 -63
- package/dist/{ManageDataModal-aOWxvxSs.js → ManageDataModal-W7YjmIsd.js} +3 -3
- package/dist/{MarkdownBody-neDkIgJl.js → MarkdownBody-k4RvO7v6.js} +3 -3
- package/dist/{SideDrawer-mSCnYOgy.js → SideDrawer-DTXPBfJl.js} +25 -25
- package/dist/{UserProfileModal-a_O8hh1O.js → UserProfileModal-CyUJ-rFm.js} +3 -3
- package/dist/{UserSettingsModal-92nP1add.js → UserSettingsModal-DJYdx8X3.js} +9 -9
- package/dist/{VideoCallOverlay-DYd3pFuD.js → VideoCallOverlay-C7flkkQ5.js} +2673 -2662
- package/dist/VisibilityModal-Dq_1oMwl.js +138 -0
- package/dist/components/MainComponents/SealedAttachmentList.d.ts +14 -0
- package/dist/components/RoomComponents/styled/LastMessageSealed.d.ts +14 -0
- package/dist/components/RoomComponents/styled/StyledRoomComponents.d.ts +4 -2
- package/dist/components/styled/AudioMessage.d.ts +38 -0
- package/dist/e2ee/fileEnvelope.d.ts +48 -0
- package/dist/{handleCopyClick-tizCFAb8.js → handleCopyClick-Cv8OCQA-.js} +2 -2
- package/dist/helpers/sealedAttachments.d.ts +25 -0
- package/dist/{main-DkokKp7d.js → main-DaxJ3rMp.js} +9701 -8964
- package/dist/main.js +5 -5
- package/dist/networking/api-requests/auth.api.d.ts +10 -1
- package/dist/networking/xmpp/sendMediaMessage.xmpp.d.ts +12 -1
- package/dist/styles/tokens.d.ts +26 -4
- package/dist/types/models/config.model.d.ts +19 -0
- package/dist/types/models/message.model.d.ts +17 -0
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/dist/VisibilityModal-BKI5XKir.js +0 -132
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { jsxs as r, jsx as s } from "react/jsx-runtime";
|
|
2
|
+
import { useState as b, useEffect as x } from "react";
|
|
3
|
+
import { x as o, a as L, p as P, u as z, aG as D, aH as I } from "./main-DaxJ3rMp.js";
|
|
4
|
+
import { S as N, D as C, a as $, b as k, e as w, f as p } from "./SideDrawer-DTXPBfJl.js";
|
|
5
|
+
o.div``;
|
|
6
|
+
o.div`
|
|
7
|
+
font-size: 16px;
|
|
8
|
+
font-weight: bold;
|
|
9
|
+
color: var(--ethora-color-text, #141414);
|
|
10
|
+
`;
|
|
11
|
+
o.p`
|
|
12
|
+
font-size: 14px;
|
|
13
|
+
color: var(--ethora-color-text-muted, #6b7280);
|
|
14
|
+
margin-bottom: 32px;
|
|
15
|
+
`;
|
|
16
|
+
const O = o.div`
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
gap: 6px;
|
|
20
|
+
`, h = o.label`
|
|
21
|
+
display: flex;
|
|
22
|
+
align-items: center;
|
|
23
|
+
font-size: 14px;
|
|
24
|
+
gap: 8px;
|
|
25
|
+
`, u = o.input`
|
|
26
|
+
accent-color: ${({ $radioColor: i }) => i || "var(--ethora-color-primary, #0052CD)"};
|
|
27
|
+
margin: 0px;
|
|
28
|
+
`, R = o.div`
|
|
29
|
+
padding: 10px 15px;
|
|
30
|
+
margin-top: 16px;
|
|
31
|
+
color: ${({ type: i }) => i === "success" ? "#10b981" : "#ef4444"};
|
|
32
|
+
background: ${({ type: i }) => i === "success" ? "#d1fae5" : "#fee2e2"};
|
|
33
|
+
border: 1px solid
|
|
34
|
+
${({ type: i }) => i === "success" ? "#10b981" : "#ef4444"};
|
|
35
|
+
border-radius: 4px;
|
|
36
|
+
|
|
37
|
+
[data-ethora-color-scheme='dark'] & {
|
|
38
|
+
color: ${({ type: i }) => i === "success" ? "var(--ethora-color-success, #10b981)" : "var(--ethora-color-danger, #ef4444)"};
|
|
39
|
+
background: ${({ type: i }) => i === "success" ? "rgba(16, 185, 129, 0.15)" : "rgba(242, 118, 107, 0.15)"};
|
|
40
|
+
border-color: ${({ type: i }) => i === "success" ? "var(--ethora-color-success, #10b981)" : "var(--ethora-color-danger, #ef4444)"};
|
|
41
|
+
}
|
|
42
|
+
`, H = ({
|
|
43
|
+
handleCloseModal: i
|
|
44
|
+
}) => {
|
|
45
|
+
const S = L(), e = P(), { user: n, config: l } = z(
|
|
46
|
+
(t) => t.chatSettingStore
|
|
47
|
+
), g = (t) => S(I(t)), [a, m] = b(n?.isProfileOpen), [c, v] = b(n?.isAssetsOpen), [f, y] = b(null), d = (t, A) => {
|
|
48
|
+
y({ message: t, type: A }), setTimeout(() => y(null), 3e3);
|
|
49
|
+
};
|
|
50
|
+
return x(() => {
|
|
51
|
+
a !== n.isProfileOpen && D({ isProfileOpen: a }).then(({ data: t }) => {
|
|
52
|
+
g(t.user), d(e("notification.saved"), "success");
|
|
53
|
+
}).catch(() => d(e("toast.error"), "error"));
|
|
54
|
+
}, [a]), x(() => {
|
|
55
|
+
c !== n?.isAssetsOpen && D({ isAssetsOpen: c }).then(({ data: t }) => {
|
|
56
|
+
g(t.user), d(e("notification.saved"), "success");
|
|
57
|
+
}).catch(() => d(e("toast.error"), "error"));
|
|
58
|
+
}, [c]), /* @__PURE__ */ r(
|
|
59
|
+
N,
|
|
60
|
+
{
|
|
61
|
+
title: e("settings.visibility.title"),
|
|
62
|
+
onClose: i,
|
|
63
|
+
backLabel: e("action.back"),
|
|
64
|
+
children: [
|
|
65
|
+
/* @__PURE__ */ r(C, { children: [
|
|
66
|
+
/* @__PURE__ */ s($, { children: e("settings.visibility.profileLabel") }),
|
|
67
|
+
/* @__PURE__ */ s(k, { children: /* @__PURE__ */ s(w, { children: /* @__PURE__ */ r(O, { children: [
|
|
68
|
+
/* @__PURE__ */ r(h, { children: [
|
|
69
|
+
/* @__PURE__ */ s(
|
|
70
|
+
u,
|
|
71
|
+
{
|
|
72
|
+
$radioColor: l?.colors?.primary,
|
|
73
|
+
type: "radio",
|
|
74
|
+
name: "ethora-profile-visibility",
|
|
75
|
+
checked: a === !0,
|
|
76
|
+
onChange: () => m(!0)
|
|
77
|
+
}
|
|
78
|
+
),
|
|
79
|
+
e("settings.visibility.open")
|
|
80
|
+
] }),
|
|
81
|
+
/* @__PURE__ */ s(p, { children: e("settings.visibility.openDescription") }),
|
|
82
|
+
/* @__PURE__ */ r(h, { children: [
|
|
83
|
+
/* @__PURE__ */ s(
|
|
84
|
+
u,
|
|
85
|
+
{
|
|
86
|
+
$radioColor: l?.colors?.primary,
|
|
87
|
+
type: "radio",
|
|
88
|
+
name: "ethora-profile-visibility",
|
|
89
|
+
checked: a === !1,
|
|
90
|
+
onChange: () => m(!1)
|
|
91
|
+
}
|
|
92
|
+
),
|
|
93
|
+
e("settings.visibility.restricted")
|
|
94
|
+
] }),
|
|
95
|
+
/* @__PURE__ */ s(p, { children: e("settings.visibility.restrictedDescription") })
|
|
96
|
+
] }) }) })
|
|
97
|
+
] }),
|
|
98
|
+
/* @__PURE__ */ r(C, { children: [
|
|
99
|
+
/* @__PURE__ */ s($, { children: e("settings.visibility.documentsLabel") }),
|
|
100
|
+
/* @__PURE__ */ s(k, { children: /* @__PURE__ */ s(w, { children: /* @__PURE__ */ r(O, { children: [
|
|
101
|
+
/* @__PURE__ */ r(h, { children: [
|
|
102
|
+
/* @__PURE__ */ s(
|
|
103
|
+
u,
|
|
104
|
+
{
|
|
105
|
+
$radioColor: l?.colors?.primary,
|
|
106
|
+
type: "radio",
|
|
107
|
+
name: "ethora-documents-visibility",
|
|
108
|
+
checked: c === !0,
|
|
109
|
+
onChange: () => v(!0)
|
|
110
|
+
}
|
|
111
|
+
),
|
|
112
|
+
e("settings.visibility.full")
|
|
113
|
+
] }),
|
|
114
|
+
/* @__PURE__ */ s(p, { children: e("settings.visibility.fullDescription") }),
|
|
115
|
+
/* @__PURE__ */ r(h, { children: [
|
|
116
|
+
/* @__PURE__ */ s(
|
|
117
|
+
u,
|
|
118
|
+
{
|
|
119
|
+
$radioColor: l?.colors?.primary,
|
|
120
|
+
type: "radio",
|
|
121
|
+
name: "ethora-documents-visibility",
|
|
122
|
+
checked: c === !1,
|
|
123
|
+
onChange: () => v(!1)
|
|
124
|
+
}
|
|
125
|
+
),
|
|
126
|
+
e("settings.visibility.individual")
|
|
127
|
+
] }),
|
|
128
|
+
/* @__PURE__ */ s(p, { children: e("settings.visibility.individualDescription") })
|
|
129
|
+
] }) }) })
|
|
130
|
+
] }),
|
|
131
|
+
f && /* @__PURE__ */ s(R, { type: f.type, children: f.message })
|
|
132
|
+
]
|
|
133
|
+
}
|
|
134
|
+
);
|
|
135
|
+
};
|
|
136
|
+
export {
|
|
137
|
+
H as default
|
|
138
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import { IAttachment } from '../../types/types';
|
|
3
|
+
/**
|
|
4
|
+
* Sealed attachments, offered as downloads rather than previews.
|
|
5
|
+
*
|
|
6
|
+
* `keys` are positional: keys[i] opens attachments[i] (getDataFromXml lifts
|
|
7
|
+
* them out of the OMEMO-decrypted <body>). An attachment with no key belongs
|
|
8
|
+
* to a message we could not decrypt, and renders as unavailable.
|
|
9
|
+
*/
|
|
10
|
+
declare const SealedAttachmentList: React.FC<{
|
|
11
|
+
attachments: IAttachment[];
|
|
12
|
+
keys?: string[];
|
|
13
|
+
}>;
|
|
14
|
+
export default SealedAttachmentList;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { LastMessage } from '../../../types/types';
|
|
3
|
+
interface LastMessageSealedProps extends Pick<LastMessage, 'user'> {
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Room-list preview for a sealed attachment.
|
|
7
|
+
*
|
|
8
|
+
* It cannot say what the file is: the name and type are inside the seal and
|
|
9
|
+
* only come out on download, and the server-held ones are placeholders - an
|
|
10
|
+
* opaque `originalName` and `application/octet-stream`. So it says exactly
|
|
11
|
+
* what is known, which is that an encrypted file arrived.
|
|
12
|
+
*/
|
|
13
|
+
declare const LastMessageSealed: FC<LastMessageSealedProps>;
|
|
14
|
+
export default LastMessageSealed;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
2
|
export declare const LastRoomMessageContainer: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
3
|
-
export declare const LastRoomMessageName: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<
|
|
4
|
-
export declare const LastRoomMessageText: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<
|
|
3
|
+
export declare const LastRoomMessageName: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, never>> & string;
|
|
4
|
+
export declare const LastRoomMessageText: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, never>> & string;
|
|
5
|
+
/** Thumbnail/icon + label for a media preview, flowing in the same line. */
|
|
6
|
+
export declare const LastRoomMessageMedia: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, never>> & string;
|
|
5
7
|
export declare const NewMessageMarker: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
6
8
|
export declare const LastMessageImg: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<React.DetailedHTMLProps<React.ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement>, never>> & string;
|
|
7
9
|
export declare const ShadeWrapper: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
@@ -2,5 +2,43 @@ import { default as React } from 'react';
|
|
|
2
2
|
interface AudioMessageProps {
|
|
3
3
|
src: string;
|
|
4
4
|
}
|
|
5
|
+
/**
|
|
6
|
+
* wavesurfer paints into a canvas, and a canvas fill takes a concrete
|
|
7
|
+
* colour - it cannot read `var(--ethora-color-primary)` or `color-mix()`
|
|
8
|
+
* portably. So the colours are resolved off the DOM and mixed here.
|
|
9
|
+
*/
|
|
10
|
+
type Rgb = {
|
|
11
|
+
r: number;
|
|
12
|
+
g: number;
|
|
13
|
+
b: number;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Played and unplayed bar colours for whatever this bubble turned out to
|
|
17
|
+
* be. The brand colour leads when it can be seen against the bubble; when
|
|
18
|
+
* it cannot (own-message bubbles, and any host whose primary is close to
|
|
19
|
+
* its own surface) the bubble's text colour takes over, which is readable
|
|
20
|
+
* on that surface by definition. Unplayed bars are the same colour mixed
|
|
21
|
+
* halfway into the surface, so they read as "the rest of the clip" rather
|
|
22
|
+
* than as a different element.
|
|
23
|
+
*/
|
|
24
|
+
export declare const resolveWaveColors: (primary: string, text: string, surface: Rgb) => {
|
|
25
|
+
wave: string;
|
|
26
|
+
progress: string;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Bars for one voice note.
|
|
30
|
+
*
|
|
31
|
+
* NOT the peak envelope wavesurfer draws by itself. Measured on a real QA
|
|
32
|
+
* recording: two transients hit 0.98 and 0.99 while the speech around them
|
|
33
|
+
* sits at an RMS of 0.002 to 0.058. Scaled against the peak, those two
|
|
34
|
+
* samples own the whole 28px and every other bar collapses to a hairline -
|
|
35
|
+
* the "faint dashed line with a couple of spikes" look these bubbles had.
|
|
36
|
+
*
|
|
37
|
+
* So: RMS per bucket (what the ear hears, not the worst sample), scaled
|
|
38
|
+
* against the 95th percentile so a single click cannot set the scale, then
|
|
39
|
+
* a square-root curve because loudness is perceived that way, and a floor
|
|
40
|
+
* so a quiet passage still reads as a bar rather than as nothing.
|
|
41
|
+
*/
|
|
42
|
+
export declare const buildWaveformPeaks: (samples: Float32Array, barCount: number) => number[];
|
|
5
43
|
declare const AudioMessage: React.FC<AudioMessageProps>;
|
|
6
44
|
export default AudioMessage;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** What the server must not learn, and the receiver needs to render. */
|
|
2
|
+
export interface FileEnvelopeMeta {
|
|
3
|
+
mimetype: string;
|
|
4
|
+
originalname: string;
|
|
5
|
+
size: number;
|
|
6
|
+
}
|
|
7
|
+
export interface SealedFile {
|
|
8
|
+
/** Ciphertext, ready to upload. */
|
|
9
|
+
ciphertext: Uint8Array;
|
|
10
|
+
/** Base64 of the 48-byte key material. Belongs in the encrypted <body>. */
|
|
11
|
+
keyMaterial: string;
|
|
12
|
+
/** Random, extension-free name to upload under. */
|
|
13
|
+
filename: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* MAGIC(8) | version(1) | headerLen(4, big-endian) | headerJSON | fileBytes
|
|
17
|
+
*
|
|
18
|
+
* Length-prefixed rather than delimited: a JSON header is arbitrary text and
|
|
19
|
+
* the body is arbitrary bytes, so there is no separator that cannot occur in
|
|
20
|
+
* either.
|
|
21
|
+
*/
|
|
22
|
+
export declare function encodeFileEnvelope(meta: FileEnvelopeMeta, bytes: Uint8Array): Uint8Array;
|
|
23
|
+
export declare function decodeFileEnvelope(envelope: Uint8Array): {
|
|
24
|
+
meta: FileEnvelopeMeta;
|
|
25
|
+
bytes: Uint8Array;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Seal a file for upload.
|
|
29
|
+
*
|
|
30
|
+
* Reuses the OMEMO payload primitives (AES-256-CBC + truncated HMAC, fresh
|
|
31
|
+
* random key per call) so attachments and message bodies are protected by the
|
|
32
|
+
* same construction rather than a second, separately-reviewed one.
|
|
33
|
+
*/
|
|
34
|
+
export declare function sealFileForUpload(file: File | Blob): Promise<SealedFile>;
|
|
35
|
+
/** Inverse of sealFileForUpload, for the receiving side. */
|
|
36
|
+
export declare function openSealedFile(ciphertext: Uint8Array, keyMaterial: string): {
|
|
37
|
+
meta: FileEnvelopeMeta;
|
|
38
|
+
bytes: Uint8Array;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Pull the attachment keys out of a decrypted media <body>.
|
|
42
|
+
*
|
|
43
|
+
* The sender put `{v:1,keys:[...]}` there instead of the usual literal
|
|
44
|
+
* "media" (see sendMediaMessage). Returns undefined for anything else, so
|
|
45
|
+
* an ordinary media message - or one from a build that predates sealing -
|
|
46
|
+
* falls through untouched.
|
|
47
|
+
*/
|
|
48
|
+
export declare function parseSealedMediaBody(body?: string): string[] | undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { x as e, z as t } from "./main-DaxJ3rMp.js";
|
|
2
2
|
const i = e.div`
|
|
3
3
|
display: flex;
|
|
4
4
|
flex-direction: column;
|
|
@@ -59,7 +59,7 @@ const i = e.div`
|
|
|
59
59
|
padding: var(--ethora-space-1, 4px) var(--ethora-space-3, 12px);
|
|
60
60
|
border-radius: var(--ethora-radius-full, 999px);
|
|
61
61
|
background: var(--ethora-color-primary-soft, #e7edf9);
|
|
62
|
-
color: var(--ethora-color-primary, #0052cd);
|
|
62
|
+
color: var(--ethora-color-primary-text, var(--ethora-color-primary, #0052cd));
|
|
63
63
|
font-size: var(--ethora-font-size-xs, 12px);
|
|
64
64
|
font-weight: var(--ethora-font-weight-semibold, 600);
|
|
65
65
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { FileEnvelopeMeta } from '../e2ee/fileEnvelope';
|
|
2
|
+
export interface OpenedAttachment {
|
|
3
|
+
bytes: Uint8Array;
|
|
4
|
+
meta: FileEnvelopeMeta;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Fetch and decrypt one sealed attachment.
|
|
8
|
+
*
|
|
9
|
+
* `url` must already carry the viewer's `?ft=` token where the file is
|
|
10
|
+
* membership-gated - this is a plain fetch of whatever it is given.
|
|
11
|
+
*/
|
|
12
|
+
export declare function openSealedAttachment(url: string, keyMaterial: string, fetchImpl?: typeof fetch): Promise<OpenedAttachment>;
|
|
13
|
+
/** Hands a blob to the browser as a download. Injectable for tests. */
|
|
14
|
+
export type SaveBlob = (blob: Blob, fileName: string) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Fetch, decrypt and save one sealed attachment under its real filename.
|
|
17
|
+
*
|
|
18
|
+
* Returns the recovered metadata so the caller can label the card with the
|
|
19
|
+
* real name once it is known - before the first download there is no way to
|
|
20
|
+
* know it, because it lives inside the seal.
|
|
21
|
+
*/
|
|
22
|
+
export declare function saveSealedAttachment(url: string, keyMaterial: string, deps?: {
|
|
23
|
+
fetchImpl?: typeof fetch;
|
|
24
|
+
save?: SaveBlob;
|
|
25
|
+
}): Promise<FileEnvelopeMeta>;
|