@marktiderman/genesis-chat 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +304 -13
- package/dist/{chunk-SAY24E6V.js → chunk-5D5A7I4Q.js} +166 -45
- package/dist/chunk-5D5A7I4Q.js.map +1 -0
- package/dist/{chunk-JQXZUEEL.js → chunk-7S6HYNWW.js} +382 -8
- package/dist/chunk-7S6HYNWW.js.map +1 -0
- package/dist/chunk-BJCE5O7V.js +70 -0
- package/dist/chunk-BJCE5O7V.js.map +1 -0
- package/dist/chunk-PZTOY2LY.js +576 -0
- package/dist/chunk-PZTOY2LY.js.map +1 -0
- package/dist/chunk-VEPZROQB.js +187 -0
- package/dist/chunk-VEPZROQB.js.map +1 -0
- package/dist/{client-BtdALFCx.d.ts → client-DMa2j7kz.d.ts} +152 -11
- package/dist/index.d.ts +78 -163
- package/dist/index.js +3 -2
- package/dist/native.d.ts +1910 -1
- package/dist/native.js +4900 -0
- package/dist/native.js.map +1 -1
- package/dist/outbox-B0y3phE0.d.ts +151 -0
- package/dist/push.d.ts +206 -0
- package/dist/push.js +3 -0
- package/dist/push.js.map +1 -0
- package/dist/react.d.ts +149 -7
- package/dist/react.js +3 -3
- package/dist/server.d.ts +510 -21
- package/dist/server.js +863 -21
- package/dist/server.js.map +1 -1
- package/dist/thread-DWXHKPdw.d.ts +57 -0
- package/dist/web.d.ts +313 -10
- package/dist/web.js +1731 -60
- package/dist/web.js.map +1 -1
- package/migrations/0002_genesis_chat_coaching.sql +934 -0
- package/migrations/tests/0002_genesis_chat_coaching.test.sql +265 -0
- package/package.json +74 -6
- package/src/__tests__/coaching-core.test.ts +332 -0
- package/src/__tests__/coaching-react.test.tsx +248 -0
- package/src/__tests__/coaching-web.test.tsx +449 -0
- package/src/__tests__/core.test.ts +91 -0
- package/src/__tests__/outbox.test.ts +122 -0
- package/src/__tests__/realtime.test.ts +70 -0
- package/src/__tests__/review-fixes-core.test.ts +193 -0
- package/src/__tests__/review-fixes-web.test.tsx +402 -0
- package/src/__tests__/server-effects.test.ts +158 -0
- package/src/__tests__/server-fakes.ts +90 -0
- package/src/__tests__/server-integrations.test.ts +247 -0
- package/src/__tests__/server-providers.test.ts +246 -0
- package/src/__tests__/server-runner.test.ts +277 -0
- package/src/__tests__/server-safety.test.ts +290 -0
- package/src/__tests__/web-markdown.test.tsx +88 -0
- package/src/__tests__/web.test.tsx +333 -0
- package/src/core/availability.ts +223 -0
- package/src/core/client.ts +757 -0
- package/src/core/extras.ts +106 -0
- package/src/core/media.ts +144 -0
- package/src/core/outbox.ts +429 -0
- package/src/core/thread.ts +171 -0
- package/src/core/types.ts +311 -0
- package/src/index.ts +13 -0
- package/src/native/__tests__/auto-retry.test.tsx +128 -0
- package/src/native/__tests__/composer-mic-lock.test.tsx +115 -0
- package/src/native/__tests__/composer-state.test.ts +188 -0
- package/src/native/__tests__/composer.test.tsx +133 -0
- package/src/native/__tests__/emoji.test.tsx +238 -0
- package/src/native/__tests__/inbox-screen.test.tsx +48 -0
- package/src/native/__tests__/message-bubble.test.tsx +127 -0
- package/src/native/__tests__/organise-logic.test.ts +164 -0
- package/src/native/__tests__/organise-screens.test.tsx +344 -0
- package/src/native/__tests__/photo-viewer.test.tsx +226 -0
- package/src/native/__tests__/push-payload.test.ts +66 -0
- package/src/native/__tests__/push.test.ts +209 -0
- package/src/native/__tests__/recorder-machine.test.ts +96 -0
- package/src/native/__tests__/rows-actions.test.ts +76 -0
- package/src/native/__tests__/thread-layout.test.ts +41 -0
- package/src/native/__tests__/thread-screen.test.tsx +116 -0
- package/src/native/__tests__/tus.test.ts +154 -0
- package/src/native/__tests__/use-voice-recorder.test.tsx +316 -0
- package/src/native/__tests__/video-media.test.tsx +421 -0
- package/src/native/__tests__/video-recorder-state.test.ts +87 -0
- package/src/native/__tests__/video-upload.test.ts +227 -0
- package/src/native/access-error.tsx +31 -0
- package/src/native/actions.ts +42 -0
- package/src/native/attachment-cards.tsx +116 -0
- package/src/native/auto-retry.ts +69 -0
- package/src/native/availability.tsx +376 -0
- package/src/native/broadcast-sheet.tsx +193 -0
- package/src/native/composer-state.ts +302 -0
- package/src/native/composer.tsx +453 -0
- package/src/native/context.tsx +70 -0
- package/src/native/copy.ts +296 -0
- package/src/native/emoji-data.ts +21 -0
- package/src/native/emoji-picker.tsx +229 -0
- package/src/native/emoji.ts +118 -0
- package/src/native/icon-button.tsx +40 -0
- package/src/native/inbox-screen.tsx +116 -0
- package/src/native/index.ts +184 -0
- package/src/native/link-preview.tsx +104 -0
- package/src/native/media-colors.ts +15 -0
- package/src/native/media.tsx +187 -0
- package/src/native/message-actions-sheet.tsx +121 -0
- package/src/native/message-bubble.tsx +374 -0
- package/src/native/moderation.tsx +42 -0
- package/src/native/modules.ts +275 -0
- package/src/native/organise-copy.ts +195 -0
- package/src/native/photo-viewer.tsx +531 -0
- package/src/native/push.ts +375 -0
- package/src/native/recorder-machine.ts +176 -0
- package/src/native/rows.ts +81 -0
- package/src/native/schedule-sheet.tsx +314 -0
- package/src/native/search-screen.tsx +168 -0
- package/src/native/stack-options.ts +36 -0
- package/src/native/start-sheet.tsx +87 -0
- package/src/native/thread-screen.tsx +620 -0
- package/src/native/topics.tsx +179 -0
- package/src/native/tus.ts +282 -0
- package/src/native/use-composer.ts +71 -0
- package/src/native/use-jump.ts +91 -0
- package/src/native/use-voice-recorder.ts +234 -0
- package/src/native/video-player.tsx +370 -0
- package/src/native/video-recorder-state.ts +140 -0
- package/src/native/video-recorder.tsx +271 -0
- package/src/native/video-upload-tray.tsx +77 -0
- package/src/native/video-upload.ts +432 -0
- package/src/native/voice-player.tsx +283 -0
- package/src/react/index.tsx +810 -0
- package/src/server/assistant.ts +235 -0
- package/src/server/crypto.ts +86 -0
- package/src/server/expo.ts +119 -0
- package/src/server/handler.ts +32 -0
- package/src/server/index.ts +57 -0
- package/src/server/moderation.ts +155 -0
- package/src/server/mux.ts +290 -0
- package/src/server/notifications.ts +130 -0
- package/src/server/runner.ts +468 -0
- package/src/server/transcribers.ts +111 -0
- package/src/server/types.ts +171 -0
- package/src/server/unfurl.ts +480 -0
- package/src/server/upload.ts +158 -0
- package/src/web/attachments.tsx +379 -0
- package/src/web/availability.tsx +299 -0
- package/src/web/broadcast.tsx +166 -0
- package/src/web/bubble.tsx +563 -0
- package/src/web/cards.tsx +136 -0
- package/src/web/composer.tsx +571 -0
- package/src/web/emoji-picker.tsx +84 -0
- package/src/web/error-boundary.tsx +33 -0
- package/src/web/format.ts +55 -0
- package/src/web/inbox.tsx +195 -0
- package/src/web/index.ts +75 -0
- package/src/web/markdown.tsx +245 -0
- package/src/web/memory-client.ts +706 -0
- package/src/web/panel.tsx +135 -0
- package/src/web/schedule.tsx +192 -0
- package/src/web/search.tsx +129 -0
- package/src/web/thread-context.ts +64 -0
- package/src/web/thread.tsx +802 -0
- package/src/web/topics.tsx +160 -0
- package/src/web/use-voice-recorder.ts +264 -0
- package/src/web/video.tsx +522 -0
- package/src/web/voice-note.tsx +197 -0
- package/dist/chunk-27UTR5FW.js +0 -322
- package/dist/chunk-27UTR5FW.js.map +0 -1
- package/dist/chunk-JQXZUEEL.js.map +0 -1
- package/dist/chunk-SAY24E6V.js.map +0 -1
package/dist/native.d.ts
CHANGED
|
@@ -1,2 +1,1911 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ComponentType, ReactNode } from 'react';
|
|
3
|
+
import { InboxPageProps, InboxItem, ScreenHeaderProps } from '@marktiderman/genesis-ui-native/layouts';
|
|
4
|
+
import { m as ChatInboxItem, h as ChatAttachment, q as ChatMessage, k as ChatConversation, t as ChatRole, P as PendingFile, o as ChatLinkPreview, E as SendInput, x as ChatTopic, T as TopicInput, S as ScheduleInput, W as Weekday, C as ChatAvailability, d as AvailabilityInput, p as ChatMember, a as WorkingHours, u as ChatSearchHit } from './client-DMa2j7kz.js';
|
|
5
|
+
import { ChatAttachmentKind, ChatVideoUpload } from './react.js';
|
|
6
|
+
import { T as ThreadItem } from './thread-DWXHKPdw.js';
|
|
7
|
+
import { ColorValue } from 'react-native';
|
|
8
|
+
import { L as LocalFile } from './outbox-B0y3phE0.js';
|
|
9
|
+
export { CHAT_MARK_READ_ACTION, CHAT_PUSH_CATEGORY, CHAT_REPLY_ACTION, ChatPushCopy, ChatPushData, ChatPushOutboxDeps, ChatPushOutcome, ChatPushPayload, ChatPushPayloadInput, ChatPushResponseLike, NotificationCategoriesLike, PUSH_REPLY_WAIT_MS, chatNotificationActions, chatPath, chatPushCategory, chatPushCopy, chatPushPayload, chatThreadMatch, conversationIdFromData, failedReply, handleChatPushResponse, registerChatNotificationActions, replyClientId, replyClientIdFor } from './push.js';
|
|
10
|
+
import { NativeIconName } from '@marktiderman/genesis-ui-native/components';
|
|
11
|
+
import '@supabase/supabase-js';
|
|
12
|
+
import '@tanstack/query-core';
|
|
1
13
|
|
|
2
|
-
|
|
14
|
+
/**
|
|
15
|
+
* @feat:chat
|
|
16
|
+
* The words of chat's organising features on native (topics, scheduled send, availability,
|
|
17
|
+
* broadcast, search, moderation), part of `ChatNativeCopy`, so an app overrides them the same
|
|
18
|
+
* way (`ChatNativeProvider copy`). `{name}`, `{count}`, `{when}` and `{query}` are filled in.
|
|
19
|
+
*/
|
|
20
|
+
interface ChatOrganiseCopy {
|
|
21
|
+
topics: string;
|
|
22
|
+
topicAll: string;
|
|
23
|
+
topicClosed: string;
|
|
24
|
+
topicOptions: string;
|
|
25
|
+
topicNew: string;
|
|
26
|
+
topicRename: string;
|
|
27
|
+
topicClose: string;
|
|
28
|
+
topicReopen: string;
|
|
29
|
+
topicName: string;
|
|
30
|
+
topicNamePlaceholder: string;
|
|
31
|
+
topicAdd: string;
|
|
32
|
+
topicSave: string;
|
|
33
|
+
topicExists: string;
|
|
34
|
+
topicClosedNotice: string;
|
|
35
|
+
topicPlaceholder: string;
|
|
36
|
+
topicClosedDone: string;
|
|
37
|
+
topicReopenedDone: string;
|
|
38
|
+
schedule: string;
|
|
39
|
+
scheduleTitle: string;
|
|
40
|
+
scheduleHint: string;
|
|
41
|
+
schedulePick: string;
|
|
42
|
+
scheduleDate: string;
|
|
43
|
+
scheduleTime: string;
|
|
44
|
+
scheduleConfirm: string;
|
|
45
|
+
scheduleOutOfRange: string;
|
|
46
|
+
scheduledFor: string;
|
|
47
|
+
scheduleFailed: string;
|
|
48
|
+
scheduleNoFiles: string;
|
|
49
|
+
scheduledCount: string;
|
|
50
|
+
scheduledCountOne: string;
|
|
51
|
+
scheduledTitle: string;
|
|
52
|
+
scheduledEmpty: string;
|
|
53
|
+
scheduledEmptyBody: string;
|
|
54
|
+
scheduledCancel: string;
|
|
55
|
+
/** On a scheduled message that couldn't be sent. */
|
|
56
|
+
scheduledRemove: string;
|
|
57
|
+
scheduledCancelled: string;
|
|
58
|
+
scheduledCouldntSend: string;
|
|
59
|
+
availabilityTitle: string;
|
|
60
|
+
awayMessage: string;
|
|
61
|
+
awayMessagePlaceholder: string;
|
|
62
|
+
awayUntil: string;
|
|
63
|
+
awayUntilOn: string;
|
|
64
|
+
imBack: string;
|
|
65
|
+
workingHours: string;
|
|
66
|
+
workingHoursOn: string;
|
|
67
|
+
workingHoursHint: string;
|
|
68
|
+
workingDay: string;
|
|
69
|
+
notWorking: string;
|
|
70
|
+
hoursFrom: string;
|
|
71
|
+
hoursTo: string;
|
|
72
|
+
hoursSameTime: string;
|
|
73
|
+
timeZone: string;
|
|
74
|
+
timeZoneSearch: string;
|
|
75
|
+
availabilitySave: string;
|
|
76
|
+
availabilitySaved: string;
|
|
77
|
+
broadcastTitle: string;
|
|
78
|
+
broadcastExplainer: string;
|
|
79
|
+
broadcastMessage: string;
|
|
80
|
+
broadcastPlaceholder: string;
|
|
81
|
+
broadcastTo: string;
|
|
82
|
+
broadcastFilter: string;
|
|
83
|
+
broadcastSelectAll: string;
|
|
84
|
+
broadcastClear: string;
|
|
85
|
+
broadcastPickSome: string;
|
|
86
|
+
broadcastPicked: string;
|
|
87
|
+
broadcastPickedOne: string;
|
|
88
|
+
broadcastSend: string;
|
|
89
|
+
broadcastSendTo: string;
|
|
90
|
+
broadcastSent: string;
|
|
91
|
+
broadcastSentOne: string;
|
|
92
|
+
broadcastSentSome: string;
|
|
93
|
+
broadcastNoMatches: string;
|
|
94
|
+
broadcastNoConversations: string;
|
|
95
|
+
search: string;
|
|
96
|
+
searchPlaceholder: string;
|
|
97
|
+
searchThreadPlaceholder: string;
|
|
98
|
+
searchHint: string;
|
|
99
|
+
searchNoResults: string;
|
|
100
|
+
searchNoResultsBody: string;
|
|
101
|
+
searchResults: string;
|
|
102
|
+
searchResultsOne: string;
|
|
103
|
+
searchIn: string;
|
|
104
|
+
transcript: string;
|
|
105
|
+
messageGone: string;
|
|
106
|
+
/** Said by VoiceOver when a jump lands. */
|
|
107
|
+
jumpedTo: string;
|
|
108
|
+
moderationHidden: string;
|
|
109
|
+
moderationHiddenMine: string;
|
|
110
|
+
moderationFlagged: string;
|
|
111
|
+
}
|
|
112
|
+
declare const chatOrganiseCopy: ChatOrganiseCopy;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @feat:chat
|
|
116
|
+
* Every word the native chat screens say, in one catalog an app can override
|
|
117
|
+
* (translate, rename "Message" to "Note"). `{name}` and `{count}` are filled in.
|
|
118
|
+
*/
|
|
119
|
+
|
|
120
|
+
/** Topics, scheduled send, availability, broadcast, search and moderation are in `ChatOrganiseCopy`. */
|
|
121
|
+
interface ChatNativeCopy extends ChatOrganiseCopy {
|
|
122
|
+
inboxTitle: string;
|
|
123
|
+
inboxEmptyTitle: string;
|
|
124
|
+
inboxEmptyBody: string;
|
|
125
|
+
inboxErrorTitle: string;
|
|
126
|
+
startConversation: string;
|
|
127
|
+
startConversationTitle: string;
|
|
128
|
+
untitled: string;
|
|
129
|
+
you: string;
|
|
130
|
+
someone: string;
|
|
131
|
+
youPrefix: string;
|
|
132
|
+
mentionedYou: string;
|
|
133
|
+
markRead: string;
|
|
134
|
+
pin: string;
|
|
135
|
+
unpin: string;
|
|
136
|
+
archive: string;
|
|
137
|
+
threadEmptyTitle: string;
|
|
138
|
+
threadEmptyBody: string;
|
|
139
|
+
loadEarlier: string;
|
|
140
|
+
newMessages: string;
|
|
141
|
+
placeholder: string;
|
|
142
|
+
send: string;
|
|
143
|
+
attach: string;
|
|
144
|
+
attachLibrary: string;
|
|
145
|
+
attachCamera: string;
|
|
146
|
+
attachPaste: string;
|
|
147
|
+
removeAttachment: string;
|
|
148
|
+
replyingTo: string;
|
|
149
|
+
cancelReply: string;
|
|
150
|
+
cantPost: string;
|
|
151
|
+
/** Your role in a conversation couldn't be read (offline): with Retry. */
|
|
152
|
+
accessError: string;
|
|
153
|
+
cantPostAdmins: string;
|
|
154
|
+
cantPostClosed: string;
|
|
155
|
+
recordVoice: string;
|
|
156
|
+
recordHint: string;
|
|
157
|
+
slideToCancel: string;
|
|
158
|
+
releaseToCancel: string;
|
|
159
|
+
lockedHint: string;
|
|
160
|
+
stopAndSend: string;
|
|
161
|
+
cancelRecording: string;
|
|
162
|
+
micDenied: string;
|
|
163
|
+
tooShort: string;
|
|
164
|
+
/** A recording whose stop failed or left no file. */
|
|
165
|
+
recordingFailed: string;
|
|
166
|
+
/** A recording ended by a call, Siri, another app or the app leaving the screen. */
|
|
167
|
+
recordingInterrupted: string;
|
|
168
|
+
play: string;
|
|
169
|
+
/** The bubble's VoiceOver actions on a voice note. */
|
|
170
|
+
playVoice: string;
|
|
171
|
+
pauseVoice: string;
|
|
172
|
+
changeSpeed: string;
|
|
173
|
+
/** The bubble's VoiceOver action for the nth of several photos. */
|
|
174
|
+
openPhotoN: string;
|
|
175
|
+
pause: string;
|
|
176
|
+
speed: string;
|
|
177
|
+
voiceMessage: string;
|
|
178
|
+
voice: string;
|
|
179
|
+
photo: string;
|
|
180
|
+
file: string;
|
|
181
|
+
video: string;
|
|
182
|
+
/** Video messages (recorder, upload, player), link previews and app attachment cards. */
|
|
183
|
+
recordVideo: string;
|
|
184
|
+
recordVideoHint: string;
|
|
185
|
+
startVideo: string;
|
|
186
|
+
stopVideo: string;
|
|
187
|
+
flipCamera: string;
|
|
188
|
+
closeRecorder: string;
|
|
189
|
+
cameraDenied: string;
|
|
190
|
+
openSettings: string;
|
|
191
|
+
cameraFailed: string;
|
|
192
|
+
videoTooShort: string;
|
|
193
|
+
/** "0:12 of 2:00", the recorder's timer. */
|
|
194
|
+
videoElapsed: string;
|
|
195
|
+
videoUploading: string;
|
|
196
|
+
videoUploadFailed: string;
|
|
197
|
+
videoOffline: string;
|
|
198
|
+
videoForbidden: string;
|
|
199
|
+
videoCantPlay: string;
|
|
200
|
+
/** A take that the camera didn't hand over. */
|
|
201
|
+
videoTakeFailed: string;
|
|
202
|
+
tryAgain: string;
|
|
203
|
+
cancelUpload: string;
|
|
204
|
+
videoProcessing: string;
|
|
205
|
+
videoFailed: string;
|
|
206
|
+
playVideo: string;
|
|
207
|
+
pauseVideo: string;
|
|
208
|
+
mute: string;
|
|
209
|
+
unmute: string;
|
|
210
|
+
fullScreen: string;
|
|
211
|
+
/** "Video, 0:42". */
|
|
212
|
+
videoMessage: string;
|
|
213
|
+
/** A link preview card's label: "{title}, {site}". */
|
|
214
|
+
linkPreview: string;
|
|
215
|
+
openLink: string;
|
|
216
|
+
/** The bubble's VoiceOver action for its link preview. */
|
|
217
|
+
openLinkAction: string;
|
|
218
|
+
attachmentCard: string;
|
|
219
|
+
openImage: string;
|
|
220
|
+
closeViewer: string;
|
|
221
|
+
photoOf: string;
|
|
222
|
+
sharePhoto: string;
|
|
223
|
+
savePhoto: string;
|
|
224
|
+
photoSaved: string;
|
|
225
|
+
photosDenied: string;
|
|
226
|
+
viewerHint: string;
|
|
227
|
+
sending: string;
|
|
228
|
+
notSent: string;
|
|
229
|
+
retry: string;
|
|
230
|
+
/** On a message whose file didn't survive a relaunch: pick it again. */
|
|
231
|
+
attachAgain: string;
|
|
232
|
+
discard: string;
|
|
233
|
+
deleted: string;
|
|
234
|
+
edited: string;
|
|
235
|
+
react: string;
|
|
236
|
+
/** The long-press sheet's "+": every emoji. */
|
|
237
|
+
moreReactions: string;
|
|
238
|
+
addEmoji: string;
|
|
239
|
+
emojiPicker: string;
|
|
240
|
+
searchEmoji: string;
|
|
241
|
+
noEmoji: string;
|
|
242
|
+
recentEmoji: string;
|
|
243
|
+
emojiSmileys: string;
|
|
244
|
+
emojiPeople: string;
|
|
245
|
+
emojiNature: string;
|
|
246
|
+
emojiFood: string;
|
|
247
|
+
emojiTravel: string;
|
|
248
|
+
emojiActivities: string;
|
|
249
|
+
emojiObjects: string;
|
|
250
|
+
emojiSymbols: string;
|
|
251
|
+
emojiFlags: string;
|
|
252
|
+
reply: string;
|
|
253
|
+
copy: string;
|
|
254
|
+
copied: string;
|
|
255
|
+
delete: string;
|
|
256
|
+
deleteConfirm: string;
|
|
257
|
+
cancel: string;
|
|
258
|
+
actionFailed: string;
|
|
259
|
+
messageActions: string;
|
|
260
|
+
reactions: string;
|
|
261
|
+
tooLarge: string;
|
|
262
|
+
notAllowedType: string;
|
|
263
|
+
tooMany: string;
|
|
264
|
+
}
|
|
265
|
+
declare const chatNativeCopy: ChatNativeCopy;
|
|
266
|
+
type ChatNativeCopyOverrides = Partial<ChatNativeCopy>;
|
|
267
|
+
declare function resolveChatCopy(overrides?: ChatNativeCopyOverrides): ChatNativeCopy;
|
|
268
|
+
/** `fill("Replying to {name}", { name: "Sam" })`. */
|
|
269
|
+
declare function fill(text: string, values: Record<string, string | number>): string;
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* @feat:chat
|
|
273
|
+
* The native modules chat can use, as the shapes it needs. The app passes the
|
|
274
|
+
* real ones (each an optional peer); chat never imports them, so it builds and
|
|
275
|
+
* runs without any of them, and the tests pass fakes:
|
|
276
|
+
*
|
|
277
|
+
* import * as Audio from "expo-audio"; // voice notes
|
|
278
|
+
* import * as ImagePicker from "expo-image-picker"; // photos and videos
|
|
279
|
+
* import * as Haptics from "expo-haptics"; // send and record
|
|
280
|
+
* import * as Clipboard from "expo-clipboard"; // copy, paste a picture
|
|
281
|
+
* import { KeyboardAvoidingView } from "react-native-keyboard-controller";
|
|
282
|
+
* import AsyncStorage from "@react-native-async-storage/async-storage"; // drafts
|
|
283
|
+
* import * as Sharing from "expo-sharing"; // share a photo
|
|
284
|
+
* import * as MediaLibrary from "expo-media-library"; // save a photo
|
|
285
|
+
* import NetInfo from "@react-native-community/netinfo"; // retry when back online
|
|
286
|
+
* import * as Camera from "expo-camera"; // video messages
|
|
287
|
+
* import * as Video from "expo-video"; // video playback
|
|
288
|
+
* import * as WebBrowser from "expo-web-browser"; // link previews
|
|
289
|
+
*
|
|
290
|
+
* FlashList and expo-image come from GenesisNativeApp's `modules` (genesis-ui-native's
|
|
291
|
+
* GenesisModulesProvider), the same objects every Genesis list and image uses.
|
|
292
|
+
* The shapes follow the SDK 58 packages' own type declarations.
|
|
293
|
+
*/
|
|
294
|
+
|
|
295
|
+
/** `RecorderState` from expo-audio (`recorder.getStatus()`). */
|
|
296
|
+
interface RecorderStateLike {
|
|
297
|
+
isRecording: boolean;
|
|
298
|
+
durationMillis: number;
|
|
299
|
+
/** dB, about -160…0, when `isMeteringEnabled`. */
|
|
300
|
+
metering?: number;
|
|
301
|
+
url: string | null;
|
|
302
|
+
}
|
|
303
|
+
/** `AudioRecorder` from expo-audio. */
|
|
304
|
+
interface AudioRecorderLike {
|
|
305
|
+
prepareToRecordAsync(options?: Record<string, unknown>): Promise<void>;
|
|
306
|
+
record(): void;
|
|
307
|
+
stop(): Promise<void>;
|
|
308
|
+
getStatus(): RecorderStateLike;
|
|
309
|
+
uri: string | null;
|
|
310
|
+
}
|
|
311
|
+
/** `AudioStatus` from expo-audio (`useAudioPlayerStatus`). Seconds. */
|
|
312
|
+
interface AudioStatusLike {
|
|
313
|
+
currentTime: number;
|
|
314
|
+
duration: number;
|
|
315
|
+
playing: boolean;
|
|
316
|
+
didJustFinish: boolean;
|
|
317
|
+
isLoaded: boolean;
|
|
318
|
+
playbackRate: number;
|
|
319
|
+
}
|
|
320
|
+
/** `AudioPlayer` from expo-audio. */
|
|
321
|
+
interface AudioPlayerLike {
|
|
322
|
+
play(): void;
|
|
323
|
+
pause(): void;
|
|
324
|
+
seekTo(seconds: number): Promise<void>;
|
|
325
|
+
setPlaybackRate(rate: number): void;
|
|
326
|
+
}
|
|
327
|
+
/** `import * as Audio from "expo-audio"` (SDK 58). */
|
|
328
|
+
interface ExpoAudioLike {
|
|
329
|
+
useAudioRecorder(options: Record<string, unknown>): AudioRecorderLike;
|
|
330
|
+
useAudioPlayer(source: {
|
|
331
|
+
uri: string;
|
|
332
|
+
} | null, options?: {
|
|
333
|
+
updateInterval?: number;
|
|
334
|
+
}): AudioPlayerLike;
|
|
335
|
+
useAudioPlayerStatus(player: AudioPlayerLike): AudioStatusLike;
|
|
336
|
+
RecordingPresets: {
|
|
337
|
+
HIGH_QUALITY: Record<string, unknown>;
|
|
338
|
+
};
|
|
339
|
+
requestRecordingPermissionsAsync(): Promise<{
|
|
340
|
+
granted: boolean;
|
|
341
|
+
canAskAgain?: boolean;
|
|
342
|
+
}>;
|
|
343
|
+
setAudioModeAsync(mode: {
|
|
344
|
+
allowsRecording?: boolean;
|
|
345
|
+
playsInSilentMode?: boolean;
|
|
346
|
+
}): Promise<void>;
|
|
347
|
+
}
|
|
348
|
+
/** One picked file (`ImagePickerAsset`). */
|
|
349
|
+
interface PickedAssetLike {
|
|
350
|
+
uri: string;
|
|
351
|
+
width?: number;
|
|
352
|
+
height?: number;
|
|
353
|
+
fileName?: string | null;
|
|
354
|
+
fileSize?: number;
|
|
355
|
+
mimeType?: string;
|
|
356
|
+
/** Milliseconds, for a video. */
|
|
357
|
+
duration?: number | null;
|
|
358
|
+
type?: string | null;
|
|
359
|
+
}
|
|
360
|
+
interface PickerResultLike {
|
|
361
|
+
canceled: boolean;
|
|
362
|
+
assets: PickedAssetLike[] | null;
|
|
363
|
+
}
|
|
364
|
+
/** `import * as ImagePicker from "expo-image-picker"`. */
|
|
365
|
+
interface ImagePickerLike {
|
|
366
|
+
launchImageLibraryAsync(options?: Record<string, unknown>): Promise<PickerResultLike>;
|
|
367
|
+
launchCameraAsync?(options?: Record<string, unknown>): Promise<PickerResultLike>;
|
|
368
|
+
requestCameraPermissionsAsync?(): Promise<{
|
|
369
|
+
granted: boolean;
|
|
370
|
+
}>;
|
|
371
|
+
}
|
|
372
|
+
/** `import * as Haptics from "expo-haptics"`. */
|
|
373
|
+
interface HapticsLike {
|
|
374
|
+
impactAsync(style?: never): Promise<void>;
|
|
375
|
+
selectionAsync(): Promise<void>;
|
|
376
|
+
notificationAsync(type?: never): Promise<void>;
|
|
377
|
+
ImpactFeedbackStyle: {
|
|
378
|
+
Light: unknown;
|
|
379
|
+
Medium: unknown;
|
|
380
|
+
};
|
|
381
|
+
NotificationFeedbackType: {
|
|
382
|
+
Success: unknown;
|
|
383
|
+
Warning: unknown;
|
|
384
|
+
Error: unknown;
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
/** `import * as Clipboard from "expo-clipboard"`. */
|
|
388
|
+
interface ClipboardLike {
|
|
389
|
+
setStringAsync(text: string): Promise<boolean>;
|
|
390
|
+
hasImageAsync?(): Promise<boolean>;
|
|
391
|
+
/** `data` is a data URI (`data:image/png;base64,…`). */
|
|
392
|
+
getImageAsync?(options: {
|
|
393
|
+
format: "png" | "jpeg";
|
|
394
|
+
}): Promise<{
|
|
395
|
+
data: string;
|
|
396
|
+
size: {
|
|
397
|
+
width: number;
|
|
398
|
+
height: number;
|
|
399
|
+
};
|
|
400
|
+
} | null>;
|
|
401
|
+
}
|
|
402
|
+
/** react-native-keyboard-controller's `KeyboardAvoidingView` (needs its `KeyboardProvider`, which GenesisNativeApp mounts). */
|
|
403
|
+
type KeyboardAvoidingLike = ComponentType<{
|
|
404
|
+
behavior?: "height" | "padding" | "position" | "translate-with-padding";
|
|
405
|
+
keyboardVerticalOffset?: number;
|
|
406
|
+
style?: unknown;
|
|
407
|
+
children?: ReactNode;
|
|
408
|
+
testID?: string;
|
|
409
|
+
}>;
|
|
410
|
+
/** Where drafts wait: AsyncStorage, or MMKV's async adapter. */
|
|
411
|
+
interface DraftStorageLike {
|
|
412
|
+
getItem(key: string): Promise<string | null>;
|
|
413
|
+
setItem(key: string, value: string): Promise<void>;
|
|
414
|
+
removeItem(key: string): Promise<void>;
|
|
415
|
+
/** For `clearDrafts` on sign-out. */
|
|
416
|
+
getAllKeys?(): Promise<readonly string[]>;
|
|
417
|
+
multiRemove?(keys: readonly string[]): Promise<void>;
|
|
418
|
+
}
|
|
419
|
+
/** `import * as Sharing from "expo-sharing"`: the system share sheet for a file on the phone. */
|
|
420
|
+
interface SharingLike {
|
|
421
|
+
isAvailableAsync(): Promise<boolean>;
|
|
422
|
+
shareAsync(url: string, options?: {
|
|
423
|
+
mimeType?: string;
|
|
424
|
+
UTI?: string;
|
|
425
|
+
dialogTitle?: string;
|
|
426
|
+
}): Promise<void>;
|
|
427
|
+
}
|
|
428
|
+
/** `import * as MediaLibrary from "expo-media-library"`: save to Photos (asks for add-only access). */
|
|
429
|
+
interface MediaLibraryLike {
|
|
430
|
+
requestPermissionsAsync(writeOnly?: boolean): Promise<{
|
|
431
|
+
granted: boolean;
|
|
432
|
+
}>;
|
|
433
|
+
saveToLibraryAsync(localUri: string): Promise<void>;
|
|
434
|
+
}
|
|
435
|
+
/** `NetInfo` from @react-native-community/netinfo: tells chat when the connection comes back. */
|
|
436
|
+
interface NetInfoLike {
|
|
437
|
+
addEventListener(listener: (state: {
|
|
438
|
+
isConnected: boolean | null;
|
|
439
|
+
isInternetReachable?: boolean | null;
|
|
440
|
+
}) => void): () => void;
|
|
441
|
+
}
|
|
442
|
+
/** A permission answer from expo-camera (`PermissionResponse`). */
|
|
443
|
+
interface PermissionLike {
|
|
444
|
+
granted: boolean;
|
|
445
|
+
canAskAgain?: boolean;
|
|
446
|
+
}
|
|
447
|
+
/** The `ref` of expo-camera's `CameraView`. */
|
|
448
|
+
interface CameraViewHandleLike {
|
|
449
|
+
/** Resolves with the file when `stopRecording` is called, the limit is reached or the preview stops. */
|
|
450
|
+
recordAsync(options?: {
|
|
451
|
+
maxDuration?: number;
|
|
452
|
+
maxFileSize?: number;
|
|
453
|
+
codec?: string;
|
|
454
|
+
}): Promise<{
|
|
455
|
+
uri: string;
|
|
456
|
+
} | undefined>;
|
|
457
|
+
stopRecording(): void;
|
|
458
|
+
}
|
|
459
|
+
/** `import * as Camera from "expo-camera"` (SDK 58): `CameraView` and the permission requests on `Camera`. */
|
|
460
|
+
interface ExpoCameraLike {
|
|
461
|
+
CameraView: ComponentType<{
|
|
462
|
+
ref?: unknown;
|
|
463
|
+
style?: unknown;
|
|
464
|
+
facing?: "front" | "back";
|
|
465
|
+
mode?: "picture" | "video";
|
|
466
|
+
mirror?: boolean;
|
|
467
|
+
mute?: boolean;
|
|
468
|
+
videoQuality?: "2160p" | "1080p" | "720p" | "480p" | "4:3";
|
|
469
|
+
onCameraReady?: () => void;
|
|
470
|
+
onMountError?: (event: {
|
|
471
|
+
message: string;
|
|
472
|
+
}) => void;
|
|
473
|
+
testID?: string;
|
|
474
|
+
}>;
|
|
475
|
+
Camera: {
|
|
476
|
+
requestCameraPermissionsAsync(): Promise<PermissionLike>;
|
|
477
|
+
requestMicrophonePermissionsAsync(): Promise<PermissionLike>;
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
/** `VideoPlayer` from expo-video. */
|
|
481
|
+
interface VideoPlayerLike {
|
|
482
|
+
play(): void;
|
|
483
|
+
pause(): void;
|
|
484
|
+
muted: boolean;
|
|
485
|
+
playing: boolean;
|
|
486
|
+
playbackRate: number;
|
|
487
|
+
currentTime: number;
|
|
488
|
+
addListener(event: "playingChange" | "statusChange" | "playToEnd", listener: (payload: never) => void): {
|
|
489
|
+
remove(): void;
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
/** `import * as Video from "expo-video"` (SDK 58). */
|
|
493
|
+
interface ExpoVideoLike {
|
|
494
|
+
useVideoPlayer(source: {
|
|
495
|
+
uri: string;
|
|
496
|
+
contentType?: "auto" | "progressive" | "hls";
|
|
497
|
+
} | null, setup?: (player: VideoPlayerLike) => void): VideoPlayerLike;
|
|
498
|
+
VideoView: ComponentType<{
|
|
499
|
+
ref?: unknown;
|
|
500
|
+
player: VideoPlayerLike;
|
|
501
|
+
style?: unknown;
|
|
502
|
+
contentFit?: "contain" | "cover" | "fill";
|
|
503
|
+
nativeControls?: boolean;
|
|
504
|
+
allowsPictureInPicture?: boolean;
|
|
505
|
+
testID?: string;
|
|
506
|
+
}>;
|
|
507
|
+
}
|
|
508
|
+
/** The `ref` of expo-video's `VideoView`. */
|
|
509
|
+
interface VideoViewHandleLike {
|
|
510
|
+
enterFullscreen(): Promise<void>;
|
|
511
|
+
}
|
|
512
|
+
/** `import * as WebBrowser from "expo-web-browser"`: links open in an in-app browser. */
|
|
513
|
+
interface WebBrowserLike {
|
|
514
|
+
openBrowserAsync(url: string, options?: Record<string, unknown>): Promise<unknown>;
|
|
515
|
+
}
|
|
516
|
+
/** An open file read a chunk at a time (expo-file-system's `new File(uri).open()`), as `createTusUpload` takes. */
|
|
517
|
+
interface ChunkFileLike {
|
|
518
|
+
size: number;
|
|
519
|
+
read(offset: number, length: number): Promise<ArrayBuffer | Uint8Array>;
|
|
520
|
+
close?(): void;
|
|
521
|
+
}
|
|
522
|
+
/** Each optional: without one its feature is hidden (no mic without audio, no attach without a picker). */
|
|
523
|
+
interface ChatNativeModules {
|
|
524
|
+
audio?: ExpoAudioLike;
|
|
525
|
+
imagePicker?: ImagePickerLike;
|
|
526
|
+
haptics?: HapticsLike;
|
|
527
|
+
clipboard?: ClipboardLike;
|
|
528
|
+
keyboardAvoidingView?: KeyboardAvoidingLike;
|
|
529
|
+
drafts?: DraftStorageLike;
|
|
530
|
+
/** Share in the photo viewer (with `downloadFile` for photos not on the phone). */
|
|
531
|
+
sharing?: SharingLike;
|
|
532
|
+
/** Save in the photo viewer (with `downloadFile`). */
|
|
533
|
+
mediaLibrary?: MediaLibraryLike;
|
|
534
|
+
/**
|
|
535
|
+
* Copies a photo from its signed link to the phone and returns the file:// URI, for sharing
|
|
536
|
+
* and saving (expo-file-system: `(await File.downloadFileAsync(url, Paths.cache, { idempotent: true })).uri`).
|
|
537
|
+
*/
|
|
538
|
+
downloadFile?: (url: string) => Promise<string>;
|
|
539
|
+
/** Unsent messages are retried when the connection comes back (`ChatAutoRetry`). */
|
|
540
|
+
netInfo?: NetInfoLike;
|
|
541
|
+
/** Video messages recorded in the thread. Without it there is no video button. */
|
|
542
|
+
camera?: ExpoCameraLike;
|
|
543
|
+
/** Video playback (HLS and files) in the bubble. Without it a video shows as a row with its name. */
|
|
544
|
+
video?: ExpoVideoLike;
|
|
545
|
+
/** Where links from previews open; without it, the system browser (`Linking`). */
|
|
546
|
+
webBrowser?: WebBrowserLike;
|
|
547
|
+
/** Reads a recording a chunk at a time for a hosted upload; without it the file is read whole. */
|
|
548
|
+
openFile?: (uri: string) => Promise<ChunkFileLike> | ChunkFileLike;
|
|
549
|
+
}
|
|
550
|
+
type HapticEvent = "send" | "record-start" | "record-lock" | "record-cancel" | "select" | "error";
|
|
551
|
+
/** Plays a haptic when the app gave expo-haptics; never throws. */
|
|
552
|
+
declare function haptic(haptics: HapticsLike | undefined, event: HapticEvent): void;
|
|
553
|
+
|
|
554
|
+
interface ChatPerson {
|
|
555
|
+
name: string;
|
|
556
|
+
avatarUrl?: string | null;
|
|
557
|
+
}
|
|
558
|
+
interface ChatNativeContextValue {
|
|
559
|
+
modules: ChatNativeModules;
|
|
560
|
+
copy: ChatNativeCopy;
|
|
561
|
+
/** A person's name and picture, or undefined while it isn't known. */
|
|
562
|
+
person(userId: string | null | undefined): ChatPerson | undefined;
|
|
563
|
+
}
|
|
564
|
+
interface ChatNativeProviderProps {
|
|
565
|
+
/** Define at module scope: a new object every render re-renders every chat screen. */
|
|
566
|
+
modules?: ChatNativeModules;
|
|
567
|
+
copy?: ChatNativeCopyOverrides;
|
|
568
|
+
/** Names and avatars the app already has; wins over `ChatProvider loadPeople`. */
|
|
569
|
+
getPerson?: (userId: string) => ChatPerson | undefined;
|
|
570
|
+
children: ReactNode;
|
|
571
|
+
}
|
|
572
|
+
/** Inside `ChatProvider` (from `/react`). Optional: without it there are no native extras and people are "Someone". */
|
|
573
|
+
declare function ChatNativeProvider({ modules, copy, getPerson, children }: ChatNativeProviderProps): react.JSX.Element;
|
|
574
|
+
declare function useChatNative(): ChatNativeContextValue;
|
|
575
|
+
/** "SA" for "Sam Altman", "S" for "sam". */
|
|
576
|
+
declare function initials(name: string | undefined): string;
|
|
577
|
+
|
|
578
|
+
interface ChatInboxScreenProps {
|
|
579
|
+
/** Opens a conversation (`router.push(chatPath(id))`). */
|
|
580
|
+
onOpen: (conversationId: string) => void;
|
|
581
|
+
/** Starts one (opens your people picker, or `ChatStartSheet`). Shows the empty state's button. */
|
|
582
|
+
onStart?: () => void;
|
|
583
|
+
/** One kind only. */
|
|
584
|
+
kind?: string;
|
|
585
|
+
/** Only these kinds. */
|
|
586
|
+
kinds?: string[];
|
|
587
|
+
/** Every kind but these (`["support"]` keeps support tickets out of a messages inbox). */
|
|
588
|
+
excludeKinds?: string[];
|
|
589
|
+
archived?: boolean;
|
|
590
|
+
/** Passed to InboxPage (a header, a section title, slots). */
|
|
591
|
+
pageProps?: Partial<Omit<InboxPageProps, "items">>;
|
|
592
|
+
testID?: string;
|
|
593
|
+
}
|
|
594
|
+
/** An inbox row from a conversation. Exported for apps that build their own list. */
|
|
595
|
+
declare function inboxRow(item: ChatInboxItem, ctx: {
|
|
596
|
+
me: string;
|
|
597
|
+
copy: ChatNativeCopy;
|
|
598
|
+
person: (id: string | null) => ChatPerson | undefined;
|
|
599
|
+
}): Omit<InboxItem, "onPress" | "swipeActions">;
|
|
600
|
+
declare function ChatInboxScreen({ onOpen, onStart, kind, kinds, excludeKinds, archived, pageProps, testID }: ChatInboxScreenProps): react.JSX.Element;
|
|
601
|
+
|
|
602
|
+
type OpenAttachment = (attachment: ChatAttachment, message: ChatMessage) => void;
|
|
603
|
+
/** A message's attachments split into the app's cards and the rest (photos, voice, video, files). */
|
|
604
|
+
declare function splitCardAttachments<T extends {
|
|
605
|
+
kind: string;
|
|
606
|
+
}>(attachments: readonly T[], kinds: Record<string, ChatAttachmentKind> | undefined): {
|
|
607
|
+
cards: T[];
|
|
608
|
+
rest: T[];
|
|
609
|
+
};
|
|
610
|
+
/**
|
|
611
|
+
* A link a card may open by itself: https only. A kind's href is often built from what another
|
|
612
|
+
* person sent, so any other scheme (tel:, sms:, another app's links) goes through the app's own
|
|
613
|
+
* `onOpenAttachment` or the kind's `onPress` instead.
|
|
614
|
+
*/
|
|
615
|
+
declare function isOpenableHref(href: string | null | undefined): href is string;
|
|
616
|
+
/** What a card does: the kind's own handler, else the app's, else an https href; null without a button. */
|
|
617
|
+
declare function cardAction(kind: ChatAttachmentKind | undefined, attachment: ChatAttachment, message: ChatMessage, onOpenAttachment?: OpenAttachment): {
|
|
618
|
+
label: string;
|
|
619
|
+
run: () => void;
|
|
620
|
+
} | null;
|
|
621
|
+
interface ChatAttachmentCardProps {
|
|
622
|
+
attachment: ChatAttachment;
|
|
623
|
+
message: ChatMessage;
|
|
624
|
+
kind: ChatAttachmentKind;
|
|
625
|
+
mine?: boolean;
|
|
626
|
+
onOpenAttachment?: OpenAttachment;
|
|
627
|
+
testID?: string;
|
|
628
|
+
}
|
|
629
|
+
declare function ChatAttachmentCard({ attachment, message, kind, mine, onOpenAttachment, testID }: ChatAttachmentCardProps): react.JSX.Element;
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* @feat:chat
|
|
633
|
+
* The thread as rows for the list: day separators, the "New messages" line,
|
|
634
|
+
* and each message with where its group of one sender starts and ends (the
|
|
635
|
+
* name goes over the first, the avatar beside the last, in a group
|
|
636
|
+
* conversation). Oldest first, top to bottom: the order the thread's list
|
|
637
|
+
* shows and VoiceOver reads (a day's heading and the "New messages" line
|
|
638
|
+
* before the messages they head).
|
|
639
|
+
* A row is keyed by the message's client id, so a message that lands keeps
|
|
640
|
+
* its row.
|
|
641
|
+
*/
|
|
642
|
+
|
|
643
|
+
type ThreadRow = {
|
|
644
|
+
type: "day";
|
|
645
|
+
key: string;
|
|
646
|
+
label: string;
|
|
647
|
+
} | {
|
|
648
|
+
type: "unread";
|
|
649
|
+
key: string;
|
|
650
|
+
} | {
|
|
651
|
+
type: "message";
|
|
652
|
+
key: string;
|
|
653
|
+
item: ThreadItem;
|
|
654
|
+
mine: boolean;
|
|
655
|
+
senderId: string | null;
|
|
656
|
+
groupStart: boolean;
|
|
657
|
+
groupEnd: boolean;
|
|
658
|
+
/** Over the first of a run, in a group, for someone else's. */
|
|
659
|
+
showName: boolean;
|
|
660
|
+
/** Beside the last of a run, in a group, for someone else's. */
|
|
661
|
+
showAvatar: boolean;
|
|
662
|
+
};
|
|
663
|
+
interface BuildRowsOptions {
|
|
664
|
+
me: string;
|
|
665
|
+
firstUnreadId?: string | null;
|
|
666
|
+
/** More than two people: names and avatars show. */
|
|
667
|
+
group: boolean;
|
|
668
|
+
now?: Date;
|
|
669
|
+
locale?: string;
|
|
670
|
+
}
|
|
671
|
+
declare function rowKey(item: ThreadItem): string;
|
|
672
|
+
/** Oldest first. */
|
|
673
|
+
declare function buildThreadRows(items: ThreadItem[], options: BuildRowsOptions): ThreadRow[];
|
|
674
|
+
/**
|
|
675
|
+
* Newest first, for an inverted list (its first row sits at the bottom). The
|
|
676
|
+
* thread screen no longer uses it: VoiceOver reads an inverted list's rows
|
|
677
|
+
* newest first, each day's heading after its messages.
|
|
678
|
+
* @deprecated Use `buildThreadRows` in a list that isn't inverted.
|
|
679
|
+
*/
|
|
680
|
+
declare function invertedRows(items: ThreadItem[], options: BuildRowsOptions): ThreadRow[];
|
|
681
|
+
|
|
682
|
+
interface JumpTarget {
|
|
683
|
+
messageId: string;
|
|
684
|
+
seq?: number;
|
|
685
|
+
}
|
|
686
|
+
/** The row a message is drawn in, or -1. */
|
|
687
|
+
declare function rowIndexOf(rows: readonly ThreadRow[], messageId: string): number;
|
|
688
|
+
interface UseJumpOptions {
|
|
689
|
+
/** What to go to; a new target (another id or seq) starts again. */
|
|
690
|
+
target: JumpTarget | null | undefined;
|
|
691
|
+
rows: readonly ThreadRow[];
|
|
692
|
+
/** The thread's first page is still loading. */
|
|
693
|
+
loading: boolean;
|
|
694
|
+
has: (messageId: string) => boolean;
|
|
695
|
+
loadUntil: (seq: number) => Promise<boolean>;
|
|
696
|
+
/** Scroll the list to this row (centred); the message id, for a retry once rows have moved. */
|
|
697
|
+
scrollTo: (index: number, messageId: string) => void;
|
|
698
|
+
/** Called before loading or scrolling (stop following the bottom). */
|
|
699
|
+
onStart?: () => void;
|
|
700
|
+
/** It was scrolled to (say so to VoiceOver). */
|
|
701
|
+
onLanded?: (messageId: string) => void;
|
|
702
|
+
/** It isn't there any more (go back to where the thread was). */
|
|
703
|
+
onGone: () => void;
|
|
704
|
+
onError: (e: unknown) => void;
|
|
705
|
+
}
|
|
706
|
+
declare function useJumpToMessage({ target, rows, loading, has, loadUntil, scrollTo, onStart, onLanded, onGone, onError }: UseJumpOptions): {
|
|
707
|
+
highlightedId: string | null;
|
|
708
|
+
jumping: boolean;
|
|
709
|
+
};
|
|
710
|
+
|
|
711
|
+
interface ChatThreadScreenProps {
|
|
712
|
+
conversationId: string;
|
|
713
|
+
/** One thread's replies instead of the main timeline. */
|
|
714
|
+
threadRootId?: string | null;
|
|
715
|
+
/**
|
|
716
|
+
* Points from the top of the screen to the top of this view, for keeping the composer above the
|
|
717
|
+
* keyboard. Measured from the screen's own position when left out (under an opaque header it is
|
|
718
|
+
* the header's bottom).
|
|
719
|
+
*/
|
|
720
|
+
keyboardVerticalOffset?: number;
|
|
721
|
+
/** Open at this message (a search result's `messageId` and `seq`): earlier pages load until it is held. */
|
|
722
|
+
jumpTo?: JumpTarget | null;
|
|
723
|
+
/** Only this topic's messages, with no topic bar. */
|
|
724
|
+
topic?: string;
|
|
725
|
+
/** Show the topic bar before the first topic exists, so an owner or admin can add one. */
|
|
726
|
+
showTopics?: boolean;
|
|
727
|
+
/** Scheduled send (touch and hold Send) and the scheduled list. Default true. */
|
|
728
|
+
scheduling?: boolean;
|
|
729
|
+
/** The away line under the header. Default true. */
|
|
730
|
+
availability?: boolean;
|
|
731
|
+
/** An app attachment card's button (a form, a check-in), when its kind gives no `onPress`. */
|
|
732
|
+
onOpenAttachment?: OpenAttachment;
|
|
733
|
+
testID?: string;
|
|
734
|
+
}
|
|
735
|
+
/** Points from the bottom that still count as "at the bottom" (FlatList, which follows by hand). */
|
|
736
|
+
declare const AT_BOTTOM_POINTS = 120;
|
|
737
|
+
/**
|
|
738
|
+
* How the thread's list is set up, oldest first and not inverted. FlashList:
|
|
739
|
+
* its chat mode (opens at the bottom, follows new rows while within a fifth of
|
|
740
|
+
* a screen of it, keeps the first visible row in place when earlier ones load
|
|
741
|
+
* at the top). FlatList: keeps its place when earlier rows load at the top;
|
|
742
|
+
* the screen follows new rows itself (`followsByHand`).
|
|
743
|
+
*/
|
|
744
|
+
declare function threadListMode(flash: boolean, reducedMotion: boolean): {
|
|
745
|
+
followsByHand: boolean;
|
|
746
|
+
maintainVisibleContentPosition: Record<string, unknown>;
|
|
747
|
+
};
|
|
748
|
+
/** Whether a scroll position is at (or near) the bottom of its content. */
|
|
749
|
+
declare function nearBottom(m: {
|
|
750
|
+
contentOffset: {
|
|
751
|
+
y: number;
|
|
752
|
+
};
|
|
753
|
+
contentSize: {
|
|
754
|
+
height: number;
|
|
755
|
+
};
|
|
756
|
+
layoutMeasurement: {
|
|
757
|
+
height: number;
|
|
758
|
+
};
|
|
759
|
+
}, slack?: number): boolean;
|
|
760
|
+
declare function ChatThreadScreen(props: ChatThreadScreenProps): react.JSX.Element;
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* @feat:chat
|
|
764
|
+
* `<Stack.Screen options>` for a screen that renders `ChatThreadScreen`.
|
|
765
|
+
*
|
|
766
|
+
* The bar is opaque. It was made so while the thread was an inverted list
|
|
767
|
+
* (`transform: scaleY(-1)` on the scroll view), which it no longer is; it
|
|
768
|
+
* stays opaque until a see-through bar is checked over the upright list. On
|
|
769
|
+
* iOS 26 and later the navigation bar is see-through and draws a scroll
|
|
770
|
+
* edge effect over the content scroll view under it; UIKit lays that effect
|
|
771
|
+
* out in the flipped scroll view's own coordinates, so once the thread is
|
|
772
|
+
* long enough to reach the bar the whole list renders blurred. An opaque bar
|
|
773
|
+
* keeps the screen's content below it: nothing scrolls under the bar, and no
|
|
774
|
+
* edge effect is drawn over the flipped list. Seen on iOS 27 with FlashList
|
|
775
|
+
* and with FlatList.
|
|
776
|
+
*/
|
|
777
|
+
|
|
778
|
+
type ChatThreadStackOptionsProps = ScreenHeaderProps & {
|
|
779
|
+
headerTitle?: string;
|
|
780
|
+
};
|
|
781
|
+
/** The thread's header options with an opaque bar in `background` (the thread's own background). */
|
|
782
|
+
declare function chatThreadStackOptions(props: ChatThreadStackOptionsProps, background: ColorValue): Record<string, unknown>;
|
|
783
|
+
/** `chatThreadStackOptions` in the theme's grouped background, the color `ChatThreadScreen` draws. */
|
|
784
|
+
declare function useChatThreadStackOptions(props: ChatThreadStackOptionsProps): Record<string, unknown>;
|
|
785
|
+
|
|
786
|
+
interface ChatStartPerson {
|
|
787
|
+
userId: string;
|
|
788
|
+
name: string;
|
|
789
|
+
avatarUrl?: string | null;
|
|
790
|
+
/** A second line ("Coach", "Team"). */
|
|
791
|
+
subtitle?: string;
|
|
792
|
+
}
|
|
793
|
+
interface ChatStartSheetProps {
|
|
794
|
+
open: boolean;
|
|
795
|
+
onClose: () => void;
|
|
796
|
+
people: ChatStartPerson[];
|
|
797
|
+
/** Default `direct`. */
|
|
798
|
+
kind?: string;
|
|
799
|
+
onStarted: (conversationId: string) => void;
|
|
800
|
+
testID?: string;
|
|
801
|
+
}
|
|
802
|
+
declare function ChatStartSheet({ open, onClose, people, kind, onStarted, testID }: ChatStartSheetProps): react.JSX.Element;
|
|
803
|
+
/** A 44pt compose button for the inbox's header (`headerRight`). */
|
|
804
|
+
declare function ChatStartButton({ onPress, testID }: {
|
|
805
|
+
onPress: () => void;
|
|
806
|
+
testID?: string;
|
|
807
|
+
}): react.JSX.Element;
|
|
808
|
+
|
|
809
|
+
/** A file in the composer, shown as a thumbnail until it is sent. */
|
|
810
|
+
interface ComposerAttachment {
|
|
811
|
+
/** Stable for keys and removal. */
|
|
812
|
+
key: string;
|
|
813
|
+
file: LocalFile;
|
|
814
|
+
}
|
|
815
|
+
interface ReplyTarget {
|
|
816
|
+
messageId: string;
|
|
817
|
+
/** Who wrote it, as shown ("Sam", "You"). */
|
|
818
|
+
name: string;
|
|
819
|
+
/** A one-line preview. */
|
|
820
|
+
preview: string;
|
|
821
|
+
}
|
|
822
|
+
interface ComposerState {
|
|
823
|
+
text: string;
|
|
824
|
+
attachments: ComposerAttachment[];
|
|
825
|
+
replyTo: ReplyTarget | null;
|
|
826
|
+
/** The last thing that couldn't be added ("x.svg can't be sent"). Cleared by the next change. */
|
|
827
|
+
notice: string | null;
|
|
828
|
+
}
|
|
829
|
+
declare const emptyComposer: ComposerState;
|
|
830
|
+
type ComposerAction = {
|
|
831
|
+
type: "text";
|
|
832
|
+
text: string;
|
|
833
|
+
}
|
|
834
|
+
/** `tooMany` is what to say when the files don't all fit (copy.tooMany). */
|
|
835
|
+
| {
|
|
836
|
+
type: "add";
|
|
837
|
+
attachments: ComposerAttachment[];
|
|
838
|
+
notice?: string | null;
|
|
839
|
+
tooMany?: string;
|
|
840
|
+
} | {
|
|
841
|
+
type: "remove";
|
|
842
|
+
key: string;
|
|
843
|
+
} | {
|
|
844
|
+
type: "reply";
|
|
845
|
+
to: ReplyTarget | null;
|
|
846
|
+
} | {
|
|
847
|
+
type: "restore";
|
|
848
|
+
draft: ComposerDraft;
|
|
849
|
+
}
|
|
850
|
+
/** Another conversation (or thread) opened in the same composer: start empty. */
|
|
851
|
+
| {
|
|
852
|
+
type: "reset";
|
|
853
|
+
} | {
|
|
854
|
+
type: "sent";
|
|
855
|
+
};
|
|
856
|
+
declare function composerReducer(state: ComposerState, action: ComposerAction): ComposerState;
|
|
857
|
+
/** Something to send: text that isn't blank, or a file. */
|
|
858
|
+
declare function canSend(state: ComposerState, canPost: boolean): boolean;
|
|
859
|
+
/**
|
|
860
|
+
* Why this person can't post here, for the disabled composer; null when they can.
|
|
861
|
+
* `canPost` is the server's answer (null while it loads, which disables quietly).
|
|
862
|
+
*/
|
|
863
|
+
declare function composerDisabledReason(canPost: boolean | null, conversation: Pick<ChatConversation, "settings" | "status" | "archivedAt"> | null, role: ChatRole | null, copy: Pick<ChatNativeCopy, "cantPost" | "cantPostAdmins" | "cantPostClosed">): string | null;
|
|
864
|
+
/** What `useConversation().send` takes: the input and the files. */
|
|
865
|
+
declare function toSend(state: ComposerState): {
|
|
866
|
+
input: {
|
|
867
|
+
body: string;
|
|
868
|
+
replyToId: string | null;
|
|
869
|
+
mentions: string[];
|
|
870
|
+
};
|
|
871
|
+
files: LocalFile[];
|
|
872
|
+
};
|
|
873
|
+
/** A reply target from a message. */
|
|
874
|
+
declare function replyTargetFor(message: ChatMessage, name: string, preview: string): ReplyTarget;
|
|
875
|
+
/**
|
|
876
|
+
* Picked photos and videos as files to send, with what was refused and why:
|
|
877
|
+
* a type the bucket won't take, a file over the limit.
|
|
878
|
+
*/
|
|
879
|
+
declare function filesFromAssets(assets: readonly PickedAssetLike[], copy: Pick<ChatNativeCopy, "tooLarge" | "notAllowedType" | "photo" | "video">): {
|
|
880
|
+
attachments: ComposerAttachment[];
|
|
881
|
+
notice: string | null;
|
|
882
|
+
};
|
|
883
|
+
/** A pasted picture (a data URI from the clipboard) as a file to send. */
|
|
884
|
+
declare function fileFromDataUri(dataUri: string, size?: {
|
|
885
|
+
width: number;
|
|
886
|
+
height: number;
|
|
887
|
+
}): ComposerAttachment | null;
|
|
888
|
+
/** Base64 to bytes without `atob` (not on every Hermes build). */
|
|
889
|
+
declare function base64ToBytes(b64: string): Uint8Array;
|
|
890
|
+
/** What a draft keeps: the text and what it replies to (never the files, which may be gone by then). */
|
|
891
|
+
interface ComposerDraft {
|
|
892
|
+
text: string;
|
|
893
|
+
replyTo?: ReplyTarget | null;
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* One draft per signed-in person, per conversation, and per thread within it.
|
|
897
|
+
* The person comes first, so `clearDrafts` can find all of theirs on sign-out,
|
|
898
|
+
* and a second login on the phone never sees the first one's drafts.
|
|
899
|
+
*/
|
|
900
|
+
declare function draftKey(userId: string, conversationId: string, threadRootId?: string | null): string;
|
|
901
|
+
/**
|
|
902
|
+
* Removes drafts from the phone: one person's (`clearDrafts(storage, userId)`,
|
|
903
|
+
* on sign-out), or everyone's but one (`{ except: userId }`, on sign-in, which
|
|
904
|
+
* also covers a session that ended while the app was closed). Drafts saved
|
|
905
|
+
* before drafts were kept per person go either way. Needs the storage's
|
|
906
|
+
* `getAllKeys` (AsyncStorage has it); without it, it reports that through
|
|
907
|
+
* `onError` and removes nothing. Never throws. Returns how many it removed.
|
|
908
|
+
*/
|
|
909
|
+
declare function clearDrafts(storage: DraftStorageLike, who: string | {
|
|
910
|
+
except: string;
|
|
911
|
+
}, onError?: (error: unknown) => void): Promise<number>;
|
|
912
|
+
declare function memoryDraftStorage(): DraftStorageLike;
|
|
913
|
+
declare function loadDraft(storage: DraftStorageLike, key: string): Promise<ComposerDraft | null>;
|
|
914
|
+
/**
|
|
915
|
+
* Saves the draft a moment after typing stops (and at once on `flush`); an
|
|
916
|
+
* empty composer removes it. Storage errors are swallowed: a draft is a nicety.
|
|
917
|
+
*/
|
|
918
|
+
declare function createDraftSaver(storage: DraftStorageLike, key: string, delayMs?: number): {
|
|
919
|
+
save(draft: ComposerDraft): void;
|
|
920
|
+
flush(): void;
|
|
921
|
+
clear(): void;
|
|
922
|
+
};
|
|
923
|
+
|
|
924
|
+
/** `userId` is the signed-in person's (`useChat().userId`): drafts are kept per person. */
|
|
925
|
+
declare function useChatComposer(conversationId: string, threadRootId: string | null | undefined, userId: string): {
|
|
926
|
+
state: ComposerState;
|
|
927
|
+
dispatch: (action: ComposerAction) => void;
|
|
928
|
+
setReply: (to: ReplyTarget | null) => void;
|
|
929
|
+
take: () => {
|
|
930
|
+
input: {
|
|
931
|
+
body: string;
|
|
932
|
+
replyToId: string | null;
|
|
933
|
+
mentions: string[];
|
|
934
|
+
};
|
|
935
|
+
files: LocalFile[];
|
|
936
|
+
} | null;
|
|
937
|
+
};
|
|
938
|
+
type ChatComposerHandle = ReturnType<typeof useChatComposer>;
|
|
939
|
+
|
|
940
|
+
interface ChatComposerProps {
|
|
941
|
+
composer: ChatComposerHandle;
|
|
942
|
+
/** null while it loads. */
|
|
943
|
+
canPost: boolean | null;
|
|
944
|
+
/** Why not, when `canPost` is false (`composerDisabledReason`). */
|
|
945
|
+
disabledReason: string | null;
|
|
946
|
+
role: ChatRole | null;
|
|
947
|
+
onSend: (payload: ReturnType<typeof toSend>) => void;
|
|
948
|
+
signalTyping?: () => void;
|
|
949
|
+
signalRecording?: () => void;
|
|
950
|
+
/** The signed-in person (`useChat().userId`), whose recent emoji the picker keeps. */
|
|
951
|
+
userId?: string | null;
|
|
952
|
+
/** Touch and hold Send (or VoiceOver's action on it) to schedule what's written (`ChatScheduleSheet`). */
|
|
953
|
+
onSchedule?: () => void;
|
|
954
|
+
/** The field's placeholder ("Message #Week 1" in a topic). */
|
|
955
|
+
placeholder?: string;
|
|
956
|
+
/** Opens the video recorder (`ChatVideoRecorder`); with it, a camera button sits beside attach. */
|
|
957
|
+
onRecordVideo?: () => void;
|
|
958
|
+
testID?: string;
|
|
959
|
+
}
|
|
960
|
+
declare function ChatComposer(props: ChatComposerProps): react.JSX.Element;
|
|
961
|
+
|
|
962
|
+
type MessageRow = Extract<ThreadRow, {
|
|
963
|
+
type: "message";
|
|
964
|
+
}>;
|
|
965
|
+
interface MessageBubbleProps {
|
|
966
|
+
row: MessageRow;
|
|
967
|
+
me: string;
|
|
968
|
+
/** Signed links by storage path. */
|
|
969
|
+
urls: Record<string, string>;
|
|
970
|
+
/** The message this one replies to, when it is loaded. */
|
|
971
|
+
repliedTo?: ChatMessage | null;
|
|
972
|
+
/** Avatars sit in a column when the conversation is a group. */
|
|
973
|
+
group: boolean;
|
|
974
|
+
reducedMotion: boolean;
|
|
975
|
+
onLongPress?: (message: ChatMessage) => void;
|
|
976
|
+
onReply?: (message: ChatMessage) => void;
|
|
977
|
+
onRetry?: (clientId: string) => void;
|
|
978
|
+
onDiscard?: (clientId: string) => void;
|
|
979
|
+
/** A failed message whose files didn't survive a relaunch: pick them again (`useConversation().reattach`). */
|
|
980
|
+
onReattach?: (clientId: string, files: readonly PendingFile[]) => void;
|
|
981
|
+
onToggleReaction?: (message: ChatMessage, emoji: string, on: boolean) => void;
|
|
982
|
+
onOpenImage?: (uri: string, alt: string) => void;
|
|
983
|
+
/** The swipe crossed the reply line (for a haptic). */
|
|
984
|
+
onSwipeReply?: () => void;
|
|
985
|
+
/** You observe this conversation: moderation's markers show (hidden, flagged). */
|
|
986
|
+
observer?: boolean;
|
|
987
|
+
/** Drawn highlighted for a moment (the message a search result opened). */
|
|
988
|
+
highlighted?: boolean;
|
|
989
|
+
/** The preview of a link in its text (`useLinkPreviews`, `previewToShow`). */
|
|
990
|
+
linkPreview?: ChatLinkPreview | null;
|
|
991
|
+
/** Show link previews' images (off by default; the thread screen passes `ChatProvider linkPreviewImages`). */
|
|
992
|
+
linkPreviewImages?: boolean;
|
|
993
|
+
/** The app's own attachment kinds, drawn as cards (`ChatProvider attachmentKinds`). */
|
|
994
|
+
attachmentKinds?: Record<string, ChatAttachmentKind>;
|
|
995
|
+
/** A card's button, when its kind gives no `onPress` of its own. */
|
|
996
|
+
onOpenAttachment?: OpenAttachment;
|
|
997
|
+
testID: string;
|
|
998
|
+
}
|
|
999
|
+
declare function formatClock(iso: string): string;
|
|
1000
|
+
/** Mentions written `@[Name](user:<id>)` read as `@Name`. */
|
|
1001
|
+
declare function displayBody(body: string): string;
|
|
1002
|
+
declare function MessageBubbleImpl(props: MessageBubbleProps): react.JSX.Element;
|
|
1003
|
+
declare const MessageBubble: react.MemoExoticComponent<typeof MessageBubbleImpl>;
|
|
1004
|
+
|
|
1005
|
+
/**
|
|
1006
|
+
* @feat:chat
|
|
1007
|
+
* Which actions a message offers this person, from the same rules migration
|
|
1008
|
+
* 0001 enforces (react and reply need can_post; pin follows
|
|
1009
|
+
* useConversation().canPin: an owner or admin, or a member of a two-person
|
|
1010
|
+
* kind; delete needs the sender, an owner or
|
|
1011
|
+
* admin, or an observer who may post). The server stays the judge: an action
|
|
1012
|
+
* it refuses shows a toast.
|
|
1013
|
+
*/
|
|
1014
|
+
|
|
1015
|
+
type MessageActionId = "reply" | "copy" | "pin" | "unpin" | "delete";
|
|
1016
|
+
/** The quick reactions on the long-press sheet. */
|
|
1017
|
+
declare const QUICK_REACTIONS: readonly ["👍", "❤️", "😂", "😮", "😢", "🙏"];
|
|
1018
|
+
interface MessageActionContext {
|
|
1019
|
+
me: string;
|
|
1020
|
+
role: ChatRole | null;
|
|
1021
|
+
canPost: boolean;
|
|
1022
|
+
pinned: boolean;
|
|
1023
|
+
/** May pin here (`useConversation().canPin`: an owner or admin, or a member of a two-person kind). */
|
|
1024
|
+
canPin: boolean;
|
|
1025
|
+
/** Copy needs expo-clipboard. */
|
|
1026
|
+
canCopy: boolean;
|
|
1027
|
+
}
|
|
1028
|
+
declare function canReactTo(message: ChatMessage, ctx: Pick<MessageActionContext, "canPost">): boolean;
|
|
1029
|
+
declare function messageActionsFor(message: ChatMessage, ctx: MessageActionContext): MessageActionId[];
|
|
1030
|
+
|
|
1031
|
+
interface MessageActionsSheetProps {
|
|
1032
|
+
message: ChatMessage | null;
|
|
1033
|
+
context: MessageActionContext;
|
|
1034
|
+
onClose: () => void;
|
|
1035
|
+
onReact: (message: ChatMessage, emoji: string, on: boolean) => void;
|
|
1036
|
+
onAction: (message: ChatMessage, action: MessageActionId) => void;
|
|
1037
|
+
testID?: string;
|
|
1038
|
+
}
|
|
1039
|
+
declare function MessageActionsSheet({ message, context, onClose, onReact, onAction, testID }: MessageActionsSheetProps): react.JSX.Element;
|
|
1040
|
+
|
|
1041
|
+
/** Called when a voice note starts: pauses whichever other one was playing. */
|
|
1042
|
+
declare function claimPlayback(id: string, pause: () => void): void;
|
|
1043
|
+
/** Called when a voice note stops or unmounts. */
|
|
1044
|
+
declare function releasePlayback(id: string): void;
|
|
1045
|
+
/** Which of these voice notes is playing, or null (re-renders only when that answer changes). */
|
|
1046
|
+
declare function usePlayingVoiceNote(ids: readonly string[]): string | null;
|
|
1047
|
+
/** Plays or pauses the voice note with this id; false when none with it is on screen with playback. */
|
|
1048
|
+
declare function toggleVoiceNote(id: string): boolean;
|
|
1049
|
+
/** Moves the voice note with this id to its next speed; false when none is on screen with playback. */
|
|
1050
|
+
declare function cycleVoiceNoteRate(id: string): boolean;
|
|
1051
|
+
/** The playing voice note's id, for tests. */
|
|
1052
|
+
declare function activePlaybackId(): string | null;
|
|
1053
|
+
/** Where a finger at `x` across a waveform `width` wide puts the playhead, in seconds. */
|
|
1054
|
+
declare function scrubSeconds(x: number, width: number, durationSec: number): number;
|
|
1055
|
+
/** How many bars fit across `width` points, one bar plus a gap each; never fewer than one. */
|
|
1056
|
+
declare function barsThatFit(width: number): number;
|
|
1057
|
+
/** Bars 0–1 from stored peaks (0–100), resampled to `count`; flat when there are none. */
|
|
1058
|
+
declare function waveformBars(peaks: readonly number[] | null | undefined, count?: number): number[];
|
|
1059
|
+
interface VoiceNotePlayerProps {
|
|
1060
|
+
/** Stable id (the attachment's), for one-at-a-time. */
|
|
1061
|
+
id: string;
|
|
1062
|
+
/** A signed URL or a local file URI; null while it is fetched. */
|
|
1063
|
+
uri: string | null;
|
|
1064
|
+
durationMs: number | null;
|
|
1065
|
+
waveform: number[] | null;
|
|
1066
|
+
/** On the tint bubble: draw in its foreground. */
|
|
1067
|
+
mine?: boolean;
|
|
1068
|
+
testID?: string;
|
|
1069
|
+
}
|
|
1070
|
+
declare function VoiceNotePlayer(props: VoiceNotePlayerProps): react.JSX.Element;
|
|
1071
|
+
|
|
1072
|
+
/** An attachment as the bubble draws it: from the server, or still on the phone. */
|
|
1073
|
+
interface BubbleAttachment {
|
|
1074
|
+
key: string;
|
|
1075
|
+
kind: string;
|
|
1076
|
+
/** What to show now: a signed URL, or the local preview. */
|
|
1077
|
+
uri: string | null;
|
|
1078
|
+
name: string | null;
|
|
1079
|
+
width: number | null;
|
|
1080
|
+
height: number | null;
|
|
1081
|
+
durationMs: number | null;
|
|
1082
|
+
waveform: number[] | null;
|
|
1083
|
+
thumbhash: string | null;
|
|
1084
|
+
/** A hosted video: who hosts it, its playback id once processed, and whether it is ready. */
|
|
1085
|
+
provider?: string | null;
|
|
1086
|
+
playbackId?: string | null;
|
|
1087
|
+
status?: "pending" | "ready" | "failed" | null;
|
|
1088
|
+
posterUri?: string | null;
|
|
1089
|
+
/** Still on its way from this phone. */
|
|
1090
|
+
uploading?: boolean;
|
|
1091
|
+
}
|
|
1092
|
+
/** The paths a set of messages needs signed links for. */
|
|
1093
|
+
declare function attachmentPaths(attachments: readonly ChatAttachment[]): string[];
|
|
1094
|
+
interface AttachmentsProps {
|
|
1095
|
+
attachments: BubbleAttachment[];
|
|
1096
|
+
mine: boolean;
|
|
1097
|
+
onOpenImage?: (uri: string, alt: string) => void;
|
|
1098
|
+
testID: string;
|
|
1099
|
+
}
|
|
1100
|
+
declare function MessageAttachments({ attachments, mine, onOpenImage, testID }: AttachmentsProps): react.JSX.Element;
|
|
1101
|
+
|
|
1102
|
+
interface ChatPhoto {
|
|
1103
|
+
key: string;
|
|
1104
|
+
/** A signed link or a file on the phone. */
|
|
1105
|
+
uri: string;
|
|
1106
|
+
/** What a screen reader says for it. */
|
|
1107
|
+
alt: string;
|
|
1108
|
+
}
|
|
1109
|
+
interface ChatPhotoViewerProps {
|
|
1110
|
+
/** The photos to page through; null closes the viewer. */
|
|
1111
|
+
photos: readonly ChatPhoto[] | null;
|
|
1112
|
+
/** The one to open at. Default the first. */
|
|
1113
|
+
initialIndex?: number;
|
|
1114
|
+
onClose: () => void;
|
|
1115
|
+
testID?: string;
|
|
1116
|
+
}
|
|
1117
|
+
/** Points pulled down (or dragged, on Android) that close the viewer on release. */
|
|
1118
|
+
declare const DISMISS_DISTANCE = 110;
|
|
1119
|
+
/** Or a quick flick down, in points per millisecond. */
|
|
1120
|
+
declare const DISMISS_VELOCITY = 1.1;
|
|
1121
|
+
declare const MAX_ZOOM = 4;
|
|
1122
|
+
/** Whether a drag down that ended here closes the viewer. */
|
|
1123
|
+
declare function shouldDismiss(dy: number, vy: number): boolean;
|
|
1124
|
+
/** The zoom a pinch has reached: from where it started, by how far the fingers spread. */
|
|
1125
|
+
declare function pinchScale(startScale: number, startDistance: number, distance: number): number;
|
|
1126
|
+
/** How far a zoomed photo may pan before an edge shows, per axis. */
|
|
1127
|
+
declare function panLimit(scale: number, size: number): number;
|
|
1128
|
+
/** A file already on the phone, which shares and saves without a download. */
|
|
1129
|
+
declare function isLocalUri(uri: string): boolean;
|
|
1130
|
+
declare function ChatPhotoViewer({ photos, initialIndex, onClose, testID }: ChatPhotoViewerProps): react.JSX.Element;
|
|
1131
|
+
/**
|
|
1132
|
+
* The photos of the message a tapped photo belongs to, and where it is among them, for
|
|
1133
|
+
* `ChatPhotoViewer`: swiping goes through that message's photos. A photo in no message
|
|
1134
|
+
* (one still sending, shown from the phone) opens alone.
|
|
1135
|
+
*/
|
|
1136
|
+
declare function galleryFor(messages: readonly ChatMessage[], urls: Record<string, string>, uri: string, alt: string, label: (a: BubbleAttachment) => string): {
|
|
1137
|
+
photos: ChatPhoto[];
|
|
1138
|
+
index: number;
|
|
1139
|
+
};
|
|
1140
|
+
interface ChatMediaViewerProps {
|
|
1141
|
+
/** The picture to show; null closes the viewer. */
|
|
1142
|
+
image: {
|
|
1143
|
+
uri: string;
|
|
1144
|
+
alt: string;
|
|
1145
|
+
} | null;
|
|
1146
|
+
onClose: () => void;
|
|
1147
|
+
testID?: string;
|
|
1148
|
+
}
|
|
1149
|
+
/** One picture in the full-screen viewer (`ChatPhotoViewer` pages through several). */
|
|
1150
|
+
declare function ChatMediaViewer({ image, onClose, testID }: ChatMediaViewerProps): react.JSX.Element;
|
|
1151
|
+
|
|
1152
|
+
interface ChatEmojiPickerProps {
|
|
1153
|
+
onSelect: (emoji: string) => void;
|
|
1154
|
+
/** The signed-in person, whose recent emoji are kept (with `modules.drafts`). Without it, none are kept. */
|
|
1155
|
+
userId?: string | null;
|
|
1156
|
+
/** Points tall for the grid. Default about 40% of the window. */
|
|
1157
|
+
height?: number;
|
|
1158
|
+
testID?: string;
|
|
1159
|
+
}
|
|
1160
|
+
/** Forgets the recent emoji held in memory (one person's, or everyone's): call it on sign-out, with `clearDrafts`. */
|
|
1161
|
+
declare function forgetRecentEmoji(userId?: string): void;
|
|
1162
|
+
declare function ChatEmojiPicker({ onSelect, userId, height, testID }: ChatEmojiPickerProps): react.JSX.Element;
|
|
1163
|
+
interface ChatEmojiPickerSheetProps extends Omit<ChatEmojiPickerProps, "onSelect"> {
|
|
1164
|
+
open: boolean;
|
|
1165
|
+
onClose: () => void;
|
|
1166
|
+
/** An emoji was chosen. The sheet stays open, for another. */
|
|
1167
|
+
onSelect: (emoji: string) => void;
|
|
1168
|
+
}
|
|
1169
|
+
/** The picker in a sheet, with a title and Done. */
|
|
1170
|
+
declare function ChatEmojiPickerSheet({ open, onClose, onSelect, testID, ...rest }: ChatEmojiPickerSheetProps): react.JSX.Element;
|
|
1171
|
+
|
|
1172
|
+
declare const EMOJI_GROUP_DATA: {
|
|
1173
|
+
readonly smileys: "😀\tgrinning face\tcheerful cheery grin\n😃\tgrinning face with big eyes\tawesome grin happy\n😄\tgrinning face with smiling eyes\teye grin happy\n😁\tbeaming face with smiling eyes\teye grin grinning\n😆\tgrinning squinting face\tclosed eyes haha\n😅\tgrinning face with sweat\tcold dejected excited\n🤣\trolling on the floor laughing\tcrying face funny\n😂\tface with tears of joy\tcrying feels funny\n🙂\tslightly smiling face\thappy smile\n🙃\tupside-down face\thehe smile upside-down\n🫠\tmelting face\tdisappear dissolve embarrassed\n😉\twinking face\tflirt heartbreaker sexy\n😊\tsmiling face with smiling eyes\tblush eye glad\n😇\tsmiling face with halo\tangel angelic angels\n🥰\tsmiling face with hearts\t3 adore crush\n😍\tsmiling face with heart-eyes\t143 bae eye\n🤩\tstar-struck\texcited eyes face\n😘\tface blowing a kiss\tadorbs bae flirt\n😗\tkissing face\t143 date dating\n☺️\tsmiling face\thappy outlined relaxed\n😚\tkissing face with closed eyes\t143 bae blush\n😙\tkissing face with smiling eyes\t143 closed date\n🥲\tsmiling face with tear\tglad grateful happy\n😋\tface savoring food\tdelicious eat full\n😛\tface with tongue\tawesome cool nice\n😜\twinking face with tongue\tcrazy epic eye\n🤪\tzany face\tcrazy eye eyes\n😝\tsquinting face with tongue\tclosed eye eyes\n🤑\tmoney-mouth face\tmoney-mouth paid\n🤗\tsmiling face with open hands\thug hugging\n🤭\tface with hand over mouth\tgiggle giggling oops\n🫢\tface with open eyes and hand over mouth\tamazement awe disbelief\n🫣\tface with peeking eye\tcaptivated embarrass hide\n🤫\tshushing face\tquiet shh shush\n🤔\tthinking face\tchin consider hmm\n🫡\tsaluting face\tgood luck ma’am\n🤐\tzipper-mouth face\tkeep quiet secret\n🤨\tface with raised eyebrow\tdisapproval disbelief distrust\n😐\tneutral face\tawkward blank deadpan\n😑\texpressionless face\tawkward dead fine\n😶\tface without mouth\tawkward blank expressionless\n🫥\tdotted line face\tdepressed disappear hidden\n😶🌫️\tface in clouds\tabsentminded fog head\n😏\tsmirking face\tboss dapper flirt\n😒\tunamused face\t... bored fine\n🙄\tface with rolling eyes\teyeroll shade ugh\n😬\tgrimacing face\tawk awkward dentist\n😮💨\tface exhaling\tblow blowing exhale\n🤥\tlying face\tliar lie pinocchio\n🫨\tshaking face\tcrazy daze earthquake\n😌\trelieved face\tcalm peace relief\n😔\tpensive face\tawful bored dejected\n😪\tsleepy face\tcrying good night\n🤤\tdrooling face\t\n😴\tsleeping face\tbed bedtime good\n😷\tface with medical mask\tcold dentist dermatologist\n🤒\tface with thermometer\till sick\n🤕\tface with head-bandage\thead-bandage hurt injury\n🤢\tnauseated face\tgross nasty sick\n🤮\tface vomiting\tbarf ew gross\n🤧\tsneezing face\tfever flu gesundheit\n🥵\thot face\tdying feverish heat\n🥶\tcold face\tblue blue-faced freezing\n🥴\twoozy face\tdizzy drunk eyes\n😵\tface with crossed-out eyes\tcrossed-out dead dizzy\n😵💫\tface with spiral eyes\tconfused dizzy hypnotized\n🤯\texploding head\tblown explode mind\n🤠\tcowboy hat face\tcowgirl\n🥳\tpartying face\tbday birthday celebrate\n🥸\tdisguised face\tdisguise eyebrow glasses\n😎\tsmiling face with sunglasses\tawesome beach bright\n🤓\tnerd face\tbrainy clever expert\n🧐\tface with monocle\tclassy fancy rich\n😕\tconfused face\tbefuddled confusing dunno\n🫤\tface with diagonal mouth\tconfused confusion disappointed\n😟\tworried face\tanxious butterflies nerves\n🙁\tslightly frowning face\tfrown sad\n☹️\tfrowning face\tfrown sad\n😮\tface with open mouth\tbelieve forgot omg\n😯\thushed face\tepic omg stunned\n😲\tastonished face\tcost no omg\n😳\tflushed face\tamazed awkward crazy\n🥺\tpleading face\tbegging big eyes\n🥹\tface holding back tears\tadmiration aww cry\n😦\tfrowning face with open mouth\tcaught frown guard\n😧\tanguished face\tforgot scared scary\n😨\tfearful face\tafraid anxious blame\n😰\tanxious face with sweat\tblue cold eek\n😥\tsad but relieved face\tanxious call close\n😢\tcrying face\tawful cry feels\n😭\tloudly crying face\tbawling cry sad\n😱\tface screaming in fear\tepic fearful munch\n😖\tconfounded face\tannoyed confused cringe\n😣\tpersevering face\tconcentrate concentration focus\n😞\tdisappointed face\tawful blame dejected\n😓\tdowncast face with sweat\tclose cold feels\n😩\tweary face\tcrying fail feels\n😫\ttired face\tcost feels nap\n🥱\tyawning face\tbedtime bored goodnight\n😤\tface with steam from nose\tanger angry feels\n😡\tenraged face\tanger angry feels\n😠\tangry face\tanger blame feels\n🤬\tface with symbols on mouth\tcensor cursing cussing\n😈\tsmiling face with horns\tdemon devil evil\n👿\tangry face with horns\tdemon devil evil\n💀\tskull\tbody dead death\n☠️\tskull and crossbones\tbone dead death\n💩\tpile of poo\tbs comic doo\n🤡\tclown face\t\n👹\togre\tcreature devil face\n👺\tgoblin\tangry creature face\n👻\tghost\tboo creature excited\n👽\talien\tcreature extraterrestrial face\n👾\talien monster\tcreature extraterrestrial face\n🤖\trobot\tface monster\n😺\tgrinning cat\tanimal face mouth\n😸\tgrinning cat with smiling eyes\tanimal eye face\n😹\tcat with tears of joy\tanimal face laugh\n😻\tsmiling cat with heart-eyes\tanimal eye face\n😼\tcat with wry smile\tanimal face ironic\n😽\tkissing cat\tanimal closed eye\n🙀\tweary cat\tanimal face oh\n😿\tcrying cat\tanimal cry face\n😾\tpouting cat\tanimal face\n🙈\tsee-no-evil monkey\tembarrassed face forbidden\n🙉\thear-no-evil monkey\tanimal ears face\n🙊\tspeak-no-evil monkey\tanimal face forbidden\n💌\tlove letter\theart mail romance\n💘\theart with arrow\t143 adorbs cupid\n💝\theart with ribbon\t143 anniversary emotion\n💖\tsparkling heart\t143 emotion excited\n💗\tgrowing heart\t143 emotion excited\n💓\tbeating heart\t143 cardio emotion\n💞\trevolving hearts\t143 adorbs anniversary\n💕\ttwo hearts\t143 anniversary date\n💟\theart decoration\t143 emotion hearth\n❣️\theart exclamation\theavy mark punctuation\n💔\tbroken heart\tbreak crushed emotion\n❤️🔥\theart on fire\tburn love lust\n❤️🩹\tmending heart\thealthier improving recovering\n❤️\tred heart\temotion love\n🩷\tpink heart\t143 adorable cute\n🧡\torange heart\t143\n💛\tyellow heart\t143 cardiac emotion\n💚\tgreen heart\t143 emotion ily\n💙\tblue heart\t143 emotion ily\n🩵\tlight blue heart\t143 cute cyan\n💜\tpurple heart\t143 bestest emotion\n🤎\tbrown heart\t143\n🖤\tblack heart\tevil wicked\n🩶\tgrey heart\t143 emotion gray\n🤍\twhite heart\t143\n💋\tkiss mark\tdating emotion heart\n💯\thundred points\t100 a+ agree\n💢\tanger symbol\tangry comic mad\n💥\tcollision\tbomb boom collide\n💫\tdizzy\tcomic shining shooting\n💦\tsweat droplets\tcomic drip droplet\n💨\tdashing away\tcloud comic dash\n🕳️\thole\t\n💬\tspeech balloon\tbubble comic dialog\n👁️🗨️\teye in speech bubble\tballoon witness\n🗨️\tleft speech bubble\tballoon dialog\n🗯️\tright anger bubble\tangry balloon mad\n💭\tthought balloon\tbubble cartoon cloud\n💤\tZZZ\tcomic good goodnight";
|
|
1174
|
+
readonly people: "👋\twaving hand\tbye cya g2g\n🤚\traised back of hand\tbackhand\n🖐️\thand with fingers splayed\tfinger raised stop\n✋\traised hand\t5 five high\n🖖\tvulcan salute\tfinger hand hands\n🫱\trightwards hand\thandshake hold reach\n🫲\tleftwards hand\thandshake hold left\n🫳\tpalm down hand\tdismiss drop dropped\n🫴\tpalm up hand\tbeckon catch come\n🫷\tleftwards pushing hand\tblock five halt\n🫸\trightwards pushing hand\tblock five halt\n👌\tOK hand\tawesome bet dope\n🤌\tpinched fingers\tgesture hand hold\n🤏\tpinching hand\tamount bit fingers\n✌️\tvictory hand\tpeace v\n🤞\tcrossed fingers\tcross finger hand\n🫰\thand with index finger and thumb crossed\t<3 expensive heart\n🤟\tlove-you gesture\tfingers hand ily\n🤘\tsign of the horns\tfinger hand rock-on\n🤙\tcall me hand\thang loose shaka\n👈\tbackhand index pointing left\tfinger hand point\n👉\tbackhand index pointing right\tfinger hand point\n👆\tbackhand index pointing up\tfinger hand point\n🖕\tmiddle finger\thand\n👇\tbackhand index pointing down\tfinger hand point\n☝️\tindex pointing up\tfinger hand point\n🫵\tindex pointing at the viewer\tfinger hand poke\n👍\tthumbs up\t+1 good hand\n👎\tthumbs down\t-1 bad dislike\n✊\traised fist\tclenched hand punch\n👊\toncoming fist\tabsolutely agree boom\n🤛\tleft-facing fist\tleft-facing leftwards\n🤜\tright-facing fist\tright-facing rightwards\n👏\tclapping hands\tapplause approval awesome\n🙌\traising hands\tcelebration gesture hand\n🫶\theart hands\t<3 love you\n👐\topen hands\thand hug jazz\n🤲\tpalms up together\tcupped dua hands\n🤝\thandshake\tagreement deal hand\n🙏\tfolded hands\tappreciate ask beg\n✍️\twriting hand\twrite\n💅\tnail polish\tbored care cosmetics\n🤳\tselfie\tcamera phone\n💪\tflexed biceps\tarm beast bench\n🦾\tmechanical arm\taccessibility prosthetic\n🦿\tmechanical leg\taccessibility prosthetic\n🦵\tleg\tbent foot kick\n🦶\tfoot\tankle feet kick\n👂\tear\tbody ears hear\n🦻\tear with hearing aid\taccessibility hard\n👃\tnose\tbody noses nosey\n🧠\tbrain\tintelligent smart\n🫀\tanatomical heart\tbeat cardiology heartbeat\n🫁\tlungs\tbreath breathe exhalation\n🦷\ttooth\tdentist pearly teeth\n🦴\tbone\tbones dog skeleton\n👀\teyes\tbody eye face\n👁️\teye\t1 body one\n👅\ttongue\tbody lick slurp\n👄\tmouth\tbeauty body kiss\n🫦\tbiting lip\tanxious bite fear\n👶\tbaby\tbabies children goo\n🧒\tchild\tbright-eyed grandchild kid\n👦\tboy\tbright-eyed child grandson\n👧\tgirl\tbright-eyed child daughter\n🧑\tperson\tadult\n👱\tperson: blond hair\tblond-haired human\n👨\tman\tadult bro\n🧔\tperson: beard\tbearded whiskers\n🧔♂️\tman: beard\tbearded whiskers\n🧔♀️\twoman: beard\tbearded whiskers\n👨🦰\tman: red hair\tadult bro red hair\n👨🦱\tman: curly hair\tadult bro curly hair\n👨🦳\tman: white hair\tadult bro white hair\n👨🦲\tman: bald\tadult bro\n👩\twoman\tadult lady\n👩🦰\twoman: red hair\tadult lady red hair\n🧑🦰\tperson: red hair\tadult red hair\n👩🦱\twoman: curly hair\tadult curly hair lady\n🧑🦱\tperson: curly hair\tadult curly hair\n👩🦳\twoman: white hair\tadult lady white hair\n🧑🦳\tperson: white hair\tadult white hair\n👩🦲\twoman: bald\tadult lady\n🧑🦲\tperson: bald\tadult\n👱♀️\twoman: blond hair\tblond-haired blonde\n👱♂️\tman: blond hair\tblond-haired\n🧓\tolder person\tadult elderly grandparent\n👴\told man\tadult bald elderly\n👵\told woman\tadult elderly grandma\n🙍\tperson frowning\tannoyed disappointed disgruntled\n🙍♂️\tman frowning\tannoyed disappointed disgruntled\n🙍♀️\twoman frowning\tannoyed disappointed disgruntled\n🙎\tperson pouting\tdisappointed downtrodden frown\n🙎♂️\tman pouting\tdisappointed downtrodden frown\n🙎♀️\twoman pouting\tdisappointed downtrodden frown\n🙅\tperson gesturing NO\tforbidden gesture hand\n🙅♂️\tman gesturing NO\tforbidden gesture hand\n🙅♀️\twoman gesturing NO\tforbidden gesture hand\n🙆\tperson gesturing OK\texercise gesture hand\n🙆♂️\tman gesturing OK\texercise gesture hand\n🙆♀️\twoman gesturing OK\texercise gesture hand\n💁\tperson tipping hand\tfetch flick flip\n💁♂️\tman tipping hand\tfetch flick flip\n💁♀️\twoman tipping hand\tfetch flick flip\n🙋\tperson raising hand\tgesture here know\n🙋♂️\tman raising hand\tgesture here know\n🙋♀️\twoman raising hand\tgesture here know\n🧏\tdeaf person\taccessibility ear gesture\n🧏♂️\tdeaf man\taccessibility ear gesture\n🧏♀️\tdeaf woman\taccessibility ear gesture\n🙇\tperson bowing\tapology ask beg\n🙇♂️\tman bowing\tapology ask beg\n🙇♀️\twoman bowing\tapology ask beg\n🤦\tperson facepalming\tagain bewilder disbelief\n🤦♂️\tman facepalming\tagain bewilder disbelief\n🤦♀️\twoman facepalming\tagain bewilder disbelief\n🤷\tperson shrugging\tdoubt dunno guess\n🤷♂️\tman shrugging\tdoubt dunno guess\n🤷♀️\twoman shrugging\tdoubt dunno guess\n🧑⚕️\thealth worker\tdoctor healthcare nurse\n👨⚕️\tman health worker\tdoctor healthcare nurse\n👩⚕️\twoman health worker\tdoctor healthcare nurse\n🧑🎓\tstudent\tgraduate\n👨🎓\tman student\tgraduate\n👩🎓\twoman student\tgraduate\n🧑🏫\tteacher\tinstructor lecturer professor\n👨🏫\tman teacher\tinstructor lecturer professor\n👩🏫\twoman teacher\tinstructor lecturer professor\n🧑⚖️\tjudge\tjustice law scales\n👨⚖️\tman judge\tjustice law scales\n👩⚖️\twoman judge\tjustice law scales\n🧑🌾\tfarmer\tgardener rancher\n👨🌾\tman farmer\tgardener rancher\n👩🌾\twoman farmer\tgardener rancher\n🧑🍳\tcook\tchef\n👨🍳\tman cook\tchef\n👩🍳\twoman cook\tchef\n🧑🔧\tmechanic\telectrician plumber tradesperson\n👨🔧\tman mechanic\telectrician plumber tradesperson\n👩🔧\twoman mechanic\telectrician plumber tradesperson\n🧑🏭\tfactory worker\tassembly industrial\n👨🏭\tman factory worker\tassembly industrial\n👩🏭\twoman factory worker\tassembly industrial\n🧑💼\toffice worker\tarchitect business manager\n👨💼\tman office worker\tarchitect business manager\n👩💼\twoman office worker\tarchitect business manager\n🧑🔬\tscientist\tbiologist chemist engineer\n👨🔬\tman scientist\tbiologist chemist engineer\n👩🔬\twoman scientist\tbiologist chemist engineer\n🧑💻\ttechnologist\tcoder computer developer\n👨💻\tman technologist\tcoder computer developer\n👩💻\twoman technologist\tcoder computer developer\n🧑🎤\tsinger\tactor entertainer rock\n👨🎤\tman singer\tactor entertainer rock\n👩🎤\twoman singer\tactor entertainer rock\n🧑🎨\tartist\tpalette\n👨🎨\tman artist\tpalette\n👩🎨\twoman artist\tpalette\n🧑✈️\tpilot\tplane\n👨✈️\tman pilot\tplane\n👩✈️\twoman pilot\tplane\n🧑🚀\tastronaut\trocket space\n👨🚀\tman astronaut\trocket space\n👩🚀\twoman astronaut\trocket space\n🧑🚒\tfirefighter\tfire firetruck\n👨🚒\tman firefighter\tfire firetruck\n👩🚒\twoman firefighter\tfire firetruck\n👮\tpolice officer\tapprehend arrest citation\n👮♂️\tman police officer\tapprehend arrest citation\n👮♀️\twoman police officer\tapprehend arrest citation\n🕵️\tdetective\tsleuth spy\n🕵️♂️\tman detective\tsleuth spy\n🕵️♀️\twoman detective\tsleuth spy\n💂\tguard\tbuckingham helmet london\n💂♂️\tman guard\tbuckingham helmet london\n💂♀️\twoman guard\tbuckingham helmet london\n🥷\tninja\tassassin fight fighter\n👷\tconstruction worker\tbuild fix hardhat\n👷♂️\tman construction worker\tbuild fix hardhat\n👷♀️\twoman construction worker\tbuild fix hardhat\n🫅\tperson with crown\tmonarch noble regal\n🤴\tprince\tcrown fairy fairytale\n👸\tprincess\tcrown fairy fairytale\n👳\tperson wearing turban\t\n👳♂️\tman wearing turban\t\n👳♀️\twoman wearing turban\t\n👲\tperson with skullcap\tcap chinese gua\n🧕\twoman with headscarf\tbandana head hijab\n🤵\tperson in tuxedo\tformal wedding\n🤵♂️\tman in tuxedo\tformal groom wedding\n🤵♀️\twoman in tuxedo\tformal wedding\n👰\tperson with veil\twedding\n👰♂️\tman with veil\twedding\n👰♀️\twoman with veil\tbride wedding\n🤰\tpregnant woman\t\n🫃\tpregnant man\tbelly bloated full\n🫄\tpregnant person\tbelly bloated full\n🤱\tbreast-feeding\tbaby mom mother\n👩🍼\twoman feeding baby\tfeed mom mother\n👨🍼\tman feeding baby\tdad father feed\n🧑🍼\tperson feeding baby\tfeed nanny newborn\n👼\tbaby angel\tchurch face fairy\n🎅\tSanta Claus\tcelebration christmas fairy\n🤶\tMrs. Claus\tcelebration christmas fairy\n🧑🎄\tMx Claus\tcelebration christmas fairy\n🦸\tsuperhero\tgood hero superpower\n🦸♂️\tman superhero\tgood hero superpower\n🦸♀️\twoman superhero\tgood hero heroine\n🦹\tsupervillain\tbad criminal evil\n🦹♂️\tman supervillain\tbad criminal evil\n🦹♀️\twoman supervillain\tbad criminal evil\n🧙\tmage\tfantasy magic play\n🧙♂️\tman mage\tfantasy magic play\n🧙♀️\twoman mage\tfantasy magic play\n🧚\tfairy\tfairytale fantasy myth\n🧚♂️\tman fairy\tfairytale fantasy myth\n🧚♀️\twoman fairy\tfairytale fantasy myth\n🧛\tvampire\tblood dracula fangs\n🧛♂️\tman vampire\tblood fangs halloween\n🧛♀️\twoman vampire\tblood fangs halloween\n🧜\tmerperson\tcreature fairytale folklore\n🧜♂️\tmerman\tcreature fairytale folklore\n🧜♀️\tmermaid\tcreature fairytale folklore\n🧝\telf\telves enchantment fantasy\n🧝♂️\tman elf\telves enchantment fantasy\n🧝♀️\twoman elf\telves enchantment fantasy\n🧞\tgenie\tdjinn fantasy jinn\n🧞♂️\tman genie\tdjinn fantasy jinn\n🧞♀️\twoman genie\tdjinn fantasy jinn\n🧟\tzombie\tapocalypse dead halloween\n🧟♂️\tman zombie\tapocalypse dead halloween\n🧟♀️\twoman zombie\tapocalypse dead halloween\n🧌\ttroll\tfairy fantasy monster\n💆\tperson getting massage\tface headache relax\n💆♂️\tman getting massage\tface headache relax\n💆♀️\twoman getting massage\tface headache relax\n💇\tperson getting haircut\tbarber beauty chop\n💇♂️\tman getting haircut\tbarber beauty chop\n💇♀️\twoman getting haircut\tbarber beauty chop\n🚶\tperson walking\tamble gait hike\n🚶♂️\tman walking\tamble gait hike\n🚶♀️\twoman walking\tamble gait hike\n🧍\tperson standing\tstand\n🧍♂️\tman standing\tstand\n🧍♀️\twoman standing\tstand\n🧎\tperson kneeling\tkneel knees\n🧎♂️\tman kneeling\tkneel knees\n🧎♀️\twoman kneeling\tkneel knees\n🧑🦯\tperson with white cane\taccessibility blind probing\n👨🦯\tman with white cane\taccessibility blind probing\n👩🦯\twoman with white cane\taccessibility blind probing\n🧑🦼\tperson in motorized wheelchair\taccessibility\n👨🦼\tman in motorized wheelchair\taccessibility\n👩🦼\twoman in motorized wheelchair\taccessibility\n🧑🦽\tperson in manual wheelchair\taccessibility\n👨🦽\tman in manual wheelchair\taccessibility\n👩🦽\twoman in manual wheelchair\taccessibility\n🏃\tperson running\tfast hurry marathon\n🏃♂️\tman running\tfast hurry marathon\n🏃♀️\twoman running\tfast hurry marathon\n💃\twoman dancing\tdance dancer elegant\n🕺\tman dancing\tdance dancer elegant\n🕴️\tperson in suit levitating\tbusiness\n👯\tpeople with bunny ears\tbestie bff counterpart\n👯♂️\tmen with bunny ears\tbestie bff counterpart\n👯♀️\twomen with bunny ears\tbestie bff counterpart\n🧖\tperson in steamy room\tday luxurious pamper\n🧖♂️\tman in steamy room\tday luxurious pamper\n🧖♀️\twoman in steamy room\tday luxurious pamper\n🧗\tperson climbing\tclimb climber mountain\n🧗♂️\tman climbing\tclimb climber mountain\n🧗♀️\twoman climbing\tclimb climber mountain\n🤺\tperson fencing\tfencer sword\n🏇\thorse racing\tjockey racehorse riding\n⛷️\tskier\tski snow\n🏂\tsnowboarder\tski snow snowboard\n🏌️\tperson golfing\tball birdie caddy\n🏌️♂️\tman golfing\tball birdie caddy\n🏌️♀️\twoman golfing\tball birdie caddy\n🏄\tperson surfing\tbeach ocean sport\n🏄♂️\tman surfing\tbeach ocean sport\n🏄♀️\twoman surfing\tbeach ocean person\n🚣\tperson rowing boat\tcanoe cruise fishing\n🚣♂️\tman rowing boat\tcanoe cruise fishing\n🚣♀️\twoman rowing boat\tcanoe cruise fishing\n🏊\tperson swimming\tfreestyle sport swim\n🏊♂️\tman swimming\tfreestyle sport swim\n🏊♀️\twoman swimming\tfreestyle man sport\n⛹️\tperson bouncing ball\tathletic basketball championship\n⛹️♂️\tman bouncing ball\tathletic basketball championship\n⛹️♀️\twoman bouncing ball\tathletic basketball championship\n🏋️\tperson lifting weights\tbarbell bodybuilder deadlift\n🏋️♂️\tman lifting weights\tbarbell bodybuilder deadlift\n🏋️♀️\twoman lifting weights\tbarbell bodybuilder deadlift\n🚴\tperson biking\tbicycle bicyclist bike\n🚴♂️\tman biking\tbicycle bicyclist bike\n🚴♀️\twoman biking\tbicycle bicyclist bike\n🚵\tperson mountain biking\tbicycle bicyclist bike\n🚵♂️\tman mountain biking\tbicycle bicyclist bike\n🚵♀️\twoman mountain biking\tbicycle bicyclist bike\n🤸\tperson cartwheeling\tactive cartwheel excited\n🤸♂️\tman cartwheeling\tactive cartwheel excited\n🤸♀️\twoman cartwheeling\tactive cartwheel excited\n🤼\tpeople wrestling\tcombat duel grapple\n🤼♂️\tmen wrestling\tcombat duel grapple\n🤼♀️\twomen wrestling\tcombat duel grapple\n🤽\tperson playing water polo\tsport swimming waterpolo\n🤽♂️\tman playing water polo\tsport swimming waterpolo\n🤽♀️\twoman playing water polo\tsport swimming waterpolo\n🤾\tperson playing handball\tathletics ball catch\n🤾♂️\tman playing handball\tathletics ball catch\n🤾♀️\twoman playing handball\tathletics ball catch\n🤹\tperson juggling\tact balance balancing\n🤹♂️\tman juggling\tact balance balancing\n🤹♀️\twoman juggling\tact balance balancing\n🧘\tperson in lotus position\tcross legged legs\n🧘♂️\tman in lotus position\tcross legged legs\n🧘♀️\twoman in lotus position\tcross legged legs\n🛀\tperson taking bath\tbathtub tub\n🛌\tperson in bed\tbedtime good goodnight\n🧑🤝🧑\tpeople holding hands\tbae bestie bff\n👭\twomen holding hands\tbae bestie bff\n👫\twoman and man holding hands\tbae bestie bff\n👬\tmen holding hands\tbae bestie bff\n💏\tkiss\tanniversary babe bae\n👩❤️💋👨\tkiss: woman, man\tanniversary babe bae\n👨❤️💋👨\tkiss: man, man\tanniversary babe bae\n👩❤️💋👩\tkiss: woman, woman\tanniversary babe bae\n💑\tcouple with heart\tanniversary babe bae\n👩❤️👨\tcouple with heart: woman, man\tanniversary babe bae\n👨❤️👨\tcouple with heart: man, man\tanniversary babe bae\n👩❤️👩\tcouple with heart: woman, woman\tanniversary babe bae\n👨👩👦\tfamily: man, woman, boy\tchild\n👨👩👧\tfamily: man, woman, girl\tchild\n👨👩👧👦\tfamily: man, woman, girl, boy\tchild\n👨👩👦👦\tfamily: man, woman, boy, boy\tchild\n👨👩👧👧\tfamily: man, woman, girl, girl\tchild\n👨👨👦\tfamily: man, man, boy\tchild\n👨👨👧\tfamily: man, man, girl\tchild\n👨👨👧👦\tfamily: man, man, girl, boy\tchild\n👨👨👦👦\tfamily: man, man, boy, boy\tchild\n👨👨👧👧\tfamily: man, man, girl, girl\tchild\n👩👩👦\tfamily: woman, woman, boy\tchild\n👩👩👧\tfamily: woman, woman, girl\tchild\n👩👩👧👦\tfamily: woman, woman, girl, boy\tchild\n👩👩👦👦\tfamily: woman, woman, boy, boy\tchild\n👩👩👧👧\tfamily: woman, woman, girl, girl\tchild\n👨👦\tfamily: man, boy\tchild\n👨👦👦\tfamily: man, boy, boy\tchild\n👨👧\tfamily: man, girl\tchild\n👨👧👦\tfamily: man, girl, boy\tchild\n👨👧👧\tfamily: man, girl, girl\tchild\n👩👦\tfamily: woman, boy\tchild\n👩👦👦\tfamily: woman, boy, boy\tchild\n👩👧\tfamily: woman, girl\tchild\n👩👧👦\tfamily: woman, girl, boy\tchild\n👩👧👧\tfamily: woman, girl, girl\tchild\n🗣️\tspeaking head\tface silhouette speak\n👤\tbust in silhouette\tmysterious shadow\n👥\tbusts in silhouette\tbff bust everyone\n🫂\tpeople hugging\tcomfort embrace farewell\n👪\tfamily\tchild\n👣\tfootprints\tbarefoot clothing footprint";
|
|
1175
|
+
readonly nature: "🐵\tmonkey face\tanimal banana\n🐒\tmonkey\tanimal banana\n🦍\tgorilla\tanimal\n🦧\torangutan\tanimal ape monkey\n🐶\tdog face\tadorbs animal pet\n🐕\tdog\tanimal animals dogs\n🦮\tguide dog\taccessibility animal blind\n🐕🦺\tservice dog\taccessibility animal assistance\n🐩\tpoodle\tanimal dog fluffy\n🐺\twolf\tanimal face\n🦊\tfox\tanimal face\n🦝\traccoon\tanimal curious sly\n🐱\tcat face\tanimal kitten kitty\n🐈\tcat\tanimal animals cats\n🐈⬛\tblack cat\tanimal feline halloween\n🦁\tlion\talpha animal face\n🐯\ttiger face\tanimal big cat\n🐅\ttiger\tanimal big cat\n🐆\tleopard\tanimal big cat\n🐴\thorse face\tanimal dressage equine\n🫎\tmoose\talces animal antlers\n🫏\tdonkey\tanimal ass burro\n🐎\thorse\tanimal equestrian farm\n🦄\tunicorn\tface\n🦓\tzebra\tanimal stripe\n🦌\tdeer\tanimal\n🦬\tbison\tanimal buffalo herd\n🐮\tcow face\tanimal farm milk\n🐂\tox\tanimal animals bull\n🐃\twater buffalo\tanimal zoo\n🐄\tcow\tanimal animals farm\n🐷\tpig face\tanimal bacon farm\n🐖\tpig\tanimal bacon farm\n🐗\tboar\tanimal pig\n🐽\tpig nose\tanimal face farm\n🐏\tram\tanimal aries horns\n🐑\tewe\tanimal baa farm\n🐐\tgoat\tanimal capricorn farm\n🐪\tcamel\tanimal desert dromedary\n🐫\ttwo-hump camel\tanimal bactrian desert\n🦙\tllama\talpaca animal guanaco\n🦒\tgiraffe\tanimal spots\n🐘\telephant\tanimal\n🦣\tmammoth\tanimal extinction large\n🦏\trhinoceros\tanimal\n🦛\thippopotamus\tanimal hippo\n🐭\tmouse face\tanimal\n🐁\tmouse\tanimal animals\n🐀\trat\tanimal\n🐹\thamster\tanimal face pet\n🐰\trabbit face\tanimal bunny pet\n🐇\trabbit\tanimal bunny pet\n🐿️\tchipmunk\tanimal squirrel\n🦫\tbeaver\tanimal dam teeth\n🦔\thedgehog\tanimal spiny\n🦇\tbat\tanimal vampire\n🐻\tbear\tanimal face grizzly\n🐻❄️\tpolar bear\tanimal arctic white\n🐨\tkoala\tanimal australia bear\n🐼\tpanda\tanimal bamboo face\n🦥\tsloth\tlazy slow\n🦦\totter\tanimal fishing playful\n🦨\tskunk\tanimal stink\n🦘\tkangaroo\tanimal joey jump\n🦡\tbadger\tanimal honey pester\n🐾\tpaw prints\tfeet paws print\n🦃\tturkey\tbird gobble thanksgiving\n🐔\tchicken\tanimal bird ornithology\n🐓\trooster\tanimal bird ornithology\n🐣\thatching chick\tanimal baby bird\n🐤\tbaby chick\tanimal bird ornithology\n🐥\tfront-facing baby chick\tanimal bird front-facing\n🐦\tbird\tanimal ornithology\n🐧\tpenguin\tanimal antarctica bird\n🕊️\tdove\tbird fly ornithology\n🦅\teagle\tanimal bird ornithology\n🦆\tduck\tanimal bird ornithology\n🦢\tswan\tanimal bird cygnet\n🦉\towl\tanimal bird ornithology\n🦤\tdodo\tanimal bird extinction\n🪶\tfeather\tbird flight light\n🦩\tflamingo\tanimal bird flamboyant\n🦚\tpeacock\tanimal bird colorful\n🦜\tparrot\tanimal bird ornithology\n🪽\twing\tangelic ascend aviation\n🐦⬛\tblack bird\tanimal beak caw\n🪿\tgoose\tanimal bird duck\n🐸\tfrog\tanimal face\n🐊\tcrocodile\tanimal zoo\n🐢\tturtle\tanimal terrapin tortoise\n🦎\tlizard\tanimal reptile\n🐍\tsnake\tanimal bearer ophiuchus\n🐲\tdragon face\tanimal fairy fairytale\n🐉\tdragon\tanimal fairy fairytale\n🦕\tsauropod\tbrachiosaurus brontosaurus dinosaur\n🦖\tT-Rex\tdinosaur t-rex tyrannosaurus\n🐳\tspouting whale\tanimal beach face\n🐋\twhale\tanimal beach ocean\n🐬\tdolphin\tanimal beach flipper\n🦭\tseal\tanimal lion ocean\n🐟\tfish\tanimal dinner fishes\n🐠\ttropical fish\tanimal fishes\n🐡\tblowfish\tanimal fish\n🦈\tshark\tanimal fish\n🐙\toctopus\tanimal creature ocean\n🐚\tspiral shell\tanimal beach conch\n🪸\tcoral\tchange climate ocean\n🪼\tjellyfish\tanimal aquarium burn\n🦀\tcrab\tcancer zodiac\n🦞\tlobster\tanimal bisque claws\n🦐\tshrimp\tfood shellfish small\n🦑\tsquid\tanimal food mollusk\n🦪\toyster\tdiving pearl\n🐌\tsnail\tanimal escargot garden\n🦋\tbutterfly\tinsect pretty\n🐛\tbug\tanimal garden insect\n🐜\tant\tanimal garden insect\n🐝\thoneybee\tanimal bee bumblebee\n🪲\tbeetle\tanimal bug insect\n🐞\tlady beetle\tanimal garden insect\n🦗\tcricket\tanimal bug grasshopper\n🪳\tcockroach\tanimal insect pest\n🕷️\tspider\tanimal insect\n🕸️\tspider web\t\n🦂\tscorpion\tscorpio scorpius zodiac\n🦟\tmosquito\tbite disease fever\n🪰\tfly\tanimal disease insect\n🪱\tworm\tanimal annelid earthworm\n🦠\tmicrobe\tamoeba bacteria science\n💐\tbouquet\tanniversary birthday date\n🌸\tcherry blossom\tflower plant spring\n💮\twhite flower\t\n🪷\tlotus\tbeauty buddhism calm\n🏵️\trosette\tplant\n🌹\trose\tbeauty elegant flower\n🥀\twilted flower\tdying\n🌺\thibiscus\tflower plant\n🌻\tsunflower\tflower outdoors plant\n🌼\tblossom\tbuttercup dandelion flower\n🌷\ttulip\tblossom flower growth\n🪻\thyacinth\tbloom bluebonnet flower\n🌱\tseedling\tplant sapling sprout\n🪴\tpotted plant\tdecor grow house\n🌲\tevergreen tree\tchristmas forest pine\n🌳\tdeciduous tree\tforest green habitat\n🌴\tpalm tree\tbeach plant tropical\n🌵\tcactus\tdesert drought nature\n🌾\tsheaf of rice\tear grain grains\n🌿\therb\tleaf plant\n☘️\tshamrock\tirish plant\n🍀\tfour leaf clover\t4 four-leaf irish\n🍁\tmaple leaf\tfalling\n🍂\tfallen leaf\tautumn fall falling\n🍃\tleaf fluttering in wind\tblow flutter\n🪹\tempty nest\tbranch home nesting\n🪺\tnest with eggs\tbird branch egg\n🍄\tmushroom\tfungus toadstool";
|
|
1176
|
+
readonly food: "🍇\tgrapes\tdionysus fruit grape\n🍈\tmelon\tcantaloupe fruit\n🍉\twatermelon\tfruit\n🍊\ttangerine\tc citrus fruit\n🍋\tlemon\tcitrus fruit sour\n🍌\tbanana\tfruit potassium\n🍍\tpineapple\tcolada fruit pina\n🥭\tmango\tfood fruit tropical\n🍎\tred apple\tdiet food fruit\n🍏\tgreen apple\tfruit\n🍐\tpear\tfruit\n🍑\tpeach\tfruit\n🍒\tcherries\tberries cherry fruit\n🍓\tstrawberry\tberry fruit\n🫐\tblueberries\tberries berry bilberry\n🥝\tkiwi fruit\tfood\n🍅\ttomato\tfood fruit vegetable\n🫒\tolive\tfood\n🥥\tcoconut\tcolada palm piña\n🥑\tavocado\tfood fruit\n🍆\teggplant\taubergine vegetable\n🥔\tpotato\tfood vegetable\n🥕\tcarrot\tfood vegetable\n🌽\tear of corn\tcrops farm maize\n🌶️\thot pepper\t\n🫑\tbell pepper\tcapsicum food vegetable\n🥒\tcucumber\tfood pickle vegetable\n🥬\tleafy green\tbok burgers cabbage\n🥦\tbroccoli\tcabbage wild\n🧄\tgarlic\tflavoring\n🧅\tonion\tflavoring\n🥜\tpeanuts\tfood nut peanut\n🫘\tbeans\tfood kidney legume\n🌰\tchestnut\talmond plant\n🫚\tginger root\tbeer health herb\n🫛\tpea pod\tbeans beanstalk edamame\n🍞\tbread\tcarbs food grain\n🥐\tcroissant\tbread breakfast crescent\n🥖\tbaguette bread\tfood french\n🫓\tflatbread\tarepa bread food\n🥨\tpretzel\tconvoluted twisted\n🥯\tbagel\tbakery bread breakfast\n🥞\tpancakes\tbreakfast crêpe food\n🧇\twaffle\tbreakfast indecisive iron\n🧀\tcheese wedge\t\n🍖\tmeat on bone\t\n🍗\tpoultry leg\tbone chicken drumstick\n🥩\tcut of meat\tchop lambchop porkchop\n🥓\tbacon\tbreakfast food meat\n🍔\thamburger\tburger eat fast\n🍟\tfrench fries\tfast food\n🍕\tpizza\tcheese food hungry\n🌭\thot dog\tfrankfurter hotdog sausage\n🥪\tsandwich\tbread\n🌮\ttaco\tmexican\n🌯\tburrito\tmexican wrap\n🫔\ttamale\tfood mexican pamonha\n🥙\tstuffed flatbread\tfalafel food gyro\n🧆\tfalafel\tchickpea meatball\n🥚\tegg\tbreakfast food\n🍳\tcooking\tbreakfast easy egg\n🥘\tshallow pan of food\tcasserole paella\n🍲\tpot of food\tsoup stew\n🫕\tfondue\tcheese chocolate food\n🥣\tbowl with spoon\tbreakfast cereal congee\n🥗\tgreen salad\tfood\n🍿\tpopcorn\tcorn movie pop\n🧈\tbutter\tdairy\n🧂\tsalt\tcondiment flavor mad\n🥫\tcanned food\tcan\n🍱\tbento box\tfood\n🍘\trice cracker\tfood\n🍙\trice ball\tfood japanese\n🍚\tcooked rice\tfood\n🍛\tcurry rice\tfood\n🍜\tsteaming bowl\tchopsticks food noodle\n🍝\tspaghetti\tfood meatballs pasta\n🍠\troasted sweet potato\tfood\n🍢\toden\tfood kebab restaurant\n🍣\tsushi\tfood\n🍤\tfried shrimp\tprawn tempura\n🍥\tfish cake with swirl\tfood pastry restaurant\n🥮\tmoon cake\tautumn festival yuèbǐng\n🍡\tdango\tdessert japanese skewer\n🥟\tdumpling\tempanada gyōza jiaozi\n🥠\tfortune cookie\tprophecy\n🥡\ttakeout box\tchopsticks delivery food\n🍦\tsoft ice cream\tdessert food icecream\n🍧\tshaved ice\tdessert restaurant sweet\n🍨\tice cream\tdessert food restaurant\n🍩\tdoughnut\tbreakfast dessert donut\n🍪\tcookie\tchip chocolate dessert\n🎂\tbirthday cake\tbday celebration dessert\n🍰\tshortcake\tcake dessert pastry\n🧁\tcupcake\tbakery dessert sprinkles\n🥧\tpie\tapple filling fruit\n🍫\tchocolate bar\tcandy dessert halloween\n🍬\tcandy\tcavities dessert halloween\n🍭\tlollipop\tcandy dessert food\n🍮\tcustard\tdessert pudding sweet\n🍯\thoney pot\tbarrel bear food\n🍼\tbaby bottle\tbabies birth born\n🥛\tglass of milk\tdrink\n☕\thot beverage\tcafe caffeine chai\n🫖\tteapot\tbrew drink food\n🍵\tteacup without handle\tbeverage cup drink\n🍶\tsake\tbar beverage bottle\n🍾\tbottle with popping cork\tbar drink\n🍷\twine glass\talcohol bar beverage\n🍸\tcocktail glass\talcohol bar booze\n🍹\ttropical drink\talcohol bar booze\n🍺\tbeer mug\talcohol ale bar\n🍻\tclinking beer mugs\talcohol bar booze\n🥂\tclinking glasses\tcelebrate clink drink\n🥃\ttumbler glass\tliquor scotch shot\n🫗\tpouring liquid\taccident drink empty\n🥤\tcup with straw\tdrink juice malt\n🧋\tbubble tea\tboba food milk\n🧃\tbeverage box\tjuice straw sweet\n🧉\tmate\tdrink\n🧊\tice\tcold cube iceberg\n🥢\tchopsticks\thashi jeotgarak kuaizi\n🍽️\tfork and knife with plate\tcooking dinner eat\n🍴\tfork and knife\tbreakfast breaky cooking\n🥄\tspoon\teat tableware\n🔪\tkitchen knife\tchef cooking hocho\n🫙\tjar\tcondiment container empty\n🏺\tamphora\taquarius cooking drink";
|
|
1177
|
+
readonly travel: "🌍\tglobe showing Europe-Africa\tearth europe-africa world\n🌎\tglobe showing Americas\tearth world\n🌏\tglobe showing Asia-Australia\tasia-australia earth world\n🌐\tglobe with meridians\tearth internet web\n🗺️\tworld map\t\n🗾\tmap of Japan\t\n🧭\tcompass\tdirection magnetic navigation\n🏔️\tsnow-capped mountain\tcold snow-capped\n⛰️\tmountain\t\n🌋\tvolcano\teruption mountain nature\n🗻\tmount fuji\tmountain nature\n🏕️\tcamping\t\n🏖️\tbeach with umbrella\t\n🏜️\tdesert\t\n🏝️\tdesert island\t\n🏞️\tnational park\t\n🏟️\tstadium\t\n🏛️\tclassical building\t\n🏗️\tbuilding construction\tcrane\n🧱\tbrick\tbricks clay mortar\n🪨\trock\tboulder heavy solid\n🪵\twood\tlog lumber timber\n🛖\thut\thome house roundhouse\n🏘️\thouses\thouse\n🏚️\tderelict house\thome\n🏠\thouse\tbuilding country heart\n🏡\thouse with garden\tbuilding country heart\n🏢\toffice building\tcity cubical job\n🏣\tJapanese post office\tbuilding\n🏤\tpost office\tbuilding european\n🏥\thospital\tbuilding doctor medicine\n🏦\tbank\tbuilding\n🏨\thotel\tbuilding\n🏩\tlove hotel\tbuilding\n🏪\tconvenience store\t24 building hours\n🏫\tschool\tbuilding\n🏬\tdepartment store\tbuilding\n🏭\tfactory\tbuilding\n🏯\tJapanese castle\tbuilding\n🏰\tcastle\tbuilding european\n💒\twedding\tchapel hitched nuptials\n🗼\tTokyo tower\t\n🗽\tStatue of Liberty\tnew ny nyc\n⛪\tchurch\tbless chapel christian\n🕌\tmosque\tislam masjid muslim\n🛕\thindu temple\t\n🕍\tsynagogue\tjew jewish judaism\n⛩️\tshinto shrine\treligion\n🕋\tkaaba\thajj islam muslim\n⛲\tfountain\t\n⛺\ttent\tcamping\n🌁\tfoggy\tfog\n🌃\tnight with stars\tstar\n🏙️\tcityscape\tcity\n🌄\tsunrise over mountains\tmorning sun\n🌅\tsunrise\tmorning nature sun\n🌆\tcityscape at dusk\tbuilding city evening\n🌇\tsunset\tbuilding dusk sun\n🌉\tbridge at night\t\n♨️\thot springs\thotsprings steaming\n🎠\tcarousel horse\tentertainment\n🛝\tplayground slide\tamusement park play\n🎡\tferris wheel\tamusement park theme\n🎢\troller coaster\tamusement park theme\n💈\tbarber pole\tcut fresh haircut\n🎪\tcircus tent\t\n🚂\tlocomotive\tcaboose engine railway\n🚃\trailway car\telectric train tram\n🚄\thigh-speed train\thigh-speed railway shinkansen\n🚅\tbullet train\thigh-speed nose railway\n🚆\ttrain\tarrived choo railway\n🚇\tmetro\tsubway travel\n🚈\tlight rail\tarrived monorail railway\n🚉\tstation\trailway train\n🚊\ttram\ttrolleybus\n🚝\tmonorail\tvehicle\n🚞\tmountain railway\tcar trip\n🚋\ttram car\tbus trolley trolleybus\n🚌\tbus\tschool vehicle\n🚍\toncoming bus\tcars\n🚎\ttrolleybus\tbus tram trolley\n🚐\tminibus\tbus drive van\n🚑\tambulance\temergency vehicle\n🚒\tfire engine\ttruck\n🚓\tpolice car\t5–0 cops patrol\n🚔\toncoming police car\t\n🚕\ttaxi\tcab cabbie car\n🚖\toncoming taxi\tcab cabbie cars\n🚗\tautomobile\tcar driving vehicle\n🚘\toncoming automobile\tcar cars drove\n🚙\tsport utility vehicle\tcar drive recreational\n🛻\tpickup truck\tautomobile car flatbed\n🚚\tdelivery truck\tcar drive vehicle\n🚛\tarticulated lorry\tcar drive move\n🚜\ttractor\tvehicle\n🏎️\tracing car\tzoom\n🏍️\tmotorcycle\tracing\n🛵\tmotor scooter\t\n🦽\tmanual wheelchair\taccessibility\n🦼\tmotorized wheelchair\taccessibility\n🛺\tauto rickshaw\ttuk\n🚲\tbicycle\tbike class cycle\n🛴\tkick scooter\t\n🛹\tskateboard\tboard skate skater\n🛼\troller skate\tblades skates sport\n🚏\tbus stop\tbusstop\n🛣️\tmotorway\thighway road\n🛤️\trailway track\ttrain\n🛢️\toil drum\t\n⛽\tfuel pump\tdiesel fuelpump gas\n🛞\twheel\tcar circle tire\n🚨\tpolice car light\talarm alert beacon\n🚥\thorizontal traffic light\tintersection signal stop\n🚦\tvertical traffic light\tdrove intersection signal\n🛑\tstop sign\toctagonal\n🚧\tconstruction\tbarrier\n⚓\tanchor\tship tool\n🛟\tring buoy\tfloat life lifesaver\n⛵\tsailboat\tboat resort sailing\n🛶\tcanoe\tboat\n🚤\tspeedboat\tbillionaire boat lake\n🛳️\tpassenger ship\t\n⛴️\tferry\tboat passenger\n🛥️\tmotor boat\tmotorboat\n🚢\tship\tboat passenger travel\n✈️\tairplane\taeroplane fly flying\n🛩️\tsmall airplane\taeroplane plane\n🛫\tairplane departure\taeroplane check-in departures\n🛬\tairplane arrival\taeroplane arrivals arriving\n🪂\tparachute\thang-glide parasail skydive\n💺\tseat\tchair\n🚁\thelicopter\tcopter roflcopter travel\n🚟\tsuspension railway\t\n🚠\tmountain cableway\tcable gondola lift\n🚡\taerial tramway\tcable car gondola\n🛰️\tsatellite\tspace\n🚀\trocket\tlaunch rockets space\n🛸\tflying saucer\taliens extra terrestrial\n🛎️\tbellhop bell\thotel\n🧳\tluggage\tbag packing roller\n⌛\thourglass done\tsand time timer\n⏳\thourglass not done\tflowing hours sand\n⌚\twatch\tclock time\n⏰\talarm clock\thours hrs late\n⏱️\tstopwatch\tclock time\n⏲️\ttimer clock\t\n🕰️\tmantelpiece clock\ttime\n🕛\ttwelve o’clock\t12 12:00 o’clock\n🕧\ttwelve-thirty\t12 12:30 30\n🕐\tone o’clock\t1 1:00 o’clock\n🕜\tone-thirty\t1 1:30 30\n🕑\ttwo o’clock\t2 2:00 o’clock\n🕝\ttwo-thirty\t2 2:30 30\n🕒\tthree o’clock\t3 3:00 o’clock\n🕞\tthree-thirty\t3 30 3:30\n🕓\tfour o’clock\t4 4:00 o’clock\n🕟\tfour-thirty\t30 4 4:30\n🕔\tfive o’clock\t5 5:00 o’clock\n🕠\tfive-thirty\t30 5 5:30\n🕕\tsix o’clock\t6 6:00 o’clock\n🕡\tsix-thirty\t30 6 6:30\n🕖\tseven o’clock\t0 7 7:00\n🕢\tseven-thirty\t30 7 7:30\n🕗\teight o’clock\t8 8:00 o’clock\n🕣\teight-thirty\t30 8 8:30\n🕘\tnine o’clock\t9 9:00 o’clock\n🕤\tnine-thirty\t30 9 9:30\n🕙\tten o’clock\t0 10 10:00\n🕥\tten-thirty\t10 10:30 30\n🕚\televen o’clock\t11 11:00 o’clock\n🕦\televen-thirty\t11 11:30 30\n🌑\tnew moon\tdark space\n🌒\twaxing crescent moon\tdreams space\n🌓\tfirst quarter moon\tspace\n🌔\twaxing gibbous moon\tspace\n🌕\tfull moon\tspace\n🌖\twaning gibbous moon\tspace\n🌗\tlast quarter moon\tspace\n🌘\twaning crescent moon\tspace\n🌙\tcrescent moon\tramadan space\n🌚\tnew moon face\tspace\n🌛\tfirst quarter moon face\tspace\n🌜\tlast quarter moon face\tdreams\n🌡️\tthermometer\tweather\n☀️\tsun\tbright rays space\n🌝\tfull moon face\tbright\n🌞\tsun with face\tbeach bright day\n🪐\tringed planet\tsaturn saturnine\n⭐\tstar\tastronomy medium stars\n🌟\tglowing star\tglittery glow night\n🌠\tshooting star\tfalling night space\n🌌\tmilky way\tspace\n☁️\tcloud\tweather\n⛅\tsun behind cloud\tcloudy weather\n⛈️\tcloud with lightning and rain\tthunder thunderstorm\n🌤️\tsun behind small cloud\tweather\n🌥️\tsun behind large cloud\tweather\n🌦️\tsun behind rain cloud\tweather\n🌧️\tcloud with rain\tweather\n🌨️\tcloud with snow\tcold weather\n🌩️\tcloud with lightning\tweather\n🌪️\ttornado\tcloud weather whirlwind\n🌫️\tfog\tcloud weather\n🌬️\twind face\tblow cloud\n🌀\tcyclone\tdizzy hurricane twister\n🌈\trainbow\tgay genderqueer glbt\n🌂\tclosed umbrella\tclothing rain\n☂️\tumbrella\tclothing rain\n☔\tumbrella with rain drops\tclothing drop weather\n⛱️\tumbrella on ground\train sun\n⚡\thigh voltage\tdanger electric electricity\n❄️\tsnowflake\tcold snow weather\n☃️\tsnowman\tcold man snow\n⛄\tsnowman without snow\tcold man\n☄️\tcomet\tspace\n🔥\tfire\taf burn flame\n💧\tdroplet\tcold comic drop\n🌊\twater wave\tnature ocean surf";
|
|
1178
|
+
readonly activities: "🎃\tjack-o-lantern\tcelebration halloween pumpkin\n🎄\tChristmas tree\tcelebration\n🎆\tfireworks\tboom celebration entertainment\n🎇\tsparkler\tboom celebration fireworks\n🧨\tfirecracker\tdynamite explosive fire\n✨\tsparkles\t* magic sparkle\n🎈\tballoon\tbirthday celebrate celebration\n🎉\tparty popper\tawesome birthday celebrate\n🎊\tconfetti ball\tcelebrate celebration party\n🎋\ttanabata tree\tbanner celebration japanese\n🎍\tpine decoration\tbamboo celebration japanese\n🎎\tJapanese dolls\tcelebration doll festival\n🎏\tcarp streamer\tcelebration\n🎐\twind chime\tbell celebration\n🎑\tmoon viewing ceremony\tcelebration\n🧧\tred envelope\tgift good hóngbāo\n🎀\tribbon\tcelebration\n🎁\twrapped gift\tbirthday bow box\n🎗️\treminder ribbon\tcelebration\n🎟️\tadmission tickets\tticket\n🎫\tticket\tadmission stub\n🎖️\tmilitary medal\taward celebration\n🏆\ttrophy\tchampion champs prize\n🏅\tsports medal\taward gold winner\n🥇\t1st place medal\tfirst gold\n🥈\t2nd place medal\tsecond silver\n🥉\t3rd place medal\tbronze third\n⚽\tsoccer ball\tfootball futbol sport\n⚾\tbaseball\tball sport\n🥎\tsoftball\tball glove sports\n🏀\tbasketball\tball hoop sport\n🏐\tvolleyball\tball game\n🏈\tamerican football\tball bowl sport\n🏉\trugby football\tball sport\n🎾\ttennis\tball racquet sport\n🥏\tflying disc\tultimate\n🎳\tbowling\tball game sport\n🏏\tcricket game\tball bat\n🏑\tfield hockey\tball game stick\n🏒\tice hockey\tgame puck stick\n🥍\tlacrosse\tball goal sports\n🏓\tping pong\tball bat game\n🏸\tbadminton\tbirdie game racquet\n🥊\tboxing glove\t\n🥋\tmartial arts uniform\tjudo karate taekwondo\n🥅\tgoal net\t\n⛳\tflag in hole\tgolf sport\n⛸️\tice skate\tskating\n🎣\tfishing pole\tentertainment fish sport\n🤿\tdiving mask\tscuba snorkeling\n🎽\trunning shirt\tathletics sash\n🎿\tskis\tski snow sport\n🛷\tsled\tluge sledge sleigh\n🥌\tcurling stone\tgame rock\n🎯\tbullseye\tbull dart direct\n🪀\tyo-yo\tfluctuate toy\n🪁\tkite\tfly soar\n🔫\twater pistol\tgun handgun revolver\n🎱\tpool 8 ball\t8ball billiard eight\n🔮\tcrystal ball\tfairy fairytale fantasy\n🪄\tmagic wand\tmagician witch wizard\n🎮\tvideo game\tcontroller entertainment\n🕹️\tjoystick\tgame video videogame\n🎰\tslot machine\tcasino gamble gambling\n🎲\tgame die\tdice entertainment\n🧩\tpuzzle piece\tclue interlocking jigsaw\n🧸\tteddy bear\tplaything plush stuffed\n🪅\tpiñata\tcandy celebrate celebration\n🪩\tmirror ball\tdance disco glitter\n🪆\tnesting dolls\tbabooshka baboushka babushka\n♠️\tspade suit\tcard game\n♥️\theart suit\tcard emotion game\n♦️\tdiamond suit\tcard game\n♣️\tclub suit\tcard clubs game\n♟️\tchess pawn\tdupe expendable\n🃏\tjoker\tcard game wildcard\n🀄\tmahjong red dragon\tgame\n🎴\tflower playing cards\tcard game japanese\n🎭\tperforming arts\tactor actress art\n🖼️\tframed picture\tart frame museum\n🎨\tartist palette\tart artsy arty\n🧵\tthread\tneedle sewing spool\n🪡\tsewing needle\tembroidery sew stitches\n🧶\tyarn\tball crochet knit\n🪢\tknot\tcord rope tangled";
|
|
1179
|
+
readonly objects: "👓\tglasses\tclothing eye eyeglasses\n🕶️\tsunglasses\tdark eye eyewear\n🥽\tgoggles\tdive eye protection\n🥼\tlab coat\tclothes doctor dr\n🦺\tsafety vest\temergency\n👔\tnecktie\tclothing employed serious\n👕\tt-shirt\tblue casual clothes\n👖\tjeans\tblue casual clothes\n🧣\tscarf\tbundle cold neck\n🧤\tgloves\thand\n🧥\tcoat\tbrr bundle cold\n🧦\tsocks\tstocking\n👗\tdress\tclothes clothing dressed\n👘\tkimono\tclothing comfortable\n🥻\tsari\tclothing dress\n🩱\tone-piece swimsuit\tbathing one-piece suit\n🩲\tbriefs\tbathing one-piece suit\n🩳\tshorts\tbathing pants suit\n👙\tbikini\tbathing beach clothing\n👚\twoman’s clothes\tblouse clothing collar\n🪭\tfolding hand fan\tclack clap cool\n👛\tpurse\tclothes clothing coin\n👜\thandbag\tbag clothes clothing\n👝\tclutch bag\tclothes clothing dress\n🛍️\tshopping bags\tbag hotel\n🎒\tbackpack\tbackpacking bag bookbag\n🩴\tthong sandal\tbeach flip flop\n👞\tman’s shoe\tbrown clothes clothing\n👟\trunning shoe\tathletic clothes clothing\n🥾\thiking boot\tbackpacking brown camping\n🥿\tflat shoe\tballet comfy flats\n👠\thigh-heeled shoe\tclothes clothing dress\n👡\twoman’s sandal\tclothing shoe woman’s\n🩰\tballet shoes\tdance\n👢\twoman’s boot\tclothes clothing dress\n🪮\thair pick\tafro comb groom\n👑\tcrown\tclothing family king\n👒\twoman’s hat\tclothes clothing garden\n🎩\ttop hat\tclothes clothing fancy\n🎓\tgraduation cap\tcelebration clothing education\n🧢\tbilled cap\tbaseball bent dad\n🪖\tmilitary helmet\tarmy soldier war\n⛑️\trescue worker’s helmet\taid cross face\n📿\tprayer beads\tclothing necklace religion\n💄\tlipstick\tcosmetics date makeup\n💍\tring\tdiamond engaged engagement\n💎\tgem stone\tdiamond engagement jewel\n🔇\tmuted speaker\tmute quiet silent\n🔈\tspeaker low volume\tsoft sound\n🔉\tspeaker medium volume\tsound\n🔊\tspeaker high volume\tloud music sound\n📢\tloudspeaker\taddress communication loud\n📣\tmegaphone\tcheering sound\n📯\tpostal horn\tpost\n🔔\tbell\tbreak church sound\n🔕\tbell with slash\tforbidden mute no\n🎼\tmusical score\tmusic note\n🎵\tmusical note\tmusic sound\n🎶\tmusical notes\tmusic note sound\n🎙️\tstudio microphone\tmic music\n🎚️\tlevel slider\tmusic\n🎛️\tcontrol knobs\tmusic\n🎤\tmicrophone\tkaraoke mic music\n🎧\theadphone\tearbud sound\n📻\tradio\tentertainment tbt video\n🎷\tsaxophone\tinstrument music sax\n🪗\taccordion\tbox concertina instrument\n🎸\tguitar\tinstrument music strat\n🎹\tmusical keyboard\tinstrument music piano\n🎺\ttrumpet\tinstrument music\n🎻\tviolin\tinstrument music\n🪕\tbanjo\tmusic stringed\n🥁\tdrum\tdrumsticks music\n🪘\tlong drum\tbeat conga instrument\n🪇\tmaracas\tcha dance instrument\n🪈\tflute\tband fife flautist\n📱\tmobile phone\tcell communication telephone\n📲\tmobile phone with arrow\tbuild call cell\n☎️\ttelephone\tphone\n📞\ttelephone receiver\tcommunication phone voip\n📟\tpager\tcommunication\n📠\tfax machine\tcommunication\n🔋\tbattery\t\n🪫\tlow battery\tdrained electronic energy\n🔌\telectric plug\telectricity\n💻\tlaptop\tcomputer office pc\n🖥️\tdesktop computer\tmonitor\n🖨️\tprinter\tcomputer\n⌨️\tkeyboard\tcomputer\n🖱️\tcomputer mouse\t\n🖲️\ttrackball\tcomputer\n💽\tcomputer disk\tminidisk optical\n💾\tfloppy disk\tcomputer\n💿\toptical disk\tblu-ray cd computer\n📀\tdvd\tblu-ray cd computer\n🧮\tabacus\tcalculation calculator\n🎥\tmovie camera\tbollywood cinema film\n🎞️\tfilm frames\tcinema movie\n📽️\tfilm projector\tcinema movie video\n🎬\tclapper board\taction movie\n📺\ttelevision\ttv video\n📷\tcamera\tphoto selfie snap\n📸\tcamera with flash\tvideo\n📹\tvideo camera\tcamcorder tbt\n📼\tvideocassette\told school tape\n🔍\tmagnifying glass tilted left\tlab left-pointing science\n🔎\tmagnifying glass tilted right\tcontact lab right-pointing\n🕯️\tcandle\tlight\n💡\tlight bulb\tcomic electric idea\n🔦\tflashlight\telectric light tool\n🏮\tred paper lantern\tbar light restaurant\n🪔\tdiya lamp\tlight oil\n📔\tnotebook with decorative cover\tbook decorated education\n📕\tclosed book\teducation\n📖\topen book\teducation fantasy knowledge\n📗\tgreen book\teducation fantasy library\n📘\tblue book\teducation fantasy library\n📙\torange book\teducation fantasy library\n📚\tbooks\tbook education fantasy\n📓\tnotebook\t\n📒\tledger\tnotebook\n📃\tpage with curl\tdocument paper\n📜\tscroll\tpaper\n📄\tpage facing up\tdocument paper\n📰\tnewspaper\tcommunication news paper\n🗞️\trolled-up newspaper\tnews paper rolled-up\n📑\tbookmark tabs\tmark marker\n🔖\tbookmark\tmark\n🏷️\tlabel\ttag\n💰\tmoney bag\tbank bet billion\n🪙\tcoin\tdollar euro gold\n💴\tyen banknote\tbank bill currency\n💵\tdollar banknote\tbank bill currency\n💶\teuro banknote\t100 bank bill\n💷\tpound banknote\tbank bill billion\n💸\tmoney with wings\tbank banknote bill\n💳\tcredit card\tbank cash charge\n🧾\treceipt\taccounting bookkeeping evidence\n💹\tchart increasing with yen\tbank currency graph\n✉️\tenvelope\te-mail email letter\n📧\te-mail\temail letter\n📨\tincoming envelope\tdelivering e-mail email\n📩\tenvelope with arrow\tcommunication down e-mail\n📤\toutbox tray\tbox email letter\n📥\tinbox tray\tbox email letter\n📦\tpackage\tbox communication delivery\n📫\tclosed mailbox with raised flag\tcommunication mail postbox\n📪\tclosed mailbox with lowered flag\tmail postbox\n📬\topen mailbox with raised flag\tmail postbox\n📭\topen mailbox with lowered flag\tmail postbox\n📮\tpostbox\tmail mailbox\n🗳️\tballot box with ballot\t\n✏️\tpencil\t\n✒️\tblack nib\tpen\n🖋️\tfountain pen\t\n🖊️\tpen\tballpoint\n🖌️\tpaintbrush\tpainting\n🖍️\tcrayon\t\n📝\tmemo\tcommunication media notes\n💼\tbriefcase\toffice\n📁\tfile folder\t\n📂\topen file folder\t\n🗂️\tcard index dividers\t\n📅\tcalendar\tdate\n📆\ttear-off calendar\ttear-off\n🗒️\tspiral notepad\tnote pad\n🗓️\tspiral calendar\tpad\n📇\tcard index\told rolodex school\n📈\tchart increasing\tdata graph growth\n📉\tchart decreasing\tdata down downward\n📊\tbar chart\tdata graph\n📋\tclipboard\tdo list notes\n📌\tpushpin\tcollage pin\n📍\tround pushpin\tlocation map pin\n📎\tpaperclip\t\n🖇️\tlinked paperclips\tlink paperclip\n📏\tstraight ruler\tangle edge math\n📐\ttriangular ruler\tangle math rule\n✂️\tscissors\tcut cutting paper\n🗃️\tcard file box\t\n🗄️\tfile cabinet\tfiling paper\n🗑️\twastebasket\tcan garbage trash\n🔒\tlocked\tclosed lock private\n🔓\tunlocked\tcracked lock open\n🔏\tlocked with pen\tink lock nib\n🔐\tlocked with key\tbike closed lock\n🔑\tkey\tkeys lock major\n🗝️\told key\tclue lock\n🔨\thammer\thome improvement repairs\n🪓\taxe\tax chop hatchet\n⛏️\tpick\thammer mining tool\n⚒️\thammer and pick\ttool\n🛠️\thammer and wrench\tspanner tool\n🗡️\tdagger\tknife weapon\n⚔️\tcrossed swords\tweapon\n💣\tbomb\tboom comic dangerous\n🪃\tboomerang\trebound repercussion weapon\n🏹\tbow and arrow\tarcher archery sagittarius\n🛡️\tshield\tweapon\n🪚\tcarpentry saw\tcarpenter cut lumber\n🔧\twrench\thome improvement spanner\n🪛\tscrewdriver\tflathead handy screw\n🔩\tnut and bolt\thome improvement tool\n⚙️\tgear\tcog cogwheel tool\n🗜️\tclamp\tcompress tool vice\n⚖️\tbalance scale\tjustice libra scales\n🦯\twhite cane\taccessibility blind probing\n🔗\tlink\tlinks\n⛓️\tchains\tchain\n🪝\thook\tcatch crook curve\n🧰\ttoolbox\tbox chest mechanic\n🧲\tmagnet\tattraction horseshoe magnetic\n🪜\tladder\tclimb rung step\n⚗️\talembic\tchemistry tool\n🧪\ttest tube\tchemist chemistry experiment\n🧫\tpetri dish\tbacteria biologist biology\n🧬\tdna\tbiologist evolution gene\n🔬\tmicroscope\texperiment lab science\n🔭\ttelescope\tcontact extraterrestrial science\n📡\tsatellite antenna\taliens contact dish\n💉\tsyringe\tdoctor flu medicine\n🩸\tdrop of blood\tbleed donation injury\n💊\tpill\tdoctor drugs medicated\n🩹\tadhesive bandage\t\n🩼\tcrutch\taid cane disability\n🩺\tstethoscope\tdoctor heart medicine\n🩻\tx-ray\tbones doctor medical\n🚪\tdoor\tback closet front\n🛗\televator\taccessibility hoist lift\n🪞\tmirror\tmakeup reflection reflector\n🪟\twindow\tair frame fresh\n🛏️\tbed\thotel sleep\n🛋️\tcouch and lamp\thotel\n🪑\tchair\tseat sit\n🚽\ttoilet\tbathroom\n🪠\tplunger\tcup force plumber\n🚿\tshower\twater\n🛁\tbathtub\tbath\n🪤\tmouse trap\tbait cheese lure\n🪒\trazor\tsharp shave\n🧴\tlotion bottle\tmoisturizer shampoo sunscreen\n🧷\tsafety pin\tdiaper punk rock\n🧹\tbroom\tcleaning sweeping witch\n🧺\tbasket\tfarming laundry picnic\n🧻\troll of paper\ttoilet towels\n🪣\tbucket\tcask pail vat\n🧼\tsoap\tbar bathing clean\n🫧\tbubbles\tbubble burp clean\n🪥\ttoothbrush\tbathroom brush clean\n🧽\tsponge\tabsorbing cleaning porous\n🧯\tfire extinguisher\textinguish quench\n🛒\tshopping cart\ttrolley\n🚬\tcigarette\tsmoking\n⚰️\tcoffin\tdead death vampire\n🪦\theadstone\tcemetery dead grave\n⚱️\tfuneral urn\tashes death\n🧿\tnazar amulet\tbead blue charm\n🪬\thamsa\tamulet fatima fortune\n🗿\tmoai\tface moyai statue\n🪧\tplacard\tcard demonstration notice\n🪪\tidentification card\tcredentials document id";
|
|
1180
|
+
readonly symbols: "🏧\tATM sign\tautomated bank cash\n🚮\tlitter in bin sign\tlitterbin\n🚰\tpotable water\tdrinking\n♿\twheelchair symbol\taccess handicap\n🚹\tmen’s room\tbathroom lavatory man\n🚺\twomen’s room\tbathroom lavatory restroom\n🚻\trestroom\tbathroom lavatory toilet\n🚼\tbaby symbol\tchanging\n🚾\twater closet\tbathroom lavatory restroom\n🛂\tpassport control\t\n🛃\tcustoms\tpacking\n🛄\tbaggage claim\tarrived bags case\n🛅\tleft luggage\tbaggage case locker\n⚠️\twarning\tcaution\n🚸\tchildren crossing\tchild pedestrian traffic\n⛔\tno entry\tdo fail forbidden\n🚫\tprohibited\tentry forbidden no\n🚳\tno bicycles\tbicycle bike forbidden\n🚭\tno smoking\tforbidden not prohibited\n🚯\tno littering\tforbidden litter not\n🚱\tnon-potable water\tdry non-drinking non-potable\n🚷\tno pedestrians\tforbidden not pedestrian\n📵\tno mobile phones\tcell forbidden not\n🔞\tno one under eighteen\t18 age forbidden\n☢️\tradioactive\tsign\n☣️\tbiohazard\tsign\n⬆️\tup arrow\tcardinal direction north\n↗️\tup-right arrow\tdirection intercardinal northeast\n➡️\tright arrow\tcardinal direction east\n↘️\tdown-right arrow\tdirection down-right intercardinal\n⬇️\tdown arrow\tcardinal direction south\n↙️\tdown-left arrow\tdirection down-left intercardinal\n⬅️\tleft arrow\tcardinal direction west\n↖️\tup-left arrow\tdirection intercardinal northwest\n↕️\tup-down arrow\tup-down\n↔️\tleft-right arrow\tleft-right\n↩️\tright arrow curving left\t\n↪️\tleft arrow curving right\t\n⤴️\tright arrow curving up\t\n⤵️\tright arrow curving down\t\n🔃\tclockwise vertical arrows\tarrow refresh reload\n🔄\tcounterclockwise arrows button\tagain anticlockwise arrow\n🔙\tBACK arrow\t\n🔚\tEND arrow\t\n🔛\tON! arrow\tmark on!\n🔜\tSOON arrow\tbrb omw\n🔝\tTOP arrow\thomie up\n🛐\tplace of worship\tpray religion\n⚛️\tatom symbol\tatheist\n🕉️\tom\thindu religion\n✡️\tstar of David\tjew jewish judaism\n☸️\twheel of dharma\tbuddhist religion\n☯️\tyin yang\tdifficult lives religion\n✝️\tlatin cross\tchrist christian religion\n☦️\torthodox cross\tchristian religion\n☪️\tstar and crescent\tislam muslim ramadan\n☮️\tpeace symbol\thealing peaceful\n🕎\tmenorah\tcandelabrum candlestick hanukkah\n🔯\tdotted six-pointed star\tfortune jewish judaism\n🪯\tkhanda\tdeg fateh khalsa\n♈\tAries\thoroscope ram zodiac\n♉\tTaurus\tbull horoscope ox\n♊\tGemini\thoroscope twins zodiac\n♋\tCancer\tcrab horoscope zodiac\n♌\tLeo\thoroscope lion zodiac\n♍\tVirgo\thoroscope zodiac\n♎\tLibra\tbalance horoscope justice\n♏\tScorpio\thoroscope scorpion scorpius\n♐\tSagittarius\tarcher horoscope zodiac\n♑\tCapricorn\tgoat horoscope zodiac\n♒\tAquarius\tbearer horoscope water\n♓\tPisces\tfish horoscope zodiac\n⛎\tOphiuchus\tbearer serpent snake\n🔀\tshuffle tracks button\tarrow crossed\n🔁\trepeat button\tarrow clockwise\n🔂\trepeat single button\tarrow clockwise once\n▶️\tplay button\tarrow right triangle\n⏩\tfast-forward button\tarrow double fast-forward\n⏭️\tnext track button\tarrow scene triangle\n⏯️\tplay or pause button\tarrow right triangle\n◀️\treverse button\tarrow left triangle\n⏪\tfast reverse button\tarrow double rewind\n⏮️\tlast track button\tarrow previous scene\n🔼\tupwards button\tarrow red up\n⏫\tfast up button\tarrow double\n🔽\tdownwards button\tarrow down red\n⏬\tfast down button\tarrow double\n⏸️\tpause button\tbar double vertical\n⏹️\tstop button\tsquare\n⏺️\trecord button\tcircle\n⏏️\teject button\t\n🎦\tcinema\tcamera film movie\n🔅\tdim button\tbrightness low\n🔆\tbright button\tbrightness light\n📶\tantenna bars\tbar cell communication\n🛜\twireless\tbroadband computer connectivity\n📳\tvibration mode\tcell communication mobile\n📴\tmobile phone off\tcell telephone\n♀️\tfemale sign\twoman\n♂️\tmale sign\tman\n⚧️\ttransgender symbol\t\n✖️\tmultiply\tcancel multiplication sign\n➕\tplus\t+\n➖\tminus\t- heavy math\n➗\tdivide\tdivision heavy math\n🟰\theavy equals sign\tanswer equal equality\n♾️\tinfinity\tforever unbounded universal\n‼️\tdouble exclamation mark\t! !! bangbang\n⁉️\texclamation question mark\t! !? ?\n❓\tred question mark\t? punctuation\n❔\twhite question mark\t? outlined punctuation\n❕\twhite exclamation mark\t! outlined punctuation\n❗\tred exclamation mark\t! punctuation\n〰️\twavy dash\tpunctuation\n💱\tcurrency exchange\tbank money\n💲\theavy dollar sign\tbillion cash charge\n⚕️\tmedical symbol\taesculapius medicine staff\n♻️\trecycling symbol\trecycle\n⚜️\tfleur-de-lis\tknights\n🔱\ttrident emblem\tanchor poseidon ship\n📛\tname badge\t\n🔰\tJapanese symbol for beginner\tchevron green leaf\n⭕\thollow red circle\theavy large o\n✅\tcheck mark button\tchecked checkmark complete\n☑️\tcheck box with check\tballot checked done\n✔️\tcheck mark\tchecked checkmark done\n❌\tcross mark\tcancel multiplication multiply\n❎\tcross mark button\tmultiplication multiply square\n➰\tcurly loop\tcurl\n➿\tdouble curly loop\tcurl\n〽️\tpart alternation mark\t\n✳️\teight-spoked asterisk\t* eight-spoked\n✴️\teight-pointed star\t* eight-pointed\n❇️\tsparkle\t*\n©️\tcopyright\tc\n®️\tregistered\tr\n™️\ttrade mark\ttm trademark\n#️⃣\tkeycap: #\t\n*️⃣\tkeycap: *\t\n0️⃣\tkeycap: 0\t\n1️⃣\tkeycap: 1\t\n2️⃣\tkeycap: 2\t\n3️⃣\tkeycap: 3\t\n4️⃣\tkeycap: 4\t\n5️⃣\tkeycap: 5\t\n6️⃣\tkeycap: 6\t\n7️⃣\tkeycap: 7\t\n8️⃣\tkeycap: 8\t\n9️⃣\tkeycap: 9\t\n🔟\tkeycap: 10\t\n🔠\tinput latin uppercase\tabcd letters\n🔡\tinput latin lowercase\tabcd letters\n🔢\tinput numbers\t1234\n🔣\tinput symbols\t% & ♪\n🔤\tinput latin letters\tabc alphabet\n🅰️\tA button (blood type)\t\n🆎\tAB button (blood type)\t\n🅱️\tB button (blood type)\t\n🆑\tCL button\t\n🆒\tCOOL button\t\n🆓\tFREE button\t\nℹ️\tinformation\ti\n🆔\tID button\tidentity\nⓂ️\tcircled M\tcircle\n🆕\tNEW button\t\n🆖\tNG button\t\n🅾️\tO button (blood type)\t\n🆗\tOK button\tokay\n🅿️\tP button\tparking\n🆘\tSOS button\thelp\n🆙\tUP! button\tmark up!\n🆚\tVS button\tversus\n🈁\tJapanese “here” button\tkatakana\n🈂️\tJapanese “service charge” button\tkatakana\n🈷️\tJapanese “monthly amount” button\tideograph\n🈶\tJapanese “not free of charge” button\tideograph\n🈯\tJapanese “reserved” button\tideograph\n🉐\tJapanese “bargain” button\tideograph\n🈹\tJapanese “discount” button\tideograph\n🈚\tJapanese “free of charge” button\tideograph\n🈲\tJapanese “prohibited” button\tideograph\n🉑\tJapanese “acceptable” button\tideograph\n🈸\tJapanese “application” button\tideograph\n🈴\tJapanese “passing grade” button\tideograph\n🈳\tJapanese “vacancy” button\tideograph\n㊗️\tJapanese “congratulations” button\tideograph\n㊙️\tJapanese “secret” button\tideograph\n🈺\tJapanese “open for business” button\tideograph\n🈵\tJapanese “no vacancy” button\tideograph\n🔴\tred circle\tgeometric\n🟠\torange circle\t\n🟡\tyellow circle\t\n🟢\tgreen circle\t\n🔵\tblue circle\tgeometric\n🟣\tpurple circle\t\n🟤\tbrown circle\t\n⚫\tblack circle\tgeometric\n⚪\twhite circle\tgeometric\n🟥\tred square\tcard penalty\n🟧\torange square\t\n🟨\tyellow square\tcard penalty\n🟩\tgreen square\t\n🟦\tblue square\t\n🟪\tpurple square\t\n🟫\tbrown square\t\n⬛\tblack large square\tgeometric\n⬜\twhite large square\tgeometric\n◼️\tblack medium square\tgeometric\n◻️\twhite medium square\tgeometric\n◾\tblack medium-small square\tgeometric medium-small\n◽\twhite medium-small square\tgeometric medium-small\n▪️\tblack small square\tgeometric\n▫️\twhite small square\tgeometric\n🔶\tlarge orange diamond\tgeometric\n🔷\tlarge blue diamond\tgeometric\n🔸\tsmall orange diamond\tgeometric\n🔹\tsmall blue diamond\tgeometric\n🔺\tred triangle pointed up\tgeometric\n🔻\tred triangle pointed down\tgeometric\n💠\tdiamond with a dot\tcomic geometric\n🔘\tradio button\tgeometric\n🔳\twhite square button\tgeometric outlined\n🔲\tblack square button\tgeometric";
|
|
1181
|
+
readonly flags: "🏁\tchequered flag\tcheckered finish flags\n🚩\ttriangular flag\tconstruction golf post\n🎌\tcrossed flags\tcelebration cross japanese\n🏴\tblack flag\twaving\n🏳️\twhite flag\twaving\n🏳️🌈\trainbow flag\tbisexual gay genderqueer\n🏳️⚧️\ttransgender flag\tblue light pink\n🏴☠️\tpirate flag\tjolly plunder roger\n🇦🇨\tflag: Ascension Island\tAC\n🇦🇩\tflag: Andorra\tAD\n🇦🇪\tflag: United Arab Emirates\tAE\n🇦🇫\tflag: Afghanistan\tAF\n🇦🇬\tflag: Antigua & Barbuda\tAG\n🇦🇮\tflag: Anguilla\tAI\n🇦🇱\tflag: Albania\tAL\n🇦🇲\tflag: Armenia\tAM\n🇦🇴\tflag: Angola\tAO\n🇦🇶\tflag: Antarctica\tAQ\n🇦🇷\tflag: Argentina\tAR\n🇦🇸\tflag: American Samoa\tAS\n🇦🇹\tflag: Austria\tAT\n🇦🇺\tflag: Australia\tAU\n🇦🇼\tflag: Aruba\tAW\n🇦🇽\tflag: Åland Islands\tAX\n🇦🇿\tflag: Azerbaijan\tAZ\n🇧🇦\tflag: Bosnia & Herzegovina\tBA\n🇧🇧\tflag: Barbados\tBB\n🇧🇩\tflag: Bangladesh\tBD\n🇧🇪\tflag: Belgium\tBE\n🇧🇫\tflag: Burkina Faso\tBF\n🇧🇬\tflag: Bulgaria\tBG\n🇧🇭\tflag: Bahrain\tBH\n🇧🇮\tflag: Burundi\tBI\n🇧🇯\tflag: Benin\tBJ\n🇧🇱\tflag: St. Barthélemy\tBL\n🇧🇲\tflag: Bermuda\tBM\n🇧🇳\tflag: Brunei\tBN\n🇧🇴\tflag: Bolivia\tBO\n🇧🇶\tflag: Caribbean Netherlands\tBQ\n🇧🇷\tflag: Brazil\tBR\n🇧🇸\tflag: Bahamas\tBS\n🇧🇹\tflag: Bhutan\tBT\n🇧🇻\tflag: Bouvet Island\tBV\n🇧🇼\tflag: Botswana\tBW\n🇧🇾\tflag: Belarus\tBY\n🇧🇿\tflag: Belize\tBZ\n🇨🇦\tflag: Canada\tCA\n🇨🇨\tflag: Cocos (Keeling) Islands\tCC\n🇨🇩\tflag: Congo - Kinshasa\tCD\n🇨🇫\tflag: Central African Republic\tCF\n🇨🇬\tflag: Congo - Brazzaville\tCG\n🇨🇭\tflag: Switzerland\tCH\n🇨🇮\tflag: Côte d’Ivoire\tCI\n🇨🇰\tflag: Cook Islands\tCK\n🇨🇱\tflag: Chile\tCL\n🇨🇲\tflag: Cameroon\tCM\n🇨🇳\tflag: China\tCN\n🇨🇴\tflag: Colombia\tCO\n🇨🇵\tflag: Clipperton Island\tCP\n🇨🇷\tflag: Costa Rica\tCR\n🇨🇺\tflag: Cuba\tCU\n🇨🇻\tflag: Cape Verde\tCV\n🇨🇼\tflag: Curaçao\tCW\n🇨🇽\tflag: Christmas Island\tCX\n🇨🇾\tflag: Cyprus\tCY\n🇨🇿\tflag: Czechia\tCZ\n🇩🇪\tflag: Germany\tDE\n🇩🇬\tflag: Diego Garcia\tDG\n🇩🇯\tflag: Djibouti\tDJ\n🇩🇰\tflag: Denmark\tDK\n🇩🇲\tflag: Dominica\tDM\n🇩🇴\tflag: Dominican Republic\tDO\n🇩🇿\tflag: Algeria\tDZ\n🇪🇦\tflag: Ceuta & Melilla\tEA\n🇪🇨\tflag: Ecuador\tEC\n🇪🇪\tflag: Estonia\tEE\n🇪🇬\tflag: Egypt\tEG\n🇪🇭\tflag: Western Sahara\tEH\n🇪🇷\tflag: Eritrea\tER\n🇪🇸\tflag: Spain\tES\n🇪🇹\tflag: Ethiopia\tET\n🇪🇺\tflag: European Union\tEU\n🇫🇮\tflag: Finland\tFI\n🇫🇯\tflag: Fiji\tFJ\n🇫🇰\tflag: Falkland Islands\tFK\n🇫🇲\tflag: Micronesia\tFM\n🇫🇴\tflag: Faroe Islands\tFO\n🇫🇷\tflag: France\tFR\n🇬🇦\tflag: Gabon\tGA\n🇬🇧\tflag: United Kingdom\tGB\n🇬🇩\tflag: Grenada\tGD\n🇬🇪\tflag: Georgia\tGE\n🇬🇫\tflag: French Guiana\tGF\n🇬🇬\tflag: Guernsey\tGG\n🇬🇭\tflag: Ghana\tGH\n🇬🇮\tflag: Gibraltar\tGI\n🇬🇱\tflag: Greenland\tGL\n🇬🇲\tflag: Gambia\tGM\n🇬🇳\tflag: Guinea\tGN\n🇬🇵\tflag: Guadeloupe\tGP\n🇬🇶\tflag: Equatorial Guinea\tGQ\n🇬🇷\tflag: Greece\tGR\n🇬🇸\tflag: South Georgia & South Sandwich Islands\tGS\n🇬🇹\tflag: Guatemala\tGT\n🇬🇺\tflag: Guam\tGU\n🇬🇼\tflag: Guinea-Bissau\tGW\n🇬🇾\tflag: Guyana\tGY\n🇭🇰\tflag: Hong Kong SAR China\tHK\n🇭🇲\tflag: Heard & McDonald Islands\tHM\n🇭🇳\tflag: Honduras\tHN\n🇭🇷\tflag: Croatia\tHR\n🇭🇹\tflag: Haiti\tHT\n🇭🇺\tflag: Hungary\tHU\n🇮🇨\tflag: Canary Islands\tIC\n🇮🇩\tflag: Indonesia\tID\n🇮🇪\tflag: Ireland\tIE\n🇮🇱\tflag: Israel\tIL\n🇮🇲\tflag: Isle of Man\tIM\n🇮🇳\tflag: India\tIN\n🇮🇴\tflag: British Indian Ocean Territory\tIO\n🇮🇶\tflag: Iraq\tIQ\n🇮🇷\tflag: Iran\tIR\n🇮🇸\tflag: Iceland\tIS\n🇮🇹\tflag: Italy\tIT\n🇯🇪\tflag: Jersey\tJE\n🇯🇲\tflag: Jamaica\tJM\n🇯🇴\tflag: Jordan\tJO\n🇯🇵\tflag: Japan\tJP\n🇰🇪\tflag: Kenya\tKE\n🇰🇬\tflag: Kyrgyzstan\tKG\n🇰🇭\tflag: Cambodia\tKH\n🇰🇮\tflag: Kiribati\tKI\n🇰🇲\tflag: Comoros\tKM\n🇰🇳\tflag: St. Kitts & Nevis\tKN\n🇰🇵\tflag: North Korea\tKP\n🇰🇷\tflag: South Korea\tKR\n🇰🇼\tflag: Kuwait\tKW\n🇰🇾\tflag: Cayman Islands\tKY\n🇰🇿\tflag: Kazakhstan\tKZ\n🇱🇦\tflag: Laos\tLA\n🇱🇧\tflag: Lebanon\tLB\n🇱🇨\tflag: St. Lucia\tLC\n🇱🇮\tflag: Liechtenstein\tLI\n🇱🇰\tflag: Sri Lanka\tLK\n🇱🇷\tflag: Liberia\tLR\n🇱🇸\tflag: Lesotho\tLS\n🇱🇹\tflag: Lithuania\tLT\n🇱🇺\tflag: Luxembourg\tLU\n🇱🇻\tflag: Latvia\tLV\n🇱🇾\tflag: Libya\tLY\n🇲🇦\tflag: Morocco\tMA\n🇲🇨\tflag: Monaco\tMC\n🇲🇩\tflag: Moldova\tMD\n🇲🇪\tflag: Montenegro\tME\n🇲🇫\tflag: St. Martin\tMF\n🇲🇬\tflag: Madagascar\tMG\n🇲🇭\tflag: Marshall Islands\tMH\n🇲🇰\tflag: North Macedonia\tMK\n🇲🇱\tflag: Mali\tML\n🇲🇲\tflag: Myanmar (Burma)\tMM\n🇲🇳\tflag: Mongolia\tMN\n🇲🇴\tflag: Macao SAR China\tMO\n🇲🇵\tflag: Northern Mariana Islands\tMP\n🇲🇶\tflag: Martinique\tMQ\n🇲🇷\tflag: Mauritania\tMR\n🇲🇸\tflag: Montserrat\tMS\n🇲🇹\tflag: Malta\tMT\n🇲🇺\tflag: Mauritius\tMU\n🇲🇻\tflag: Maldives\tMV\n🇲🇼\tflag: Malawi\tMW\n🇲🇽\tflag: Mexico\tMX\n🇲🇾\tflag: Malaysia\tMY\n🇲🇿\tflag: Mozambique\tMZ\n🇳🇦\tflag: Namibia\tNA\n🇳🇨\tflag: New Caledonia\tNC\n🇳🇪\tflag: Niger\tNE\n🇳🇫\tflag: Norfolk Island\tNF\n🇳🇬\tflag: Nigeria\tNG\n🇳🇮\tflag: Nicaragua\tNI\n🇳🇱\tflag: Netherlands\tNL\n🇳🇴\tflag: Norway\tNO\n🇳🇵\tflag: Nepal\tNP\n🇳🇷\tflag: Nauru\tNR\n🇳🇺\tflag: Niue\tNU\n🇳🇿\tflag: New Zealand\tNZ\n🇴🇲\tflag: Oman\tOM\n🇵🇦\tflag: Panama\tPA\n🇵🇪\tflag: Peru\tPE\n🇵🇫\tflag: French Polynesia\tPF\n🇵🇬\tflag: Papua New Guinea\tPG\n🇵🇭\tflag: Philippines\tPH\n🇵🇰\tflag: Pakistan\tPK\n🇵🇱\tflag: Poland\tPL\n🇵🇲\tflag: St. Pierre & Miquelon\tPM\n🇵🇳\tflag: Pitcairn Islands\tPN\n🇵🇷\tflag: Puerto Rico\tPR\n🇵🇸\tflag: Palestinian Territories\tPS\n🇵🇹\tflag: Portugal\tPT\n🇵🇼\tflag: Palau\tPW\n🇵🇾\tflag: Paraguay\tPY\n🇶🇦\tflag: Qatar\tQA\n🇷🇪\tflag: Réunion\tRE\n🇷🇴\tflag: Romania\tRO\n🇷🇸\tflag: Serbia\tRS\n🇷🇺\tflag: Russia\tRU\n🇷🇼\tflag: Rwanda\tRW\n🇸🇦\tflag: Saudi Arabia\tSA\n🇸🇧\tflag: Solomon Islands\tSB\n🇸🇨\tflag: Seychelles\tSC\n🇸🇩\tflag: Sudan\tSD\n🇸🇪\tflag: Sweden\tSE\n🇸🇬\tflag: Singapore\tSG\n🇸🇭\tflag: St. Helena\tSH\n🇸🇮\tflag: Slovenia\tSI\n🇸🇯\tflag: Svalbard & Jan Mayen\tSJ\n🇸🇰\tflag: Slovakia\tSK\n🇸🇱\tflag: Sierra Leone\tSL\n🇸🇲\tflag: San Marino\tSM\n🇸🇳\tflag: Senegal\tSN\n🇸🇴\tflag: Somalia\tSO\n🇸🇷\tflag: Suriname\tSR\n🇸🇸\tflag: South Sudan\tSS\n🇸🇹\tflag: São Tomé & Príncipe\tST\n🇸🇻\tflag: El Salvador\tSV\n🇸🇽\tflag: Sint Maarten\tSX\n🇸🇾\tflag: Syria\tSY\n🇸🇿\tflag: Eswatini\tSZ\n🇹🇦\tflag: Tristan da Cunha\tTA\n🇹🇨\tflag: Turks & Caicos Islands\tTC\n🇹🇩\tflag: Chad\tTD\n🇹🇫\tflag: French Southern Territories\tTF\n🇹🇬\tflag: Togo\tTG\n🇹🇭\tflag: Thailand\tTH\n🇹🇯\tflag: Tajikistan\tTJ\n🇹🇰\tflag: Tokelau\tTK\n🇹🇱\tflag: Timor-Leste\tTL\n🇹🇲\tflag: Turkmenistan\tTM\n🇹🇳\tflag: Tunisia\tTN\n🇹🇴\tflag: Tonga\tTO\n🇹🇷\tflag: Türkiye\tTR\n🇹🇹\tflag: Trinidad & Tobago\tTT\n🇹🇻\tflag: Tuvalu\tTV\n🇹🇼\tflag: Taiwan\tTW\n🇹🇿\tflag: Tanzania\tTZ\n🇺🇦\tflag: Ukraine\tUA\n🇺🇬\tflag: Uganda\tUG\n🇺🇲\tflag: U.S. Outlying Islands\tUM\n🇺🇳\tflag: United Nations\tUN\n🇺🇸\tflag: United States\tUS\n🇺🇾\tflag: Uruguay\tUY\n🇺🇿\tflag: Uzbekistan\tUZ\n🇻🇦\tflag: Vatican City\tVA\n🇻🇨\tflag: St. Vincent & Grenadines\tVC\n🇻🇪\tflag: Venezuela\tVE\n🇻🇬\tflag: British Virgin Islands\tVG\n🇻🇮\tflag: U.S. Virgin Islands\tVI\n🇻🇳\tflag: Vietnam\tVN\n🇻🇺\tflag: Vanuatu\tVU\n🇼🇫\tflag: Wallis & Futuna\tWF\n🇼🇸\tflag: Samoa\tWS\n🇽🇰\tflag: Kosovo\tXK\n🇾🇪\tflag: Yemen\tYE\n🇾🇹\tflag: Mayotte\tYT\n🇿🇦\tflag: South Africa\tZA\n🇿🇲\tflag: Zambia\tZM\n🇿🇼\tflag: Zimbabwe\tZW\n🏴\tflag: England\tgbeng\n🏴\tflag: Scotland\tgbsct\n🏴\tflag: Wales\tgbwls";
|
|
1182
|
+
};
|
|
1183
|
+
|
|
1184
|
+
type EmojiCategoryId = keyof typeof EMOJI_GROUP_DATA;
|
|
1185
|
+
interface EmojiEntry {
|
|
1186
|
+
emoji: string;
|
|
1187
|
+
label: string;
|
|
1188
|
+
/** Search words beyond the label. */
|
|
1189
|
+
tags: string;
|
|
1190
|
+
}
|
|
1191
|
+
/** The categories in the order the picker shows them, each with the emoji its tab shows. */
|
|
1192
|
+
declare const EMOJI_CATEGORIES: readonly {
|
|
1193
|
+
id: EmojiCategoryId;
|
|
1194
|
+
icon: string;
|
|
1195
|
+
}[];
|
|
1196
|
+
/** One category's emoji, parsed the first time it's asked for. */
|
|
1197
|
+
declare function emojiInCategory(id: EmojiCategoryId): readonly EmojiEntry[];
|
|
1198
|
+
/** An emoji's entry (its label for screen readers), or a bare one for an emoji the data doesn't have. */
|
|
1199
|
+
declare function emojiEntry(emoji: string): EmojiEntry;
|
|
1200
|
+
/**
|
|
1201
|
+
* Emoji whose label or tags have a word starting with every word of the query, those whose
|
|
1202
|
+
* label starts with the query first. "thumbs up" finds 👍, "heart" every heart, "gri" 😀.
|
|
1203
|
+
*/
|
|
1204
|
+
declare function searchEmoji(query: string, limit?: number): EmojiEntry[];
|
|
1205
|
+
/** How many recent emoji the picker keeps. */
|
|
1206
|
+
declare const RECENT_EMOJI_MAX = 24;
|
|
1207
|
+
/** `emoji` first, once, the oldest dropped past `max`. */
|
|
1208
|
+
declare function pushRecentEmoji(recents: readonly string[], emoji: string, max?: number): string[];
|
|
1209
|
+
/** Where a person's recent emoji are kept: under their drafts' prefix, so `clearDrafts` removes them too. */
|
|
1210
|
+
declare function recentEmojiKey(userId: string): string;
|
|
1211
|
+
declare function loadRecentEmoji(storage: DraftStorageLike, userId: string): Promise<string[]>;
|
|
1212
|
+
declare function saveRecentEmoji(storage: DraftStorageLike, userId: string, recents: readonly string[]): Promise<void>;
|
|
1213
|
+
/** Puts `insert` in place of the selected text (or at the cursor), and where the cursor goes after it. */
|
|
1214
|
+
declare function insertAtSelection(text: string, selection: {
|
|
1215
|
+
start: number;
|
|
1216
|
+
end: number;
|
|
1217
|
+
} | null, insert: string): {
|
|
1218
|
+
text: string;
|
|
1219
|
+
cursor: number;
|
|
1220
|
+
};
|
|
1221
|
+
|
|
1222
|
+
interface ChatAccessErrorProps {
|
|
1223
|
+
onRetry: () => void;
|
|
1224
|
+
/** A retry is under way. */
|
|
1225
|
+
retrying?: boolean;
|
|
1226
|
+
testID?: string;
|
|
1227
|
+
}
|
|
1228
|
+
declare function ChatAccessError({ onRetry, retrying, testID }: ChatAccessErrorProps): react.JSX.Element;
|
|
1229
|
+
|
|
1230
|
+
/** React Native's `AppState`, as the hook uses it. */
|
|
1231
|
+
interface AppStateLike {
|
|
1232
|
+
currentState: string | null;
|
|
1233
|
+
addEventListener(type: "change", listener: (state: string) => void): {
|
|
1234
|
+
remove(): void;
|
|
1235
|
+
};
|
|
1236
|
+
}
|
|
1237
|
+
interface ChatAutoRetryOptions {
|
|
1238
|
+
/** Default `modules.netInfo`. */
|
|
1239
|
+
netInfo?: NetInfoLike;
|
|
1240
|
+
/** Default React Native's AppState. */
|
|
1241
|
+
appState?: AppStateLike;
|
|
1242
|
+
}
|
|
1243
|
+
/** Whether a NetInfo state is online: unknown counts as online, so only a real "offline" waits. */
|
|
1244
|
+
declare function isOnline(state: {
|
|
1245
|
+
isConnected: boolean | null;
|
|
1246
|
+
isInternetReachable?: boolean | null;
|
|
1247
|
+
}): boolean;
|
|
1248
|
+
declare function useChatAutoRetry({ netInfo, appState }?: ChatAutoRetryOptions): void;
|
|
1249
|
+
/** `useChatAutoRetry` as a component, for a provider tree. */
|
|
1250
|
+
declare function ChatAutoRetry(props: ChatAutoRetryOptions): null;
|
|
1251
|
+
|
|
1252
|
+
/** 8 MiB: a multiple of 256 KiB, as resumable uploads require for every chunk but the last. */
|
|
1253
|
+
declare const VIDEO_CHUNK_BYTES: number;
|
|
1254
|
+
/** What an upload reads: a file a chunk at a time, or bytes in memory. */
|
|
1255
|
+
interface ByteSource {
|
|
1256
|
+
size: number;
|
|
1257
|
+
read(from: number, to: number): Promise<ArrayBuffer>;
|
|
1258
|
+
close(): void;
|
|
1259
|
+
}
|
|
1260
|
+
/** Opens a file URI for upload: in chunks with `openFile` (expo-file-system), else read whole through fetch. */
|
|
1261
|
+
declare function fileSource(uri: string, openFile?: (uri: string) => Promise<ChunkFileLike> | ChunkFileLike, doFetch?: typeof fetch): Promise<ByteSource>;
|
|
1262
|
+
interface ChunkedPutOptions {
|
|
1263
|
+
url: string;
|
|
1264
|
+
contentType: string;
|
|
1265
|
+
source: ByteSource;
|
|
1266
|
+
fetch?: typeof fetch;
|
|
1267
|
+
chunkBytes?: number;
|
|
1268
|
+
/** Network failures retried per chunk before it fails (the person can Retry). Default 4. */
|
|
1269
|
+
retries?: number;
|
|
1270
|
+
/** Bytes the server has, after each chunk. */
|
|
1271
|
+
onProgress?: (sent: number, total: number) => void;
|
|
1272
|
+
/** Stops between requests (Cancel). */
|
|
1273
|
+
isCancelled?: () => boolean;
|
|
1274
|
+
/** Ask the server where it is before sending anything (a Retry of an upload that got partway). */
|
|
1275
|
+
resume?: boolean;
|
|
1276
|
+
/** For tests. */
|
|
1277
|
+
sleep?: (ms: number) => Promise<void>;
|
|
1278
|
+
}
|
|
1279
|
+
/** The next byte the server wants, from a 308's `Range: bytes=0-N` (none: it has nothing yet). */
|
|
1280
|
+
declare function nextOffset(range: string | null | undefined): number;
|
|
1281
|
+
/** Uploads `source` to a resumable upload URL, a chunk at a time. Resolves once the server has every byte. */
|
|
1282
|
+
declare function chunkedPut(options: ChunkedPutOptions): Promise<void>;
|
|
1283
|
+
/** A recording as the recorder hands it over. */
|
|
1284
|
+
interface RecordedVideo {
|
|
1285
|
+
uri: string;
|
|
1286
|
+
name: string;
|
|
1287
|
+
contentType: string;
|
|
1288
|
+
durationMs: number;
|
|
1289
|
+
}
|
|
1290
|
+
type VideoUploadStatus = "uploading" | "failed";
|
|
1291
|
+
/** Why a video didn't go: the tray words it (`copy`); `detail` is the error, for logs. */
|
|
1292
|
+
type VideoUploadError = "offline" | "forbidden" | "failed";
|
|
1293
|
+
/** One video on its way, shown in the thread above the composer until its message is queued. */
|
|
1294
|
+
interface VideoUploadTask {
|
|
1295
|
+
id: string;
|
|
1296
|
+
conversationId: string;
|
|
1297
|
+
/** Whose it is: the tray shows only the signed-in person's. */
|
|
1298
|
+
userId: string | null;
|
|
1299
|
+
video: RecordedVideo;
|
|
1300
|
+
status: VideoUploadStatus;
|
|
1301
|
+
/** 0 to 1. */
|
|
1302
|
+
progress: number;
|
|
1303
|
+
error: VideoUploadError | null;
|
|
1304
|
+
detail: string | null;
|
|
1305
|
+
}
|
|
1306
|
+
/** The outbox's send (`useChat().outbox.send`): the message goes in the conversation's order. */
|
|
1307
|
+
type OutboxSend = (input: Omit<SendInput, "clientId"> & {
|
|
1308
|
+
clientId?: string;
|
|
1309
|
+
}, files?: LocalFile[]) => Promise<string>;
|
|
1310
|
+
type MintedUpload = {
|
|
1311
|
+
url: string;
|
|
1312
|
+
providerId: string;
|
|
1313
|
+
provider: "mux";
|
|
1314
|
+
};
|
|
1315
|
+
interface StartVideoInput {
|
|
1316
|
+
conversationId: string;
|
|
1317
|
+
userId?: string | null;
|
|
1318
|
+
video: RecordedVideo;
|
|
1319
|
+
/** What goes with it: a reply, a thread, a topic. */
|
|
1320
|
+
extra?: Omit<SendInput, "conversationId" | "clientId" | "attachments" | "body">;
|
|
1321
|
+
send: OutboxSend;
|
|
1322
|
+
videoUpload?: ChatVideoUpload;
|
|
1323
|
+
/** The upload minted when recording started. */
|
|
1324
|
+
prepared?: Promise<MintedUpload> | null;
|
|
1325
|
+
openFile?: (uri: string) => Promise<ChunkFileLike> | ChunkFileLike;
|
|
1326
|
+
fetch?: typeof fetch;
|
|
1327
|
+
chunkBytes?: number;
|
|
1328
|
+
sleep?: (ms: number) => Promise<void>;
|
|
1329
|
+
}
|
|
1330
|
+
/** The video as a file the outbox uploads to storage (without `videoUpload`). */
|
|
1331
|
+
declare function recordedVideoFile(video: RecordedVideo): LocalFile;
|
|
1332
|
+
declare function createVideoUploads(newId?: () => string): {
|
|
1333
|
+
/** Starts sending a recording. Returns the task's id. */
|
|
1334
|
+
start(input: StartVideoInput): string;
|
|
1335
|
+
retry(id: string): void;
|
|
1336
|
+
/** Stops it and forgets it (an upload Mux never receives expires on its own). */
|
|
1337
|
+
cancel(id: string): void;
|
|
1338
|
+
/** Stops and forgets every one (call it on sign-out, beside `clearOutbox`). */
|
|
1339
|
+
cancelAll(): void;
|
|
1340
|
+
list(conversationId?: string): VideoUploadTask[];
|
|
1341
|
+
subscribe(listener: () => void): () => undefined;
|
|
1342
|
+
snapshot: () => VideoUploadTask[];
|
|
1343
|
+
};
|
|
1344
|
+
type VideoUploads = ReturnType<typeof createVideoUploads>;
|
|
1345
|
+
/** The app's uploads in flight (one for the app; the thread screen reads it). */
|
|
1346
|
+
declare const videoUploads: VideoUploads;
|
|
1347
|
+
/** The signed-in person's videos on their way to this conversation, live. */
|
|
1348
|
+
declare function useVideoUploads(conversationId: string, userId?: string | null, store?: VideoUploads): VideoUploadTask[];
|
|
1349
|
+
|
|
1350
|
+
interface ChatVideoRecorderProps {
|
|
1351
|
+
open: boolean;
|
|
1352
|
+
onClose: () => void;
|
|
1353
|
+
/** The role's limit (`recordingLimitMs(role)`). */
|
|
1354
|
+
limitMs: number;
|
|
1355
|
+
/** A take began: the moment to mint the upload, so the bytes can go as soon as it stops. */
|
|
1356
|
+
onStart?: () => void;
|
|
1357
|
+
/** About every second while recording (to tell the others: `signalRecordingVideo`). */
|
|
1358
|
+
onTick?: () => void;
|
|
1359
|
+
onRecorded: (video: RecordedVideo) => void;
|
|
1360
|
+
/** For tests: the clock. */
|
|
1361
|
+
now?: () => number;
|
|
1362
|
+
testID?: string;
|
|
1363
|
+
}
|
|
1364
|
+
/** Renders nothing without `modules.camera`. */
|
|
1365
|
+
declare function ChatVideoRecorder(props: ChatVideoRecorderProps): react.JSX.Element | null;
|
|
1366
|
+
|
|
1367
|
+
/**
|
|
1368
|
+
* @feat:chat
|
|
1369
|
+
* The video recorder's rules, without React or the camera, so they are tested
|
|
1370
|
+
* directly. One button does both gestures: a tap starts a recording that a
|
|
1371
|
+
* second tap stops (what VoiceOver and TalkBack do too), and a hold records
|
|
1372
|
+
* until the finger lifts. Stopping sends; Cancel throws the take away. The
|
|
1373
|
+
* camera flips only between takes (expo-camera stops a recording that flips).
|
|
1374
|
+
* A take the camera doesn't hand over is only that take: the camera stays
|
|
1375
|
+
* ready. A camera that fails to start offers Try again.
|
|
1376
|
+
*/
|
|
1377
|
+
type VideoRecorderPhase = "asking" | "denied" | "starting" | "ready" | "recording" | "stopping" | "failed";
|
|
1378
|
+
interface VideoRecorderState {
|
|
1379
|
+
phase: VideoRecorderPhase;
|
|
1380
|
+
facing: "front" | "back";
|
|
1381
|
+
/** When recording began (ms, the clock the events carry). */
|
|
1382
|
+
startedAt: number;
|
|
1383
|
+
elapsedMs: number;
|
|
1384
|
+
/** "hold": a finger is down and lifting it stops; "tap": recording hands-free until the next tap. */
|
|
1385
|
+
gesture: "hold" | "tap" | "stop-on-release" | null;
|
|
1386
|
+
/** The take is to be thrown away when the camera hands it over. */
|
|
1387
|
+
discard: boolean;
|
|
1388
|
+
notice: "too-short" | "take-failed" | null;
|
|
1389
|
+
/** Bumped by Try again after the camera failed: the camera view starts over. */
|
|
1390
|
+
cameraKey: number;
|
|
1391
|
+
}
|
|
1392
|
+
type VideoRecorderEvent = {
|
|
1393
|
+
type: "permissions";
|
|
1394
|
+
granted: boolean;
|
|
1395
|
+
} | {
|
|
1396
|
+
type: "camera-ready";
|
|
1397
|
+
} | {
|
|
1398
|
+
type: "camera-failed";
|
|
1399
|
+
} | {
|
|
1400
|
+
type: "press";
|
|
1401
|
+
at: number;
|
|
1402
|
+
} | {
|
|
1403
|
+
type: "release";
|
|
1404
|
+
at: number;
|
|
1405
|
+
} | {
|
|
1406
|
+
type: "tick";
|
|
1407
|
+
at: number;
|
|
1408
|
+
} | {
|
|
1409
|
+
type: "recorded";
|
|
1410
|
+
at: number;
|
|
1411
|
+
uri: string | null;
|
|
1412
|
+
} | {
|
|
1413
|
+
type: "flip";
|
|
1414
|
+
} | {
|
|
1415
|
+
type: "retry";
|
|
1416
|
+
} | {
|
|
1417
|
+
type: "cancel";
|
|
1418
|
+
};
|
|
1419
|
+
type VideoRecorderEffect = {
|
|
1420
|
+
type: "start";
|
|
1421
|
+
} | {
|
|
1422
|
+
type: "stop";
|
|
1423
|
+
} | {
|
|
1424
|
+
type: "send";
|
|
1425
|
+
uri: string;
|
|
1426
|
+
durationMs: number;
|
|
1427
|
+
} | {
|
|
1428
|
+
type: "close";
|
|
1429
|
+
} | {
|
|
1430
|
+
type: "haptic";
|
|
1431
|
+
event: "record-start" | "send" | "record-cancel" | "error";
|
|
1432
|
+
};
|
|
1433
|
+
interface VideoRecorderConfig {
|
|
1434
|
+
limitMs: number;
|
|
1435
|
+
/** A press held this long is a hold; shorter is a tap. */
|
|
1436
|
+
holdMs?: number;
|
|
1437
|
+
/** Shorter takes aren't sent. */
|
|
1438
|
+
minMs?: number;
|
|
1439
|
+
}
|
|
1440
|
+
declare const VIDEO_HOLD_MS = 350;
|
|
1441
|
+
declare const VIDEO_MIN_MS = 1000;
|
|
1442
|
+
declare const initialVideoRecorder: (facing?: "front" | "back") => VideoRecorderState;
|
|
1443
|
+
type Step$1 = {
|
|
1444
|
+
state: VideoRecorderState;
|
|
1445
|
+
effects: VideoRecorderEffect[];
|
|
1446
|
+
};
|
|
1447
|
+
declare function videoRecorderStep(state: VideoRecorderState, event: VideoRecorderEvent, config: VideoRecorderConfig): Step$1;
|
|
1448
|
+
/** The kind of file a recording is, from its URI: .mov on iOS, .mp4 on Android. */
|
|
1449
|
+
declare function recordingType(uri: string): {
|
|
1450
|
+
contentType: string;
|
|
1451
|
+
ext: string;
|
|
1452
|
+
};
|
|
1453
|
+
|
|
1454
|
+
declare const muxStreamUrl: (playbackId: string) => string;
|
|
1455
|
+
declare const muxPosterUrl: (playbackId: string, width?: number) => string;
|
|
1456
|
+
interface ChatVideoPlayerProps {
|
|
1457
|
+
/** Stable id (the attachment's), for one-at-a-time and the bubble's actions. */
|
|
1458
|
+
id: string;
|
|
1459
|
+
/** A stored video's signed link, or the file on the phone while it uploads. */
|
|
1460
|
+
uri: string | null;
|
|
1461
|
+
/** Mux's playback id: streamed as HLS. */
|
|
1462
|
+
playbackId?: string | null;
|
|
1463
|
+
/** The attachment's poster (a signed link). */
|
|
1464
|
+
posterUri?: string | null;
|
|
1465
|
+
provider?: string | null;
|
|
1466
|
+
status?: "pending" | "ready" | "failed" | null;
|
|
1467
|
+
durationMs: number | null;
|
|
1468
|
+
width?: number | null;
|
|
1469
|
+
height?: number | null;
|
|
1470
|
+
/** Still uploading from this phone. */
|
|
1471
|
+
uploading?: boolean;
|
|
1472
|
+
testID?: string;
|
|
1473
|
+
}
|
|
1474
|
+
type ChatVideoState = "failed" | "processing" | "loading" | "playable";
|
|
1475
|
+
/** What the player can show: a hosted video waits for its provider, a stored one for its link. */
|
|
1476
|
+
declare function videoState(p: Pick<ChatVideoPlayerProps, "uri" | "playbackId" | "provider" | "status">): ChatVideoState;
|
|
1477
|
+
/** What expo-video loads: HLS for a playback id, else the file. */
|
|
1478
|
+
declare function videoSource(p: Pick<ChatVideoPlayerProps, "uri" | "playbackId">): {
|
|
1479
|
+
uri: string;
|
|
1480
|
+
contentType: "hls" | "auto";
|
|
1481
|
+
} | null;
|
|
1482
|
+
/** Which of these videos is playing, or null (re-renders only when that answer changes). */
|
|
1483
|
+
declare function usePlayingVideo(ids: readonly string[]): string | null;
|
|
1484
|
+
/** Mutes or unmutes the video with this id while it plays. */
|
|
1485
|
+
declare function toggleVideoMute(id: string): boolean;
|
|
1486
|
+
/** Moves the playing video with this id to its next speed. */
|
|
1487
|
+
declare function cycleVideoRate(id: string): boolean;
|
|
1488
|
+
/** Plays or pauses the video with this id; false when none with it is on screen. */
|
|
1489
|
+
declare function toggleVideo(id: string): boolean;
|
|
1490
|
+
/** Opens the video with this id full screen (starting it); false when none is on screen. */
|
|
1491
|
+
declare function fullScreenVideo(id: string): boolean;
|
|
1492
|
+
/** Whether the video with this id is playing. */
|
|
1493
|
+
declare function isVideoPlaying(id: string): boolean;
|
|
1494
|
+
/** The bubble's video. Renders nothing without `modules.video` (the bubble shows a row instead). */
|
|
1495
|
+
declare function ChatVideoPlayer(props: ChatVideoPlayerProps): react.JSX.Element | null;
|
|
1496
|
+
|
|
1497
|
+
interface VideoUploadTrayProps {
|
|
1498
|
+
conversationId: string;
|
|
1499
|
+
/** The signed-in person: the tray shows only their uploads. */
|
|
1500
|
+
userId?: string | null;
|
|
1501
|
+
uploads?: VideoUploads;
|
|
1502
|
+
testID?: string;
|
|
1503
|
+
}
|
|
1504
|
+
declare function VideoUploadTray({ conversationId, userId, uploads, testID }: VideoUploadTrayProps): react.JSX.Element | null;
|
|
1505
|
+
|
|
1506
|
+
/** The site a link is on, without `www.`. */
|
|
1507
|
+
declare function linkHost(url: string): string;
|
|
1508
|
+
/** The preview a bubble shows: the first one the server fetched for a link in its text, https only. */
|
|
1509
|
+
declare function previewToShow(previews: readonly ChatLinkPreview[] | undefined): ChatLinkPreview | null;
|
|
1510
|
+
/** The messages to ask previews for: shown (not deleted, not hidden by moderation), with a link. */
|
|
1511
|
+
declare function linkPreviewIds(messages: readonly ChatMessage[]): string[];
|
|
1512
|
+
/** Opens a link from chat: https only, in the in-app browser when there is one. */
|
|
1513
|
+
declare function openChatLink(url: string, webBrowser?: WebBrowserLike): Promise<boolean>;
|
|
1514
|
+
interface ChatLinkPreviewCardProps {
|
|
1515
|
+
preview: ChatLinkPreview;
|
|
1516
|
+
/** On the tint bubble. */
|
|
1517
|
+
mine?: boolean;
|
|
1518
|
+
/** Show the og:image. Off by default: fetching it tells the site the reader's address. */
|
|
1519
|
+
images?: boolean;
|
|
1520
|
+
testID?: string;
|
|
1521
|
+
}
|
|
1522
|
+
declare function ChatLinkPreviewCard({ preview, mine, images, testID }: ChatLinkPreviewCardProps): react.JSX.Element | null;
|
|
1523
|
+
|
|
1524
|
+
/**
|
|
1525
|
+
* @feat:chat
|
|
1526
|
+
* The voice recorder as a state machine, apart from expo-audio so it can be
|
|
1527
|
+
* tested: hold the mic to record, slide left to cancel, slide up to lock and
|
|
1528
|
+
* record hands-free, release (or tap send when locked) to send. A recording
|
|
1529
|
+
* shorter than `minMs` is dropped, and one that reaches the role's limit is
|
|
1530
|
+
* sent. Each step returns the effects the hook carries out.
|
|
1531
|
+
*/
|
|
1532
|
+
|
|
1533
|
+
type RecorderPhase = "idle" | "starting" | "recording" | "finishing";
|
|
1534
|
+
type RecorderNotice = "denied" | "too-short" | "error" | "interrupted";
|
|
1535
|
+
interface RecorderState {
|
|
1536
|
+
phase: RecorderPhase;
|
|
1537
|
+
/** Hands-free: released without stopping. */
|
|
1538
|
+
locked: boolean;
|
|
1539
|
+
/** The finger has slid far enough left that letting go cancels. */
|
|
1540
|
+
cancelArmed: boolean;
|
|
1541
|
+
/** Let go while the microphone was still starting. */
|
|
1542
|
+
releasedEarly: boolean;
|
|
1543
|
+
durationMs: number;
|
|
1544
|
+
/** Level readings (dB) since the start, for the live waveform and the sent one. */
|
|
1545
|
+
levels: number[];
|
|
1546
|
+
/**
|
|
1547
|
+
* Why the last attempt ended without a voice note: "denied" (no microphone), "too-short",
|
|
1548
|
+
* "error" (the recording couldn't be saved), "interrupted" (a call, Siri, another app or the
|
|
1549
|
+
* app going to the background took the microphone; what was recorded is thrown away).
|
|
1550
|
+
*/
|
|
1551
|
+
notice: RecorderNotice | null;
|
|
1552
|
+
}
|
|
1553
|
+
declare const idleRecorder: RecorderState;
|
|
1554
|
+
type RecorderEvent = {
|
|
1555
|
+
type: "press";
|
|
1556
|
+
} | {
|
|
1557
|
+
type: "ready";
|
|
1558
|
+
} | {
|
|
1559
|
+
type: "failed";
|
|
1560
|
+
reason: "denied" | "error";
|
|
1561
|
+
} | {
|
|
1562
|
+
type: "move";
|
|
1563
|
+
dx: number;
|
|
1564
|
+
dy: number;
|
|
1565
|
+
} | {
|
|
1566
|
+
type: "release";
|
|
1567
|
+
} | {
|
|
1568
|
+
type: "send";
|
|
1569
|
+
} | {
|
|
1570
|
+
type: "cancel";
|
|
1571
|
+
} | {
|
|
1572
|
+
type: "tick";
|
|
1573
|
+
durationMs: number;
|
|
1574
|
+
meteringDb?: number;
|
|
1575
|
+
}
|
|
1576
|
+
/** Something else took the microphone (a call, Siri, the app went to the background). */
|
|
1577
|
+
| {
|
|
1578
|
+
type: "interrupted";
|
|
1579
|
+
}
|
|
1580
|
+
/** The stop is done: `ok` false when no voice note came of it (the stop threw, no file). */
|
|
1581
|
+
| {
|
|
1582
|
+
type: "finished";
|
|
1583
|
+
ok?: boolean;
|
|
1584
|
+
};
|
|
1585
|
+
type RecorderEffect = {
|
|
1586
|
+
type: "prepare";
|
|
1587
|
+
} | {
|
|
1588
|
+
type: "record";
|
|
1589
|
+
}
|
|
1590
|
+
/** Stop and send what was recorded. */
|
|
1591
|
+
| {
|
|
1592
|
+
type: "finish";
|
|
1593
|
+
}
|
|
1594
|
+
/** Stop and throw it away. */
|
|
1595
|
+
| {
|
|
1596
|
+
type: "discard";
|
|
1597
|
+
} | {
|
|
1598
|
+
type: "haptic";
|
|
1599
|
+
event: HapticEvent;
|
|
1600
|
+
}
|
|
1601
|
+
/** Tell the others you are recording. */
|
|
1602
|
+
| {
|
|
1603
|
+
type: "signal";
|
|
1604
|
+
};
|
|
1605
|
+
interface RecorderConfig {
|
|
1606
|
+
/** The longest recording (recordingLimitMs(role)). */
|
|
1607
|
+
limitMs: number;
|
|
1608
|
+
/** Shorter than this is a tap, not a voice note. Default 700 ms. */
|
|
1609
|
+
minMs?: number;
|
|
1610
|
+
/** Points left that cancel. Default 120. */
|
|
1611
|
+
cancelDistance?: number;
|
|
1612
|
+
/** Points up that lock. Default 80. */
|
|
1613
|
+
lockDistance?: number;
|
|
1614
|
+
/** Most level readings kept (one every ~100 ms). Default 3000 (5 minutes). */
|
|
1615
|
+
maxLevels?: number;
|
|
1616
|
+
}
|
|
1617
|
+
interface Step {
|
|
1618
|
+
state: RecorderState;
|
|
1619
|
+
effects: RecorderEffect[];
|
|
1620
|
+
}
|
|
1621
|
+
declare function recorderStep(state: RecorderState, event: RecorderEvent, config: RecorderConfig): Step;
|
|
1622
|
+
/** The last `count` levels as bar heights 0–1, for the live waveform while recording. */
|
|
1623
|
+
declare function liveBars(levelsDb: readonly number[], count?: number): number[];
|
|
1624
|
+
|
|
1625
|
+
interface UseVoiceRecorderOptions extends Omit<RecorderConfig, "limitMs"> {
|
|
1626
|
+
limitMs: number;
|
|
1627
|
+
onRecorded: (file: LocalFile) => void;
|
|
1628
|
+
/** `useConversation().signalRecording` (throttled there). */
|
|
1629
|
+
signalRecording?: () => void;
|
|
1630
|
+
haptics?: HapticsLike;
|
|
1631
|
+
/** How often the level is read. Default 100 ms. */
|
|
1632
|
+
intervalMs?: number;
|
|
1633
|
+
}
|
|
1634
|
+
/** The voice note a finished recording becomes. */
|
|
1635
|
+
declare function voiceNoteFile(uri: string, durationMs: number, levelsDb: readonly number[]): LocalFile;
|
|
1636
|
+
declare function useVoiceRecorder(audio: ExpoAudioLike, options: UseVoiceRecorderOptions): {
|
|
1637
|
+
state: RecorderState;
|
|
1638
|
+
dispatch: (event: RecorderEvent) => void;
|
|
1639
|
+
};
|
|
1640
|
+
|
|
1641
|
+
/** Supabase requires 6 MB chunks for TUS (every chunk but the last). */
|
|
1642
|
+
declare const TUS_CHUNK_BYTES: number;
|
|
1643
|
+
interface TusUploadOptions {
|
|
1644
|
+
/** The project URL (`https://<ref>.supabase.co`, or the local stack's). */
|
|
1645
|
+
supabaseUrl: string;
|
|
1646
|
+
/** The signed-in user's id (auth.users), the second folder of the path. */
|
|
1647
|
+
userId: string | (() => string);
|
|
1648
|
+
/** The signed-in user's access token (`(await supabase.auth.getSession()).data.session?.access_token`). */
|
|
1649
|
+
getAccessToken: () => Promise<string | null | undefined>;
|
|
1650
|
+
/** The publishable key, sent as `apikey` (the API gateway asks for it on hosted projects). */
|
|
1651
|
+
apikey?: string;
|
|
1652
|
+
/**
|
|
1653
|
+
* Opens a file URI for reading one chunk at a time (expo-file-system's `new File(uri).open()`),
|
|
1654
|
+
* so a big video is never read whole into memory. Without it, `readFile` reads the whole file.
|
|
1655
|
+
*/
|
|
1656
|
+
openFile?: (uri: string) => Promise<TusFileHandle> | TusFileHandle;
|
|
1657
|
+
/** Reads a file URI into bytes, whole: used without `openFile`. Default `fetch(uri).arrayBuffer()`. */
|
|
1658
|
+
readFile?: (uri: string) => Promise<ArrayBuffer>;
|
|
1659
|
+
fetch?: typeof fetch;
|
|
1660
|
+
chunkBytes?: number;
|
|
1661
|
+
/** Network failures retried per chunk before the send fails (it can be retried). Default 3. */
|
|
1662
|
+
retries?: number;
|
|
1663
|
+
/** A uuid for each file's name. Default core's `randomId` (crypto.randomUUID where there is one, else getRandomValues). */
|
|
1664
|
+
newId?: () => string;
|
|
1665
|
+
bucket?: string;
|
|
1666
|
+
}
|
|
1667
|
+
/** An open file, read a chunk at a time. */
|
|
1668
|
+
interface TusFileHandle {
|
|
1669
|
+
/** Bytes in the file. */
|
|
1670
|
+
size: number;
|
|
1671
|
+
/** Up to `length` bytes from `offset`. */
|
|
1672
|
+
read(offset: number, length: number): Promise<ArrayBuffer | Uint8Array>;
|
|
1673
|
+
close?(): void;
|
|
1674
|
+
}
|
|
1675
|
+
/** Base64 of a UTF-8 string, for TUS's Upload-Metadata (no `btoa`, which isn't on every Hermes build). */
|
|
1676
|
+
declare function base64Utf8(text: string): string;
|
|
1677
|
+
declare function createTusUpload(options: TusUploadOptions): (conversationId: string, file: LocalFile) => Promise<string>;
|
|
1678
|
+
|
|
1679
|
+
interface ChatIconButtonProps {
|
|
1680
|
+
icon: NativeIconName;
|
|
1681
|
+
/** Read by screen readers (the button has no text). */
|
|
1682
|
+
label: string;
|
|
1683
|
+
onPress: () => void;
|
|
1684
|
+
onLongPress?: () => void;
|
|
1685
|
+
disabled?: boolean;
|
|
1686
|
+
/** The theme's tint by default. */
|
|
1687
|
+
color?: unknown;
|
|
1688
|
+
size?: number;
|
|
1689
|
+
testID?: string;
|
|
1690
|
+
}
|
|
1691
|
+
declare function ChatIconButton({ icon, label, onPress, onLongPress, disabled, color, size, testID }: ChatIconButtonProps): react.JSX.Element;
|
|
1692
|
+
|
|
1693
|
+
/** Owners and admins add, rename, close and reopen topics. */
|
|
1694
|
+
declare function canManageTopics(role: ChatRole | null | undefined): boolean;
|
|
1695
|
+
/** A closed topic takes posts only from owners, admins and observers. */
|
|
1696
|
+
declare function topicClosedFor(topic: ChatTopic | null | undefined, role: ChatRole | null | undefined): boolean;
|
|
1697
|
+
type TopicMenuAction = "new" | "rename" | "close" | "reopen";
|
|
1698
|
+
/** What the options sheet offers: always New; Rename and Close or Reopen when a topic is shown. */
|
|
1699
|
+
declare function topicMenuFor(current: ChatTopic | null | undefined): TopicMenuAction[];
|
|
1700
|
+
/**
|
|
1701
|
+
* Checks a topic's name: its key, or why it can't be saved. Renaming keeps the key (messages
|
|
1702
|
+
* carry it), so only a new topic can clash with another's key.
|
|
1703
|
+
*/
|
|
1704
|
+
declare function checkTopicName(title: string, topics: readonly ChatTopic[], renaming?: string | null): {
|
|
1705
|
+
key: string;
|
|
1706
|
+
} | {
|
|
1707
|
+
error: "empty" | "exists";
|
|
1708
|
+
};
|
|
1709
|
+
interface ChatTopicBarProps {
|
|
1710
|
+
topics: ChatTopic[];
|
|
1711
|
+
/** The topic shown; null for "All". */
|
|
1712
|
+
value: string | null;
|
|
1713
|
+
onValueChange: (topic: string | null) => void;
|
|
1714
|
+
/** Owners and admins (`canManageTopics(role)`): the options button. */
|
|
1715
|
+
canManage?: boolean;
|
|
1716
|
+
/** Saves a topic (`useTopics().setTopic`). */
|
|
1717
|
+
onSetTopic?: (topic: string, input: TopicInput) => Promise<void>;
|
|
1718
|
+
testID?: string;
|
|
1719
|
+
}
|
|
1720
|
+
declare function ChatTopicBar({ topics, value, onValueChange, canManage, onSetTopic, testID }: ChatTopicBarProps): react.JSX.Element;
|
|
1721
|
+
|
|
1722
|
+
/** An hour from now, on the next five minutes: where the custom picker starts. */
|
|
1723
|
+
declare function defaultScheduleAt(now?: Date): Date;
|
|
1724
|
+
/** `base` on the day of `day` (its time kept). */
|
|
1725
|
+
declare function withDay(base: Date, day: Date): Date;
|
|
1726
|
+
/** `base` at the time of `time` (its day kept). */
|
|
1727
|
+
declare function withTime(base: Date, time: Date): Date;
|
|
1728
|
+
/**
|
|
1729
|
+
* What the composer holds, as a message to schedule, or why it can't be: nothing to send, or
|
|
1730
|
+
* files (a native file is a URI, and `useScheduled` uploads bytes only).
|
|
1731
|
+
*/
|
|
1732
|
+
declare function scheduleInputFrom(state: ComposerState, opts: {
|
|
1733
|
+
conversationId: string;
|
|
1734
|
+
topic?: string | null;
|
|
1735
|
+
at: Date;
|
|
1736
|
+
}): {
|
|
1737
|
+
input: Omit<ScheduleInput, "clientId">;
|
|
1738
|
+
} | {
|
|
1739
|
+
error: "empty" | "files";
|
|
1740
|
+
};
|
|
1741
|
+
/**
|
|
1742
|
+
* Scheduled send from a thread's composer: `request` (Send held) checks there's text and no
|
|
1743
|
+
* files and opens the sheet; `scheduleAt` schedules what's written, then empties the composer
|
|
1744
|
+
* if it still holds just that. Pass `enabled: false` where scheduling isn't offered.
|
|
1745
|
+
*/
|
|
1746
|
+
declare function useScheduleSend(opts: {
|
|
1747
|
+
conversationId: string;
|
|
1748
|
+
topic?: string | null;
|
|
1749
|
+
composer: ChatComposerHandle;
|
|
1750
|
+
enabled: boolean;
|
|
1751
|
+
}): {
|
|
1752
|
+
open: boolean;
|
|
1753
|
+
setOpen: react.Dispatch<react.SetStateAction<boolean>>;
|
|
1754
|
+
request: () => void;
|
|
1755
|
+
scheduleAt: (at: Date) => Promise<void>;
|
|
1756
|
+
};
|
|
1757
|
+
interface ChatScheduleSheetProps {
|
|
1758
|
+
open: boolean;
|
|
1759
|
+
onOpenChange: (open: boolean) => void;
|
|
1760
|
+
/** The time chosen; the sheet closes first. */
|
|
1761
|
+
onSchedule: (at: Date) => void;
|
|
1762
|
+
/** For tests and demos. */
|
|
1763
|
+
now?: Date;
|
|
1764
|
+
testID?: string;
|
|
1765
|
+
}
|
|
1766
|
+
declare function ChatScheduleSheet({ open, onOpenChange, onSchedule, now, testID }: ChatScheduleSheetProps): react.JSX.Element;
|
|
1767
|
+
interface ChatScheduledListProps {
|
|
1768
|
+
/** One conversation's; all of yours without it. */
|
|
1769
|
+
conversationId?: string | null;
|
|
1770
|
+
/** Names a conversation in the all-conversations list. */
|
|
1771
|
+
conversationTitle?: (conversationId: string) => string;
|
|
1772
|
+
testID?: string;
|
|
1773
|
+
}
|
|
1774
|
+
/** Your scheduled messages, soonest first, each with when it goes and Cancel. */
|
|
1775
|
+
declare function ChatScheduledList({ conversationId, conversationTitle, testID }: ChatScheduledListProps): react.JSX.Element;
|
|
1776
|
+
interface ChatScheduledBarProps {
|
|
1777
|
+
conversationId: string;
|
|
1778
|
+
testID?: string;
|
|
1779
|
+
}
|
|
1780
|
+
/** "2 scheduled messages" over the composer when there are any, opening their list. */
|
|
1781
|
+
declare function ChatScheduledBar({ conversationId, testID }: ChatScheduledBarProps): react.JSX.Element | null;
|
|
1782
|
+
|
|
1783
|
+
/**
|
|
1784
|
+
* The away line for a conversation, or null when everyone is around: the other person in a
|
|
1785
|
+
* two-person conversation; in a group, everyone (but you) away or outside their hours.
|
|
1786
|
+
*/
|
|
1787
|
+
declare function awayLineFor(members: readonly Pick<ChatMember, "userId" | "role">[], me: string, forUser: (userId: string) => ChatAvailability | null, nameOf: (userId: string) => string, now?: Date): string | null;
|
|
1788
|
+
/** `awayLineFor` over the conversation's members, live (`useAvailability`), re-read each minute. */
|
|
1789
|
+
declare function useAwayLine(conversationId: string | null | undefined, members: readonly Pick<ChatMember, "userId" | "role">[], me: string, nameOf: (userId: string) => string): string | null;
|
|
1790
|
+
interface ChatAwayLineProps {
|
|
1791
|
+
text: string | null;
|
|
1792
|
+
testID?: string;
|
|
1793
|
+
}
|
|
1794
|
+
/** The away line, as a quiet strip under the header; nothing when `text` is null. */
|
|
1795
|
+
declare function ChatAwayLine({ text, testID }: ChatAwayLineProps): react.JSX.Element | null;
|
|
1796
|
+
interface DayHours {
|
|
1797
|
+
on: boolean;
|
|
1798
|
+
from: string;
|
|
1799
|
+
to: string;
|
|
1800
|
+
}
|
|
1801
|
+
type WeekHours = Record<Weekday, DayHours>;
|
|
1802
|
+
/** Monday to Friday, 9 to 5: where a first setup starts. */
|
|
1803
|
+
declare function defaultWeek(): WeekHours;
|
|
1804
|
+
/** Each day's first range from saved hours (a day with none is off). */
|
|
1805
|
+
declare function weekFromHours(hours: WorkingHours | null | undefined): WeekHours;
|
|
1806
|
+
/** The hours to save: each day that's on, its range first, then any extra ranges saved elsewhere. */
|
|
1807
|
+
declare function hoursFromWeek(week: WeekHours, saved: WorkingHours | null | undefined): WorkingHours;
|
|
1808
|
+
/** A day that's on with the same start and end (the database refuses it). */
|
|
1809
|
+
declare function sameTimeDays(week: WeekHours): Weekday[];
|
|
1810
|
+
/** "09:30" as a Date today at that time (for a time picker), and back. */
|
|
1811
|
+
declare function clockToDate(hhmm: string, base?: Date): Date;
|
|
1812
|
+
declare function dateToClock(d: Date): string;
|
|
1813
|
+
interface AvailabilityForm {
|
|
1814
|
+
message: string;
|
|
1815
|
+
until: Date | null;
|
|
1816
|
+
zone: string;
|
|
1817
|
+
useHours: boolean;
|
|
1818
|
+
week: WeekHours;
|
|
1819
|
+
}
|
|
1820
|
+
declare function formFrom(mine: ChatAvailability | null, deviceZone: string): AvailabilityForm;
|
|
1821
|
+
/** What `setAvailability` gets from the form. */
|
|
1822
|
+
declare function availabilityInput(form: AvailabilityForm, saved: ChatAvailability | null): AvailabilityInput;
|
|
1823
|
+
/** Every IANA zone the engine knows (Hermes has `Intl.supportedValuesOf`), else a short list. */
|
|
1824
|
+
declare function timeZones(): string[];
|
|
1825
|
+
declare function deviceTimeZone(): string;
|
|
1826
|
+
interface ChatAvailabilityScreenProps {
|
|
1827
|
+
/** Called after a save (go back, say). */
|
|
1828
|
+
onSaved?: () => void;
|
|
1829
|
+
testID?: string;
|
|
1830
|
+
}
|
|
1831
|
+
/**
|
|
1832
|
+
* Your availability, as a Settings sub-page: an away message and, optionally, until when;
|
|
1833
|
+
* working hours per weekday (one range a day here; extra ranges saved elsewhere are kept for
|
|
1834
|
+
* the days you don't turn off); the time zone they're read in. Save at the bottom.
|
|
1835
|
+
*/
|
|
1836
|
+
declare function ChatAvailabilityScreen({ onSaved, testID }: ChatAvailabilityScreenProps): react.JSX.Element;
|
|
1837
|
+
|
|
1838
|
+
/** What a broadcast's toast says: all reached, one, or some of them. */
|
|
1839
|
+
declare function broadcastOutcome(sent: number, picked: number, copy: {
|
|
1840
|
+
broadcastSent: string;
|
|
1841
|
+
broadcastSentOne: string;
|
|
1842
|
+
broadcastSentSome: string;
|
|
1843
|
+
}): {
|
|
1844
|
+
title: string;
|
|
1845
|
+
partial: boolean;
|
|
1846
|
+
};
|
|
1847
|
+
/** Rows to pick from: the filter's, named as the inbox names them, in name order, matching the query. */
|
|
1848
|
+
declare function broadcastRows(items: readonly ChatInboxItem[], opts: {
|
|
1849
|
+
filter?: (item: ChatInboxItem) => boolean;
|
|
1850
|
+
person: (id: string | null) => ChatPerson | undefined;
|
|
1851
|
+
untitled: string;
|
|
1852
|
+
query?: string;
|
|
1853
|
+
}): {
|
|
1854
|
+
item: ChatInboxItem;
|
|
1855
|
+
title: string;
|
|
1856
|
+
avatarUrl: string | null;
|
|
1857
|
+
}[];
|
|
1858
|
+
interface ChatBroadcastSheetProps {
|
|
1859
|
+
open: boolean;
|
|
1860
|
+
onOpenChange: (open: boolean) => void;
|
|
1861
|
+
/** Only this kind of conversation (`direct`, say). */
|
|
1862
|
+
kind?: string;
|
|
1863
|
+
/** Which inbox rows may be picked; every one by default. */
|
|
1864
|
+
filter?: (item: ChatInboxItem) => boolean;
|
|
1865
|
+
/** Called with how many it went to, after a send. */
|
|
1866
|
+
onSent?: (sent: number, picked: number) => void;
|
|
1867
|
+
testID?: string;
|
|
1868
|
+
}
|
|
1869
|
+
declare function ChatBroadcastSheet({ open, onOpenChange, kind, filter, onSent, testID }: ChatBroadcastSheetProps): react.JSX.Element;
|
|
1870
|
+
|
|
1871
|
+
/** A snippet with its « » marks drawn highlighted, inside one Text (so it wraps as one). */
|
|
1872
|
+
declare function SearchSnippet({ snippet, numberOfLines, testID }: {
|
|
1873
|
+
snippet: string;
|
|
1874
|
+
numberOfLines?: number;
|
|
1875
|
+
testID?: string;
|
|
1876
|
+
}): react.JSX.Element;
|
|
1877
|
+
/** The snippet without its marks, for a screen reader. */
|
|
1878
|
+
declare function snippetText(snippet: string): string;
|
|
1879
|
+
interface ChatSearchScreenProps {
|
|
1880
|
+
/** Search one conversation; all of yours without it. */
|
|
1881
|
+
conversationId?: string | null;
|
|
1882
|
+
/** Called with the chosen result (open the thread at it). */
|
|
1883
|
+
onOpen: (hit: ChatSearchHit) => void;
|
|
1884
|
+
/** Names a conversation on results across conversations; the inbox's names by default. */
|
|
1885
|
+
conversationTitle?: (conversationId: string) => string;
|
|
1886
|
+
debounceMs?: number;
|
|
1887
|
+
/** The clock, for tests. */
|
|
1888
|
+
now?: () => Date;
|
|
1889
|
+
testID?: string;
|
|
1890
|
+
}
|
|
1891
|
+
declare function ChatSearchScreen({ conversationId, onOpen, conversationTitle, debounceMs, now, testID }: ChatSearchScreenProps): react.JSX.Element;
|
|
1892
|
+
|
|
1893
|
+
type ModerationNote = "hidden-mine" | "hidden" | "flagged";
|
|
1894
|
+
/** The note a bubble shows, or null. Deleted messages carry none. */
|
|
1895
|
+
declare function moderationNote(message: Pick<ChatMessage, "moderation" | "deletedAt"> | null | undefined, opts: {
|
|
1896
|
+
mine: boolean;
|
|
1897
|
+
observer: boolean;
|
|
1898
|
+
}): ModerationNote | null;
|
|
1899
|
+
/** The note's words (for the bubble's accessibility label too). */
|
|
1900
|
+
declare function moderationText(note: ModerationNote, copy: {
|
|
1901
|
+
moderationHidden: string;
|
|
1902
|
+
moderationHiddenMine: string;
|
|
1903
|
+
moderationFlagged: string;
|
|
1904
|
+
}): string;
|
|
1905
|
+
declare function ModerationMarker({ note, mine, testID }: {
|
|
1906
|
+
note: ModerationNote;
|
|
1907
|
+
mine: boolean;
|
|
1908
|
+
testID?: string;
|
|
1909
|
+
}): react.JSX.Element;
|
|
1910
|
+
|
|
1911
|
+
export { AT_BOTTOM_POINTS, type AppStateLike, type AudioPlayerLike, type AudioRecorderLike, type AudioStatusLike, type AvailabilityForm, type BubbleAttachment, type BuildRowsOptions, type ByteSource, type CameraViewHandleLike, ChatAccessError, type ChatAccessErrorProps, ChatAttachmentCard, type ChatAttachmentCardProps, ChatAutoRetry, type ChatAutoRetryOptions, ChatAvailabilityScreen, type ChatAvailabilityScreenProps, ChatAwayLine, type ChatAwayLineProps, ChatBroadcastSheet, type ChatBroadcastSheetProps, ChatComposer, type ChatComposerHandle, type ChatComposerProps, ChatEmojiPicker, type ChatEmojiPickerProps, ChatEmojiPickerSheet, type ChatEmojiPickerSheetProps, ChatIconButton, type ChatIconButtonProps, ChatInboxScreen, type ChatInboxScreenProps, ChatLinkPreviewCard, type ChatLinkPreviewCardProps, ChatMediaViewer, type ChatMediaViewerProps, type ChatNativeContextValue, type ChatNativeCopy, type ChatNativeCopyOverrides, type ChatNativeModules, ChatNativeProvider, type ChatNativeProviderProps, type ChatOrganiseCopy, type ChatPerson, type ChatPhoto, ChatPhotoViewer, type ChatPhotoViewerProps, ChatScheduleSheet, type ChatScheduleSheetProps, ChatScheduledBar, type ChatScheduledBarProps, ChatScheduledList, type ChatScheduledListProps, ChatSearchScreen, type ChatSearchScreenProps, ChatStartButton, type ChatStartPerson, ChatStartSheet, type ChatStartSheetProps, ChatThreadScreen, type ChatThreadScreenProps, type ChatThreadStackOptionsProps, ChatTopicBar, type ChatTopicBarProps, ChatVideoPlayer, type ChatVideoPlayerProps, ChatVideoRecorder, type ChatVideoRecorderProps, type ChatVideoState, type ChunkFileLike, type ChunkedPutOptions, type ClipboardLike, type ComposerAction, type ComposerAttachment, type ComposerDraft, type ComposerState, DISMISS_DISTANCE, DISMISS_VELOCITY, type DayHours, type DraftStorageLike, EMOJI_CATEGORIES, type EmojiCategoryId, type EmojiEntry, type ExpoAudioLike, type ExpoCameraLike, type ExpoVideoLike, type HapticEvent, type HapticsLike, type ImagePickerLike, type JumpTarget, type KeyboardAvoidingLike, MAX_ZOOM, type MediaLibraryLike, type MessageActionContext, type MessageActionId, MessageActionsSheet, type MessageActionsSheetProps, MessageAttachments, MessageBubble, type MessageBubbleProps, type MintedUpload, ModerationMarker, type ModerationNote, type NetInfoLike, type OpenAttachment, type OutboxSend, type PermissionLike, type PickedAssetLike, type PickerResultLike, QUICK_REACTIONS, RECENT_EMOJI_MAX, type RecordedVideo, type RecorderConfig, type RecorderEffect, type RecorderEvent, type RecorderNotice, type RecorderPhase, type RecorderState, type RecorderStateLike, type ReplyTarget, SearchSnippet, type SharingLike, type StartVideoInput, TUS_CHUNK_BYTES, type ThreadRow, type TopicMenuAction, type TusUploadOptions, type UseJumpOptions, type UseVoiceRecorderOptions, VIDEO_CHUNK_BYTES, VIDEO_HOLD_MS, VIDEO_MIN_MS, type VideoPlayerLike, type VideoRecorderEffect, type VideoRecorderEvent, type VideoRecorderPhase, type VideoRecorderState, type VideoUploadError, type VideoUploadTask, VideoUploadTray, type VideoUploadTrayProps, type VideoUploads, type VideoViewHandleLike, VoiceNotePlayer, type VoiceNotePlayerProps, type WebBrowserLike, type WeekHours, activePlaybackId, attachmentPaths, availabilityInput, awayLineFor, barsThatFit, base64ToBytes, base64Utf8, broadcastOutcome, broadcastRows, buildThreadRows, canManageTopics, canReactTo, canSend, cardAction, chatNativeCopy, chatOrganiseCopy, chatThreadStackOptions, checkTopicName, chunkedPut, claimPlayback, clearDrafts, clockToDate, composerDisabledReason, composerReducer, createDraftSaver, createTusUpload, createVideoUploads, cycleVideoRate, cycleVoiceNoteRate, dateToClock, defaultScheduleAt, defaultWeek, deviceTimeZone, displayBody, draftKey, emojiEntry, emojiInCategory, emptyComposer, fileFromDataUri, fileSource, filesFromAssets, fill, forgetRecentEmoji, formFrom, formatClock, fullScreenVideo, galleryFor, haptic, hoursFromWeek, idleRecorder, inboxRow, initialVideoRecorder, initials, insertAtSelection, invertedRows, isLocalUri, isOnline, isOpenableHref, isVideoPlaying, linkHost, linkPreviewIds, liveBars, loadDraft, loadRecentEmoji, memoryDraftStorage, messageActionsFor, moderationNote, moderationText, muxPosterUrl, muxStreamUrl, nearBottom, nextOffset, openChatLink, panLimit, pinchScale, previewToShow, pushRecentEmoji, recentEmojiKey, recordedVideoFile, recorderStep, recordingType, releasePlayback, replyTargetFor, resolveChatCopy, rowIndexOf, rowKey, sameTimeDays, saveRecentEmoji, scheduleInputFrom, scrubSeconds, searchEmoji, shouldDismiss, snippetText, splitCardAttachments, threadListMode, timeZones, toSend, toggleVideo, toggleVideoMute, toggleVoiceNote, topicClosedFor, topicMenuFor, useAwayLine, useChatAutoRetry, useChatComposer, useChatNative, useChatThreadStackOptions, useJumpToMessage, usePlayingVideo, usePlayingVoiceNote, useScheduleSend, useVideoUploads, useVoiceRecorder, videoRecorderStep, videoSource, videoState, videoUploads, voiceNoteFile, waveformBars, weekFromHours, withDay, withTime };
|