@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.
Files changed (27) hide show
  1. package/README.md +1 -0
  2. package/dist/{ChatProfileModal-DJcii-VO.js → ChatProfileModal-DJu41Pd7.js} +6 -3
  3. package/dist/{FilePreviewModal-CZtgkIHl.js → FilePreviewModal-C9LtTRwf.js} +63 -63
  4. package/dist/{ManageDataModal-aOWxvxSs.js → ManageDataModal-W7YjmIsd.js} +3 -3
  5. package/dist/{MarkdownBody-neDkIgJl.js → MarkdownBody-k4RvO7v6.js} +3 -3
  6. package/dist/{SideDrawer-mSCnYOgy.js → SideDrawer-DTXPBfJl.js} +25 -25
  7. package/dist/{UserProfileModal-a_O8hh1O.js → UserProfileModal-CyUJ-rFm.js} +3 -3
  8. package/dist/{UserSettingsModal-92nP1add.js → UserSettingsModal-DJYdx8X3.js} +9 -9
  9. package/dist/{VideoCallOverlay-DYd3pFuD.js → VideoCallOverlay-C7flkkQ5.js} +2673 -2662
  10. package/dist/VisibilityModal-Dq_1oMwl.js +138 -0
  11. package/dist/components/MainComponents/SealedAttachmentList.d.ts +14 -0
  12. package/dist/components/RoomComponents/styled/LastMessageSealed.d.ts +14 -0
  13. package/dist/components/RoomComponents/styled/StyledRoomComponents.d.ts +4 -2
  14. package/dist/components/styled/AudioMessage.d.ts +38 -0
  15. package/dist/e2ee/fileEnvelope.d.ts +48 -0
  16. package/dist/{handleCopyClick-tizCFAb8.js → handleCopyClick-Cv8OCQA-.js} +2 -2
  17. package/dist/helpers/sealedAttachments.d.ts +25 -0
  18. package/dist/{main-DkokKp7d.js → main-DaxJ3rMp.js} +9701 -8964
  19. package/dist/main.js +5 -5
  20. package/dist/networking/api-requests/auth.api.d.ts +10 -1
  21. package/dist/networking/xmpp/sendMediaMessage.xmpp.d.ts +12 -1
  22. package/dist/styles/tokens.d.ts +26 -4
  23. package/dist/types/models/config.model.d.ts +19 -0
  24. package/dist/types/models/message.model.d.ts +17 -0
  25. package/dist/version.d.ts +1 -1
  26. package/package.json +1 -1
  27. 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<HTMLDivElement>, HTMLDivElement>, never>> & string;
4
- export declare const LastRoomMessageText: 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<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 { v as e, x as t } from "./main-DkokKp7d.js";
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>;