@ethora/chat-component 26.9.5 → 26.9.8
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 +45 -1
- package/dist/{ChatProfileModal-Pr4--RdW.js → ChatProfileModal-3UH0H5Ec.js} +725 -714
- package/dist/{FilePreviewModal-CYHlKzRc.js → FilePreviewModal-Cgj1iHry.js} +161 -161
- package/dist/{ManageDataModal-BE1eGlYh.js → ManageDataModal-BblTuE9g.js} +14 -14
- package/dist/{MarkdownBody-G0_J0NL6.js → MarkdownBody-RMEx8yW2.js} +1 -1
- package/dist/MessageSearchModal-CrYR2EH8.js +381 -0
- package/dist/PublicChatsModal-Bi5r4mrV.js +202 -0
- package/dist/{SideDrawer-BG2m7Bcm.js → SideDrawer-CDAoobvX.js} +34 -34
- package/dist/{UserProfileModal-B2Hp5bAF.js → UserProfileModal-4Frgvam-.js} +3 -3
- package/dist/{UserSettingsModal-D871FDAT.js → UserSettingsModal-C-mb-9cb.js} +2 -2
- package/dist/{VideoCallOverlay-Br0ji0nZ.js → VideoCallOverlay-92dFLb9U.js} +1 -1
- package/dist/{VisibilityModal-DdFBV1_g.js → VisibilityModal-zIAV6dAP.js} +9 -9
- package/dist/assets/icons.d.ts +9 -0
- package/dist/components/InputComponents/MentionDropdown.d.ts +2 -0
- package/dist/components/InputComponents/Search.d.ts +4 -0
- package/dist/components/MainComponents/ArchivedMessageCard.d.ts +11 -0
- package/dist/components/MainComponents/scrollAnchor.d.ts +19 -0
- package/dist/components/MainComponents/useJumpToMessage.d.ts +94 -0
- package/dist/components/MainComponents/useScrollAnchor.d.ts +29 -0
- package/dist/components/MainComponents/useStickToBottom.d.ts +27 -0
- package/dist/components/Modals/MessageSearchModal/MessageHitResults.d.ts +27 -0
- package/dist/components/Modals/MessageSearchModal/MessageSearchModal.d.ts +6 -0
- package/dist/components/Modals/MessageSearchModal/resolveSender.d.ts +33 -0
- package/dist/components/Modals/MessageSearchModal/searchFilters.d.ts +34 -0
- package/dist/components/Modals/MessageSearchModal/snippet.d.ts +14 -0
- package/dist/components/Modals/MessageSearchModal/useMessageSearch.d.ts +22 -0
- package/dist/components/Modals/PublicChatsModal/PublicChatsModal.d.ts +13 -0
- package/dist/components/Modals/styledModalComponents.d.ts +0 -8
- package/dist/components/styled/RoomListComponents/index.d.ts +3 -0
- package/dist/context/ToastContext.d.ts +7 -0
- package/dist/firebase-messaging-sw.js +21 -3
- package/dist/{handleCopyClick-DPjxMoJn.js → handleCopyClick-BtXos0AJ.js} +1 -1
- package/dist/helpers/checkUniqueUsers.d.ts +2 -2
- package/dist/helpers/constants/MODAL_TYPES.d.ts +2 -0
- package/dist/helpers/dedupeMembers.d.ts +10 -0
- package/dist/helpers/historyPreloadConfig.d.ts +25 -0
- package/dist/helpers/historyPreloadScheduler.d.ts +3 -0
- package/dist/helpers/insertMessageWithDelimiter.d.ts +1 -0
- package/dist/helpers/isMessageSearchEnabled.d.ts +9 -0
- package/dist/helpers/jumpThread.d.ts +56 -0
- package/dist/helpers/jumpWindow.d.ts +73 -0
- package/dist/helpers/nanoToMs.d.ts +1 -1
- package/dist/helpers/openRoomAtMessage.d.ts +17 -0
- package/dist/helpers/openThreadForJump.d.ts +26 -0
- package/dist/helpers/pdf/loadPdfjs.d.ts +8 -1
- package/dist/helpers/roomUserCount.d.ts +19 -0
- package/dist/helpers/userResolver.d.ts +57 -0
- package/dist/helpers/xmppIdShape.d.ts +1 -1
- package/dist/hooks/useChatWrapperInit.d.ts +6 -0
- package/dist/hooks/useMessageSearchShortcut.d.ts +14 -0
- package/dist/hooks/usePdfThumbnail.d.ts +2 -1
- package/dist/hooks/useRoomDirectory.d.ts +11 -0
- package/dist/hooks/useRoomState.d.ts +1 -0
- package/dist/{main-BRIZ98KV.js → main-BnLtessp.js} +24764 -21859
- package/dist/main.css +1 -1
- package/dist/main.js +16 -16
- package/dist/networking/api-requests/messageSearch.api.d.ts +56 -0
- package/dist/networking/api-requests/publicChats.api.d.ts +23 -0
- package/dist/networking/api-requests/roomMembers.api.d.ts +18 -0
- package/dist/networking/api-requests/xmppToken.api.d.ts +1 -0
- package/dist/networking/ethoraEvents.d.ts +44 -0
- package/dist/networking/stanzaHandlers.d.ts +2 -2
- package/dist/networking/xmpp/allRoomPresences.xmpp.d.ts +18 -1
- package/dist/networking/xmpp/presenceInRoom.xmpp.d.ts +1 -1
- package/dist/networking/xmppClient.d.ts +58 -2
- package/dist/roomStore/Middleware/jumpThreadMiddleware.d.ts +2 -0
- package/dist/roomStore/chatSettingsSlice.d.ts +1 -1
- package/dist/roomStore/index.d.ts +12 -0
- package/dist/roomStore/roomsSlice.d.ts +70 -1
- package/dist/roomStore/safeKey.d.ts +12 -0
- package/dist/roomStore/usersSetCap.d.ts +15 -0
- package/dist/styles/classNames.d.ts +2 -0
- package/dist/types/models/config.model.d.ts +78 -0
- package/dist/types/models/room.model.d.ts +14 -0
- package/dist/types/models/xmpp.model.d.ts +21 -1
- package/dist/utils/notificationUtils.d.ts +12 -0
- package/dist/version.d.ts +1 -1
- package/package.json +2 -1
- package/public/firebase-messaging-sw.js +21 -3
- package/dist/components/Modals/NewChatModal/UpdNewChatModal.d.ts +0 -3
- package/dist/components/SortDropDown/index.d.ts +0 -8
|
@@ -9,8 +9,6 @@ export declare const ModalContainer: import('styled-components/dist/types').ISty
|
|
|
9
9
|
export declare const ModalTitle: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>, never>> & string;
|
|
10
10
|
/** Small uppercase-weight label above a field/list inside a modal - e.g. "Select Users (max 20)". */
|
|
11
11
|
export declare const ModalSectionLabel: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
12
|
-
/** 48px-min-height row used for members/blocked-users/etc. lists inside modals. */
|
|
13
|
-
export declare const ModalListRow: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
14
12
|
export declare const ModalDescription: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>, never>> & string;
|
|
15
13
|
export declare const GroupContainer: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
16
14
|
export declare const ModalContainerFullScreen: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<import('styled-components').FastOmit<import('styled-components/dist/types').Substitute<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, Omit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
|
|
@@ -24,15 +22,9 @@ export declare const HeaderRight: import('styled-components/dist/types').IStyled
|
|
|
24
22
|
export declare const CenterContainer: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<Omit<import('styled-components').FastOmit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>, "ref"> & {
|
|
25
23
|
ref?: import('react').Ref<HTMLDivElement>;
|
|
26
24
|
}, never>> & string;
|
|
27
|
-
export declare const ProfileImage: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
28
|
-
export declare const UserInfo: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
29
25
|
export declare const UserName: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
30
|
-
export declare const UserStatus: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
31
|
-
export declare const BorderedContainer: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
32
|
-
export declare const LabelData: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
33
26
|
export declare const Label: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, never>> & string;
|
|
34
27
|
export declare const ActionButton: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<Omit<ButtonProps & import('react').RefAttributes<HTMLButtonElement>, "ref"> & {
|
|
35
28
|
ref?: import('react').Ref<HTMLButtonElement>;
|
|
36
29
|
}, never>> & string & Omit<import('react').ForwardRefExoticComponent<ButtonProps & import('react').RefAttributes<HTMLButtonElement>>, keyof import('react').Component<any, {}, any>>;
|
|
37
|
-
export declare const EmptySection: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
38
30
|
export declare const Divider: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
@@ -37,3 +37,6 @@ export declare const SkeletonLines: import('styled-components/dist/types').IStyl
|
|
|
37
37
|
export declare const SkeletonLine: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components/dist/types').Substitute<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
|
|
38
38
|
$width?: string;
|
|
39
39
|
}>> & string;
|
|
40
|
+
export declare const MessageMatches: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
41
|
+
export declare const MessageMatchesTitle: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
42
|
+
export declare const MessageMatchesNote: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
@@ -7,4 +7,11 @@ export declare const ToastProvider: React.FC<{
|
|
|
7
7
|
children: ReactNode;
|
|
8
8
|
}>;
|
|
9
9
|
export declare const useToast: () => ToastContextType;
|
|
10
|
+
/**
|
|
11
|
+
* `useToast` for code that can run outside a ToastProvider (a MessageList
|
|
12
|
+
* rendered on its own, a host that mounts pieces of the chat directly): it
|
|
13
|
+
* gets `undefined` instead of a thrown error, and simply has nowhere to show
|
|
14
|
+
* a toast.
|
|
15
|
+
*/
|
|
16
|
+
export declare const useOptionalToast: () => ToastContextType | undefined;
|
|
10
17
|
export {};
|
|
@@ -172,8 +172,26 @@ function pickUrl(raw = {}, data = {}, notification = {}) {
|
|
|
172
172
|
);
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
|
|
176
|
-
|
|
175
|
+
// An icon the push itself carries (data.icon, notification.icon or icon on
|
|
176
|
+
// the payload) wins over the one configured for the whole app: a push about a
|
|
177
|
+
// specific chat or sender knows better than the app-wide default. Only http(s)
|
|
178
|
+
// and same-origin paths are accepted; anything else (data:, javascript:, a
|
|
179
|
+
// bare word) is ignored and the configured/default icon is used instead.
|
|
180
|
+
function sanitizeIconUrl(value) {
|
|
181
|
+
const url = typeof value === 'string' ? value.trim() : '';
|
|
182
|
+
if (!url) return '';
|
|
183
|
+
if (url.charAt(0) === '/' && url.charAt(1) !== '/') return url;
|
|
184
|
+
return /^https?:\/\/[^\s]+$/i.test(url) ? url : '';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function pickIcon(raw = {}, data = {}, notification = {}) {
|
|
188
|
+
return (
|
|
189
|
+
sanitizeIconUrl(data.icon) ||
|
|
190
|
+
sanitizeIconUrl(notification.icon) ||
|
|
191
|
+
sanitizeIconUrl(raw.icon) ||
|
|
192
|
+
CUSTOM_ICON ||
|
|
193
|
+
DEFAULT_ICON
|
|
194
|
+
);
|
|
177
195
|
}
|
|
178
196
|
|
|
179
197
|
function pickBadge() {
|
|
@@ -332,7 +350,7 @@ async function handlePush(raw = {}, source = 'push') {
|
|
|
332
350
|
const title = pickTitle(raw, data, notification);
|
|
333
351
|
const body = pickBody(raw, data, notification);
|
|
334
352
|
const url = pickUrl(raw, data, notification);
|
|
335
|
-
const icon = pickIcon();
|
|
353
|
+
const icon = pickIcon(raw, data, notification);
|
|
336
354
|
const badge = pickBadge();
|
|
337
355
|
const image = pickImage(raw, data, notification);
|
|
338
356
|
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { IMessage, RoomMember } from '../types/types';
|
|
2
|
-
export declare const checkUniqueUsers: (messages: IMessage[]) =>
|
|
3
|
-
export declare const checkSingleUser: (usersSet: Record<string, RoomMember>, xmppUsername: string) => Promise<
|
|
2
|
+
export declare const checkUniqueUsers: (messages: IMessage[]) => any;
|
|
3
|
+
export declare const checkSingleUser: (usersSet: Record<string, RoomMember>, xmppUsername: string) => Promise<any>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { RoomMember } from '../types/types';
|
|
2
|
+
/** Stable identity of a member: normalised xmppUsername local part, else _id. */
|
|
3
|
+
export declare const memberKey: (m: Partial<RoomMember> | null | undefined) => string;
|
|
4
|
+
/**
|
|
5
|
+
* Drops repeated members (keeps the first occurrence, order preserved).
|
|
6
|
+
* The room directory endpoint can return the same user on several pages;
|
|
7
|
+
* duplicates would produce duplicate React keys and inflated counts.
|
|
8
|
+
* Members without any identity are dropped.
|
|
9
|
+
*/
|
|
10
|
+
export declare const dedupeMembers: <T extends Partial<RoomMember>>(list: ReadonlyArray<T> | null | undefined) => T[];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { IConfig } from '../types/types';
|
|
2
|
+
export type HistoryPreloadMode = 'staged' | 'all' | 'off';
|
|
3
|
+
export interface ResolvedHistoryPreload {
|
|
4
|
+
/**
|
|
5
|
+
* 'legacy' only when a host explicitly pinned
|
|
6
|
+
* `historyQoS.stagedPreloadEnabled: false`: the old cache catch-up path
|
|
7
|
+
* (updateMessagesTillLast). Everything else resolves to a public mode.
|
|
8
|
+
*/
|
|
9
|
+
mode: HistoryPreloadMode | 'legacy';
|
|
10
|
+
/** Rooms (by recent activity) the background sweep covers. 0 = no cap. */
|
|
11
|
+
topRooms: number;
|
|
12
|
+
concurrency: number;
|
|
13
|
+
firstPassSize: number;
|
|
14
|
+
secondPassSize: number;
|
|
15
|
+
}
|
|
16
|
+
export declare const DEFAULT_PRELOAD_TOP_ROOMS = 8;
|
|
17
|
+
export declare const DEFAULT_PRELOAD_CONCURRENCY = 3;
|
|
18
|
+
export declare const TEASER_ROOM_FACTOR = 4;
|
|
19
|
+
/**
|
|
20
|
+
* One place that turns `config.historyPreload` (and the older
|
|
21
|
+
* `historyQoS.stagedPreload*` / `preloadTopKRooms` names, still honoured)
|
|
22
|
+
* into the numbers the preload sweeps use. Staged preload is the default:
|
|
23
|
+
* the sweep covers the top N rooms, every other room loads when it is opened.
|
|
24
|
+
*/
|
|
25
|
+
export declare const resolveHistoryPreloadConfig: (config?: Pick<IConfig, "historyPreload" | "historyQoS"> | null) => ResolvedHistoryPreload;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { default as XmppClient } from '../networking/xmppClient';
|
|
2
|
+
import { IRoom } from '../types/types';
|
|
2
3
|
interface HistoryPreloadSchedulerOptions {
|
|
3
4
|
client: XmppClient;
|
|
4
5
|
signal?: AbortSignal;
|
|
@@ -10,6 +11,8 @@ interface HistoryPreloadSchedulerOptions {
|
|
|
10
11
|
defaultRoomJids?: string[];
|
|
11
12
|
forceReload?: boolean;
|
|
12
13
|
completionState?: 'done' | 'partial';
|
|
14
|
+
skipApiPreview?: boolean;
|
|
13
15
|
}
|
|
16
|
+
export declare const hasApiPreview: (room?: IRoom) => boolean;
|
|
14
17
|
export declare const runHistoryPreloadScheduler: (options: HistoryPreloadSchedulerOptions) => Promise<void>;
|
|
15
18
|
export default runHistoryPreloadScheduler;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { IMessage } from '../types/types';
|
|
2
|
+
export declare function deepMerge(target: any, source: any): any;
|
|
2
3
|
export declare function insertMessageWithDelimiter(roomMessages: Partial<IMessage>[], message: IMessage, lastViewedTimestamp: {
|
|
3
4
|
toString: () => string;
|
|
4
5
|
}): void;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { IConfig } from '../types/types';
|
|
2
|
+
/**
|
|
3
|
+
* The single rule for message search. It is opt-in: the host must set
|
|
4
|
+
* `enableMessageSearch` AND provide an `appId` (the archive endpoint is scoped
|
|
5
|
+
* by app). A leftover `disableMessageSearch: true` still wins as a hard off.
|
|
6
|
+
* Every entry point (header button, profile entry, chat-list matches, the
|
|
7
|
+
* Ctrl/Cmd+F shortcut, the modal itself) goes through this helper.
|
|
8
|
+
*/
|
|
9
|
+
export declare const isMessageSearchEnabled: (config?: Pick<IConfig, "enableMessageSearch" | "disableMessageSearch" | "appId"> | null) => boolean;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { IMessage } from '../types/types';
|
|
2
|
+
/** How long a jump request may stay pending before it is dropped. */
|
|
3
|
+
export declare const JUMP_TTL_MS = 60000;
|
|
4
|
+
/**
|
|
5
|
+
* Index of the message a jump names.
|
|
6
|
+
*
|
|
7
|
+
* By id first (message.id is the MAM stanza id for history, xmppId the client
|
|
8
|
+
* message id). When the request has no id at all, which is common for archive
|
|
9
|
+
* rows, by content: same text, sent within a few seconds of the same time.
|
|
10
|
+
*/
|
|
11
|
+
export declare const findMessageIndex: (messages: IMessage[], ids: string[], content?: {
|
|
12
|
+
createdAt?: string;
|
|
13
|
+
body?: string;
|
|
14
|
+
}) => number;
|
|
15
|
+
/**
|
|
16
|
+
* Where a jump to a thread REPLY lands, and the thread panel's parent message
|
|
17
|
+
* when that message is not in the room's live list.
|
|
18
|
+
*
|
|
19
|
+
* Replies never appear in the main list (it hides them unless they are also
|
|
20
|
+
* shown in the channel), so a jump to one has to open its parent's thread and
|
|
21
|
+
* highlight the reply there. Two things have to be remembered for that:
|
|
22
|
+
* - which list owns the jump: `at` is the pending request this entry serves,
|
|
23
|
+
* `parentId` the thread that owns it. The main list stays inert for a jump
|
|
24
|
+
* that has an owner thread, and a thread list stays inert for any jump that
|
|
25
|
+
* is not its own (see useJumpToMessage);
|
|
26
|
+
* - the parent and the replies when they exist only in a jump window or came
|
|
27
|
+
* from a separate fetch: the thread panel reads them from here, so it does
|
|
28
|
+
* not depend on the window that found them still being on screen.
|
|
29
|
+
*
|
|
30
|
+
* Kept outside redux on purpose: the room slice's thread flag lives on
|
|
31
|
+
* messages of the live list, which is exactly what these messages are not in.
|
|
32
|
+
*/
|
|
33
|
+
export interface JumpThreadState {
|
|
34
|
+
roomJID: string;
|
|
35
|
+
parentId: string;
|
|
36
|
+
/** The pending request this entry serves; null for a thread opened by hand. */
|
|
37
|
+
at: number | null;
|
|
38
|
+
/** Set only when the parent is not in the room's live messages. */
|
|
39
|
+
parent: IMessage | null;
|
|
40
|
+
/** Replies known to belong to the thread beyond the live list. */
|
|
41
|
+
replies: IMessage[];
|
|
42
|
+
}
|
|
43
|
+
export declare const getJumpThread: () => JumpThreadState | null;
|
|
44
|
+
export declare const setJumpThread: (next: JumpThreadState | null) => void;
|
|
45
|
+
export declare const clearJumpThread: () => void;
|
|
46
|
+
export declare const subscribeJumpThread: (listener: () => void) => (() => void);
|
|
47
|
+
export declare const useJumpThread: () => JumpThreadState | null;
|
|
48
|
+
/** The thread that owns the jump `at`, or null when the main list does. */
|
|
49
|
+
export declare const ownerThreadFor: (thread: JumpThreadState | null, at: number | undefined) => string | null;
|
|
50
|
+
/**
|
|
51
|
+
* The parent id of a message that is a thread reply and nothing else, or null:
|
|
52
|
+
* a reply also shown in the channel is in the main list, so it needs no thread.
|
|
53
|
+
*/
|
|
54
|
+
export declare const replyParentId: (message: IMessage) => string | null;
|
|
55
|
+
/** `a` then `b`, one entry per message id, oldest first. */
|
|
56
|
+
export declare const mergeById: (a: IMessage[], b: IMessage[]) => IMessage[];
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { HistoryWindowPage } from '../networking/xmppClient';
|
|
2
|
+
import { JumpWindow } from '../roomStore/roomsSlice';
|
|
3
|
+
import { IMessage } from '../types/types';
|
|
4
|
+
/** Messages shown above / below the target when a window opens (tunable). */
|
|
5
|
+
export declare const JUMP_WINDOW_BEFORE = 10;
|
|
6
|
+
export declare const JUMP_WINDOW_AFTER = 10;
|
|
7
|
+
/** Page size for loading more once the reader scrolls toward either edge. */
|
|
8
|
+
export declare const JUMP_WINDOW_PAGE = 20;
|
|
9
|
+
/** The slice of the XMPP client the window helpers need. */
|
|
10
|
+
export interface HistoryWindowClient {
|
|
11
|
+
getHistoryWindow: (chatJID: string, max: number, cursor: {
|
|
12
|
+
before?: number;
|
|
13
|
+
after?: number;
|
|
14
|
+
start?: string;
|
|
15
|
+
end?: string;
|
|
16
|
+
}) => Promise<HistoryWindowPage>;
|
|
17
|
+
}
|
|
18
|
+
export type JumpWindowResult =
|
|
19
|
+
/** The window around the target is ready to be shown. */
|
|
20
|
+
{
|
|
21
|
+
status: 'found';
|
|
22
|
+
window: JumpWindow;
|
|
23
|
+
}
|
|
24
|
+
/** The server answered, and the target is not in the archive. */
|
|
25
|
+
| {
|
|
26
|
+
status: 'missing';
|
|
27
|
+
}
|
|
28
|
+
/** No usable id, or a request failed: use the paging path instead. */
|
|
29
|
+
| {
|
|
30
|
+
status: 'unavailable';
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* How far apart the archive's timestamp and the transcript's may be for the
|
|
34
|
+
* same message. They are recorded by different components, so they are not
|
|
35
|
+
* identical, but never more than a moment apart.
|
|
36
|
+
*/
|
|
37
|
+
export declare const CONTENT_MATCH_WINDOW_MS = 5000;
|
|
38
|
+
/** What a hit with no archive id can be located by. */
|
|
39
|
+
export interface JumpContent {
|
|
40
|
+
createdAt?: string;
|
|
41
|
+
body?: string;
|
|
42
|
+
}
|
|
43
|
+
export declare const mamIdCandidates: (ids: string[]) => string[];
|
|
44
|
+
/**
|
|
45
|
+
* Fetches about JUMP_WINDOW_BEFORE messages before the target, the target and
|
|
46
|
+
* about JUMP_WINDOW_AFTER after it: two MAM queries in parallel (the target
|
|
47
|
+
* itself comes with the "after" page, asked from one microsecond before it).
|
|
48
|
+
* Nothing is written to the store here.
|
|
49
|
+
*/
|
|
50
|
+
export declare function loadJumpWindow(client: HistoryWindowClient, roomJID: string, ids: string[], content?: JumpContent): Promise<JumpWindowResult>;
|
|
51
|
+
/**
|
|
52
|
+
* For a hit with no usable archive id (older archive documents carry neither a
|
|
53
|
+
* stanza id nor a message id): ask the server for the rows within a few
|
|
54
|
+
* seconds of its timestamp (MAM start/end filter), find the one with the same
|
|
55
|
+
* text, and continue exactly as the id-based window does with that row's id.
|
|
56
|
+
* The server's answer is final: 'missing' only when it said the range holds no
|
|
57
|
+
* such row.
|
|
58
|
+
*/
|
|
59
|
+
export declare function loadJumpWindowByTime(client: HistoryWindowClient, roomJID: string, content: JumpContent): Promise<JumpWindowResult>;
|
|
60
|
+
export interface OlderWindowPage {
|
|
61
|
+
messages: IMessage[];
|
|
62
|
+
olderCursor: number | null;
|
|
63
|
+
hasOlder: boolean;
|
|
64
|
+
}
|
|
65
|
+
export interface NewerWindowPage {
|
|
66
|
+
messages: IMessage[];
|
|
67
|
+
newerCursor: number | null;
|
|
68
|
+
hasNewer: boolean;
|
|
69
|
+
}
|
|
70
|
+
/** The page above the window; null when the request failed. */
|
|
71
|
+
export declare function loadOlderWindowPage(client: HistoryWindowClient, window: JumpWindow): Promise<OlderWindowPage | null>;
|
|
72
|
+
/** The page below the window; null when the request failed. */
|
|
73
|
+
export declare function loadNewerWindowPage(client: HistoryWindowClient, window: JumpWindow): Promise<NewerWindowPage | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const nanoToMs: (number
|
|
1
|
+
export declare const nanoToMs: (number?: string | number | null) => number;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { AppDispatch } from '../roomStore';
|
|
2
|
+
type MaybeId = string | number | null | undefined;
|
|
3
|
+
/**
|
|
4
|
+
* Every id a caller may know the target message by, without blanks and
|
|
5
|
+
* duplicates. A notification, a push payload or a link can carry the client
|
|
6
|
+
* (xmpp) message id, the archive stanza id or the stored message id, and the
|
|
7
|
+
* jump matcher checks message.id and message.xmppId, so all of them go in.
|
|
8
|
+
*/
|
|
9
|
+
export declare const collectMessageIds: (...candidates: MaybeId[]) => string[];
|
|
10
|
+
/**
|
|
11
|
+
* Open a room and, when the caller knows which message it is about, ask the
|
|
12
|
+
* mounted room to land on it (scroll if mounted, widen the window, or page
|
|
13
|
+
* older history; see useJumpToMessage). With no message id this is just
|
|
14
|
+
* "open the room".
|
|
15
|
+
*/
|
|
16
|
+
export declare const openRoomAtMessage: (dispatch: AppDispatch, roomJID: string, ...messageIds: MaybeId[]) => void;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { AppDispatch } from '../roomStore';
|
|
2
|
+
import { IMessage } from '../types/types';
|
|
3
|
+
import { HistoryWindowClient } from './jumpWindow';
|
|
4
|
+
export interface OpenThreadForJumpParams {
|
|
5
|
+
client?: HistoryWindowClient | null;
|
|
6
|
+
dispatch: AppDispatch;
|
|
7
|
+
roomJID: string;
|
|
8
|
+
/** The pending request this opens a thread for. */
|
|
9
|
+
at: number;
|
|
10
|
+
/** The reply the jump named. */
|
|
11
|
+
reply: IMessage;
|
|
12
|
+
/** The room's live messages, unfiltered. */
|
|
13
|
+
live: IMessage[];
|
|
14
|
+
/** Messages found alongside the reply (a window), unfiltered. */
|
|
15
|
+
nearby?: IMessage[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Opens the thread a reply belongs to, so a jump to the reply can highlight it
|
|
19
|
+
* there. Resolves true when the thread is open (or about to be), false when
|
|
20
|
+
* the parent could not be obtained.
|
|
21
|
+
*
|
|
22
|
+
* The parent comes from the live list (the usual thread flag), from the
|
|
23
|
+
* messages found next to the reply, or, when it is in neither, from a window
|
|
24
|
+
* around the parent fetched for the purpose.
|
|
25
|
+
*/
|
|
26
|
+
export declare function openThreadForJump({ client, dispatch, roomJID, at, reply, live, nearby, }: OpenThreadForJumpParams): Promise<boolean>;
|
|
@@ -61,4 +61,11 @@ export interface PdfjsLib {
|
|
|
61
61
|
}
|
|
62
62
|
/** Test seam - the module-level cache would otherwise leak between cases. */
|
|
63
63
|
export declare const resetPdfjsCache: () => void;
|
|
64
|
-
|
|
64
|
+
/**
|
|
65
|
+
* `libUrl`: load pdf.js from this URL (an ES module build such as
|
|
66
|
+
* `pdfjs-dist/legacy/build/pdf.min.mjs` served by the host) instead of the
|
|
67
|
+
* copy bundled with the component. For hosts that ship the component as one
|
|
68
|
+
* self-contained script (the embeddable widget), where a bundled pdf.js is
|
|
69
|
+
* 1.7 MB that every visitor downloads whether or not a PDF ever appears.
|
|
70
|
+
*/
|
|
71
|
+
export declare const loadPdfjs: (workerSrc?: string, libUrl?: string) => Promise<PdfjsLib | null>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { IRoom } from '../types/types';
|
|
2
|
+
type CountSource = Pick<IRoom, 'members' | 'usersCnt'> | {
|
|
3
|
+
members?: unknown;
|
|
4
|
+
usersCnt?: unknown;
|
|
5
|
+
} | null | undefined;
|
|
6
|
+
/**
|
|
7
|
+
* User count to DISPLAY for a room. /chats/my returns `usersCnt` (the true
|
|
8
|
+
* total) but at most 30 members for big public rooms, so the truncated
|
|
9
|
+
* members array must never win over usersCnt.
|
|
10
|
+
*/
|
|
11
|
+
export declare const getRoomUserCount: (room: CountSource) => number;
|
|
12
|
+
/** True when the room is known to have more users than members loaded. */
|
|
13
|
+
export declare const isRoomMembersTruncated: (room: CountSource) => boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Next usersCnt after a live join (+1) or leave (-1). Adjusts the CURRENT
|
|
16
|
+
* count instead of recomputing from the (possibly truncated) members array.
|
|
17
|
+
*/
|
|
18
|
+
export declare const adjustUsersCnt: (room: CountSource, delta: 1 | -1, nextMembersLength: number) => number;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { RoomMember } from '../types/types';
|
|
2
|
+
export type RoomDirectoryState = 'idle' | 'loading' | 'done' | 'error';
|
|
3
|
+
/** Re-render hook: called when a directory state or a negative cache entry changes. */
|
|
4
|
+
export declare const subscribeUserResolver: (listener: () => void) => (() => void);
|
|
5
|
+
/**
|
|
6
|
+
* True for an id the backend answered 404 for: the user really is gone.
|
|
7
|
+
* (An id merely not resolved yet is NOT "not found".)
|
|
8
|
+
*/
|
|
9
|
+
export declare const isUserNotFound: (rawId: string) => boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Where a sender's lookup stands, for the display chain:
|
|
12
|
+
* - resolved: in usersSet
|
|
13
|
+
* - pending: queued, in flight, or not requested yet (a bubble just mounted)
|
|
14
|
+
* - notfound / forbidden: the backend answered, nothing to show from it
|
|
15
|
+
* - failed: the last attempt errored (backoff), or the lookup is exhausted
|
|
16
|
+
* - unavailable: this id can never be looked up (plain handle) or there is
|
|
17
|
+
* no session token, so nothing is coming
|
|
18
|
+
*/
|
|
19
|
+
export type UserLookupStatus = 'resolved' | 'pending' | 'notfound' | 'forbidden' | 'failed' | 'unavailable';
|
|
20
|
+
export declare const getUserLookupStatus: (rawId: string) => UserLookupStatus;
|
|
21
|
+
/**
|
|
22
|
+
* Ask for the profiles of these senders. Cheap to call from anywhere (a
|
|
23
|
+
* bubble mounting, a history batch parse): known, in-flight and recently
|
|
24
|
+
* failed ids are dropped, the rest are debounced into one pass.
|
|
25
|
+
*/
|
|
26
|
+
export declare function requestUsers(ids: string[]): void;
|
|
27
|
+
/**
|
|
28
|
+
* Re-fetch one user's profile even though it is already in usersSet, because
|
|
29
|
+
* the server said it changed (ethora-event user-profile-updated). Bypasses
|
|
30
|
+
* the cache and forgets a negative-cache entry, but shares everything else
|
|
31
|
+
* with requestUsers: the same route chain (lookupUser), the same worker
|
|
32
|
+
* queue and concurrency cap, one request at a time per user. Calls for the
|
|
33
|
+
* same user within REFRESH_DEBOUNCE_MS coalesce into one fetch. The answer
|
|
34
|
+
* goes through insertUsers (display fields only), so existing bubbles are
|
|
35
|
+
* re-named. Never throws.
|
|
36
|
+
*/
|
|
37
|
+
export declare function refreshUser(rawId: string): void;
|
|
38
|
+
/**
|
|
39
|
+
* Members loaded so far by ensureRoomDirectory for this room (a new array
|
|
40
|
+
* identity after every page, a stable empty array before any).
|
|
41
|
+
*/
|
|
42
|
+
export declare const getRoomDirectoryMembers: (roomJID: string) => RoomMember[];
|
|
43
|
+
/** requestUsers for the senders of a batch of messages (history page, jump window). */
|
|
44
|
+
export declare function requestSendersOf(messages: ReadonlyArray<{
|
|
45
|
+
user?: {
|
|
46
|
+
id?: string;
|
|
47
|
+
};
|
|
48
|
+
} | null | undefined> | undefined): void;
|
|
49
|
+
export declare const getRoomDirectoryState: (roomJID: string) => RoomDirectoryState;
|
|
50
|
+
/**
|
|
51
|
+
* Loads every member of one room into usersSet, once. Pages are not
|
|
52
|
+
* guaranteed to be full, so it stops on an empty page or offset >= total,
|
|
53
|
+
* never on `items.length < limit`.
|
|
54
|
+
*/
|
|
55
|
+
export declare function ensureRoomDirectory(roomJID: string): Promise<void>;
|
|
56
|
+
/** For tests and logout: drops every cache, timer and in-flight result. */
|
|
57
|
+
export declare function resetUserResolver(): void;
|
|
@@ -2,7 +2,7 @@ export declare const toLocalPart: (value?: string) => string;
|
|
|
2
2
|
/**
|
|
3
3
|
* True when `value` (a full JID or already-bare local part) is a
|
|
4
4
|
* machine-generated Ethora id rather than anything a human would recognize
|
|
5
|
-
* as their own name - `<24 hex>_<24 hex>`
|
|
5
|
+
* as their own name - `<24 hex>_<24 hex>`, `<appId>_<uuid>` or a bare 24-hex
|
|
6
6
|
* id. Kept narrow on purpose: a handle a person actually picked is never
|
|
7
7
|
* this shape, so it is never mistaken for one.
|
|
8
8
|
*/
|
|
@@ -22,5 +22,11 @@ export declare const hasHostRefreshedSession: (user: {
|
|
|
22
22
|
xmppUsername?: string;
|
|
23
23
|
xmppPassword?: string;
|
|
24
24
|
}) => boolean;
|
|
25
|
+
export declare const startBackgroundJoinSweep: (client: {
|
|
26
|
+
sendAllPresencesAndMarkReady: () => Promise<unknown>;
|
|
27
|
+
}) => void;
|
|
28
|
+
export declare const loadRoomsThenJoinInBackground: <T>(client: {
|
|
29
|
+
sendAllPresencesAndMarkReady: () => Promise<unknown>;
|
|
30
|
+
}, fetchRooms: () => Promise<T>, onListReady: () => void) => Promise<T>;
|
|
25
31
|
declare const useChatWrapperInit: ({ roomJID, wasAutoSelected, config, }: useChatWrapperInitProps) => useChatWrapperInitResult;
|
|
26
32
|
export default useChatWrapperInit;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface Options {
|
|
2
|
+
enabled: boolean;
|
|
3
|
+
/** Whether the search panel is already open (then the shortcut re-focuses it). */
|
|
4
|
+
isOpen: boolean;
|
|
5
|
+
open: () => void;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Ctrl/Cmd+F opens message search, but only while the reader is working in the
|
|
9
|
+
* chat (focus or pointer inside it). A host page that embeds the chat keeps its
|
|
10
|
+
* browser find everywhere else: hijacking it page-wide would be a hostile thing
|
|
11
|
+
* for an SDK to do.
|
|
12
|
+
*/
|
|
13
|
+
export declare function useMessageSearchShortcut({ enabled, isOpen, open }: Options): void;
|
|
14
|
+
export {};
|
|
@@ -9,6 +9,7 @@ interface UsePdfThumbnailParams {
|
|
|
9
9
|
maxBytes?: number;
|
|
10
10
|
sizeInBytes?: number;
|
|
11
11
|
workerSrc?: string;
|
|
12
|
+
libUrl?: string;
|
|
12
13
|
}
|
|
13
14
|
interface UsePdfThumbnailResult {
|
|
14
15
|
status: PdfThumbnailStatus;
|
|
@@ -18,5 +19,5 @@ interface UsePdfThumbnailResult {
|
|
|
18
19
|
/** Re-runs a render that failed or was skipped for being too big. */
|
|
19
20
|
retry: () => void;
|
|
20
21
|
}
|
|
21
|
-
export declare const usePdfThumbnail: ({ url, enabled, width, maxBytes, sizeInBytes, workerSrc, }: UsePdfThumbnailParams) => UsePdfThumbnailResult;
|
|
22
|
+
export declare const usePdfThumbnail: ({ url, enabled, width, maxBytes, sizeInBytes, workerSrc, libUrl, }: UsePdfThumbnailParams) => UsePdfThumbnailResult;
|
|
22
23
|
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { RoomDirectoryState } from '../helpers/userResolver';
|
|
2
|
+
import { RoomMember } from '../types/types';
|
|
3
|
+
/**
|
|
4
|
+
* Directory of a big room (usersCnt > the members the API returned).
|
|
5
|
+
* `enabled` starts the (once per room, deduplicated) load; the result grows
|
|
6
|
+
* page by page. Rooms that are not truncated never trigger a request.
|
|
7
|
+
*/
|
|
8
|
+
export declare const useRoomDirectory: (roomJid: string | undefined, enabled: boolean) => {
|
|
9
|
+
members: RoomMember[];
|
|
10
|
+
state: RoomDirectoryState;
|
|
11
|
+
};
|