@glyphteck/veyl 0.77.0 → 0.78.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/dist/account.js +1221 -694
- package/dist/accountprofiles.js +2 -0
- package/dist/auth.js +4 -1
- package/dist/cli.js +1283 -779
- package/dist/index.js +1283 -779
- package/docs/validation.md +8 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5856,7 +5856,7 @@ var init_canonical = __esm(() => {
|
|
|
5856
5856
|
});
|
|
5857
5857
|
|
|
5858
5858
|
// ../../core/config.js
|
|
5859
|
-
var MS_PER_SECOND = 1000, MINUTE_MS, HOUR_MS, DAY_MS, KIB_BYTES = 1024, MIB_BYTES, SATS_PER_BITCOIN = 100000000n, USERNAME_MAX_CHARS = 12, PASSWORD_MIN_CHARS = 15, PASSWORD_MAX_CHARS = 64, PASSWORD_MAX_BYTES, ACCOUNT_CONNECTION_SLOW_MS, ACCOUNT_CONNECTION_UNAVAILABLE_MS, ACCOUNT_CONNECTION_RETRY_MIN_MS, ACCOUNT_CONNECTION_RETRY_MAX_MS, TICKET_MESSAGE_LIMIT = 4000, TICKET_ATTACHMENT_LIMIT = 3, TICKET_ATTACHMENT_MAX_BYTES, AUTOLOCK_MIN_MINUTES = 1, AUTOLOCK_MAX_MINUTES = 60, BTC_PRICE_FALLBACK = 80000, CHAT_MESSAGE_EDIT_WINDOW_MS, CHAT_SEND_CONNECTION_WAIT_MS, CALL_SPEAKING_THRESHOLD = 0.02, CALL_SPEAKING_HOLD_MS = 320, LOCAL_CACHE_WRITE_DELAY_MS = 350, WEB_LAYOUT_SAVE_DELAY_MS, WEB_CHAT_LAYOUT_LIMIT = 32, LOCAL_MEDIA_CACHE_MAX_BYTES, LOCAL_MEDIA_CACHE_MAX_ITEMS = 1000, LOCAL_MEDIA_ACCESS_TOUCH_MIN_MS, LOCAL_PROFILE_CACHE_MAX_ITEMS = 500, LOCAL_PROFILE_CACHE_MAX_AGE_MS, LOCAL_CHAT_CACHE_MAX_ITEMS = 1000, LOCAL_CHAT_MESSAGE_CACHE_MAX_VISIBLE = 1000, LOCAL_CHAT_MESSAGE_CACHE_WRITE_DELAY_MS = 2500, LOCAL_AVATAR_CACHE_MAX_BYTES, LOCAL_AVATAR_CACHE_MAX_AGE_MS, AVATAR_IMAGE_MAX_BYTES, AVATAR_IMAGE_QUALITY_ATTEMPTS, CHAT_AVATAR_IMAGE_MAX_BYTES, CHAT_AVATAR_REF_MAX_CHARS = 256, CHAT_MESSAGE_FILE_CACHE_MAX_BYTES, IDLE_CALLBACK_MIN_TIMEOUT_MS = 50, ATTACHMENT_CACHE_IDLE_TIMEOUT_MS = 2500, ATTACHMENT_CACHE_FALLBACK_DELAY_MS = 250, CHAT_UNSAVED_TTL_DAYS = 21, CHAT_UNSAVED_TTL_MS, CHAT_AFTER_SEEN_MS, CHAT_MEDIA_TTL_DAYS, CHAT_MEDIA_TTL_MS, CHAT_UPLOAD_MAX_BYTES, CHAT_AUDIO_TRANSCODE_BITRATE_BPS = 128000, CHAT_VIDEO_TRANSCODE_VIDEO_BITRATE_BPS = 3000000, CHAT_VIDEO_TRANSCODE_AUDIO_BITRATE_BPS, CHAT_MESSAGE_BATCH_SIZE = 40, CHAT_MESSAGE_QUERY_MAX_DOCS = 120, CHAT_MESSAGE_MUTATION_MAX_ITEMS = 256, CHAT_TTL_CLIENT_DELETE_GRACE_MS, CHAT_BATCH_CLEANUP_IDLE_TIMEOUT_MS = 1500, CHAT_BATCH_CLEANUP_IDLE_DELAY_MS = 250, CHAT_LIST_PAGE_SIZE = 20, CHAT_ADMISSION_CACHE_PEERS = 1024, CHAT_LIST_LIVE_COUNT, CHAT_INBOX_PING_PAGE_SIZE = 25, CHAT_INBOX_DISCOVERY_PARALLEL_CHATS, CHAT_INBOX_SETTLEMENT_PARALLEL_CHATS = 4, CHAT_INBOX_CURSOR_OVERLAP_MS, CHAT_LIST_CACHE_WRITE_DELAY_MS = 1500, CHAT_LIST_SNAPSHOT_COALESCE_MS = 80, CHAT_LIST_COMPLETE_SYNC_DELAY_MS = 250, CHAT_LIST_LISTENER_RETRY_MS = 1000, CHAT_TOP_WARM_COUNT = 0, CHAT_EAGER_WARM_COUNT = 0, CHAT_WARM_DELAY_MS = 3000, CHAT_WARM_BATCH_SIZE, CHAT_MESSAGE_VIEW_CACHE_SIZE = 30, CHAT_MEDIA_WARM_MESSAGES_PER_CHAT, CHAT_MEDIA_WARM_START_DELAY_MS = 600, CHAT_MEDIA_WARM_STEP_DELAY_MS = 120, CHAT_MEDIA_WARM_TYPES, CHAT_MEDIA_WARM_MAX_BYTES = 0, CHAT_READ_WRITE_INTERVAL_MS, CHAT_READ_WRITE_MAX_WAIT_MS, CHAT_LIVE_PING_INTERVAL_MS, CHAT_LIVE_SWEEP_INTERVAL_MS, CHAT_LIVE_STALE_AFTER_MS, LIVE_RECONNECT_MIN_MS = 500, LIVE_RECONNECT_MAX_MS, LIVE_RECONNECT_STABLE_MS, LIVE_READY_TIMEOUT_MS, CHAT_LIVE_TYPING_IDLE_MS, CHAT_LIVE_TYPING_RENEW_MS, CHAT_LIVE_COMPOSITION_HANDOFF_MS, CHAT_LIVE_READ_SEND_INTERVAL_MS = 120, CHAT_LIVE_READ_SEND_MAX_WAIT_MS = 500, CHAT_LIVE_READ_WRITE_INTERVAL_MS, CHAT_LIVE_READ_WRITE_MAX_WAIT_MS, CHAT_SEND_QUEUE_RATE_LIMIT_COUNT = 12, CHAT_SEND_QUEUE_RATE_LIMIT_WINDOW_MS, CHAT_SETTINGS_BODY_MAX_BYTES, CHAT_TITLE_STATE_MAX_CHARS = 128, CHAT_MANIFEST_MAX_BYTES, CHAT_RECEIPT_MAX_MEMBERS = 32, CHAT_ORDINARY_DELIVERY_MAX_MEMBERS = 128, CHAT_MAX_MEMBERS = 256, CHAT_LARGE_GROUP_MENTION_MAX_TARGETS = 16, CHAT_MAX_TEXT_CHARS = 2048, CHAT_TITLE_INPUT_MAX_CHARS = 16, CHAT_MAX_REACTIONS, SEARCH_DEBOUNCE_MS = 300, SEARCH_USERNAME_LIMIT = 15, SEARCH_ROLE_LIMIT = 15, RECENT_PEER_REFRESH_LIMIT = 50, RECENT_PEER_REFRESH_DELAY_MS = 250, RECENT_PEER_REFRESH_THROTTLE_MS = 120, BAN_REFRESH_GRACE_MS = 50, REQUEST_MONEY_MAX_SATS, WALLET_TRANSFER_POLL_MS, WALLET_TRANSFER_REPAIR_MS, WALLET_CACHE_HYDRATE_DELAY_MS = 0, WALLET_BOOT_REFRESH_DELAY_MS = 500, WALLET_BOOT_CACHED_REFRESH_DELAY_MS = 2500, WALLET_REGTEST_DEPOSIT_CLAIM_POLL_MS, WALLET_MAINNET_DEPOSIT_CLAIM_POLL_MS, WALLET_BALANCE_EVENT_COALESCE_MS = 100, WALLET_INCOMING_UPDATE_COALESCE_MS = 250, WALLET_AUTO_CLAIM_MAX_FEE_SATS = 5000, WALLET_CLAIM_PAGE_SIZE = 100, WALLET_PENDING_TRANSFER_CLAIM_BATCH_SIZE = 50, WALLET_PENDING_TRANSFER_STATUS_BATCH_SIZE = 2, WALLET_PENDING_TRANSFER_COLD_REFRESH_BATCH_SIZE = 50, WALLET_PENDING_TRANSFER_COLD_REFRESH_INTERVAL_MS, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_SMALL_QUEUE = 3, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_LARGE_QUEUE = 10, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_SMALL_SIZE = 2, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_LARGE_SIZE = 3, WALLET_PENDING_TRANSFER_SLOW_CLAIM_MS, WALLET_PENDING_TRANSFER_CLAIM_COOLDOWN_MS, WALLET_SDK_BACKGROUND_QUIET_MS, WALLET_RECENT_TRANSFER_LIMIT = 100, WALLET_TRANSFER_PAGE_LIMIT = 100, WALLET_TOKEN_HISTORY_PAGE_SIZE = 50, WALLET_TRANSFER_FETCH_THROTTLE_MS = 150, WALLET_PENDING_TRANSFER_CLAIM_RETRY_MS, WALLET_SENT_TRANSFER_REFRESH_DELAY_MS, WALLET_PENDING_TRANSFER_BOOT_GRACE_MS, WALLET_PENDING_TRANSFER_HOT_AGE_MS, WALLET_PENDING_TRANSFER_WARM_AGE_MS, WALLET_PENDING_TRANSFER_WARM_RETRY_MS, WALLET_PENDING_TRANSFER_STALE_RETRY_MS, WALLET_PENDING_TRANSFER_STUCK_RETRY_MS, WALLET_PENDING_TRANSFER_DORMANT_RETRY_MS, WALLET_TRANSFER_CACHE_WRITE_DELAY_MS, BITCOIN_POLL_MS = 60000, BITCOIN_FETCH_TIMEOUT_MS = 8000, BITCOIN_FEES_MAX_AGE_MS;
|
|
5859
|
+
var MS_PER_SECOND = 1000, MINUTE_MS, HOUR_MS, DAY_MS, KIB_BYTES = 1024, MIB_BYTES, SATS_PER_BITCOIN = 100000000n, USERNAME_MAX_CHARS = 12, PASSWORD_MIN_CHARS = 15, PASSWORD_MAX_CHARS = 64, PASSWORD_MAX_BYTES, ACCOUNT_CONNECTION_SLOW_MS, ACCOUNT_CONNECTION_UNAVAILABLE_MS, ACCOUNT_CONNECTION_RETRY_MIN_MS, ACCOUNT_CONNECTION_RETRY_MAX_MS, TICKET_MESSAGE_LIMIT = 4000, TICKET_ATTACHMENT_LIMIT = 3, TICKET_ATTACHMENT_MAX_BYTES, AUTOLOCK_MIN_MINUTES = 1, AUTOLOCK_MAX_MINUTES = 60, BTC_PRICE_FALLBACK = 80000, CHAT_MESSAGE_EDIT_WINDOW_MS, CHAT_SEND_CONNECTION_WAIT_MS, CALL_SPEAKING_THRESHOLD = 0.02, CALL_SPEAKING_HOLD_MS = 320, LOCAL_CACHE_WRITE_DELAY_MS = 350, WEB_LAYOUT_SAVE_DELAY_MS, WEB_CHAT_LAYOUT_LIMIT = 32, LOCAL_MEDIA_CACHE_MAX_BYTES, LOCAL_MEDIA_CACHE_MAX_ITEMS = 1000, LOCAL_MEDIA_ACCESS_TOUCH_MIN_MS, LOCAL_PROFILE_CACHE_MAX_ITEMS = 500, LOCAL_PROFILE_CACHE_MAX_AGE_MS, LOCAL_CHAT_CACHE_MAX_ITEMS = 1000, LOCAL_CHAT_MESSAGE_CACHE_MAX_VISIBLE = 1000, LOCAL_CHAT_MESSAGE_CACHE_WRITE_DELAY_MS = 2500, LOCAL_AVATAR_CACHE_MAX_BYTES, LOCAL_AVATAR_CACHE_MAX_AGE_MS, AVATAR_IMAGE_MAX_BYTES, AVATAR_IMAGE_QUALITY_ATTEMPTS, CHAT_AVATAR_IMAGE_MAX_BYTES, CHAT_AVATAR_REF_MAX_CHARS = 256, CHAT_MESSAGE_FILE_CACHE_MAX_BYTES, IDLE_CALLBACK_MIN_TIMEOUT_MS = 50, ATTACHMENT_CACHE_IDLE_TIMEOUT_MS = 2500, ATTACHMENT_CACHE_FALLBACK_DELAY_MS = 250, CHAT_UNSAVED_TTL_DAYS = 21, CHAT_UNSAVED_TTL_MS, CHAT_AFTER_SEEN_MS, CHAT_MEDIA_TTL_DAYS, CHAT_MEDIA_TTL_MS, CHAT_UPLOAD_MAX_BYTES, CHAT_AUDIO_TRANSCODE_BITRATE_BPS = 128000, CHAT_VIDEO_TRANSCODE_VIDEO_BITRATE_BPS = 3000000, CHAT_VIDEO_TRANSCODE_AUDIO_BITRATE_BPS, CHAT_MESSAGE_BATCH_SIZE = 40, CHAT_MESSAGE_QUERY_MAX_DOCS = 120, CHAT_MESSAGE_MUTATION_MAX_ITEMS = 256, CHAT_TTL_CLIENT_DELETE_GRACE_MS, CHAT_BATCH_CLEANUP_IDLE_TIMEOUT_MS = 1500, CHAT_BATCH_CLEANUP_IDLE_DELAY_MS = 250, CHAT_LIST_PAGE_SIZE = 20, CHAT_ADMISSION_CACHE_PEERS = 1024, CHAT_LIST_LIVE_COUNT, CHAT_INBOX_PING_PAGE_SIZE = 25, CHAT_INBOX_DISCOVERY_PARALLEL_CHATS, CHAT_INBOX_SETTLEMENT_PARALLEL_CHATS = 4, CHAT_INBOX_CURSOR_OVERLAP_MS, CHAT_LIST_CACHE_WRITE_DELAY_MS = 1500, CHAT_LIST_SNAPSHOT_COALESCE_MS = 80, CHAT_LIST_COMPLETE_SYNC_DELAY_MS = 250, CHAT_LIST_LISTENER_RETRY_MS = 1000, CHAT_TOP_WARM_COUNT = 0, CHAT_EAGER_WARM_COUNT = 0, CHAT_WARM_DELAY_MS = 3000, CHAT_WARM_BATCH_SIZE, CHAT_MESSAGE_VIEW_CACHE_SIZE = 30, CHAT_MEDIA_WARM_MESSAGES_PER_CHAT, CHAT_MEDIA_WARM_START_DELAY_MS = 600, CHAT_MEDIA_WARM_STEP_DELAY_MS = 120, CHAT_MEDIA_WARM_TYPES, CHAT_MEDIA_WARM_MAX_BYTES = 0, CHAT_READ_WRITE_INTERVAL_MS, CHAT_READ_WRITE_MAX_WAIT_MS, CHAT_LIVE_PING_INTERVAL_MS, CHAT_LIVE_SWEEP_INTERVAL_MS, CHAT_LIVE_STALE_AFTER_MS, LIVE_RECONNECT_MIN_MS = 500, LIVE_RECONNECT_MAX_MS, LIVE_RECONNECT_STABLE_MS, LIVE_READY_TIMEOUT_MS, CHAT_LIVE_TYPING_IDLE_MS, CHAT_LIVE_TYPING_RENEW_MS, CHAT_LIVE_COMPOSITION_HANDOFF_MS, CHAT_LIVE_READ_SEND_INTERVAL_MS = 120, CHAT_LIVE_READ_SEND_MAX_WAIT_MS = 500, CHAT_LIVE_READ_WRITE_INTERVAL_MS, CHAT_LIVE_READ_WRITE_MAX_WAIT_MS, CHAT_SEND_QUEUE_RATE_LIMIT_COUNT = 12, CHAT_SEND_QUEUE_RATE_LIMIT_WINDOW_MS, CHAT_SETTINGS_BODY_MAX_BYTES, CHAT_TITLE_STATE_MAX_CHARS = 128, CHAT_MANIFEST_MAX_BYTES, CHAT_RECEIPT_MAX_MEMBERS = 32, CHAT_ORDINARY_DELIVERY_MAX_MEMBERS = 128, CHAT_MAX_MEMBERS = 256, CHAT_LARGE_GROUP_MENTION_MAX_TARGETS = 16, CHAT_MAX_TEXT_CHARS = 2048, CHAT_TITLE_INPUT_MAX_CHARS = 16, CHAT_MAX_REACTIONS, SEARCH_DEBOUNCE_MS = 300, SEARCH_USERNAME_LIMIT = 15, SEARCH_ROLE_LIMIT = 15, RECENT_PEER_REFRESH_LIMIT = 50, RECENT_PEER_REFRESH_DELAY_MS = 250, RECENT_PEER_REFRESH_THROTTLE_MS = 120, BAN_REFRESH_GRACE_MS = 50, REQUEST_MONEY_MAX_SATS, WALLET_TRANSFER_POLL_MS, WALLET_TRANSFER_REPAIR_MS, WALLET_CACHE_HYDRATE_DELAY_MS = 0, WALLET_BOOT_REFRESH_DELAY_MS = 500, WALLET_BOOT_CACHED_REFRESH_DELAY_MS = 2500, WALLET_REGTEST_DEPOSIT_CLAIM_POLL_MS, WALLET_MAINNET_DEPOSIT_CLAIM_POLL_MS, WALLET_BALANCE_EVENT_COALESCE_MS = 100, WALLET_INCOMING_UPDATE_COALESCE_MS = 250, WALLET_AUTO_CLAIM_MAX_FEE_SATS = 5000, WALLET_CLAIM_PAGE_SIZE = 100, WALLET_PENDING_TRANSFER_CLAIM_BATCH_SIZE = 50, WALLET_PENDING_TRANSFER_STATUS_BATCH_SIZE = 2, WALLET_PENDING_TRANSFER_COLD_REFRESH_BATCH_SIZE = 50, WALLET_PENDING_TRANSFER_COLD_REFRESH_INTERVAL_MS, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_SMALL_QUEUE = 3, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_LARGE_QUEUE = 10, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_SMALL_SIZE = 2, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_LARGE_SIZE = 3, WALLET_PENDING_TRANSFER_SLOW_CLAIM_MS, WALLET_PENDING_TRANSFER_CLAIM_COOLDOWN_MS, WALLET_SDK_BACKGROUND_QUIET_MS, WALLET_RECENT_TRANSFER_LIMIT = 100, WALLET_TRANSFER_PAGE_LIMIT = 100, WALLET_TOKEN_HISTORY_PAGE_SIZE = 50, WALLET_TRANSFER_FETCH_THROTTLE_MS = 150, WALLET_PENDING_TRANSFER_CLAIM_RETRY_MS, WALLET_SENT_TRANSFER_REFRESH_DELAY_MS, WALLET_PENDING_TRANSFER_BOOT_GRACE_MS, WALLET_PENDING_TRANSFER_HOT_AGE_MS, WALLET_PENDING_TRANSFER_WARM_AGE_MS, WALLET_PENDING_TRANSFER_WARM_RETRY_MS, WALLET_PENDING_TRANSFER_STALE_RETRY_MS, WALLET_PENDING_TRANSFER_STUCK_RETRY_MS, WALLET_PENDING_TRANSFER_DORMANT_RETRY_MS, WALLET_TRANSFER_CACHE_WRITE_DELAY_MS, BITCOIN_POLL_MS = 60000, BITCOIN_FETCH_TIMEOUT_MS = 8000, BITCOIN_FEES_MAX_AGE_MS, CALL_MAX_VIDEO_SOURCES = 4, CALL_MAX_VIDEO_SOURCES_PER_PARTICIPANT = 1;
|
|
5860
5860
|
var init_config = __esm(() => {
|
|
5861
5861
|
MINUTE_MS = 60 * MS_PER_SECOND;
|
|
5862
5862
|
HOUR_MS = 60 * MINUTE_MS;
|
|
@@ -6127,150 +6127,6 @@ function sameText(left, right) {
|
|
|
6127
6127
|
return lowerText(left) === lowerText(right);
|
|
6128
6128
|
}
|
|
6129
6129
|
|
|
6130
|
-
// ../../core/utils/time.js
|
|
6131
|
-
function timestampMs(value, fallback = null, options = {}) {
|
|
6132
|
-
let ms = null;
|
|
6133
|
-
if (typeof value?.toMillis === "function") {
|
|
6134
|
-
ms = value.toMillis();
|
|
6135
|
-
} else if (value instanceof Date) {
|
|
6136
|
-
ms = value.getTime();
|
|
6137
|
-
} else if (typeof value?.seconds === "number") {
|
|
6138
|
-
ms = value.seconds * 1000 + Math.floor((value.nanoseconds || 0) / 1e6);
|
|
6139
|
-
} else if (typeof value?._seconds === "number") {
|
|
6140
|
-
ms = value._seconds * 1000 + Math.floor((value._nanoseconds || 0) / 1e6);
|
|
6141
|
-
} else if (Number.isFinite(value)) {
|
|
6142
|
-
ms = value;
|
|
6143
|
-
} else if (options.parseString && typeof value === "string") {
|
|
6144
|
-
const numberMs = Number(value);
|
|
6145
|
-
ms = Number.isFinite(numberMs) ? numberMs : Date.parse(value);
|
|
6146
|
-
}
|
|
6147
|
-
if (!Number.isFinite(ms) || options.positive && ms <= 0) {
|
|
6148
|
-
return fallback;
|
|
6149
|
-
}
|
|
6150
|
-
return ms;
|
|
6151
|
-
}
|
|
6152
|
-
function timestampKey(value) {
|
|
6153
|
-
if (value == null) {
|
|
6154
|
-
return null;
|
|
6155
|
-
}
|
|
6156
|
-
return timestampMs(value, null) ?? String(value);
|
|
6157
|
-
}
|
|
6158
|
-
function makeTimestamp(ms) {
|
|
6159
|
-
return {
|
|
6160
|
-
toMillis() {
|
|
6161
|
-
return ms;
|
|
6162
|
-
},
|
|
6163
|
-
toDate() {
|
|
6164
|
-
return new Date(ms);
|
|
6165
|
-
}
|
|
6166
|
-
};
|
|
6167
|
-
}
|
|
6168
|
-
function twoDigits(value) {
|
|
6169
|
-
return String(value).padStart(2, "0");
|
|
6170
|
-
}
|
|
6171
|
-
function dayKey(date) {
|
|
6172
|
-
return `${date.getFullYear()}-${twoDigits(date.getMonth() + 1)}-${twoDigits(date.getDate())}`;
|
|
6173
|
-
}
|
|
6174
|
-
function localDayKey(value) {
|
|
6175
|
-
const ms = timestampMs(value, null, { parseString: true });
|
|
6176
|
-
if (!Number.isFinite(ms))
|
|
6177
|
-
return "";
|
|
6178
|
-
return dayKey(new Date(ms));
|
|
6179
|
-
}
|
|
6180
|
-
function hourKey(dateOrHour) {
|
|
6181
|
-
const hour = dateOrHour instanceof Date ? dateOrHour.getHours() : dateOrHour;
|
|
6182
|
-
return twoDigits(hour);
|
|
6183
|
-
}
|
|
6184
|
-
function dayHourKey(date) {
|
|
6185
|
-
return `${dayKey(date)}-${hourKey(date)}`;
|
|
6186
|
-
}
|
|
6187
|
-
function nextLocalDayStartMs(ms) {
|
|
6188
|
-
const date = new Date(ms);
|
|
6189
|
-
date.setDate(date.getDate() + 1);
|
|
6190
|
-
date.setHours(0, 0, 0, 0);
|
|
6191
|
-
return date.getTime();
|
|
6192
|
-
}
|
|
6193
|
-
function nextRowDateTimeRefreshMs(value, now = Date.now()) {
|
|
6194
|
-
const ms = timestampMs(value, null, { parseString: true });
|
|
6195
|
-
const nowMs2 = timestampMs(now, Date.now(), { parseString: true });
|
|
6196
|
-
if (!Number.isFinite(ms) || !Number.isFinite(nowMs2))
|
|
6197
|
-
return null;
|
|
6198
|
-
const age = nowMs2 - ms;
|
|
6199
|
-
if (age < -MINUTE_MS2)
|
|
6200
|
-
return ms - MINUTE_MS2;
|
|
6201
|
-
if (age < MINUTE_MS2)
|
|
6202
|
-
return ms + MINUTE_MS2;
|
|
6203
|
-
if (age < HOUR_MS2)
|
|
6204
|
-
return ms + (Math.floor(age / MINUTE_MS2) + 1) * MINUTE_MS2;
|
|
6205
|
-
if (localDayKey(ms) === localDayKey(nowMs2))
|
|
6206
|
-
return nextLocalDayStartMs(nowMs2);
|
|
6207
|
-
return null;
|
|
6208
|
-
}
|
|
6209
|
-
function nextRelativeTimeRefreshMs(value, now = Date.now()) {
|
|
6210
|
-
const ms = timestampMs(value, null, { parseString: true });
|
|
6211
|
-
const nowMs2 = timestampMs(now, Date.now(), { parseString: true });
|
|
6212
|
-
if (!Number.isFinite(ms) || !Number.isFinite(nowMs2))
|
|
6213
|
-
return null;
|
|
6214
|
-
const age = Math.max(0, nowMs2 - ms);
|
|
6215
|
-
if (age < 30000)
|
|
6216
|
-
return ms + 30000;
|
|
6217
|
-
if (age < 60 * 60000)
|
|
6218
|
-
return ms + (Math.floor(age / 60000) + 1) * 60000;
|
|
6219
|
-
if (age < 24 * 60 * 60000)
|
|
6220
|
-
return ms + (Math.floor(age / (60 * 60000)) + 1) * 60 * 60000;
|
|
6221
|
-
return ms + (Math.floor(age / (24 * 60 * 60000)) + 1) * 24 * 60 * 60000;
|
|
6222
|
-
}
|
|
6223
|
-
var MINUTE_MS2 = 60000, HOUR_MS2;
|
|
6224
|
-
var init_time = __esm(() => {
|
|
6225
|
-
HOUR_MS2 = 60 * MINUTE_MS2;
|
|
6226
|
-
});
|
|
6227
|
-
|
|
6228
|
-
// ../../core/chat/state.js
|
|
6229
|
-
function makeCid() {
|
|
6230
|
-
return `${Date.now().toString(36)}${toHex(randomBytes3(3))}`;
|
|
6231
|
-
}
|
|
6232
|
-
function getMessageKey(message) {
|
|
6233
|
-
return message?.cid || message?.id || null;
|
|
6234
|
-
}
|
|
6235
|
-
function getCidMs(cid) {
|
|
6236
|
-
if (typeof cid !== "string" || !/^[0-9a-z]+[0-9a-f]{6}$/u.test(cid)) {
|
|
6237
|
-
return null;
|
|
6238
|
-
}
|
|
6239
|
-
const base = cid.slice(0, -6);
|
|
6240
|
-
const ms = Number.parseInt(base, 36);
|
|
6241
|
-
return Number.isSafeInteger(ms) && ms > 0 ? ms : null;
|
|
6242
|
-
}
|
|
6243
|
-
function getMessageOrderMs(message) {
|
|
6244
|
-
return getCidMs(message?.cid) ?? timestampMs(message?.ts, Infinity);
|
|
6245
|
-
}
|
|
6246
|
-
function sortMessages(messages) {
|
|
6247
|
-
return [...messages].sort((a, b) => {
|
|
6248
|
-
const aMs = getMessageOrderMs(a);
|
|
6249
|
-
const bMs = getMessageOrderMs(b);
|
|
6250
|
-
if (aMs !== bMs) {
|
|
6251
|
-
return aMs - bMs;
|
|
6252
|
-
}
|
|
6253
|
-
return String(a?.id || "").localeCompare(String(b?.id || ""));
|
|
6254
|
-
});
|
|
6255
|
-
}
|
|
6256
|
-
function mergeMessages(...groups) {
|
|
6257
|
-
const merged = new Map;
|
|
6258
|
-
for (const group of groups) {
|
|
6259
|
-
for (const message of group || []) {
|
|
6260
|
-
const key = getMessageKey(message);
|
|
6261
|
-
if (!key) {
|
|
6262
|
-
continue;
|
|
6263
|
-
}
|
|
6264
|
-
merged.set(key, message);
|
|
6265
|
-
}
|
|
6266
|
-
}
|
|
6267
|
-
return sortMessages([...merged.values()]);
|
|
6268
|
-
}
|
|
6269
|
-
var init_state = __esm(() => {
|
|
6270
|
-
init_core();
|
|
6271
|
-
init_time();
|
|
6272
|
-
});
|
|
6273
|
-
|
|
6274
6130
|
// ../../core/chat/messages/call.js
|
|
6275
6131
|
function readCallMessage(message) {
|
|
6276
6132
|
if (message?.t !== CALL_MSG_TYPE || typeof message.callId !== "string" || !/^[0-9a-f]{64}$/u.test(message.callId) || !["started", "ended"].includes(message.event))
|
|
@@ -6287,17 +6143,7 @@ function callMessageText(message) {
|
|
|
6287
6143
|
const call = readCallMessage(message);
|
|
6288
6144
|
return call ? `call ${call.event}` : "";
|
|
6289
6145
|
}
|
|
6290
|
-
function latestCallStartKey(messages) {
|
|
6291
|
-
for (let index = messages.length - 1;index >= 0; index -= 1) {
|
|
6292
|
-
if (readCallMessage(messages[index])?.event === "started")
|
|
6293
|
-
return getMessageKey(messages[index]);
|
|
6294
|
-
}
|
|
6295
|
-
return "";
|
|
6296
|
-
}
|
|
6297
6146
|
var CALL_MSG_TYPE = "call";
|
|
6298
|
-
var init_call = __esm(() => {
|
|
6299
|
-
init_state();
|
|
6300
|
-
});
|
|
6301
6147
|
|
|
6302
6148
|
// ../../core/chat/messages/types.js
|
|
6303
6149
|
var ATTACHMENT_MSG_TYPES, MAX_TXT_CHARS, REACTION_MSG_TYPE = "rxn", DELETE_MSG_TYPE = "del", SYSTEM_MSG_TYPE = "sys", EPOCH_TRANSITION_MSG_TYPE = "epoch", EPOCH_PROPOSAL_MSG_TYPE = "epoch_proposal", SYSTEM_SETTINGS_KIND = "settings", DEFAULT_REACTION_EMOJI = "❤️", MAX_REACTIONS, HOLD_VISIBLE_KEY = "__holdVisible", SOURCE_GONE_VISIBLE_KEY = "__sourceGoneVisible";
|
|
@@ -6548,7 +6394,6 @@ var init_notifications = __esm(() => {
|
|
|
6548
6394
|
init_seed();
|
|
6549
6395
|
init_sign();
|
|
6550
6396
|
init_config();
|
|
6551
|
-
init_call();
|
|
6552
6397
|
init_types();
|
|
6553
6398
|
"use client";
|
|
6554
6399
|
CHAT_NOTIFICATION_MODES = Object.freeze({
|
|
@@ -6858,6 +6703,7 @@ var init_origins = __esm(() => {
|
|
|
6858
6703
|
root: ROOT_DOMAIN,
|
|
6859
6704
|
rootDev: `dev.${ROOT_DOMAIN}`,
|
|
6860
6705
|
veyl: `veyl.${ROOT_DOMAIN}`,
|
|
6706
|
+
veylOpen: `open.veyl.${ROOT_DOMAIN}`,
|
|
6861
6707
|
veylDev: `dev.veyl.${ROOT_DOMAIN}`,
|
|
6862
6708
|
live: `live.veyl.${ROOT_DOMAIN}`,
|
|
6863
6709
|
liveDev: `live.dev.veyl.${ROOT_DOMAIN}`,
|
|
@@ -6869,6 +6715,7 @@ var init_origins = __esm(() => {
|
|
|
6869
6715
|
root: `https://${domains.root}`,
|
|
6870
6716
|
rootDev: `https://${domains.rootDev}`,
|
|
6871
6717
|
veyl: `https://${domains.veyl}`,
|
|
6718
|
+
veylOpen: `https://${domains.veylOpen}`,
|
|
6872
6719
|
veylDev: `https://${domains.veylDev}`,
|
|
6873
6720
|
veylDevWeb: getVeylDevWebOrigin(VEYL_DEV_WEB_PORT_MIN)
|
|
6874
6721
|
});
|
|
@@ -6883,7 +6730,8 @@ var init_origins = __esm(() => {
|
|
|
6883
6730
|
]);
|
|
6884
6731
|
appLinkDomains = Object.freeze([
|
|
6885
6732
|
domains.veyl,
|
|
6886
|
-
domains.veylDev
|
|
6733
|
+
domains.veylDev,
|
|
6734
|
+
domains.veylOpen
|
|
6887
6735
|
]);
|
|
6888
6736
|
});
|
|
6889
6737
|
|
|
@@ -7012,9 +6860,6 @@ function normalizeSettings(settings, base = defaultSettings) {
|
|
|
7012
6860
|
if (!MONEY_FORMATS.includes(next.moneyFormat)) {
|
|
7013
6861
|
throw new SettingsValidationError("money-format", "bad moneyFormat");
|
|
7014
6862
|
}
|
|
7015
|
-
if (!WEB_LAYOUTS.includes(next.webLayout)) {
|
|
7016
|
-
throw new SettingsValidationError("web-layout", "bad webLayout");
|
|
7017
|
-
}
|
|
7018
6863
|
if (typeof next.glass !== "boolean") {
|
|
7019
6864
|
throw new SettingsValidationError("glass", "glass must be boolean");
|
|
7020
6865
|
}
|
|
@@ -7036,7 +6881,7 @@ function normalizeSettings(settings, base = defaultSettings) {
|
|
|
7036
6881
|
if (next.faceID !== null && typeof next.faceID !== "boolean") {
|
|
7037
6882
|
throw new SettingsValidationError("face-id", "faceID must be boolean or null");
|
|
7038
6883
|
}
|
|
7039
|
-
return next;
|
|
6884
|
+
return Object.fromEntries(Object.keys(defaultSettings).map((key) => [key, next[key]]));
|
|
7040
6885
|
}
|
|
7041
6886
|
function normalizeCallAudio(patch, base = defaultSettings.callAudio) {
|
|
7042
6887
|
if (patch !== undefined && (!patch || typeof patch !== "object" || Array.isArray(patch)))
|
|
@@ -7064,12 +6909,11 @@ function normalizePeerAudio(patch, base = {}) {
|
|
|
7064
6909
|
}
|
|
7065
6910
|
return Object.fromEntries(entries);
|
|
7066
6911
|
}
|
|
7067
|
-
var SEND_ON_SCAN_ENABLED = false,
|
|
6912
|
+
var SEND_ON_SCAN_ENABLED = false, WEB_SIDEBAR_WIDTH, DEFAULT_WEB_CHAT_LAYOUT, WALLET_NETWORKS, SettingsValidationError, defaultSettings;
|
|
7068
6913
|
var init_settings = __esm(() => {
|
|
7069
6914
|
init_config();
|
|
7070
6915
|
init_money();
|
|
7071
6916
|
init_network();
|
|
7072
|
-
WEB_LAYOUTS = ["floating", "sidebar"];
|
|
7073
6917
|
WEB_SIDEBAR_WIDTH = { default: 320, min: 64, max: 480 };
|
|
7074
6918
|
DEFAULT_WEB_CHAT_LAYOUT = Object.freeze({ open: null, width: null });
|
|
7075
6919
|
WALLET_NETWORKS = new Set([MAINNET_NETWORK, REGTEST_NETWORK]);
|
|
@@ -7081,7 +6925,6 @@ var init_settings = __esm(() => {
|
|
|
7081
6925
|
};
|
|
7082
6926
|
defaultSettings = {
|
|
7083
6927
|
glass: true,
|
|
7084
|
-
webLayout: "floating",
|
|
7085
6928
|
sidebarWidth: WEB_SIDEBAR_WIDTH.default,
|
|
7086
6929
|
walletChartHeight: null,
|
|
7087
6930
|
webChatLayouts: [],
|
|
@@ -8496,15 +8339,13 @@ async function bootWallet(walletMnemonic, user, {
|
|
|
8496
8339
|
decodeSparkAddress,
|
|
8497
8340
|
network
|
|
8498
8341
|
});
|
|
8499
|
-
const initializeWallet = WalletClass.getOrCreateWallet || WalletClass.initialize;
|
|
8500
8342
|
try {
|
|
8501
8343
|
let wallet;
|
|
8502
8344
|
const sparkStartedAt = Date.now();
|
|
8503
8345
|
markDiag(diag, "vault.bootWallet.spark.start", {});
|
|
8504
|
-
({ wallet } = await
|
|
8346
|
+
({ wallet } = await WalletClass.initialize({
|
|
8505
8347
|
mnemonicOrSeed: walletMnemonic,
|
|
8506
8348
|
accountNumber,
|
|
8507
|
-
forceReinit: true,
|
|
8508
8349
|
options: {
|
|
8509
8350
|
log: false,
|
|
8510
8351
|
network,
|
|
@@ -8929,7 +8770,14 @@ async function openVaultAccountSession(vault, password, options = {}) {
|
|
|
8929
8770
|
return Promise.reject(new Error("account session closed"));
|
|
8930
8771
|
if (connecting)
|
|
8931
8772
|
return connecting;
|
|
8773
|
+
let releaseBoot;
|
|
8932
8774
|
connecting = (async () => {
|
|
8775
|
+
const walletStartedAt = Date.now();
|
|
8776
|
+
mark(diag, "vault.unlock.wallet.start", { source });
|
|
8777
|
+
const boot = startWalletBoot(bootWallet2, mnemonicFromWalletEntropy(sessionWalletEntropy), currentUser, { network, diag });
|
|
8778
|
+
walletBoot = boot;
|
|
8779
|
+
releaseBoot = session.own(boot);
|
|
8780
|
+
boot.result.catch(() => {});
|
|
8933
8781
|
const authStartedAt = Date.now();
|
|
8934
8782
|
mark(diag, "vault.unlock.session.start", { source });
|
|
8935
8783
|
await cloud.auth.user.getIdToken(true);
|
|
@@ -8963,11 +8811,6 @@ async function openVaultAccountSession(vault, password, options = {}) {
|
|
|
8963
8811
|
requireCurrent(isCurrent);
|
|
8964
8812
|
chatIdentitiesRegistered = true;
|
|
8965
8813
|
}
|
|
8966
|
-
const walletStartedAt = Date.now();
|
|
8967
|
-
mark(diag, "vault.unlock.wallet.start", { source });
|
|
8968
|
-
const boot = startWalletBoot(bootWallet2, mnemonicFromWalletEntropy(sessionWalletEntropy), currentUser, { network, diag });
|
|
8969
|
-
walletBoot = boot;
|
|
8970
|
-
const releaseBoot = session.own(boot);
|
|
8971
8814
|
const walletReady = boot.result.then(async (walletIdentity) => {
|
|
8972
8815
|
if (session.closed)
|
|
8973
8816
|
throw new Error("account session closed");
|
|
@@ -8999,6 +8842,7 @@ async function openVaultAccountSession(vault, password, options = {}) {
|
|
|
8999
8842
|
walletReady.catch(() => {});
|
|
9000
8843
|
return { walletReady };
|
|
9001
8844
|
})().catch((error) => {
|
|
8845
|
+
releaseBoot?.();
|
|
9002
8846
|
connecting = null;
|
|
9003
8847
|
throw error;
|
|
9004
8848
|
});
|
|
@@ -9062,6 +8906,104 @@ var init_agreement = __esm(() => {
|
|
|
9062
8906
|
TERMS_VERSION_PATTERN = /^\d{4}-\d{2}-\d{2}(?:\.\d+)?$/u;
|
|
9063
8907
|
});
|
|
9064
8908
|
|
|
8909
|
+
// ../../core/utils/time.js
|
|
8910
|
+
function timestampMs(value, fallback = null, options = {}) {
|
|
8911
|
+
let ms = null;
|
|
8912
|
+
if (typeof value?.toMillis === "function") {
|
|
8913
|
+
ms = value.toMillis();
|
|
8914
|
+
} else if (value instanceof Date) {
|
|
8915
|
+
ms = value.getTime();
|
|
8916
|
+
} else if (typeof value?.seconds === "number") {
|
|
8917
|
+
ms = value.seconds * 1000 + Math.floor((value.nanoseconds || 0) / 1e6);
|
|
8918
|
+
} else if (typeof value?._seconds === "number") {
|
|
8919
|
+
ms = value._seconds * 1000 + Math.floor((value._nanoseconds || 0) / 1e6);
|
|
8920
|
+
} else if (Number.isFinite(value)) {
|
|
8921
|
+
ms = value;
|
|
8922
|
+
} else if (options.parseString && typeof value === "string") {
|
|
8923
|
+
const numberMs = Number(value);
|
|
8924
|
+
ms = Number.isFinite(numberMs) ? numberMs : Date.parse(value);
|
|
8925
|
+
}
|
|
8926
|
+
if (!Number.isFinite(ms) || options.positive && ms <= 0) {
|
|
8927
|
+
return fallback;
|
|
8928
|
+
}
|
|
8929
|
+
return ms;
|
|
8930
|
+
}
|
|
8931
|
+
function timestampKey(value) {
|
|
8932
|
+
if (value == null) {
|
|
8933
|
+
return null;
|
|
8934
|
+
}
|
|
8935
|
+
return timestampMs(value, null) ?? String(value);
|
|
8936
|
+
}
|
|
8937
|
+
function makeTimestamp(ms) {
|
|
8938
|
+
return {
|
|
8939
|
+
toMillis() {
|
|
8940
|
+
return ms;
|
|
8941
|
+
},
|
|
8942
|
+
toDate() {
|
|
8943
|
+
return new Date(ms);
|
|
8944
|
+
}
|
|
8945
|
+
};
|
|
8946
|
+
}
|
|
8947
|
+
function twoDigits(value) {
|
|
8948
|
+
return String(value).padStart(2, "0");
|
|
8949
|
+
}
|
|
8950
|
+
function dayKey(date) {
|
|
8951
|
+
return `${date.getFullYear()}-${twoDigits(date.getMonth() + 1)}-${twoDigits(date.getDate())}`;
|
|
8952
|
+
}
|
|
8953
|
+
function localDayKey(value) {
|
|
8954
|
+
const ms = timestampMs(value, null, { parseString: true });
|
|
8955
|
+
if (!Number.isFinite(ms))
|
|
8956
|
+
return "";
|
|
8957
|
+
return dayKey(new Date(ms));
|
|
8958
|
+
}
|
|
8959
|
+
function hourKey(dateOrHour) {
|
|
8960
|
+
const hour = dateOrHour instanceof Date ? dateOrHour.getHours() : dateOrHour;
|
|
8961
|
+
return twoDigits(hour);
|
|
8962
|
+
}
|
|
8963
|
+
function dayHourKey(date) {
|
|
8964
|
+
return `${dayKey(date)}-${hourKey(date)}`;
|
|
8965
|
+
}
|
|
8966
|
+
function nextLocalDayStartMs(ms) {
|
|
8967
|
+
const date = new Date(ms);
|
|
8968
|
+
date.setDate(date.getDate() + 1);
|
|
8969
|
+
date.setHours(0, 0, 0, 0);
|
|
8970
|
+
return date.getTime();
|
|
8971
|
+
}
|
|
8972
|
+
function nextRowDateTimeRefreshMs(value, now = Date.now()) {
|
|
8973
|
+
const ms = timestampMs(value, null, { parseString: true });
|
|
8974
|
+
const nowMs2 = timestampMs(now, Date.now(), { parseString: true });
|
|
8975
|
+
if (!Number.isFinite(ms) || !Number.isFinite(nowMs2))
|
|
8976
|
+
return null;
|
|
8977
|
+
const age = nowMs2 - ms;
|
|
8978
|
+
if (age < -MINUTE_MS2)
|
|
8979
|
+
return ms - MINUTE_MS2;
|
|
8980
|
+
if (age < MINUTE_MS2)
|
|
8981
|
+
return ms + MINUTE_MS2;
|
|
8982
|
+
if (age < HOUR_MS2)
|
|
8983
|
+
return ms + (Math.floor(age / MINUTE_MS2) + 1) * MINUTE_MS2;
|
|
8984
|
+
if (localDayKey(ms) === localDayKey(nowMs2))
|
|
8985
|
+
return nextLocalDayStartMs(nowMs2);
|
|
8986
|
+
return null;
|
|
8987
|
+
}
|
|
8988
|
+
function nextRelativeTimeRefreshMs(value, now = Date.now()) {
|
|
8989
|
+
const ms = timestampMs(value, null, { parseString: true });
|
|
8990
|
+
const nowMs2 = timestampMs(now, Date.now(), { parseString: true });
|
|
8991
|
+
if (!Number.isFinite(ms) || !Number.isFinite(nowMs2))
|
|
8992
|
+
return null;
|
|
8993
|
+
const age = Math.max(0, nowMs2 - ms);
|
|
8994
|
+
if (age < 30000)
|
|
8995
|
+
return ms + 30000;
|
|
8996
|
+
if (age < 60 * 60000)
|
|
8997
|
+
return ms + (Math.floor(age / 60000) + 1) * 60000;
|
|
8998
|
+
if (age < 24 * 60 * 60000)
|
|
8999
|
+
return ms + (Math.floor(age / (60 * 60000)) + 1) * 60 * 60000;
|
|
9000
|
+
return ms + (Math.floor(age / (24 * 60 * 60000)) + 1) * 24 * 60 * 60000;
|
|
9001
|
+
}
|
|
9002
|
+
var MINUTE_MS2 = 60000, HOUR_MS2;
|
|
9003
|
+
var init_time = __esm(() => {
|
|
9004
|
+
HOUR_MS2 = 60 * MINUTE_MS2;
|
|
9005
|
+
});
|
|
9006
|
+
|
|
9065
9007
|
// ../../core/moderation.js
|
|
9066
9008
|
function banUntilMs(ban) {
|
|
9067
9009
|
if (!ban || typeof ban !== "object" || Array.isArray(ban) || ban.until == null) {
|
|
@@ -9598,6 +9540,7 @@ function createUser({ cloud, network, avatarCache = null, diag = null, onSession
|
|
|
9598
9540
|
let avatarPreview;
|
|
9599
9541
|
let blockedSource = state.blocked;
|
|
9600
9542
|
let blockedSet = new Set(blockedSource);
|
|
9543
|
+
const blockedChanges = new Map;
|
|
9601
9544
|
let banTimer = null;
|
|
9602
9545
|
let unsubscribeAuth = NOOP;
|
|
9603
9546
|
let userWatches = [];
|
|
@@ -9611,11 +9554,27 @@ function createUser({ cloud, network, avatarCache = null, diag = null, onSession
|
|
|
9611
9554
|
function makeSnapshot() {
|
|
9612
9555
|
const bans = banState(state.banned);
|
|
9613
9556
|
const userBan = bans.full || bans.chat;
|
|
9557
|
+
const visibleBlocked = new Set(state.blocked);
|
|
9558
|
+
const blockedPending = [];
|
|
9559
|
+
for (const [uid, change] of blockedChanges) {
|
|
9560
|
+
if (change.blocked)
|
|
9561
|
+
visibleBlocked.add(uid);
|
|
9562
|
+
else
|
|
9563
|
+
visibleBlocked.delete(uid);
|
|
9564
|
+
if (!change.written)
|
|
9565
|
+
blockedPending.push(uid);
|
|
9566
|
+
}
|
|
9567
|
+
if (visibleBlocked.size !== blockedSet.size || [...visibleBlocked].some((uid) => !blockedSet.has(uid))) {
|
|
9568
|
+
blockedSet = visibleBlocked;
|
|
9569
|
+
}
|
|
9570
|
+
const blocked = snapshot?.blocked && snapshot.blocked.length === blockedSet.size && snapshot.blocked.every((uid) => blockedSet.has(uid)) ? snapshot.blocked : [...blockedSet];
|
|
9614
9571
|
return {
|
|
9615
9572
|
...state,
|
|
9616
9573
|
avatar: avatarPreview === undefined ? state.avatar : avatarPreview,
|
|
9617
9574
|
network: activeNetwork,
|
|
9618
9575
|
blockedSet,
|
|
9576
|
+
blocked,
|
|
9577
|
+
blockedPending: snapshot?.blockedPending && snapshot.blockedPending.length === blockedPending.length && snapshot.blockedPending.every((uid, index) => uid === blockedPending[index]) ? snapshot.blockedPending : blockedPending,
|
|
9619
9578
|
chatBanned: bans.chatBanned,
|
|
9620
9579
|
avatarBanned: bans.avatarBanned,
|
|
9621
9580
|
chatBanUntil: userBan?.until ?? null,
|
|
@@ -9899,6 +9858,7 @@ function createUser({ cloud, network, avatarCache = null, diag = null, onSession
|
|
|
9899
9858
|
const authStartedAt = Date.now();
|
|
9900
9859
|
const session = authSession + 1;
|
|
9901
9860
|
authSession = session;
|
|
9861
|
+
blockedChanges.clear();
|
|
9902
9862
|
closeUserWatches();
|
|
9903
9863
|
markDiag(diag, "user.auth.state", { signedIn: !!authUser });
|
|
9904
9864
|
if (!authUser) {
|
|
@@ -9998,6 +9958,20 @@ function createUser({ cloud, network, avatarCache = null, diag = null, onSession
|
|
|
9998
9958
|
setState((user) => ({ ...user, banned: null }));
|
|
9999
9959
|
})));
|
|
10000
9960
|
userWatches.push(cloud.user.blocked.watch(authUser.uid, current((blocked, info = {}) => {
|
|
9961
|
+
if (info.pending || info.fromCache && state.blockedReady)
|
|
9962
|
+
return;
|
|
9963
|
+
const confirmed = new Set(blocked);
|
|
9964
|
+
for (const [uid, change] of blockedChanges) {
|
|
9965
|
+
if (!change.written)
|
|
9966
|
+
continue;
|
|
9967
|
+
if (!info.fromCache && confirmed.has(uid) === change.blocked)
|
|
9968
|
+
blockedChanges.delete(uid);
|
|
9969
|
+
else if (state.blocked.includes(uid))
|
|
9970
|
+
confirmed.add(uid);
|
|
9971
|
+
else
|
|
9972
|
+
confirmed.delete(uid);
|
|
9973
|
+
}
|
|
9974
|
+
blocked = [...confirmed];
|
|
10001
9975
|
setState((user) => {
|
|
10002
9976
|
const blockedReady = user.blockedReady || !info.fromCache;
|
|
10003
9977
|
if (user.blockedReady === blockedReady && user.blocked.length === blocked.length && user.blocked.every((id, index) => id === blocked[index])) {
|
|
@@ -10073,6 +10047,7 @@ function createUser({ cloud, network, avatarCache = null, diag = null, onSession
|
|
|
10073
10047
|
if (!started)
|
|
10074
10048
|
return;
|
|
10075
10049
|
authSession += 1;
|
|
10050
|
+
blockedChanges.clear();
|
|
10076
10051
|
closeUserWatches();
|
|
10077
10052
|
unsubscribeAuth();
|
|
10078
10053
|
setState((user) => ({ ...user, authError: null, authSessionError: null, profileError: null, settingsError: null }));
|
|
@@ -10094,6 +10069,7 @@ function createUser({ cloud, network, avatarCache = null, diag = null, onSession
|
|
|
10094
10069
|
agreementStored = null;
|
|
10095
10070
|
agreementOverride = null;
|
|
10096
10071
|
agreementAcceptance = null;
|
|
10072
|
+
blockedChanges.clear();
|
|
10097
10073
|
userUid = null;
|
|
10098
10074
|
avatarFetch = { uid: null, key: null, promise: null };
|
|
10099
10075
|
avatarPreview = undefined;
|
|
@@ -10102,25 +10078,63 @@ function createUser({ cloud, network, avatarCache = null, diag = null, onSession
|
|
|
10102
10078
|
blockedSet = new Set(blockedSource);
|
|
10103
10079
|
snapshot = makeSnapshot();
|
|
10104
10080
|
}
|
|
10105
|
-
function
|
|
10081
|
+
function changePeerBlock(peer, blocked, { beforeCommit } = {}) {
|
|
10106
10082
|
const nextPeerUid = peerUid(peer);
|
|
10107
10083
|
const uid = cloud.auth.user?.uid;
|
|
10084
|
+
const session = authSession;
|
|
10108
10085
|
if (!uid)
|
|
10109
10086
|
throw new Error("auth");
|
|
10110
10087
|
if (!nextPeerUid)
|
|
10111
10088
|
throw new Error("peer uid required");
|
|
10112
|
-
if (nextPeerUid === uid)
|
|
10089
|
+
if (blocked && nextPeerUid === uid)
|
|
10113
10090
|
return;
|
|
10114
|
-
|
|
10091
|
+
const prior = blockedChanges.get(nextPeerUid);
|
|
10092
|
+
if (prior && prior.blocked === blocked)
|
|
10093
|
+
return prior.promise;
|
|
10094
|
+
const current = () => isCurrentUser(uid, session);
|
|
10095
|
+
const assertCurrent = () => {
|
|
10096
|
+
if (!current())
|
|
10097
|
+
throw new Error("account changed during block update");
|
|
10098
|
+
};
|
|
10099
|
+
const change = { blocked, written: false, promise: null };
|
|
10100
|
+
change.promise = Promise.resolve().then(async () => {
|
|
10101
|
+
try {
|
|
10102
|
+
await prior?.promise.catch(NOOP);
|
|
10103
|
+
assertCurrent();
|
|
10104
|
+
await beforeCommit?.();
|
|
10105
|
+
assertCurrent();
|
|
10106
|
+
const result = await cloud.user.blocked[blocked ? "add" : "remove"](uid, nextPeerUid);
|
|
10107
|
+
assertCurrent();
|
|
10108
|
+
change.written = true;
|
|
10109
|
+
if (blockedChanges.get(nextPeerUid) === change && state.blocked.includes(nextPeerUid) === blocked) {
|
|
10110
|
+
blockedChanges.delete(nextPeerUid);
|
|
10111
|
+
}
|
|
10112
|
+
const confirmed = new Set(state.blocked);
|
|
10113
|
+
if (blocked)
|
|
10114
|
+
confirmed.add(nextPeerUid);
|
|
10115
|
+
else
|
|
10116
|
+
confirmed.delete(nextPeerUid);
|
|
10117
|
+
setState((user) => ({ ...user, blocked: [...confirmed] }));
|
|
10118
|
+
return result;
|
|
10119
|
+
} catch (error) {
|
|
10120
|
+
if (current() && blockedChanges.get(nextPeerUid) === change) {
|
|
10121
|
+
blockedChanges.delete(nextPeerUid);
|
|
10122
|
+
if (prior?.written)
|
|
10123
|
+
blockedChanges.set(nextPeerUid, prior);
|
|
10124
|
+
setState(state, true);
|
|
10125
|
+
}
|
|
10126
|
+
throw error;
|
|
10127
|
+
}
|
|
10128
|
+
});
|
|
10129
|
+
blockedChanges.set(nextPeerUid, change);
|
|
10130
|
+
setState(state, true);
|
|
10131
|
+
return change.promise;
|
|
10115
10132
|
}
|
|
10116
|
-
function
|
|
10117
|
-
|
|
10118
|
-
|
|
10119
|
-
|
|
10120
|
-
|
|
10121
|
-
if (!nextPeerUid)
|
|
10122
|
-
throw new Error("peer uid required");
|
|
10123
|
-
return cloud.user.blocked.remove(uid, nextPeerUid);
|
|
10133
|
+
function blockPeer(peer, options) {
|
|
10134
|
+
return changePeerBlock(peer, true, options);
|
|
10135
|
+
}
|
|
10136
|
+
function unblockPeer(peer, options) {
|
|
10137
|
+
return changePeerBlock(peer, false, options);
|
|
10124
10138
|
}
|
|
10125
10139
|
async function acceptAgreement(requestedAgreement = CURRENT_AGREEMENT) {
|
|
10126
10140
|
const uid = cloud.auth.user?.uid;
|
|
@@ -10334,6 +10348,13 @@ function createUser({ cloud, network, avatarCache = null, diag = null, onSession
|
|
|
10334
10348
|
syncAvatar();
|
|
10335
10349
|
return true;
|
|
10336
10350
|
}
|
|
10351
|
+
function restoreBlocked({ uid, blocked }) {
|
|
10352
|
+
if (!started || !uid || uid !== state.uid || cloud.auth.user?.uid !== uid)
|
|
10353
|
+
return;
|
|
10354
|
+
if (state.blockedReady || !Array.isArray(blocked))
|
|
10355
|
+
return;
|
|
10356
|
+
setState((user) => ({ ...user, blocked: [...blocked], blockedReady: true }));
|
|
10357
|
+
}
|
|
10337
10358
|
snapshot = makeSnapshot();
|
|
10338
10359
|
return {
|
|
10339
10360
|
getSnapshot() {
|
|
@@ -10359,6 +10380,7 @@ function createUser({ cloud, network, avatarCache = null, diag = null, onSession
|
|
|
10359
10380
|
previewAvatar,
|
|
10360
10381
|
confirmAvatar,
|
|
10361
10382
|
restoreBootstrap,
|
|
10383
|
+
restoreBlocked,
|
|
10362
10384
|
retry
|
|
10363
10385
|
};
|
|
10364
10386
|
}
|
|
@@ -22168,6 +22190,52 @@ var init_bootstrap = __esm(() => {
|
|
|
22168
22190
|
PROFILE_FIELDS = ["username", "identities", "walletPK", "chatPK", "chatSigningPK", "notificationPK", "chatAdmission", "accountType", "hasAvatarEntry", "avatarVersion"];
|
|
22169
22191
|
});
|
|
22170
22192
|
|
|
22193
|
+
// ../../core/chat/state.js
|
|
22194
|
+
function makeCid() {
|
|
22195
|
+
return `${Date.now().toString(36)}${toHex(randomBytes3(3))}`;
|
|
22196
|
+
}
|
|
22197
|
+
function getMessageKey(message) {
|
|
22198
|
+
return message?.cid || message?.id || null;
|
|
22199
|
+
}
|
|
22200
|
+
function getCidMs(cid) {
|
|
22201
|
+
if (typeof cid !== "string" || !/^[0-9a-z]+[0-9a-f]{6}$/u.test(cid)) {
|
|
22202
|
+
return null;
|
|
22203
|
+
}
|
|
22204
|
+
const base = cid.slice(0, -6);
|
|
22205
|
+
const ms = Number.parseInt(base, 36);
|
|
22206
|
+
return Number.isSafeInteger(ms) && ms > 0 ? ms : null;
|
|
22207
|
+
}
|
|
22208
|
+
function getMessageOrderMs(message) {
|
|
22209
|
+
return getCidMs(message?.cid) ?? timestampMs(message?.ts, Infinity);
|
|
22210
|
+
}
|
|
22211
|
+
function sortMessages(messages) {
|
|
22212
|
+
return [...messages].sort((a, b) => {
|
|
22213
|
+
const aMs = getMessageOrderMs(a);
|
|
22214
|
+
const bMs = getMessageOrderMs(b);
|
|
22215
|
+
if (aMs !== bMs) {
|
|
22216
|
+
return aMs - bMs;
|
|
22217
|
+
}
|
|
22218
|
+
return String(a?.id || "").localeCompare(String(b?.id || ""));
|
|
22219
|
+
});
|
|
22220
|
+
}
|
|
22221
|
+
function mergeMessages(...groups) {
|
|
22222
|
+
const merged = new Map;
|
|
22223
|
+
for (const group of groups) {
|
|
22224
|
+
for (const message of group || []) {
|
|
22225
|
+
const key = getMessageKey(message);
|
|
22226
|
+
if (!key) {
|
|
22227
|
+
continue;
|
|
22228
|
+
}
|
|
22229
|
+
merged.set(key, message);
|
|
22230
|
+
}
|
|
22231
|
+
}
|
|
22232
|
+
return sortMessages([...merged.values()]);
|
|
22233
|
+
}
|
|
22234
|
+
var init_state = __esm(() => {
|
|
22235
|
+
init_core();
|
|
22236
|
+
init_time();
|
|
22237
|
+
});
|
|
22238
|
+
|
|
22171
22239
|
// ../../core/chat/ids.js
|
|
22172
22240
|
function isChatMessageForParticipants(message, chatPK, peerChatPK, memberChatPKs = null) {
|
|
22173
22241
|
const members = Array.isArray(memberChatPKs) ? new Set(memberChatPKs.filter(Boolean)) : null;
|
|
@@ -24906,6 +24974,24 @@ async function openStateRecordsWithEvidence(epochState, records) {
|
|
|
24906
24974
|
closeStateEpoch(opened);
|
|
24907
24975
|
}
|
|
24908
24976
|
}
|
|
24977
|
+
async function readChatMemberState(cloud, epochState, actorChatPK) {
|
|
24978
|
+
if (!chatEpochCapabilities(epochState).sharedReceipts)
|
|
24979
|
+
return null;
|
|
24980
|
+
const member = epochState?.manifest?.members?.find((value) => value.chatPK === actorChatPK);
|
|
24981
|
+
if (!member)
|
|
24982
|
+
return null;
|
|
24983
|
+
const opened = openStateEpoch(epochState);
|
|
24984
|
+
try {
|
|
24985
|
+
const slotId = deriveChatMemberStateSlot(opened.epoch, member.chatSigningPK);
|
|
24986
|
+
const record = await cloud.chat.memberState.readSlot(opened.epoch.epochId, slotId);
|
|
24987
|
+
if (!record || !stateRecordActive(record))
|
|
24988
|
+
return null;
|
|
24989
|
+
const state = await openChatMemberState(opened.epoch, record);
|
|
24990
|
+
return state.actor === actorChatPK ? stateRecordProjection(opened.epoch.epochId, record, state) : null;
|
|
24991
|
+
} finally {
|
|
24992
|
+
closeStateEpoch(opened);
|
|
24993
|
+
}
|
|
24994
|
+
}
|
|
24909
24995
|
function watchChatMemberStateEvidence(cloud, epochState, onUpdate, onError) {
|
|
24910
24996
|
if (!chatEpochCapabilities(epochState).sharedReceipts)
|
|
24911
24997
|
return () => {};
|
|
@@ -26093,8 +26179,6 @@ function cleanChatPayment(message, { requireTx }) {
|
|
|
26093
26179
|
} catch {
|
|
26094
26180
|
return null;
|
|
26095
26181
|
}
|
|
26096
|
-
if (message.mode || message.transfers?.length !== 1)
|
|
26097
|
-
return null;
|
|
26098
26182
|
} else if (message.tokenIdentifier != null)
|
|
26099
26183
|
return null;
|
|
26100
26184
|
const transfers = cleanChatPaymentTransfers(message?.transfers, { requireTx, token });
|
|
@@ -26106,6 +26190,13 @@ function cleanChatPayment(message, { requireTx }) {
|
|
|
26106
26190
|
if (transfers.length > 1 && !mode || rawMode && !mode)
|
|
26107
26191
|
return null;
|
|
26108
26192
|
const total = transfers.reduce((sum, transfer) => sum + BigInt(transfer.a), 0n);
|
|
26193
|
+
if (token) {
|
|
26194
|
+
try {
|
|
26195
|
+
tokenUnits(total);
|
|
26196
|
+
} catch {
|
|
26197
|
+
return null;
|
|
26198
|
+
}
|
|
26199
|
+
}
|
|
26109
26200
|
if (!mode && (transfers.length !== 1 || BigInt(transfers[0].a) !== amount))
|
|
26110
26201
|
return null;
|
|
26111
26202
|
if (mode === GROUP_SEND_MODES.EACH && transfers.some((transfer) => BigInt(transfer.a) !== amount))
|
|
@@ -26131,14 +26222,14 @@ function makeChatPaymentPayload({ amountSats, amountUnits, tokenIdentifier, tran
|
|
|
26131
26222
|
if (token) {
|
|
26132
26223
|
tokenAsset(tokenIdentifier, paymentNetwork);
|
|
26133
26224
|
tokenUnits(amount);
|
|
26134
|
-
if (mode || transfers.length !== 1)
|
|
26135
|
-
throw new Error("token payments require one direct recipient");
|
|
26136
26225
|
}
|
|
26137
26226
|
const rawMode = cleanText(mode);
|
|
26138
26227
|
const groupMode = GROUP_PAYMENT_MODES.has(rawMode) ? rawMode : null;
|
|
26139
26228
|
if (transfers.length > 1 && !groupMode || rawMode && !groupMode)
|
|
26140
26229
|
throw new Error("chat payment mode required");
|
|
26141
26230
|
const total = transfers.reduce((sum, transfer) => sum + BigInt(transfer.a), 0n);
|
|
26231
|
+
if (token)
|
|
26232
|
+
tokenUnits(total);
|
|
26142
26233
|
if (!groupMode && (transfers.length !== 1 || BigInt(transfers[0].a) !== amount)) {
|
|
26143
26234
|
throw new Error("direct payment amount must match its transfer");
|
|
26144
26235
|
}
|
|
@@ -26188,8 +26279,6 @@ function chatPaymentMembersValid(message, payment) {
|
|
|
26188
26279
|
if (sender && recipients.includes(sender))
|
|
26189
26280
|
return false;
|
|
26190
26281
|
const epochMembers = Array.isArray(message?.epochMemberChatPKs) ? new Set(message.epochMemberChatPKs.map((member) => cleanText(member).toLowerCase()).filter(Boolean)) : null;
|
|
26191
|
-
if (message.tokenIdentifier != null && epochMembers?.size > 2)
|
|
26192
|
-
return false;
|
|
26193
26282
|
return !epochMembers?.size || recipients.every((recipient) => epochMembers.has(recipient));
|
|
26194
26283
|
}
|
|
26195
26284
|
function isChatPayment(message) {
|
|
@@ -26797,7 +26886,6 @@ var init_control = __esm(() => {
|
|
|
26797
26886
|
init_files2();
|
|
26798
26887
|
init_text();
|
|
26799
26888
|
init_tab2();
|
|
26800
|
-
init_call();
|
|
26801
26889
|
init_request();
|
|
26802
26890
|
init_types();
|
|
26803
26891
|
sourceGoneVisibleMessageCache = new WeakMap;
|
|
@@ -26913,17 +27001,10 @@ function analyzeMessageExpiry(messages, selfChatPublicKey, _peerChatPublicKey, o
|
|
|
26913
27001
|
const heldExpired = [];
|
|
26914
27002
|
const shorten = [];
|
|
26915
27003
|
let nextExpiryAt = null;
|
|
26916
|
-
const latestStart = latestCallStartKey(projectedMessages.filter(isServerConfirmedMsg));
|
|
26917
|
-
const endedCalls = new Set(projectedMessages.filter(isServerConfirmedMsg).filter((message) => readCallMessage(message)?.event === "ended").map((message) => message.callId));
|
|
26918
27004
|
for (const message of projectedMessages) {
|
|
26919
27005
|
if (!isServerConfirmedMsg(message) || !canShowMsg(message) || message.ttl == null)
|
|
26920
27006
|
continue;
|
|
26921
|
-
const
|
|
26922
|
-
if (call?.event === "started" && getMessageKey(message) === latestStart && (options.activeCallId === undefined && !endedCalls.has(call.callId) || options.activeCallId === call.callId))
|
|
26923
|
-
continue;
|
|
26924
|
-
const readTarget = isPaymentRequest(message) ? message.paymentConfirmation : message;
|
|
26925
|
-
if (!readTarget)
|
|
26926
|
-
continue;
|
|
27007
|
+
const readTarget = isPaymentRequest(message) && message.paymentConfirmation ? message.paymentConfirmation : message;
|
|
26927
27008
|
const originalSeenAt = allReadersSeenAt(message, members, evidence, options.memberStatesByEpoch);
|
|
26928
27009
|
const resultSeenAt = readTarget === message ? originalSeenAt : allReadersSeenAt(readTarget, members, evidence, options.memberStatesByEpoch);
|
|
26929
27010
|
if (originalSeenAt == null || resultSeenAt == null)
|
|
@@ -26969,7 +27050,6 @@ var init_expiry = __esm(() => {
|
|
|
26969
27050
|
init_control();
|
|
26970
27051
|
init_receipt();
|
|
26971
27052
|
init_config();
|
|
26972
|
-
init_call();
|
|
26973
27053
|
init_request();
|
|
26974
27054
|
});
|
|
26975
27055
|
|
|
@@ -27119,7 +27199,6 @@ function sendableMsgPayload(message) {
|
|
|
27119
27199
|
var init_compose = __esm(() => {
|
|
27120
27200
|
init_control();
|
|
27121
27201
|
init_messagekeys();
|
|
27122
|
-
init_call();
|
|
27123
27202
|
});
|
|
27124
27203
|
|
|
27125
27204
|
// ../../core/chat/messages/preview.js
|
|
@@ -27471,7 +27550,6 @@ var init_preview = __esm(() => {
|
|
|
27471
27550
|
init_files2();
|
|
27472
27551
|
init_text();
|
|
27473
27552
|
init_request();
|
|
27474
|
-
init_call();
|
|
27475
27553
|
CHAT_PREVIEW_TEXT = Object.freeze({
|
|
27476
27554
|
hidden: "",
|
|
27477
27555
|
fallback: "sent a message",
|
|
@@ -27498,7 +27576,6 @@ var init_messages = __esm(() => {
|
|
|
27498
27576
|
init_preview();
|
|
27499
27577
|
init_request();
|
|
27500
27578
|
init_tab2();
|
|
27501
|
-
init_call();
|
|
27502
27579
|
"use client";
|
|
27503
27580
|
});
|
|
27504
27581
|
|
|
@@ -31160,6 +31237,20 @@ function uniqueValues(items) {
|
|
|
31160
31237
|
function sortedUniqueValues(items) {
|
|
31161
31238
|
return uniqueValues(items).sort();
|
|
31162
31239
|
}
|
|
31240
|
+
function sameArray(a, b) {
|
|
31241
|
+
if (a === b) {
|
|
31242
|
+
return true;
|
|
31243
|
+
}
|
|
31244
|
+
if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) {
|
|
31245
|
+
return false;
|
|
31246
|
+
}
|
|
31247
|
+
for (let index = 0;index < a.length; index += 1) {
|
|
31248
|
+
if (a[index] !== b[index]) {
|
|
31249
|
+
return false;
|
|
31250
|
+
}
|
|
31251
|
+
}
|
|
31252
|
+
return true;
|
|
31253
|
+
}
|
|
31163
31254
|
|
|
31164
31255
|
// ../../core/chat/chats.js
|
|
31165
31256
|
function sameSigningKeys(left, right) {
|
|
@@ -32683,7 +32774,8 @@ function writeCachedChats(cache, chats, options = {}) {
|
|
|
32683
32774
|
}
|
|
32684
32775
|
}
|
|
32685
32776
|
for (const item of items) {
|
|
32686
|
-
|
|
32777
|
+
const previous = reviveChat(payload.chatsById?.[item.id]);
|
|
32778
|
+
byId.set(item.id, previous ? serializeChat(mergeChatActivity(item, previous, options.chatPK)) : item);
|
|
32687
32779
|
}
|
|
32688
32780
|
const merged = [...byId.values()];
|
|
32689
32781
|
const capped = merged.length > LOCAL_CHAT_CACHE_MAX_ITEMS;
|
|
@@ -32859,18 +32951,24 @@ function readCachedReadFrontiers(cache, chatId) {
|
|
|
32859
32951
|
return [];
|
|
32860
32952
|
return Object.values(source).map(cleanFrontier2).filter(Boolean);
|
|
32861
32953
|
}
|
|
32862
|
-
function
|
|
32954
|
+
function writeCachedChatRead(cache, chatId, { frontier: value, message, openedAt, chatPK } = {}) {
|
|
32863
32955
|
const frontier = cleanFrontier2(value);
|
|
32864
|
-
if (!cache?.patch || !chatId || !frontier)
|
|
32956
|
+
if (!cache?.patch || !chatId || !frontier && !message)
|
|
32865
32957
|
return Promise.resolve(false);
|
|
32866
32958
|
return cache.patch((payload) => {
|
|
32867
|
-
|
|
32868
|
-
|
|
32869
|
-
|
|
32870
|
-
|
|
32871
|
-
|
|
32872
|
-
|
|
32873
|
-
|
|
32959
|
+
if (frontier) {
|
|
32960
|
+
const chats = payload.pendingReadFrontiersByChat || {};
|
|
32961
|
+
const current = chats[chatId] || {};
|
|
32962
|
+
chats[chatId] = {
|
|
32963
|
+
...current,
|
|
32964
|
+
[frontier.epochId]: laterFrontier(cleanFrontier2(current[frontier.epochId]), frontier)
|
|
32965
|
+
};
|
|
32966
|
+
payload.pendingReadFrontiersByChat = chats;
|
|
32967
|
+
}
|
|
32968
|
+
const chat = payload.chatsById?.[chatId];
|
|
32969
|
+
if (chat && message) {
|
|
32970
|
+
[payload.chatsById[chatId]] = markChatsRead([chat], chatId, message, { openedAt, chatPK });
|
|
32971
|
+
}
|
|
32874
32972
|
return payload;
|
|
32875
32973
|
}, { flush: true }).then(() => true);
|
|
32876
32974
|
}
|
|
@@ -32895,6 +32993,7 @@ function dropCachedReadFrontier(cache, chatId, value) {
|
|
|
32895
32993
|
}, { flush: true }).then(() => true);
|
|
32896
32994
|
}
|
|
32897
32995
|
var init_reads = __esm(() => {
|
|
32996
|
+
init_read();
|
|
32898
32997
|
"use client";
|
|
32899
32998
|
});
|
|
32900
32999
|
|
|
@@ -33848,6 +33947,15 @@ function createPendingSendQueue({ diag = null } = {}) {
|
|
|
33848
33947
|
const generationRef = { current: 0 };
|
|
33849
33948
|
const sentAtRef = { current: [] };
|
|
33850
33949
|
const timerRef = { current: null };
|
|
33950
|
+
let active = true;
|
|
33951
|
+
let scheduleVersion = 0;
|
|
33952
|
+
function clearSchedule() {
|
|
33953
|
+
scheduleVersion += 1;
|
|
33954
|
+
if (timerRef.current !== null)
|
|
33955
|
+
clearTimeout(timerRef.current);
|
|
33956
|
+
timerRef.current = null;
|
|
33957
|
+
scheduledRef.current = false;
|
|
33958
|
+
}
|
|
33851
33959
|
function nextRateDelay() {
|
|
33852
33960
|
const now = Date.now();
|
|
33853
33961
|
const since = now - CHAT_SEND_QUEUE_RATE_LIMIT_WINDOW_MS;
|
|
@@ -33889,6 +33997,8 @@ function createPendingSendQueue({ diag = null } = {}) {
|
|
|
33889
33997
|
}
|
|
33890
33998
|
}
|
|
33891
33999
|
const flush = () => {
|
|
34000
|
+
if (!active)
|
|
34001
|
+
return;
|
|
33892
34002
|
if (scheduledRef.current || !queueRef.current.length) {
|
|
33893
34003
|
markDiag(diag, "chat.send.queue.flush.skip", {
|
|
33894
34004
|
scheduled: !!scheduledRef.current,
|
|
@@ -33914,7 +34024,10 @@ function createPendingSendQueue({ diag = null } = {}) {
|
|
|
33914
34024
|
running: runningJobsRef.current.size,
|
|
33915
34025
|
sentWindow: sentAtRef.current.length
|
|
33916
34026
|
});
|
|
34027
|
+
const version = ++scheduleVersion;
|
|
33917
34028
|
timerRef.current = setTimeout(() => {
|
|
34029
|
+
if (!active || version !== scheduleVersion)
|
|
34030
|
+
return;
|
|
33918
34031
|
timerRef.current = null;
|
|
33919
34032
|
scheduledRef.current = false;
|
|
33920
34033
|
const nextIndex = nextRunnableJobIndex();
|
|
@@ -34009,6 +34122,7 @@ function createPendingSendQueue({ diag = null } = {}) {
|
|
|
34009
34122
|
};
|
|
34010
34123
|
const resetPendingSendQueue = () => {
|
|
34011
34124
|
generationRef.current += 1;
|
|
34125
|
+
clearSchedule();
|
|
34012
34126
|
if (queueRef.current.length) {
|
|
34013
34127
|
const error = new Error("chat reset");
|
|
34014
34128
|
queueRef.current.forEach((job) => {
|
|
@@ -34017,16 +34131,18 @@ function createPendingSendQueue({ diag = null } = {}) {
|
|
|
34017
34131
|
});
|
|
34018
34132
|
}
|
|
34019
34133
|
queueRef.current = [];
|
|
34020
|
-
scheduledRef.current = false;
|
|
34021
34134
|
sentAtRef.current = [];
|
|
34022
|
-
if (timerRef.current) {
|
|
34023
|
-
clearTimeout(timerRef.current);
|
|
34024
|
-
timerRef.current = null;
|
|
34025
|
-
}
|
|
34026
34135
|
};
|
|
34027
34136
|
return {
|
|
34028
34137
|
enqueuePendingSendJob,
|
|
34029
34138
|
resetPendingSendQueue,
|
|
34139
|
+
setActive(value) {
|
|
34140
|
+
active = value === true;
|
|
34141
|
+
clearSchedule();
|
|
34142
|
+
markDiag(diag, "chat.send.queue.activity", { active, queued: queueRef.current.length, running: runningJobsRef.current.size });
|
|
34143
|
+
if (active)
|
|
34144
|
+
flush();
|
|
34145
|
+
},
|
|
34030
34146
|
close: resetPendingSendQueue
|
|
34031
34147
|
};
|
|
34032
34148
|
}
|
|
@@ -35737,6 +35853,7 @@ ${cid}` : "";
|
|
|
35737
35853
|
};
|
|
35738
35854
|
return {
|
|
35739
35855
|
resetSending,
|
|
35856
|
+
setActive: pendingSendQueueRef.current.setActive,
|
|
35740
35857
|
ackMessages,
|
|
35741
35858
|
stageMessage: showLocalMessage,
|
|
35742
35859
|
discardLocalMessage: (chatId, cid) => ackMessages(chatId, [cid], { remove: true }),
|
|
@@ -38444,7 +38561,7 @@ function createChatListCache({ getSources, state }) {
|
|
|
38444
38561
|
const pending = pendingWrite;
|
|
38445
38562
|
pendingWrite = null;
|
|
38446
38563
|
if (pending?.cache && Array.isArray(pending.chats)) {
|
|
38447
|
-
writeCachedChats(pending.cache, pending.chats, { ownerListToken: pending.ownerListToken });
|
|
38564
|
+
writeCachedChats(pending.cache, pending.chats, { ownerListToken: pending.ownerListToken, chatPK: pending.chatPK });
|
|
38448
38565
|
}
|
|
38449
38566
|
};
|
|
38450
38567
|
const queue = (nextChats, options = {}) => {
|
|
@@ -38456,6 +38573,7 @@ function createChatListCache({ getSources, state }) {
|
|
|
38456
38573
|
const requestedToken = requestedCoverage === CACHE_COVERAGE.COMPLETE ? cleanOwnerListToken(options.ownerListToken) : requestedCoverage === CACHE_COVERAGE.INCOMPLETE ? null : pendingWrite?.ownerListToken;
|
|
38457
38574
|
pendingWrite = {
|
|
38458
38575
|
cache: sources.localCache,
|
|
38576
|
+
chatPK: sources.chatPK,
|
|
38459
38577
|
chats: nextChats,
|
|
38460
38578
|
ownerListToken: requestedToken
|
|
38461
38579
|
};
|
|
@@ -38482,6 +38600,11 @@ function createChatListCache({ getSources, state }) {
|
|
|
38482
38600
|
state.setHasMoreChats(false);
|
|
38483
38601
|
}
|
|
38484
38602
|
hydrateReadCache(cachedChats, sources.readCacheRef.current);
|
|
38603
|
+
for (const chatId of Object.keys(cachedPayload?.pendingReadFrontiersByChat || {})) {
|
|
38604
|
+
const at = Math.max(0, ...readCachedReadFrontiers(sources.localCache, chatId).map((frontier) => frontier.at));
|
|
38605
|
+
if (at > (sources.readCacheRef.current.get(chatId) || 0))
|
|
38606
|
+
sources.readCacheRef.current.set(chatId, at);
|
|
38607
|
+
}
|
|
38485
38608
|
const cachedChatsWithRead = applyReadCache(cachedChats, sources.chatPK, sources.readCacheRef.current);
|
|
38486
38609
|
if (hasSavedChatSnapshot) {
|
|
38487
38610
|
const shownCachedChats = hydrateRows(cachedChatsWithRead);
|
|
@@ -41316,6 +41439,14 @@ var init_listepoch = __esm(() => {
|
|
|
41316
41439
|
});
|
|
41317
41440
|
|
|
41318
41441
|
// ../../core/chat/listindex.js
|
|
41442
|
+
function sameRecord(left, right) {
|
|
41443
|
+
if (left === right)
|
|
41444
|
+
return true;
|
|
41445
|
+
if (!left || !right)
|
|
41446
|
+
return false;
|
|
41447
|
+
const keys = Object.keys(left);
|
|
41448
|
+
return keys.length === Object.keys(right).length && keys.every((key) => Object.hasOwn(right, key) && Object.is(left[key], right[key]));
|
|
41449
|
+
}
|
|
41319
41450
|
function ownerRevision(chat) {
|
|
41320
41451
|
return Number.isSafeInteger(chat?.ownEntry?.ownerRevision) ? chat.ownEntry.ownerRevision : 0;
|
|
41321
41452
|
}
|
|
@@ -41345,11 +41476,26 @@ function activityFromChat(chat) {
|
|
|
41345
41476
|
unseen: chat.unseen === true
|
|
41346
41477
|
};
|
|
41347
41478
|
}
|
|
41348
|
-
function structuralChat(chat) {
|
|
41479
|
+
function structuralChat(chat, previous) {
|
|
41349
41480
|
if (!chat)
|
|
41350
41481
|
return null;
|
|
41482
|
+
const structure = {};
|
|
41483
|
+
if (previous && previous.epochId === chat.epochId && previous.epochVersion === chat.epochVersion) {
|
|
41484
|
+
for (const key of ["memberChatPKs", "memberUids"]) {
|
|
41485
|
+
if (sameArray(previous[key], chat[key]))
|
|
41486
|
+
structure[key] = previous[key];
|
|
41487
|
+
}
|
|
41488
|
+
for (const key of ["signingKeysByChatKey", "settings"]) {
|
|
41489
|
+
if (sameRecord(previous[key], chat[key]))
|
|
41490
|
+
structure[key] = previous[key];
|
|
41491
|
+
}
|
|
41492
|
+
if (Array.isArray(previous.members) && Array.isArray(chat.members) && previous.members.length === chat.members.length && previous.members.every((member, index) => sameRecord(member, chat.members[index]))) {
|
|
41493
|
+
structure.members = previous.members;
|
|
41494
|
+
}
|
|
41495
|
+
}
|
|
41351
41496
|
return {
|
|
41352
41497
|
...chat,
|
|
41498
|
+
...structure,
|
|
41353
41499
|
preview: null,
|
|
41354
41500
|
readMs: null,
|
|
41355
41501
|
inboxMessageId: null,
|
|
@@ -41413,7 +41559,7 @@ function sourceEntry() {
|
|
|
41413
41559
|
presentation: null
|
|
41414
41560
|
};
|
|
41415
41561
|
}
|
|
41416
|
-
function createChatListIndex({ cache, filterHiddenChats, getSources, isHiddenChatId, state }) {
|
|
41562
|
+
function createChatListIndex({ cache, filterHiddenChats, getSources, isHiddenChatId, state, onOwnersChanged }) {
|
|
41417
41563
|
const entries = new Map;
|
|
41418
41564
|
const authorityBoundaries = new Map;
|
|
41419
41565
|
let authorityRevision = 0;
|
|
@@ -41534,8 +41680,14 @@ function createChatListIndex({ cache, filterHiddenChats, getSources, isHiddenCha
|
|
|
41534
41680
|
const visible = sources.blockedReady ? filterBlockedChats(composedRows()) : [];
|
|
41535
41681
|
const trimmed = trimExpiredChatPreviews(visible);
|
|
41536
41682
|
const shown = sources.blockedReady ? sortedChats(filterBlockedChats(setLocalChats(trimmed, sources.localByChatRef.current))) : [];
|
|
41537
|
-
|
|
41538
|
-
|
|
41683
|
+
state.setChats((current) => {
|
|
41684
|
+
const byId = new Map(current.map((chat) => [chat.id, chat]));
|
|
41685
|
+
const next = shown.map((chat) => {
|
|
41686
|
+
const previous = byId.get(chat.id);
|
|
41687
|
+
return previous && sameChats([previous], [chat]) ? previous : chat;
|
|
41688
|
+
});
|
|
41689
|
+
return sameArray(current, next) ? current : next;
|
|
41690
|
+
});
|
|
41539
41691
|
const last = getLastChat(shown);
|
|
41540
41692
|
state.setLastChat((current) => sameLastChat(current, last) ? current : last);
|
|
41541
41693
|
updatePeers(shown);
|
|
@@ -41553,6 +41705,7 @@ function createChatListIndex({ cache, filterHiddenChats, getSources, isHiddenCha
|
|
|
41553
41705
|
if (options.warm !== false) {
|
|
41554
41706
|
sources.warmChats(sources.blockedReady ? filterBlockedChats(owners) : []);
|
|
41555
41707
|
}
|
|
41708
|
+
onOwnersChanged?.(owners);
|
|
41556
41709
|
return owners;
|
|
41557
41710
|
};
|
|
41558
41711
|
const applyBlockedFilter = () => {
|
|
@@ -41578,7 +41731,7 @@ function createChatListIndex({ cache, filterHiddenChats, getSources, isHiddenCha
|
|
|
41578
41731
|
for (const chat of filterHiddenChats(rows)) {
|
|
41579
41732
|
const entry = entryFor(chat.id);
|
|
41580
41733
|
captureActivity(entry, chat);
|
|
41581
|
-
entry.cache = structuralChat(chat);
|
|
41734
|
+
entry.cache = structuralChat(chat, entry.cache);
|
|
41582
41735
|
}
|
|
41583
41736
|
const shown = render();
|
|
41584
41737
|
updatePeers(shown);
|
|
@@ -41628,7 +41781,7 @@ function createChatListIndex({ cache, filterHiddenChats, getSources, isHiddenCha
|
|
|
41628
41781
|
for (const chat of incoming) {
|
|
41629
41782
|
const entry = entryFor(chat.id);
|
|
41630
41783
|
captureActivity(entry, chat);
|
|
41631
|
-
entry.observedOwner = selectOwner(entry.observedOwner, structuralChat(chat));
|
|
41784
|
+
entry.observedOwner = selectOwner(entry.observedOwner, structuralChat(chat, structuralOwnerFor(entry) || entry.cache));
|
|
41632
41785
|
if (entry.committedOwner && compareOwners(entry.observedOwner, entry.committedOwner) >= 0) {
|
|
41633
41786
|
entry.committedOwner = null;
|
|
41634
41787
|
}
|
|
@@ -41646,7 +41799,7 @@ function createChatListIndex({ cache, filterHiddenChats, getSources, isHiddenCha
|
|
|
41646
41799
|
for (const chat of (rows || []).filter((item) => item?.id && sourceCanPublish(item, options.sourceRevision))) {
|
|
41647
41800
|
const entry = entryFor(chat.id);
|
|
41648
41801
|
captureActivity(entry, chat);
|
|
41649
|
-
entry.observedOwner = selectOwner(entry.observedOwner, structuralChat(chat));
|
|
41802
|
+
entry.observedOwner = selectOwner(entry.observedOwner, structuralChat(chat, structuralOwnerFor(entry) || entry.cache));
|
|
41650
41803
|
if (entry.committedOwner && compareOwners(entry.observedOwner, entry.committedOwner) >= 0) {
|
|
41651
41804
|
entry.committedOwner = null;
|
|
41652
41805
|
}
|
|
@@ -41666,7 +41819,7 @@ function createChatListIndex({ cache, filterHiddenChats, getSources, isHiddenCha
|
|
|
41666
41819
|
return false;
|
|
41667
41820
|
const entry = entryFor(chat.id);
|
|
41668
41821
|
captureActivity(entry, chat);
|
|
41669
|
-
entry.committedOwner = selectOwner(entry.committedOwner, structuralChat(chat));
|
|
41822
|
+
entry.committedOwner = selectOwner(entry.committedOwner, structuralChat(chat, structuralOwnerFor(entry) || entry.cache));
|
|
41670
41823
|
if (ownerCoversAnnouncement(ownerFor(entry), entry.announcement))
|
|
41671
41824
|
entry.announcement = null;
|
|
41672
41825
|
entry.cache = null;
|
|
@@ -41842,6 +41995,9 @@ function createChatListIndex({ cache, filterHiddenChats, getSources, isHiddenCha
|
|
|
41842
41995
|
});
|
|
41843
41996
|
if (next === current)
|
|
41844
41997
|
return false;
|
|
41998
|
+
const readCache = getSources().readCacheRef.current;
|
|
41999
|
+
if (next.readMs > (readCache.get(chatId) || 0))
|
|
42000
|
+
readCache.set(chatId, next.readMs);
|
|
41845
42001
|
replaceActivity(entry, next);
|
|
41846
42002
|
publishOwners();
|
|
41847
42003
|
return true;
|
|
@@ -42521,6 +42677,79 @@ var init_listpreview = __esm(() => {
|
|
|
42521
42677
|
init_chats();
|
|
42522
42678
|
});
|
|
42523
42679
|
|
|
42680
|
+
// ../../core/chat/listreads.js
|
|
42681
|
+
function createChatListReads({ getSources, index }) {
|
|
42682
|
+
const attempted = new Set;
|
|
42683
|
+
let queue = [];
|
|
42684
|
+
let generation = 0;
|
|
42685
|
+
let running = false;
|
|
42686
|
+
const admitted = (chat, sources) => !sources.chatBanned && !(chat?.memberUids || []).some((uid) => sources.blockedUidSet.has(uid)) && chat?.unseen && chat.ownEntry && !isChatMembershipRemoved(chat);
|
|
42687
|
+
const eligible = (chat, sources) => sources.isActive && admitted(chat, sources);
|
|
42688
|
+
const pump = async () => {
|
|
42689
|
+
if (running)
|
|
42690
|
+
return;
|
|
42691
|
+
running = true;
|
|
42692
|
+
const run2 = generation;
|
|
42693
|
+
try {
|
|
42694
|
+
while (queue.length && run2 === generation) {
|
|
42695
|
+
const { chatId, epochId, key } = queue.shift();
|
|
42696
|
+
const sources = getSources();
|
|
42697
|
+
const chat = index.getOwnerChat(chatId);
|
|
42698
|
+
if (!eligible(chat, sources)) {
|
|
42699
|
+
attempted.delete(key);
|
|
42700
|
+
continue;
|
|
42701
|
+
}
|
|
42702
|
+
try {
|
|
42703
|
+
const epoch = epochId === chat.epochId ? chat.epochState : await sources.getEpochState(chat, epochId);
|
|
42704
|
+
if (run2 !== generation)
|
|
42705
|
+
return;
|
|
42706
|
+
const state = epoch ? await readChatMemberState(sources.cloud, epoch, sources.chatPK) : null;
|
|
42707
|
+
if (run2 !== generation || !admitted(index.getOwnerChat(chatId), getSources()))
|
|
42708
|
+
continue;
|
|
42709
|
+
const frontier = state?.readFrontier;
|
|
42710
|
+
if (frontier)
|
|
42711
|
+
index.markChatRead(chatId, { id: frontier.id, ts: frontier.at }, frontier.seenAt);
|
|
42712
|
+
} catch (error) {
|
|
42713
|
+
if (run2 !== generation)
|
|
42714
|
+
return;
|
|
42715
|
+
markError(sources.diag, "chat.list.read.restore", Date.now(), error);
|
|
42716
|
+
}
|
|
42717
|
+
}
|
|
42718
|
+
} finally {
|
|
42719
|
+
if (run2 === generation)
|
|
42720
|
+
running = false;
|
|
42721
|
+
}
|
|
42722
|
+
};
|
|
42723
|
+
return {
|
|
42724
|
+
restore(chats) {
|
|
42725
|
+
const sources = getSources();
|
|
42726
|
+
if (typeof sources.cloud?.chat?.memberState?.readSlot !== "function")
|
|
42727
|
+
return;
|
|
42728
|
+
for (const chat of chats) {
|
|
42729
|
+
if (!eligible(chat, sources))
|
|
42730
|
+
continue;
|
|
42731
|
+
const epochId = chat.preview?.epochId || chat.epochId;
|
|
42732
|
+
const key = `${chat.id}:${epochId}`;
|
|
42733
|
+
if (attempted.has(key))
|
|
42734
|
+
continue;
|
|
42735
|
+
attempted.add(key);
|
|
42736
|
+
queue.push({ chatId: chat.id, epochId, key });
|
|
42737
|
+
}
|
|
42738
|
+
return pump();
|
|
42739
|
+
},
|
|
42740
|
+
reset() {
|
|
42741
|
+
generation += 1;
|
|
42742
|
+
attempted.clear();
|
|
42743
|
+
queue = [];
|
|
42744
|
+
running = false;
|
|
42745
|
+
}
|
|
42746
|
+
};
|
|
42747
|
+
}
|
|
42748
|
+
var init_listreads = __esm(() => {
|
|
42749
|
+
init_memberstate();
|
|
42750
|
+
init_entry();
|
|
42751
|
+
});
|
|
42752
|
+
|
|
42524
42753
|
// ../../core/chat/liststate.js
|
|
42525
42754
|
function nextValue(current, next) {
|
|
42526
42755
|
return typeof next === "function" ? next(current) : next;
|
|
@@ -42699,7 +42928,16 @@ function createChatList(initialSources) {
|
|
|
42699
42928
|
const filterHiddenChats = (chats) => (chats || []).filter((chatItem) => chatItem?.id && !isHiddenChatId(chatItem.id));
|
|
42700
42929
|
const state = createChatListState({ getSources });
|
|
42701
42930
|
const cache = createChatListCache({ getSources, state });
|
|
42702
|
-
|
|
42931
|
+
let reads = null;
|
|
42932
|
+
const index = createChatListIndex({
|
|
42933
|
+
cache,
|
|
42934
|
+
filterHiddenChats,
|
|
42935
|
+
getSources,
|
|
42936
|
+
isHiddenChatId,
|
|
42937
|
+
state,
|
|
42938
|
+
onOwnersChanged: (chats) => reads?.restore(chats)
|
|
42939
|
+
});
|
|
42940
|
+
reads = createChatListReads({ getSources, index });
|
|
42703
42941
|
const epoch = createChatListEpochOwner({ getGeneration, getSources, index });
|
|
42704
42942
|
const paging = createChatListPaging({ cache, getGeneration, getSources, index, isHiddenChatId, state });
|
|
42705
42943
|
const invalidateRuntimeTasks = () => {
|
|
@@ -42708,6 +42946,7 @@ function createChatList(initialSources) {
|
|
|
42708
42946
|
epoch.reset();
|
|
42709
42947
|
};
|
|
42710
42948
|
const resetChatList = (ready = false) => {
|
|
42949
|
+
reads.reset();
|
|
42711
42950
|
invalidateRuntimeTasks();
|
|
42712
42951
|
index.reset(ready);
|
|
42713
42952
|
};
|
|
@@ -42758,6 +42997,7 @@ function createChatList(initialSources) {
|
|
|
42758
42997
|
index.applyBlockedFilter();
|
|
42759
42998
|
paging.fillVisibleChatPage();
|
|
42760
42999
|
}
|
|
43000
|
+
reads.restore(index.getOwnerChats());
|
|
42761
43001
|
};
|
|
42762
43002
|
const start2 = () => {
|
|
42763
43003
|
if (listener.isListening())
|
|
@@ -42766,6 +43006,7 @@ function createChatList(initialSources) {
|
|
|
42766
43006
|
preview2.schedule();
|
|
42767
43007
|
};
|
|
42768
43008
|
const close = () => {
|
|
43009
|
+
reads.reset();
|
|
42769
43010
|
listener.close();
|
|
42770
43011
|
preview2.close();
|
|
42771
43012
|
cache.flush();
|
|
@@ -42811,6 +43052,7 @@ var init_listsession = __esm(() => {
|
|
|
42811
43052
|
init_listlistener();
|
|
42812
43053
|
init_listpaging();
|
|
42813
43054
|
init_listpreview();
|
|
43055
|
+
init_listreads();
|
|
42814
43056
|
});
|
|
42815
43057
|
|
|
42816
43058
|
// ../../core/chat/inboxtransport.js
|
|
@@ -45193,7 +45435,7 @@ function chatDisplayTitle(chat, selfChatPK, peerByChatPK, options = {}) {
|
|
|
45193
45435
|
}));
|
|
45194
45436
|
if (chat?.lineage === "direct" && names.length === 1)
|
|
45195
45437
|
return names[0];
|
|
45196
|
-
const shown = names.slice(0, 3);
|
|
45438
|
+
const shown = names.slice(0, options.maxNames ?? 3);
|
|
45197
45439
|
const extra = names.length - shown.length;
|
|
45198
45440
|
return `${shown.join(", ")}${extra > 0 ? ` +${extra}` : ""}`;
|
|
45199
45441
|
}
|
|
@@ -45855,9 +46097,6 @@ function createChatSessionPending({
|
|
|
45855
46097
|
return track(launchId, previousOperation.then((result) => sendDurableSharedAttachment(result?.chatId || launchId, queuedAttachment, options)));
|
|
45856
46098
|
};
|
|
45857
46099
|
const sendChatMessage = (chatId, message, options = {}) => {
|
|
45858
|
-
const chat = getOwnerChat(resolvePendingChatId(chatId)) || launchForChat(chatId)?.chat;
|
|
45859
|
-
if (message?.tokenIdentifier != null && chat?.lineage === "group")
|
|
45860
|
-
throw new Error("group payments support bitcoin only");
|
|
45861
46100
|
const operation = operationForChat(chatId);
|
|
45862
46101
|
if (operation?.discarding)
|
|
45863
46102
|
throw makeChatUnavailableError();
|
|
@@ -47315,7 +47554,6 @@ function createChatSeen({
|
|
|
47315
47554
|
const scheduleRead = (chatId, message, previewMs, frontierMessage, messages = []) => {
|
|
47316
47555
|
const frontier = Number.isSafeInteger(frontierMessage?.at) && frontierMessage?.epochId ? frontierMessage : readFrontierFromMessage(frontierMessage, { seenAt: previewMs });
|
|
47317
47556
|
if (frontier) {
|
|
47318
|
-
writeCachedReadFrontier(localCache, chatId, frontier);
|
|
47319
47557
|
onReadFrontier?.(chatId, frontier);
|
|
47320
47558
|
}
|
|
47321
47559
|
const timing = getReadWriteTiming?.(chatId) || {};
|
|
@@ -47349,6 +47587,12 @@ function createChatSeen({
|
|
|
47349
47587
|
const frontier = readFrontierFromMessage(frontierMessage, { seenAt: read?.previewMs || Date.now() });
|
|
47350
47588
|
if (!read && !frontier && !cachedFrontiers.length)
|
|
47351
47589
|
return false;
|
|
47590
|
+
const savedRead = writeCachedChatRead(localCache, chatId, {
|
|
47591
|
+
frontier,
|
|
47592
|
+
message: read?.preview,
|
|
47593
|
+
openedAt: read?.previewMs,
|
|
47594
|
+
chatPK
|
|
47595
|
+
}).catch((error) => markError(diag, "chat.read.cache", Date.now(), error));
|
|
47352
47596
|
if (read) {
|
|
47353
47597
|
markChatRead?.(chatId, read.preview, read.previewMs);
|
|
47354
47598
|
readCacheRef.current.set(chatId, read.readMs);
|
|
@@ -47358,6 +47602,7 @@ function createChatSeen({
|
|
|
47358
47602
|
for (const cached of cachedFrontiers)
|
|
47359
47603
|
scheduleRead(chatId, preview2, previewMs, cached, messages);
|
|
47360
47604
|
scheduleRead(chatId, preview2, previewMs, frontierMessage, messages);
|
|
47605
|
+
await savedRead;
|
|
47361
47606
|
return true;
|
|
47362
47607
|
};
|
|
47363
47608
|
const markChatReadState = async (chatId, message, options = {}) => {
|
|
@@ -47458,6 +47703,7 @@ function createChatSessionActions({
|
|
|
47458
47703
|
diag,
|
|
47459
47704
|
readWriteInterval,
|
|
47460
47705
|
adoptLocalMessageMedia,
|
|
47706
|
+
getActive,
|
|
47461
47707
|
getIdentity,
|
|
47462
47708
|
getLiveActivity,
|
|
47463
47709
|
getOwnerChat,
|
|
@@ -47589,6 +47835,7 @@ function createChatSessionActions({
|
|
|
47589
47835
|
reconcileEpoch: (chatId) => getChatListOwner().reconcileChatEpoch(chatId),
|
|
47590
47836
|
diag
|
|
47591
47837
|
});
|
|
47838
|
+
sendActions.setActive(getActive());
|
|
47592
47839
|
reactionActions = createChatReaction({
|
|
47593
47840
|
cloud,
|
|
47594
47841
|
uid: identity().uid,
|
|
@@ -47742,10 +47989,10 @@ function sameReadFrontier(left, right) {
|
|
|
47742
47989
|
return left.actor === right.actor && left.readFrontier?.epochId === right.readFrontier?.epochId && left.readFrontier?.id === right.readFrontier?.id && left.readFrontier?.at === right.readFrontier?.at && left.readFrontier?.seenAt === right.readFrontier?.seenAt;
|
|
47743
47990
|
}
|
|
47744
47991
|
function sameActivity2(left, right) {
|
|
47745
|
-
return left.connected === right.connected && left.activeChatPKs.length === right.activeChatPKs.length && left.compositions.length === right.compositions.length && left.readFrontiers.length === right.readFrontiers.length && left.activeChatPKs.every((value, index) => value === right.activeChatPKs[index]) && left.compositions.every((value, index) => sameComposition(value, right.compositions[index])) && left.readFrontiers.every((value, index) => sameReadFrontier(value, right.readFrontiers[index]));
|
|
47992
|
+
return left.connected === right.connected && left.lastObservedAtByChatPK === right.lastObservedAtByChatPK && left.activeChatPKs.length === right.activeChatPKs.length && left.compositions.length === right.compositions.length && left.readFrontiers.length === right.readFrontiers.length && left.activeChatPKs.every((value, index) => value === right.activeChatPKs[index]) && left.compositions.every((value, index) => sameComposition(value, right.compositions[index])) && left.readFrontiers.every((value, index) => sameReadFrontier(value, right.readFrontiers[index]));
|
|
47746
47993
|
}
|
|
47747
47994
|
function hasActivity(activity) {
|
|
47748
|
-
return !!(activity?.connected || activity?.activeChatPKs?.length || activity?.compositions?.length || activity?.readFrontiers?.length);
|
|
47995
|
+
return !!(activity?.connected || activity?.activeChatPKs?.length || activity?.compositions?.length || activity?.readFrontiers?.length || Object.keys(activity?.lastObservedAtByChatPK || {}).length);
|
|
47749
47996
|
}
|
|
47750
47997
|
function summarizeLocalMessage(summary, message) {
|
|
47751
47998
|
summary.messages += 1;
|
|
@@ -47886,6 +48133,17 @@ function createChatSessionState({
|
|
|
47886
48133
|
return;
|
|
47887
48134
|
const next = new Map(liveActivityByChat);
|
|
47888
48135
|
const previous = next.get(chatId);
|
|
48136
|
+
let lastObservedAtByChatPK = previous?.lastObservedAtByChatPK;
|
|
48137
|
+
const departed = previous?.connected ? previous.activeChatPKs.filter((actor) => !activity?.connected || !activity.activeChatPKs.includes(actor)) : [];
|
|
48138
|
+
if (departed.length) {
|
|
48139
|
+
const now = Date.now();
|
|
48140
|
+
lastObservedAtByChatPK = { ...lastObservedAtByChatPK };
|
|
48141
|
+
for (const actor of departed)
|
|
48142
|
+
lastObservedAtByChatPK[actor] = Math.max(lastObservedAtByChatPK[actor] || 0, now);
|
|
48143
|
+
Object.freeze(lastObservedAtByChatPK);
|
|
48144
|
+
}
|
|
48145
|
+
if (lastObservedAtByChatPK)
|
|
48146
|
+
activity = { ...activity, lastObservedAtByChatPK };
|
|
47889
48147
|
if (previous?.connected && !activity?.connected)
|
|
47890
48148
|
onLiveDisconnect(chatId);
|
|
47891
48149
|
if (hasActivity(activity)) {
|
|
@@ -48299,6 +48557,7 @@ function normalizeChatSessionSources(sources = {}) {
|
|
|
48299
48557
|
uid: sources.uid || "",
|
|
48300
48558
|
online: sources.online !== false,
|
|
48301
48559
|
blocked: Array.isArray(sources.blocked) ? sources.blocked : [],
|
|
48560
|
+
blockedPending: Array.isArray(sources.blockedPending) ? sources.blockedPending : [],
|
|
48302
48561
|
blockedReady: sources.blockedReady === true,
|
|
48303
48562
|
chatPK: sources.chatPK || "",
|
|
48304
48563
|
chatBanned: sources.chatBanned === true,
|
|
@@ -48348,7 +48607,6 @@ function createChatSession({
|
|
|
48348
48607
|
mls = null,
|
|
48349
48608
|
live = null,
|
|
48350
48609
|
maintenance,
|
|
48351
|
-
resolveActiveCall,
|
|
48352
48610
|
incomingChatDecision = null,
|
|
48353
48611
|
getPeerProfile = null,
|
|
48354
48612
|
refreshPeerProfile = null,
|
|
@@ -48366,6 +48624,7 @@ function createChatSession({
|
|
|
48366
48624
|
uid = "",
|
|
48367
48625
|
online = true,
|
|
48368
48626
|
blocked = [],
|
|
48627
|
+
blockedPending = [],
|
|
48369
48628
|
blockedReady = false,
|
|
48370
48629
|
chatPK = "",
|
|
48371
48630
|
chatSigningPK = "",
|
|
@@ -48382,6 +48641,7 @@ function createChatSession({
|
|
|
48382
48641
|
let inboxEnabled = initialSources.inboxEnabled === true;
|
|
48383
48642
|
let started = false;
|
|
48384
48643
|
let appStateSubscription = null;
|
|
48644
|
+
const messageListeners = new Set;
|
|
48385
48645
|
let mlsPoolReplenishment = null;
|
|
48386
48646
|
let mlsPoolReplenishQueued = false;
|
|
48387
48647
|
let mlsPoolNeedsReplenish = true;
|
|
@@ -48556,6 +48816,13 @@ function createChatSession({
|
|
|
48556
48816
|
};
|
|
48557
48817
|
const handleBatchMessages = (chatId, messages) => {
|
|
48558
48818
|
chatListOwner.observeMessages(chatId, messages);
|
|
48819
|
+
for (const listener of messageListeners) {
|
|
48820
|
+
try {
|
|
48821
|
+
listener(chatId, messages);
|
|
48822
|
+
} catch (error) {
|
|
48823
|
+
diag?.("chat.message.observer.error", { code: error?.code || "chat/observer" });
|
|
48824
|
+
}
|
|
48825
|
+
}
|
|
48559
48826
|
if (![...messages || []].reverse().some(isEpochTransitionMsg))
|
|
48560
48827
|
return;
|
|
48561
48828
|
const startedAt = Date.now();
|
|
@@ -48988,8 +49255,6 @@ function createChatSession({
|
|
|
48988
49255
|
const chat = getOwnerChat(chatId);
|
|
48989
49256
|
if (!chat)
|
|
48990
49257
|
throw makeChatUnavailableError();
|
|
48991
|
-
if (payment.tokenIdentifier != null && chat.lineage !== "direct")
|
|
48992
|
-
throw new Error("group payments support bitcoin only");
|
|
48993
49258
|
const members = Array.isArray(chat?.memberChatPKs) ? chat.memberChatPKs.map((memberChatPK) => cleanText(memberChatPK).toLowerCase()) : (chat?.members || []).map((member) => cleanText(member?.chatPK).toLowerCase());
|
|
48994
49259
|
const message = pending ? makePendingChatPayment(payment) : makeChatPayment(payment);
|
|
48995
49260
|
const recipients = chatPaymentRecipients(message);
|
|
@@ -49016,6 +49281,7 @@ function createChatSession({
|
|
|
49016
49281
|
};
|
|
49017
49282
|
const getLocalMessages = (chatId) => isChatPendingDelete(chatId) || !isChatIdVisible(chatId) ? EMPTY_LOCAL_MESSAGES : localByChatRef.current.get(chatId) ?? EMPTY_LOCAL_MESSAGES;
|
|
49018
49283
|
const blockedAuthority = (values) => [...values || []].sort().join("|");
|
|
49284
|
+
const committedBlocked = () => blocked.filter((uid2) => !blockedPending.includes(uid2));
|
|
49019
49285
|
const bulkOperation = (blockedUids = null) => ({
|
|
49020
49286
|
generation: authGeneration,
|
|
49021
49287
|
identity: {
|
|
@@ -49031,7 +49297,7 @@ function createChatSession({
|
|
|
49031
49297
|
deleteActions: actionOwner.deleteActions,
|
|
49032
49298
|
blockedKey: blockedUids ? blockedAuthority(blockedUids) : null
|
|
49033
49299
|
});
|
|
49034
|
-
const isBulkOperationCurrent = (operation) => operation.generation === authGeneration && operation.deleteActions === actionOwner.deleteActions && (operation.blockedKey == null || operation.blockedKey === blockedAuthority(
|
|
49300
|
+
const isBulkOperationCurrent = (operation) => operation.generation === authGeneration && operation.deleteActions === actionOwner.deleteActions && (operation.blockedKey == null || operation.blockedKey === blockedAuthority(committedBlocked()));
|
|
49035
49301
|
const assertBulkOperationCurrent = (operation) => {
|
|
49036
49302
|
requireOnline();
|
|
49037
49303
|
if (!isBulkOperationCurrent(operation))
|
|
@@ -49045,32 +49311,42 @@ function createChatSession({
|
|
|
49045
49311
|
return 0;
|
|
49046
49312
|
let allChats = await loadOwnerChats(cloud, identity.uid, identity.chatPK, identity.chatPrivateKey);
|
|
49047
49313
|
assertBulkOperationCurrent(operation);
|
|
49048
|
-
|
|
49049
|
-
|
|
49050
|
-
|
|
49051
|
-
|
|
49052
|
-
|
|
49053
|
-
|
|
49054
|
-
|
|
49055
|
-
|
|
49056
|
-
|
|
49057
|
-
|
|
49058
|
-
|
|
49059
|
-
|
|
49060
|
-
|
|
49061
|
-
|
|
49062
|
-
|
|
49063
|
-
|
|
49064
|
-
|
|
49065
|
-
|
|
49066
|
-
assertBulkOperationCurrent(operation);
|
|
49067
|
-
}
|
|
49068
|
-
}
|
|
49314
|
+
await processInbox(cloud, identity.uid, identity.chatPK, identity.chatPrivateKey, {
|
|
49315
|
+
currentChats: allChats,
|
|
49316
|
+
chatSigningPK: identity.chatSigningPK,
|
|
49317
|
+
chatSigningSecret: identity.chatSigningSecret,
|
|
49318
|
+
notificationPK: identity.notificationPK,
|
|
49319
|
+
notificationPrivateKey: identity.notificationPrivateKey,
|
|
49320
|
+
mls,
|
|
49321
|
+
blockedUids: new Set(identity.blocked),
|
|
49322
|
+
chatAdmission,
|
|
49323
|
+
incomingChatDecision,
|
|
49324
|
+
scanAllSlots: true,
|
|
49325
|
+
requireComplete: true,
|
|
49326
|
+
isCurrent: () => isBulkOperationCurrent(operation),
|
|
49327
|
+
diag
|
|
49328
|
+
});
|
|
49329
|
+
assertBulkOperationCurrent(operation);
|
|
49330
|
+
allChats = await loadOwnerChats(cloud, identity.uid, identity.chatPK, identity.chatPrivateKey);
|
|
49331
|
+
assertBulkOperationCurrent(operation);
|
|
49069
49332
|
if (!allChats.length)
|
|
49070
49333
|
return 0;
|
|
49071
49334
|
let retired = 0;
|
|
49072
|
-
for (const
|
|
49335
|
+
for (const ownedChat of allChats) {
|
|
49073
49336
|
assertBulkOperationCurrent(operation);
|
|
49337
|
+
let chat = ownedChat;
|
|
49338
|
+
if (!isChatMembershipRemoved(chat)) {
|
|
49339
|
+
chatListOwner.commitOwner(chat);
|
|
49340
|
+
const reconciliation = await chatListOwner.reconcileChatEpoch(chat.id);
|
|
49341
|
+
assertBulkOperationCurrent(operation);
|
|
49342
|
+
chat = getOwnerChat(chat.id);
|
|
49343
|
+
if (!chat) {
|
|
49344
|
+
if (!reconciliation.removed)
|
|
49345
|
+
throw makeChatUnavailableError();
|
|
49346
|
+
retired += 1;
|
|
49347
|
+
continue;
|
|
49348
|
+
}
|
|
49349
|
+
}
|
|
49074
49350
|
if (isChatMembershipRemoved(chat)) {
|
|
49075
49351
|
await operation.deleteActions.deleteChat(chat, { cleanup: false });
|
|
49076
49352
|
assertBulkOperationCurrent(operation);
|
|
@@ -49212,9 +49488,10 @@ function createChatSession({
|
|
|
49212
49488
|
return false;
|
|
49213
49489
|
}
|
|
49214
49490
|
};
|
|
49215
|
-
const prepareCall = async (chatId) => {
|
|
49491
|
+
const prepareCall = async (chatId, options = {}) => {
|
|
49216
49492
|
const generation = authGeneration;
|
|
49217
|
-
|
|
49493
|
+
await sendReadiness.wait(chatId, options);
|
|
49494
|
+
const current = () => generation === authGeneration && !options.signal?.aborted && online && pendingOwner.canSendToChat(chatId);
|
|
49218
49495
|
if (!current())
|
|
49219
49496
|
throw new Error("chat unavailable");
|
|
49220
49497
|
await pendingOwner.assertChatAdmission(chatId);
|
|
@@ -49231,7 +49508,13 @@ function createChatSession({
|
|
|
49231
49508
|
throw new Error("chat unavailable");
|
|
49232
49509
|
return chat.epochState;
|
|
49233
49510
|
};
|
|
49234
|
-
const materializeChat =
|
|
49511
|
+
const materializeChat = async (chatId, options = {}) => {
|
|
49512
|
+
const generation = authGeneration;
|
|
49513
|
+
await sendReadiness.wait(chatId, options);
|
|
49514
|
+
if (generation !== authGeneration || options.signal?.aborted)
|
|
49515
|
+
throw makeChatUnavailableError();
|
|
49516
|
+
return pendingOwner.runChatMutation(chatId, (canonicalId) => canonicalId);
|
|
49517
|
+
};
|
|
49235
49518
|
const flushChatReadFrontier = (chatId) => {
|
|
49236
49519
|
if (!online)
|
|
49237
49520
|
return false;
|
|
@@ -49423,6 +49706,7 @@ function createChatSession({
|
|
|
49423
49706
|
lastHydratedCacheKeyRef,
|
|
49424
49707
|
chatsRef,
|
|
49425
49708
|
reconcileMessageBatches: (...args) => messageBatchOwner.reconcileChats(...args),
|
|
49709
|
+
getEpochState: (...args) => historyOwner.getEpochState(...args),
|
|
49426
49710
|
warmChats: (...args) => messageBatchOwner.warm(...args),
|
|
49427
49711
|
onOwnerRemoved: (chatIds) => {
|
|
49428
49712
|
for (const chatId of chatIds)
|
|
@@ -49481,6 +49765,7 @@ function createChatSession({
|
|
|
49481
49765
|
});
|
|
49482
49766
|
};
|
|
49483
49767
|
stateOwner.setActions({
|
|
49768
|
+
ensureChat,
|
|
49484
49769
|
loadMoreChats: loadMoreChats2,
|
|
49485
49770
|
selectChat,
|
|
49486
49771
|
resolveChatRequest,
|
|
@@ -49549,7 +49834,6 @@ function createChatSession({
|
|
|
49549
49834
|
deleteMessage,
|
|
49550
49835
|
deleteMessages,
|
|
49551
49836
|
deleteMessageDocs,
|
|
49552
|
-
resolveActiveCall,
|
|
49553
49837
|
setChatTtl,
|
|
49554
49838
|
makeMessagePermanent,
|
|
49555
49839
|
makeMessageTemporary,
|
|
@@ -49566,6 +49850,7 @@ function createChatSession({
|
|
|
49566
49850
|
diag,
|
|
49567
49851
|
readWriteInterval,
|
|
49568
49852
|
adoptLocalMessageMedia,
|
|
49853
|
+
getActive: () => isActive,
|
|
49569
49854
|
getIdentity: () => ({
|
|
49570
49855
|
uid,
|
|
49571
49856
|
chatPK,
|
|
@@ -49615,6 +49900,7 @@ function createChatSession({
|
|
|
49615
49900
|
const setActive = (nextActive) => {
|
|
49616
49901
|
foreground = nextActive === true;
|
|
49617
49902
|
const value = foreground && online;
|
|
49903
|
+
actionOwner.sendActions.setActive(value);
|
|
49618
49904
|
if (isActive === value)
|
|
49619
49905
|
return;
|
|
49620
49906
|
if (!value) {
|
|
@@ -49642,7 +49928,7 @@ function createChatSession({
|
|
|
49642
49928
|
inboxTransport.setSource(inboxSource());
|
|
49643
49929
|
};
|
|
49644
49930
|
const setSources = (nextSources = {}) => {
|
|
49645
|
-
const previousBlockedKey =
|
|
49931
|
+
const previousBlockedKey = blockedAuthority(committedBlocked());
|
|
49646
49932
|
const current = { uid, chatPK, chatBanned, chatPrivateKey, chatSigningPK, chatSigningSecret, notificationPK, notificationPrivateKey, chatAdmission, localCache };
|
|
49647
49933
|
const next = normalizeChatSessionSources(nextSources);
|
|
49648
49934
|
const admissionChanged = !sameChatAdmission(chatAdmission, next.chatAdmission);
|
|
@@ -49659,6 +49945,7 @@ function createChatSession({
|
|
|
49659
49945
|
uid,
|
|
49660
49946
|
online,
|
|
49661
49947
|
blocked,
|
|
49948
|
+
blockedPending,
|
|
49662
49949
|
blockedReady,
|
|
49663
49950
|
chatPK,
|
|
49664
49951
|
chatBanned,
|
|
@@ -49693,9 +49980,9 @@ function createChatSession({
|
|
|
49693
49980
|
drainMembershipOutbox();
|
|
49694
49981
|
replenishMlsPool({ force: true });
|
|
49695
49982
|
}
|
|
49696
|
-
const nextBlockedKey =
|
|
49983
|
+
const nextBlockedKey = blockedAuthority(committedBlocked());
|
|
49697
49984
|
if (online && blockedReady && nextBlockedKey && nextBlockedKey !== previousBlockedKey) {
|
|
49698
|
-
retireBlockedChats(new Set(
|
|
49985
|
+
retireBlockedChats(new Set(committedBlocked())).catch((error) => {
|
|
49699
49986
|
markDiag(diag, "chat.block.retire.failed", { message: error?.message || String(error) });
|
|
49700
49987
|
});
|
|
49701
49988
|
}
|
|
@@ -49715,8 +50002,8 @@ function createChatSession({
|
|
|
49715
50002
|
publish();
|
|
49716
50003
|
inboxTransport.start(inboxSource());
|
|
49717
50004
|
chatListOwner.start();
|
|
49718
|
-
if (blockedReady &&
|
|
49719
|
-
retireBlockedChats(new Set(
|
|
50005
|
+
if (blockedReady && committedBlocked().length) {
|
|
50006
|
+
retireBlockedChats(new Set(committedBlocked())).catch((error) => {
|
|
49720
50007
|
markDiag(diag, "chat.block.retire.failed", { message: error?.message || String(error) });
|
|
49721
50008
|
});
|
|
49722
50009
|
}
|
|
@@ -49729,6 +50016,7 @@ function createChatSession({
|
|
|
49729
50016
|
};
|
|
49730
50017
|
const close = () => {
|
|
49731
50018
|
authGeneration += 1;
|
|
50019
|
+
messageListeners.clear();
|
|
49732
50020
|
sendReadiness.reset();
|
|
49733
50021
|
pendingOwner.reset();
|
|
49734
50022
|
maintenance.close();
|
|
@@ -49754,7 +50042,20 @@ function createChatSession({
|
|
|
49754
50042
|
const subscribeFields = stateOwner.subscribeFields;
|
|
49755
50043
|
const getSnapshot = stateOwner.getSnapshot;
|
|
49756
50044
|
publish();
|
|
49757
|
-
return {
|
|
50045
|
+
return {
|
|
50046
|
+
close,
|
|
50047
|
+
getSnapshot,
|
|
50048
|
+
refreshAdmissionProjection,
|
|
50049
|
+
setInboxEnabled,
|
|
50050
|
+
setSources,
|
|
50051
|
+
start: start2,
|
|
50052
|
+
subscribe,
|
|
50053
|
+
subscribeFields,
|
|
50054
|
+
subscribeMessages(listener) {
|
|
50055
|
+
messageListeners.add(listener);
|
|
50056
|
+
return () => messageListeners.delete(listener);
|
|
50057
|
+
}
|
|
50058
|
+
};
|
|
49758
50059
|
}
|
|
49759
50060
|
var DEFAULT_READ_WRITE_INTERVAL_MS, EMPTY_LOCAL_MESSAGES;
|
|
49760
50061
|
var init_session3 = __esm(() => {
|
|
@@ -49962,6 +50263,7 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
|
|
|
49962
50263
|
const peerListeners = new Set;
|
|
49963
50264
|
const records = new Map;
|
|
49964
50265
|
const peers = new Map;
|
|
50266
|
+
const peersByUid = new Map;
|
|
49965
50267
|
const readyShards = new Map;
|
|
49966
50268
|
let sources = { uid: null, identity: null, enabled: false };
|
|
49967
50269
|
let channel = null;
|
|
@@ -49973,11 +50275,13 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
|
|
|
49973
50275
|
let seq = 0;
|
|
49974
50276
|
let renewTimer = null;
|
|
49975
50277
|
let expiryTimer = null;
|
|
50278
|
+
let expiryAt = Infinity;
|
|
50279
|
+
let ownTopic = null;
|
|
49976
50280
|
let command = null;
|
|
49977
50281
|
let authenticating = null;
|
|
49978
50282
|
let snapshot = Object.freeze({ visibility: null, ready: false, pending: false, pendingVisibility: null, error: null, ...UNKNOWN_PRESENCE });
|
|
49979
50283
|
function topic() {
|
|
49980
|
-
return
|
|
50284
|
+
return ownTopic;
|
|
49981
50285
|
}
|
|
49982
50286
|
function isReady() {
|
|
49983
50287
|
return Boolean(channel && challenge && policy && sources.enabled && !closed);
|
|
@@ -50010,17 +50314,19 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
|
|
|
50010
50314
|
const latest = fresh.reduce((selected, lease) => !selected || lease.issuedAt > selected.issuedAt || lease.issuedAt === selected.issuedAt && lease.session > selected.session ? lease : selected, null);
|
|
50011
50315
|
return { availability: fresh.length ? "online" : "offline", status: latest?.status ?? null, lastActiveAt };
|
|
50012
50316
|
}
|
|
50013
|
-
function refresh() {
|
|
50014
|
-
clearTimeout(expiryTimer);
|
|
50015
|
-
expiryTimer = null;
|
|
50016
|
-
let changed = false;
|
|
50317
|
+
function refresh(keys = records.keys(), changedUids = new Set) {
|
|
50017
50318
|
let deadline = Infinity;
|
|
50018
50319
|
const timestamp = now();
|
|
50019
|
-
for (const
|
|
50320
|
+
for (const key of keys) {
|
|
50321
|
+
const record = records.get(key);
|
|
50322
|
+
if (!record)
|
|
50323
|
+
continue;
|
|
50020
50324
|
const next = projection(record);
|
|
50021
50325
|
if (!samePresence(record.view, next)) {
|
|
50022
50326
|
record.view = Object.freeze(next);
|
|
50023
|
-
|
|
50327
|
+
const uid = peers.get(key)?.uid;
|
|
50328
|
+
if (uid)
|
|
50329
|
+
changedUids.add(uid);
|
|
50024
50330
|
}
|
|
50025
50331
|
if (record.ready) {
|
|
50026
50332
|
for (const lease of record.leases)
|
|
@@ -50033,11 +50339,18 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
|
|
|
50033
50339
|
}
|
|
50034
50340
|
const own = records.get(topic())?.view || UNKNOWN_PRESENCE;
|
|
50035
50341
|
publish(policy?.visibility === "private" ? OFFLINE_PRESENCE : own);
|
|
50036
|
-
if (
|
|
50342
|
+
if (changedUids.size)
|
|
50037
50343
|
for (const listener of peerListeners)
|
|
50038
|
-
listener();
|
|
50039
|
-
if (
|
|
50040
|
-
expiryTimer
|
|
50344
|
+
listener(changedUids);
|
|
50345
|
+
if (deadline < expiryAt && deadline > timestamp) {
|
|
50346
|
+
clearTimeout(expiryTimer);
|
|
50347
|
+
expiryAt = deadline;
|
|
50348
|
+
expiryTimer = setTimeout(() => {
|
|
50349
|
+
expiryAt = Infinity;
|
|
50350
|
+
expiryTimer = null;
|
|
50351
|
+
refresh();
|
|
50352
|
+
}, deadline - timestamp + 1);
|
|
50353
|
+
}
|
|
50041
50354
|
}
|
|
50042
50355
|
function finishCommand(error = null) {
|
|
50043
50356
|
if (!command)
|
|
@@ -50052,7 +50365,7 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
|
|
|
50052
50365
|
pending.resolve(snapshot);
|
|
50053
50366
|
}
|
|
50054
50367
|
function expected(identity, extra = {}) {
|
|
50055
|
-
return { realm: realm2, topic:
|
|
50368
|
+
return { realm: realm2, topic: identity.topic, signingPK: identity.chatSigningPK, now: now(), ...extra };
|
|
50056
50369
|
}
|
|
50057
50370
|
function receive(frame) {
|
|
50058
50371
|
const identity = peers.get(frame.topic);
|
|
@@ -50077,6 +50390,15 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
|
|
|
50077
50390
|
lastActiveAt = verifyPresenceObservation(frame.lastObservation, scope).lastActiveAt;
|
|
50078
50391
|
} catch {}
|
|
50079
50392
|
}
|
|
50393
|
+
const timestamp = now();
|
|
50394
|
+
if (previous?.revision === currentPolicy.revision) {
|
|
50395
|
+
lastActiveAt = Math.max(lastActiveAt || 0, previous.lastActiveAt || 0) || null;
|
|
50396
|
+
if (!frame.leases?.length && previous.ready && previous.leases.some((lease) => lease.expiresAt > timestamp)) {
|
|
50397
|
+
lastActiveAt = Math.max(lastActiveAt || 0, timestamp);
|
|
50398
|
+
}
|
|
50399
|
+
}
|
|
50400
|
+
for (const lease of leases)
|
|
50401
|
+
lastActiveAt = Math.max(lastActiveAt || 0, Math.min(lease.issuedAt, timestamp));
|
|
50080
50402
|
}
|
|
50081
50403
|
records.set(frame.topic, {
|
|
50082
50404
|
ready: true,
|
|
@@ -50085,7 +50407,7 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
|
|
|
50085
50407
|
revision: currentPolicy?.revision ?? previous?.revision ?? 0,
|
|
50086
50408
|
view: previous?.view || UNKNOWN_PRESENCE
|
|
50087
50409
|
});
|
|
50088
|
-
refresh();
|
|
50410
|
+
refresh([frame.topic]);
|
|
50089
50411
|
} catch (error) {
|
|
50090
50412
|
report(error);
|
|
50091
50413
|
}
|
|
@@ -50103,10 +50425,10 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
|
|
|
50103
50425
|
if (!session || !policy || policy.visibility !== "public" || command)
|
|
50104
50426
|
return;
|
|
50105
50427
|
const timestamp = now();
|
|
50106
|
-
const
|
|
50428
|
+
const ownTopic2 = topic();
|
|
50107
50429
|
const lease = signPresenceLease(sources.identity, {
|
|
50108
50430
|
realm: realm2,
|
|
50109
|
-
topic:
|
|
50431
|
+
topic: ownTopic2,
|
|
50110
50432
|
session,
|
|
50111
50433
|
challenge,
|
|
50112
50434
|
policyRevision: policy.revision,
|
|
@@ -50117,11 +50439,11 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
|
|
|
50117
50439
|
});
|
|
50118
50440
|
const observation = signPresenceObservation(sources.identity, {
|
|
50119
50441
|
realm: realm2,
|
|
50120
|
-
topic:
|
|
50442
|
+
topic: ownTopic2,
|
|
50121
50443
|
policyRevision: policy.revision,
|
|
50122
50444
|
lastActiveAt: Math.floor(timestamp / PRESENCE_LAST_ACTIVE_BUCKET_MS) * PRESENCE_LAST_ACTIVE_BUCKET_MS
|
|
50123
50445
|
});
|
|
50124
|
-
channel.send(presenceShard(
|
|
50446
|
+
channel.send(presenceShard(ownTopic2), { type: "lease", lease, observation });
|
|
50125
50447
|
}
|
|
50126
50448
|
function receiveOwner(frame) {
|
|
50127
50449
|
if (frame.topic !== topic() || !sources.identity)
|
|
@@ -50143,7 +50465,7 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
|
|
|
50143
50465
|
if (policy.visibility === "private") {
|
|
50144
50466
|
const previous = records.get(topic());
|
|
50145
50467
|
records.set(topic(), { ready: true, leases: [], lastActiveAt: null, revision: policy.revision, view: previous?.view || UNKNOWN_PRESENCE });
|
|
50146
|
-
refresh();
|
|
50468
|
+
refresh([topic()]);
|
|
50147
50469
|
}
|
|
50148
50470
|
if (command && policy.revision >= command.policy.revision) {
|
|
50149
50471
|
finishCommand(policy.signature === command.policy.signature ? null : new Error("activity setting changed on another device; try again"));
|
|
@@ -50254,7 +50576,7 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
|
|
|
50254
50576
|
function setPeers(profiles2) {
|
|
50255
50577
|
const next = new Map;
|
|
50256
50578
|
if (sources.uid && sources.identity)
|
|
50257
|
-
next.set(topic(), { uid: sources.uid, chatSigningPK: sources.identity.publicKey });
|
|
50579
|
+
next.set(topic(), { uid: sources.uid, topic: topic(), chatSigningPK: sources.identity.publicKey });
|
|
50258
50580
|
for (const profile of profiles2 || []) {
|
|
50259
50581
|
if (next.size >= PRESENCE_MAX_SUBSCRIPTIONS)
|
|
50260
50582
|
break;
|
|
@@ -50262,27 +50584,34 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
|
|
|
50262
50584
|
continue;
|
|
50263
50585
|
if (profile.uid === sources.uid)
|
|
50264
50586
|
continue;
|
|
50265
|
-
|
|
50587
|
+
const key = peersByUid.get(profile.uid)?.topic || derivePresenceTopic(realm2, profile.uid);
|
|
50588
|
+
next.set(key, { uid: profile.uid, topic: key, chatSigningPK: profile.chatSigningPK });
|
|
50266
50589
|
}
|
|
50267
50590
|
let changed = peers.size !== next.size;
|
|
50591
|
+
const changedUids = new Set;
|
|
50268
50592
|
for (const [key, identity] of next) {
|
|
50269
50593
|
if (peers.get(key)?.chatSigningPK !== identity.chatSigningPK) {
|
|
50270
50594
|
changed = true;
|
|
50595
|
+
changedUids.add(identity.uid);
|
|
50271
50596
|
records.delete(key);
|
|
50272
50597
|
}
|
|
50273
50598
|
}
|
|
50599
|
+
for (const [key, identity] of peers)
|
|
50600
|
+
if (!next.has(key))
|
|
50601
|
+
changedUids.add(identity.uid);
|
|
50274
50602
|
if (!changed)
|
|
50275
50603
|
return;
|
|
50276
50604
|
peers.clear();
|
|
50277
|
-
|
|
50605
|
+
peersByUid.clear();
|
|
50606
|
+
for (const [key, value] of next) {
|
|
50278
50607
|
peers.set(key, value);
|
|
50608
|
+
peersByUid.set(value.uid, value);
|
|
50609
|
+
}
|
|
50279
50610
|
for (const key of records.keys())
|
|
50280
50611
|
if (!peers.has(key))
|
|
50281
50612
|
records.delete(key);
|
|
50282
50613
|
sync();
|
|
50283
|
-
refresh();
|
|
50284
|
-
for (const listener of peerListeners)
|
|
50285
|
-
listener();
|
|
50614
|
+
refresh([], changedUids);
|
|
50286
50615
|
}
|
|
50287
50616
|
function update(next) {
|
|
50288
50617
|
const accountChanged = sources.uid !== next.uid;
|
|
@@ -50291,11 +50620,15 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
|
|
|
50291
50620
|
stop();
|
|
50292
50621
|
policy = null;
|
|
50293
50622
|
records.clear();
|
|
50294
|
-
if (accountChanged)
|
|
50623
|
+
if (accountChanged) {
|
|
50295
50624
|
peers.clear();
|
|
50625
|
+
peersByUid.clear();
|
|
50626
|
+
}
|
|
50296
50627
|
publish({ visibility: null, error: null, ...UNKNOWN_PRESENCE });
|
|
50297
50628
|
}
|
|
50298
50629
|
sources = next;
|
|
50630
|
+
if (accountChanged)
|
|
50631
|
+
ownTopic = next.uid ? derivePresenceTopic(realm2, next.uid) : null;
|
|
50299
50632
|
setPeers([...peers.values()]);
|
|
50300
50633
|
sync();
|
|
50301
50634
|
}
|
|
@@ -50336,10 +50669,10 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
|
|
|
50336
50669
|
getPeer(profile) {
|
|
50337
50670
|
if (!profile?.uid)
|
|
50338
50671
|
return UNKNOWN_PRESENCE;
|
|
50339
|
-
const
|
|
50340
|
-
if (
|
|
50672
|
+
const identity = peersByUid.get(profile.uid);
|
|
50673
|
+
if (!identity || identity.chatSigningPK !== profile.chatSigningPK)
|
|
50341
50674
|
return UNKNOWN_PRESENCE;
|
|
50342
|
-
return records.get(
|
|
50675
|
+
return records.get(identity.topic)?.view || UNKNOWN_PRESENCE;
|
|
50343
50676
|
},
|
|
50344
50677
|
subscribe(listener) {
|
|
50345
50678
|
listeners.add(listener);
|
|
@@ -50358,6 +50691,7 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
|
|
|
50358
50691
|
listeners.clear();
|
|
50359
50692
|
peerListeners.clear();
|
|
50360
50693
|
peers.clear();
|
|
50694
|
+
peersByUid.clear();
|
|
50361
50695
|
records.clear();
|
|
50362
50696
|
}
|
|
50363
50697
|
});
|
|
@@ -50628,6 +50962,10 @@ function openCallMailbox({ cloud, capability, endpoint, protocol, onChange, onEr
|
|
|
50628
50962
|
}
|
|
50629
50963
|
return Object.freeze({
|
|
50630
50964
|
getSnapshot: () => candidate || record,
|
|
50965
|
+
async settled() {
|
|
50966
|
+
await tail;
|
|
50967
|
+
return record;
|
|
50968
|
+
},
|
|
50631
50969
|
async ready() {
|
|
50632
50970
|
const response = await channel.ready();
|
|
50633
50971
|
await consume(response.record);
|
|
@@ -50980,6 +51318,23 @@ var init_accountlease = __esm(() => {
|
|
|
50980
51318
|
init_wire();
|
|
50981
51319
|
});
|
|
50982
51320
|
|
|
51321
|
+
// ../../core/calls/sources.js
|
|
51322
|
+
function callVideoCapacity(participants, holder, count = 1) {
|
|
51323
|
+
return participants.filter((peer) => peer.holder !== holder).reduce((count2, peer) => count2 + (peer.media?.sources?.length || 0), 0) + count <= CALL_MAX_VIDEO_SOURCES;
|
|
51324
|
+
}
|
|
51325
|
+
function assertCallVideoSources(participants, holder, media) {
|
|
51326
|
+
const sources = media?.sources || [];
|
|
51327
|
+
if (!Array.isArray(sources) || sources.length > CALL_MAX_VIDEO_SOURCES_PER_PARTICIPANT || sources.some((source) => !/^[A-Za-z0-9_-]{1,64}$/u.test(source?.id || "") || !["screen", "camera"].includes(source.kind) || !Number.isInteger(source.contextId) || source.contextId < 1 || source.contextId >= 2 ** 24) || new Set(sources.map((source) => source.id)).size !== sources.length || new Set(sources.map((source) => source.contextId)).size !== sources.length) {
|
|
51328
|
+
throw Object.assign(new Error("invalid call video sources"), { code: "calls/invalid-source" });
|
|
51329
|
+
}
|
|
51330
|
+
if (sources.length && !callVideoCapacity(participants, holder, sources.length)) {
|
|
51331
|
+
throw Object.assign(new Error(`this call already has ${CALL_MAX_VIDEO_SOURCES} cameras or screens`), { code: "calls/video-full" });
|
|
51332
|
+
}
|
|
51333
|
+
}
|
|
51334
|
+
var init_sources = __esm(() => {
|
|
51335
|
+
init_config();
|
|
51336
|
+
});
|
|
51337
|
+
|
|
50983
51338
|
// ../../core/calls/room.js
|
|
50984
51339
|
function openCallRoom({
|
|
50985
51340
|
cloud,
|
|
@@ -51066,6 +51421,14 @@ function openCallRoom({
|
|
|
51066
51421
|
settleAdmission(admissionError("calls/taken-over", "call moved to another device"));
|
|
51067
51422
|
}
|
|
51068
51423
|
}
|
|
51424
|
+
function retireMembership() {
|
|
51425
|
+
departing = true;
|
|
51426
|
+
joined = false;
|
|
51427
|
+
clearTimeout(pulseTimer);
|
|
51428
|
+
disposeState(state);
|
|
51429
|
+
state = null;
|
|
51430
|
+
onRemoved?.();
|
|
51431
|
+
}
|
|
51069
51432
|
async function adopt(next, admissions) {
|
|
51070
51433
|
verifyCallMembers(next, admissions, protocol);
|
|
51071
51434
|
if (closed) {
|
|
@@ -51077,7 +51440,7 @@ function openCallRoom({
|
|
|
51077
51440
|
disposeState(previous);
|
|
51078
51441
|
const self2 = next.members.find((item) => toHex(item.leafId) === localHolder);
|
|
51079
51442
|
if (!self2) {
|
|
51080
|
-
|
|
51443
|
+
retireMembership();
|
|
51081
51444
|
return;
|
|
51082
51445
|
}
|
|
51083
51446
|
if (!admissions.some((item) => item.holder === localHolder && item.actor === admission.actor && item.endpoint === endpoint.publicKey))
|
|
@@ -51151,7 +51514,15 @@ function openCallRoom({
|
|
|
51151
51514
|
if (bound)
|
|
51152
51515
|
leaving.add(packet.holder);
|
|
51153
51516
|
} else if (packet.kind === "pulse") {
|
|
51154
|
-
|
|
51517
|
+
let media = packet.data.media;
|
|
51518
|
+
try {
|
|
51519
|
+
assertCallVideoSources(participants(), packet.holder, media);
|
|
51520
|
+
} catch {
|
|
51521
|
+
media = published.get(packet.holder)?.media || null;
|
|
51522
|
+
}
|
|
51523
|
+
published.set(packet.holder, { ...packet.data, media });
|
|
51524
|
+
if (packet.holder === localHolder)
|
|
51525
|
+
ownMedia = media;
|
|
51155
51526
|
} else if (packet.kind === "signal") {
|
|
51156
51527
|
if (packet.data.to === localHolder && head?.participants.some((item) => item.holder === packet.holder && item.actor === packet.actor)) {
|
|
51157
51528
|
signals.push(packet);
|
|
@@ -51161,7 +51532,7 @@ function openCallRoom({
|
|
|
51161
51532
|
membershipChanged = true;
|
|
51162
51533
|
if (state && !replacement.participants.some((item) => item.holder === localHolder)) {
|
|
51163
51534
|
settleAdmission(cancelled2());
|
|
51164
|
-
|
|
51535
|
+
retireMembership();
|
|
51165
51536
|
return;
|
|
51166
51537
|
}
|
|
51167
51538
|
head = replacement;
|
|
@@ -51177,7 +51548,7 @@ function openCallRoom({
|
|
|
51177
51548
|
pendingCandidate = null;
|
|
51178
51549
|
if (next.removed) {
|
|
51179
51550
|
disposeState(next);
|
|
51180
|
-
|
|
51551
|
+
retireMembership();
|
|
51181
51552
|
return;
|
|
51182
51553
|
}
|
|
51183
51554
|
await adopt(next, change.participants);
|
|
@@ -51214,7 +51585,7 @@ function openCallRoom({
|
|
|
51214
51585
|
verifiedHead = null;
|
|
51215
51586
|
admittedIdentities.clear();
|
|
51216
51587
|
if (joined) {
|
|
51217
|
-
|
|
51588
|
+
retireMembership();
|
|
51218
51589
|
return;
|
|
51219
51590
|
}
|
|
51220
51591
|
if (submittedJoin)
|
|
@@ -51318,7 +51689,7 @@ function openCallRoom({
|
|
|
51318
51689
|
for (let attempt = 0;!closed && performance.now() < deadline; attempt += 1) {
|
|
51319
51690
|
if (kind === "join" && departing)
|
|
51320
51691
|
throw cancelled2();
|
|
51321
|
-
const current = mailbox.
|
|
51692
|
+
const current = await mailbox.settled();
|
|
51322
51693
|
if (!current?.head)
|
|
51323
51694
|
throw kind === "join" ? empty() : new Error("call ended");
|
|
51324
51695
|
if (kind === "join") {
|
|
@@ -51327,9 +51698,14 @@ function openCallRoom({
|
|
|
51327
51698
|
throw admissionFailure;
|
|
51328
51699
|
}
|
|
51329
51700
|
try {
|
|
51701
|
+
const value = typeof data === "function" ? data() : data;
|
|
51702
|
+
if (kind === "pulse")
|
|
51703
|
+
assertCallVideoSources(participants(), localHolder, value.media);
|
|
51330
51704
|
if (kind === "join")
|
|
51331
51705
|
submittedJoin = true;
|
|
51332
|
-
|
|
51706
|
+
const result = await mailbox.append([protocol.sign(kind, value)], { expectedRevision: current.revision });
|
|
51707
|
+
await mailbox.settled();
|
|
51708
|
+
return result;
|
|
51333
51709
|
} catch (error) {
|
|
51334
51710
|
if (closed)
|
|
51335
51711
|
throw cancelled2();
|
|
@@ -51360,7 +51736,7 @@ function openCallRoom({
|
|
|
51360
51736
|
if (closed || departing || !joined)
|
|
51361
51737
|
return;
|
|
51362
51738
|
try {
|
|
51363
|
-
await event("pulse", { ...audio, media: ownMedia });
|
|
51739
|
+
await event("pulse", () => ({ ...audio, media: ownMedia }));
|
|
51364
51740
|
} catch (error) {
|
|
51365
51741
|
fail(error);
|
|
51366
51742
|
}
|
|
@@ -51442,13 +51818,12 @@ function openCallRoom({
|
|
|
51442
51818
|
return event("signal", { to, signal });
|
|
51443
51819
|
},
|
|
51444
51820
|
async publishMedia(media) {
|
|
51445
|
-
|
|
51446
|
-
await event("pulse", { ...audio, media });
|
|
51821
|
+
await event("pulse", () => ({ ...audio, media }));
|
|
51447
51822
|
},
|
|
51448
51823
|
async setAudio(value) {
|
|
51449
51824
|
audio = { muted: value.muted === true || value.deafened === true, deafened: value.deafened === true };
|
|
51450
51825
|
if (joined && !departing && !closed)
|
|
51451
|
-
await event("pulse", { ...audio, media: ownMedia });
|
|
51826
|
+
await event("pulse", () => ({ ...audio, media: ownMedia }));
|
|
51452
51827
|
},
|
|
51453
51828
|
async leave() {
|
|
51454
51829
|
if (closed)
|
|
@@ -51486,6 +51861,7 @@ var init_room = __esm(() => {
|
|
|
51486
51861
|
init_wire();
|
|
51487
51862
|
init_protocol3();
|
|
51488
51863
|
init_mailbox();
|
|
51864
|
+
init_sources();
|
|
51489
51865
|
});
|
|
51490
51866
|
|
|
51491
51867
|
// ../../core/calls/activity.js
|
|
@@ -51645,15 +52021,15 @@ function createCallMediaSession({ port, mode, holder, preparation, provider, sig
|
|
|
51645
52021
|
const pendingSignals = [];
|
|
51646
52022
|
const incomingSignals = [];
|
|
51647
52023
|
function sourcesFor(peer) {
|
|
51648
|
-
if (!port.capabilities?.video || !videoCapable(peer) || !Array.isArray(peer.media.sources) || peer.media.sources.length >
|
|
52024
|
+
if (!port.capabilities?.video || !videoCapable(peer) || !Array.isArray(peer.media.sources) || peer.media.sources.length > CALL_MAX_VIDEO_SOURCES_PER_PARTICIPANT)
|
|
51649
52025
|
return [];
|
|
51650
|
-
const sources = peer.media.sources;
|
|
52026
|
+
const sources = peer.media.sources.filter((source) => mode === "group" ? source?.trackName != null : source?.mid != null);
|
|
51651
52027
|
if (sources.some((source) => !validSourceId(source?.id) || !["screen", "camera"].includes(source.kind) || !validContext(source.contextId) || (mode === "group" ? !/^[a-f0-9]{32}$/u.test(source.trackName || "") : !validMid(source.mid))) || new Set(sources.map((source) => source.id)).size !== sources.length || new Set(sources.map((source) => source.contextId)).size !== sources.length || new Set(sources.map((source) => mode === "group" ? source.trackName : source.mid)).size !== sources.length)
|
|
51652
52028
|
return [];
|
|
51653
52029
|
return sources;
|
|
51654
52030
|
}
|
|
51655
52031
|
function videoProjection() {
|
|
51656
|
-
const next = [...localSources.values()].map((source) => ({ id: source.id, holder, kind: source.kind, stream: source.stream, local: true }));
|
|
52032
|
+
const next = [...localSources.values()].filter((source) => source.admitted).map((source) => ({ id: source.id, holder, kind: source.kind, stream: source.stream, local: true }));
|
|
51657
52033
|
for (const peer of peers) {
|
|
51658
52034
|
if (peer.holder === holder)
|
|
51659
52035
|
continue;
|
|
@@ -51673,11 +52049,11 @@ function createCallMediaSession({ port, mode, holder, preparation, provider, sig
|
|
|
51673
52049
|
const base2 = mode === "group" ? { sessionId, trackName: microphoneTrack } : {};
|
|
51674
52050
|
if (!port.capabilities?.video)
|
|
51675
52051
|
return base2;
|
|
51676
|
-
return { ...base2, capabilities: ["video"], sources: [...localSources.values()].
|
|
52052
|
+
return { ...base2, capabilities: ["video"], sources: [...localSources.values()].map((source) => ({
|
|
51677
52053
|
id: source.id,
|
|
51678
52054
|
kind: source.kind,
|
|
51679
52055
|
contextId: source.contextId,
|
|
51680
|
-
...mode === "group" ? { trackName: source.trackName } : { mid: source.mid }
|
|
52056
|
+
...source.published ? mode === "group" ? { trackName: source.trackName } : { mid: source.mid } : {}
|
|
51681
52057
|
})) };
|
|
51682
52058
|
}
|
|
51683
52059
|
async function publishMedia() {
|
|
@@ -52132,7 +52508,7 @@ function createCallMediaSession({ port, mode, holder, preparation, provider, sig
|
|
|
52132
52508
|
current(generation);
|
|
52133
52509
|
if (videoCapable(peer))
|
|
52134
52510
|
for (const source of localSources.values()) {
|
|
52135
|
-
if (source.attached)
|
|
52511
|
+
if (!source.admitted || source.attached)
|
|
52136
52512
|
continue;
|
|
52137
52513
|
media.addSource(source);
|
|
52138
52514
|
source.attached = true;
|
|
@@ -52261,68 +52637,101 @@ function createCallMediaSession({ port, mode, holder, preparation, provider, sig
|
|
|
52261
52637
|
current();
|
|
52262
52638
|
videoProjection();
|
|
52263
52639
|
}
|
|
52640
|
+
function detachSource(source) {
|
|
52641
|
+
localSources.delete(source.id);
|
|
52642
|
+
source.stream.getTracks().forEach((track) => track.stop());
|
|
52643
|
+
if (source.attached)
|
|
52644
|
+
media?.removeSource(source.id);
|
|
52645
|
+
videoProjection();
|
|
52646
|
+
}
|
|
52647
|
+
async function closeSourceTrack(source) {
|
|
52648
|
+
if (mode === "group" && source.published) {
|
|
52649
|
+
await applyDescription(await provider("tracks/close", { sessionId, tracks: [{ mid: source.mid }] }));
|
|
52650
|
+
}
|
|
52651
|
+
}
|
|
52264
52652
|
return Object.freeze({
|
|
52265
|
-
|
|
52266
|
-
return port.capabilities?.
|
|
52653
|
+
supportsSource(kind) {
|
|
52654
|
+
return port.capabilities?.[kind] === true && port.capabilities?.video === true;
|
|
52267
52655
|
},
|
|
52268
|
-
async addSource(source) {
|
|
52656
|
+
async addSource(source, replaceId = null) {
|
|
52269
52657
|
current();
|
|
52270
52658
|
if (!started || !media?.addSource || !port.capabilities?.video)
|
|
52271
52659
|
throw new Error("video sharing unavailable");
|
|
52272
|
-
|
|
52660
|
+
const previous = localSources.get(replaceId);
|
|
52661
|
+
if (!validSourceId(source?.id) || !["screen", "camera"].includes(source.kind) || !validContext(source.contextId) || sourceContexts.has(source.contextId) || localSources.has(source.id) || localSources.size - (previous ? 1 : 0) >= CALL_MAX_VIDEO_SOURCES_PER_PARTICIPANT)
|
|
52273
52662
|
throw new Error("invalid call source");
|
|
52274
52663
|
const generation = connectionGeneration;
|
|
52275
|
-
if (
|
|
52276
|
-
|
|
52277
|
-
const local = { ...source, attached:
|
|
52664
|
+
if (previous)
|
|
52665
|
+
detachSource(previous);
|
|
52666
|
+
const local = { ...source, attached: false, admitted: false, published: false };
|
|
52278
52667
|
localSources.set(source.id, local);
|
|
52279
52668
|
sourceContexts.add(source.contextId);
|
|
52280
|
-
|
|
52281
|
-
|
|
52282
|
-
current(generation);
|
|
52283
|
-
return serialize(async () => {
|
|
52284
|
-
current(generation);
|
|
52285
|
-
if (localSources.get(source.id) !== local)
|
|
52286
|
-
return;
|
|
52287
|
-
if (mode === "group") {
|
|
52288
|
-
const offer = await media.offer();
|
|
52669
|
+
const result = await serialize(async () => {
|
|
52670
|
+
try {
|
|
52289
52671
|
current(generation);
|
|
52290
|
-
|
|
52291
|
-
|
|
52292
|
-
if (
|
|
52293
|
-
|
|
52294
|
-
|
|
52672
|
+
if (previous)
|
|
52673
|
+
await closeSourceTrack(previous);
|
|
52674
|
+
if (localSources.get(source.id) !== local)
|
|
52675
|
+
return;
|
|
52676
|
+
await publishMedia();
|
|
52295
52677
|
current(generation);
|
|
52296
|
-
if (
|
|
52297
|
-
|
|
52298
|
-
|
|
52678
|
+
if (localSources.get(source.id) !== local)
|
|
52679
|
+
return;
|
|
52680
|
+
local.admitted = true;
|
|
52681
|
+
if (mode === "group") {
|
|
52682
|
+
media.addSource(local);
|
|
52683
|
+
local.attached = true;
|
|
52684
|
+
}
|
|
52685
|
+
videoProjection();
|
|
52686
|
+
await updateKeys();
|
|
52299
52687
|
current(generation);
|
|
52300
|
-
|
|
52688
|
+
if (localSources.get(source.id) !== local)
|
|
52689
|
+
return;
|
|
52690
|
+
if (mode === "group") {
|
|
52691
|
+
const offer = await media.offer();
|
|
52692
|
+
current(generation);
|
|
52693
|
+
local.mid = media.sourceMid(source.id);
|
|
52694
|
+
local.trackName = toHex(randomBytes3(16));
|
|
52695
|
+
if (!validMid(local.mid))
|
|
52696
|
+
throw new Error("video track unavailable");
|
|
52697
|
+
const response = await provider("tracks/new", { sessionId, sessionDescription: sdp(offer), tracks: [{ location: "local", mid: local.mid, trackName: local.trackName }] });
|
|
52698
|
+
current(generation);
|
|
52699
|
+
if (response?.tracks?.length !== 1 || response.tracks[0].mid !== local.mid || response.tracks[0].trackName !== local.trackName)
|
|
52700
|
+
throw new Error("video publication failed");
|
|
52701
|
+
await applyDescription(response, true);
|
|
52702
|
+
current(generation);
|
|
52703
|
+
local.published = true;
|
|
52704
|
+
await publishMedia();
|
|
52705
|
+
} else
|
|
52706
|
+
await negotiate();
|
|
52707
|
+
} catch (error) {
|
|
52708
|
+
if (closed || generation !== connectionGeneration || !["calls/video-full", "calls/invalid-source"].includes(error?.code))
|
|
52709
|
+
throw error;
|
|
52710
|
+
if (localSources.get(source.id) === local)
|
|
52711
|
+
detachSource(local);
|
|
52712
|
+
await updateKeys();
|
|
52301
52713
|
await publishMedia();
|
|
52302
|
-
|
|
52303
|
-
|
|
52714
|
+
await closeSourceTrack(local);
|
|
52715
|
+
return { error };
|
|
52716
|
+
}
|
|
52304
52717
|
});
|
|
52718
|
+
if (result?.error)
|
|
52719
|
+
throw result.error;
|
|
52305
52720
|
},
|
|
52306
52721
|
removeSource(id) {
|
|
52307
52722
|
current();
|
|
52308
52723
|
const local = localSources.get(id);
|
|
52309
52724
|
if (!local)
|
|
52310
52725
|
return Promise.resolve();
|
|
52311
|
-
|
|
52312
|
-
local.stream.getTracks().forEach((track) => track.stop());
|
|
52313
|
-
if (local.attached)
|
|
52314
|
-
media?.removeSource(id);
|
|
52315
|
-
videoProjection();
|
|
52726
|
+
detachSource(local);
|
|
52316
52727
|
const removedKeys = updateKeys();
|
|
52317
52728
|
removedKeys.catch(() => {});
|
|
52318
52729
|
return serialize(async () => {
|
|
52319
52730
|
await removedKeys;
|
|
52320
52731
|
current();
|
|
52321
52732
|
await publishMedia();
|
|
52322
|
-
|
|
52323
|
-
|
|
52324
|
-
await applyDescription(await provider("tracks/close", { sessionId, tracks: [{ mid: local.mid }] }));
|
|
52325
|
-
} else if (local.attached) {
|
|
52733
|
+
await closeSourceTrack(local);
|
|
52734
|
+
if (mode === "direct" && local.attached) {
|
|
52326
52735
|
directDirty = true;
|
|
52327
52736
|
await negotiate();
|
|
52328
52737
|
}
|
|
@@ -52515,6 +52924,7 @@ var sdp = (value) => ({ type: value.type, sdp: value.sdp }), validMid = (value)
|
|
|
52515
52924
|
var init_media5 = __esm(() => {
|
|
52516
52925
|
init_core();
|
|
52517
52926
|
init_activity();
|
|
52927
|
+
init_config();
|
|
52518
52928
|
retiredConnection = new Error("call connection retired");
|
|
52519
52929
|
});
|
|
52520
52930
|
|
|
@@ -52613,6 +53023,7 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
52613
53023
|
const discoveries = new Map;
|
|
52614
53024
|
const discoveryReads = new Map;
|
|
52615
53025
|
const observers = new Map;
|
|
53026
|
+
const messageHints = new Map;
|
|
52616
53027
|
const retiring = new Set;
|
|
52617
53028
|
let state = initial();
|
|
52618
53029
|
const confirmedPeerAudio = new Map;
|
|
@@ -52620,6 +53031,7 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
52620
53031
|
let peerAudioSource = null;
|
|
52621
53032
|
let audioSource = normalizeCallAudio();
|
|
52622
53033
|
let pendingAudio = null;
|
|
53034
|
+
let audioSaveTail = Promise.resolve();
|
|
52623
53035
|
let identity = null;
|
|
52624
53036
|
let endpoint = null;
|
|
52625
53037
|
let leaseEndpoint = null;
|
|
@@ -52656,7 +53068,27 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
52656
53068
|
diag?.("call.message.error", { event, code: error?.code || "" });
|
|
52657
53069
|
});
|
|
52658
53070
|
}
|
|
52659
|
-
const
|
|
53071
|
+
const captureFor = (kind) => kind === "camera" ? mediaPort?.captureCamera : mediaPort?.captureScreen;
|
|
53072
|
+
const canShare = (call2, kind) => !!call2 && !call2.leaving && call2.mediaReady && !!captureFor(kind) && ["connecting", "connected", "waiting"].includes(state.phase) && call2.media?.supportsSource(kind) === true && callVideoCapacity(call2.participants, call2.endpoint.holder);
|
|
53073
|
+
const sourceState = (call2) => ({
|
|
53074
|
+
sharingScreen: call2?.visual?.kind === "screen",
|
|
53075
|
+
sharingCamera: call2?.visual?.kind === "camera",
|
|
53076
|
+
screenShareAvailable: canShare(call2, "screen"),
|
|
53077
|
+
cameraAvailable: canShare(call2, "camera")
|
|
53078
|
+
});
|
|
53079
|
+
async function captureVisual(operation) {
|
|
53080
|
+
while (operation.visual) {
|
|
53081
|
+
const source = operation.visual;
|
|
53082
|
+
try {
|
|
53083
|
+
await source.capture;
|
|
53084
|
+
} catch (error) {
|
|
53085
|
+
if (operation.visual === source)
|
|
53086
|
+
throw error;
|
|
53087
|
+
}
|
|
53088
|
+
if (operation.visual === source)
|
|
53089
|
+
return;
|
|
53090
|
+
}
|
|
53091
|
+
}
|
|
52660
53092
|
function current(token) {
|
|
52661
53093
|
if (closed || !identity || token !== generation)
|
|
52662
53094
|
throw cancelled2();
|
|
@@ -52677,17 +53109,29 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
52677
53109
|
}
|
|
52678
53110
|
function available() {
|
|
52679
53111
|
const entry = discoveries.get(focused);
|
|
52680
|
-
const value = (chatId, current2) =>
|
|
52681
|
-
|
|
52682
|
-
|
|
52683
|
-
|
|
52684
|
-
|
|
52685
|
-
|
|
52686
|
-
|
|
53112
|
+
const value = (chatId, current2) => {
|
|
53113
|
+
if (!current2?.descriptor)
|
|
53114
|
+
return null;
|
|
53115
|
+
const roster = (active?.discovery === current2 ? active.participants : current2.roster)?.filter((peer) => ![...retiring].some((call2) => call2.callId === current2.descriptor.callId && call2.endpoint.holder === peer.holder));
|
|
53116
|
+
if (roster?.length === 0)
|
|
53117
|
+
return null;
|
|
53118
|
+
return {
|
|
53119
|
+
chatId,
|
|
53120
|
+
callId: current2.descriptor.callId,
|
|
53121
|
+
startedAt: current2.descriptor.startedAt,
|
|
53122
|
+
participants: roster?.length ?? (current2.descriptor.participants || 1),
|
|
53123
|
+
roster: roster || []
|
|
53124
|
+
};
|
|
53125
|
+
};
|
|
52687
53126
|
for (const observer of observers.values()) {
|
|
53127
|
+
const observed = discoveries.get(observer.chatId);
|
|
53128
|
+
if (!observer.resolved && observed?.revision !== null && observed?.revision !== undefined) {
|
|
53129
|
+
observer.resolved = true;
|
|
53130
|
+
diag?.("call.discovery.observed", { elapsedMs: Date.now() - observer.startedAt, active: !!observed.descriptor });
|
|
53131
|
+
}
|
|
52688
53132
|
if (!observer.onAvailable)
|
|
52689
53133
|
continue;
|
|
52690
|
-
const next2 = value(observer.chatId,
|
|
53134
|
+
const next2 = value(observer.chatId, observed);
|
|
52691
53135
|
const fingerprint = JSON.stringify(next2);
|
|
52692
53136
|
if (observer.fingerprint === fingerprint)
|
|
52693
53137
|
continue;
|
|
@@ -52723,7 +53167,6 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
52723
53167
|
return;
|
|
52724
53168
|
}
|
|
52725
53169
|
entry.observation?.close();
|
|
52726
|
-
entry.roster = [];
|
|
52727
53170
|
entry.observedCallId = callId;
|
|
52728
53171
|
const token = generation;
|
|
52729
53172
|
const protocol = createCallProtocol({ realm: cloud.environment, epochState: entry.epochState, identity, endpoint, callId });
|
|
@@ -52754,7 +53197,7 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
52754
53197
|
}
|
|
52755
53198
|
function prune() {
|
|
52756
53199
|
for (const [chatId, value] of discoveries) {
|
|
52757
|
-
if (![...observers.values()].some((item) => item.chatId === chatId) && chatId !== active?.chatId && chatId !== joining?.chatId && ![...retiring].some((call2) => call2.chatId === chatId)) {
|
|
53200
|
+
if (!messageHints.get(chatId)?.watch && !discoveryReads.has(chatId) && !value.finishing.size && ![...observers.values()].some((item) => item.chatId === chatId) && chatId !== active?.chatId && chatId !== joining?.chatId && ![...retiring].some((call2) => call2.chatId === chatId)) {
|
|
52758
53201
|
closeDiscovery(value);
|
|
52759
53202
|
discoveries.delete(chatId);
|
|
52760
53203
|
} else
|
|
@@ -52776,7 +53219,7 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
52776
53219
|
mailbox: null,
|
|
52777
53220
|
observation: null,
|
|
52778
53221
|
observedCallId: null,
|
|
52779
|
-
roster:
|
|
53222
|
+
roster: null,
|
|
52780
53223
|
revision: null,
|
|
52781
53224
|
expiredCallId: null,
|
|
52782
53225
|
finishing: new Map
|
|
@@ -52793,17 +53236,25 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
52793
53236
|
if (descriptor && (!/^[a-f0-9]{64}$/u.test(descriptor.callId) || !/^[a-f0-9]{64}$/u.test(descriptor.secret) || descriptor.mode !== epochState.manifest.lineage || !Number.isSafeInteger(descriptor.startedAt) || descriptor.startedAt < 0 || descriptor.startedAt > Date.now() + 5000))
|
|
52794
53237
|
throw new Error("invalid call discovery");
|
|
52795
53238
|
const previous = entry.descriptor;
|
|
53239
|
+
if (previous?.callId !== descriptor?.callId)
|
|
53240
|
+
entry.roster = null;
|
|
52796
53241
|
entry.expiredCallId = !record.head ? previous?.callId || entry.expiredCallId : null;
|
|
52797
53242
|
entry.descriptor = descriptor;
|
|
52798
53243
|
if (!record.head && previous)
|
|
52799
53244
|
finishDiscovery(entry, previous);
|
|
52800
53245
|
const changed = entry.revision !== record.revision;
|
|
52801
53246
|
entry.revision = record.revision;
|
|
53247
|
+
if (!descriptor) {
|
|
53248
|
+
const hint = messageHints.get(chatId);
|
|
53249
|
+
if (hint)
|
|
53250
|
+
hint.watch = false;
|
|
53251
|
+
}
|
|
52802
53252
|
if (active?.discovery === entry && active.published && descriptor?.callId !== active.callId) {
|
|
52803
53253
|
leave();
|
|
52804
53254
|
}
|
|
52805
53255
|
observeRoster(entry, changed);
|
|
52806
53256
|
available();
|
|
53257
|
+
prune();
|
|
52807
53258
|
},
|
|
52808
53259
|
onError(error) {
|
|
52809
53260
|
if (token !== generation || discoveries.get(chatId) !== entry)
|
|
@@ -52850,7 +53301,7 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
52850
53301
|
function observe(chatId, onAvailable) {
|
|
52851
53302
|
if (!chatId || closed)
|
|
52852
53303
|
return () => {};
|
|
52853
|
-
const observer = { chatId, onAvailable, fingerprint: undefined };
|
|
53304
|
+
const observer = { chatId, onAvailable, fingerprint: undefined, startedAt: Date.now(), resolved: false };
|
|
52854
53305
|
observers.set(observer, observer);
|
|
52855
53306
|
focused = chatId;
|
|
52856
53307
|
if (state.error?.chatId === chatId)
|
|
@@ -52902,11 +53353,11 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
52902
53353
|
})();
|
|
52903
53354
|
const destination = (async () => {
|
|
52904
53355
|
if (!onlyExisting && !continuation)
|
|
52905
|
-
chatId = await chat.getSnapshot().materializeChat(chatId);
|
|
53356
|
+
chatId = await chat.getSnapshot().materializeChat(chatId, { signal: operation.controller.signal });
|
|
52906
53357
|
check();
|
|
52907
53358
|
joining.chatId = chatId;
|
|
52908
53359
|
step("chat");
|
|
52909
|
-
const epochState = continuation ? chat.getSnapshot().getOwnerChat(chatId)?.epochState : await chat.getSnapshot().prepareCall(chatId);
|
|
53360
|
+
const epochState = continuation ? chat.getSnapshot().getOwnerChat(chatId)?.epochState : await chat.getSnapshot().prepareCall(chatId, { signal: operation.controller.signal });
|
|
52910
53361
|
check();
|
|
52911
53362
|
if (!epochState)
|
|
52912
53363
|
throw cancelled2();
|
|
@@ -52915,7 +53366,7 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
52915
53366
|
step("discovery");
|
|
52916
53367
|
return entry2;
|
|
52917
53368
|
})();
|
|
52918
|
-
const [, entry] = await Promise.all([permission, destination, operation
|
|
53369
|
+
const [, entry] = await Promise.all([permission, destination, captureVisual(operation)]);
|
|
52919
53370
|
check();
|
|
52920
53371
|
const discoveryRecord = entry.mailbox.getSnapshot();
|
|
52921
53372
|
const create = !entry.descriptor;
|
|
@@ -52951,7 +53402,8 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
52951
53402
|
wasReady: false,
|
|
52952
53403
|
signalingReady: true,
|
|
52953
53404
|
ownershipReady: true,
|
|
52954
|
-
|
|
53405
|
+
visual: null,
|
|
53406
|
+
capturing: null,
|
|
52955
53407
|
sourceContext: 0,
|
|
52956
53408
|
leaving: false,
|
|
52957
53409
|
muteTail: Promise.resolve()
|
|
@@ -52997,7 +53449,7 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
52997
53449
|
const waiting = call2.mediaReady && descriptor.mode === "direct" && state.participants.length < 2;
|
|
52998
53450
|
const ready = call2.mediaState === "connected" || waiting;
|
|
52999
53451
|
if (ready && call2.signalingReady && call2.ownershipReady) {
|
|
53000
|
-
call2.wasReady =
|
|
53452
|
+
call2.wasReady = !waiting;
|
|
53001
53453
|
clearTimeout(call2.joinTimer);
|
|
53002
53454
|
call2.joinTimer = null;
|
|
53003
53455
|
emit({ phase: waiting ? "waiting" : "connected" });
|
|
@@ -53011,7 +53463,7 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53011
53463
|
fail(new Error("call did not connect"));
|
|
53012
53464
|
}, 30000);
|
|
53013
53465
|
}
|
|
53014
|
-
emit(
|
|
53466
|
+
emit(sourceState(call2));
|
|
53015
53467
|
};
|
|
53016
53468
|
call2.connectionProgress = connectionProgress;
|
|
53017
53469
|
call2.media = createCallMediaSession({
|
|
@@ -53058,8 +53510,8 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53058
53510
|
emit({ videoSources });
|
|
53059
53511
|
},
|
|
53060
53512
|
onSourceEnded(id) {
|
|
53061
|
-
if (valid() && call2.
|
|
53062
|
-
|
|
53513
|
+
if (valid() && call2.visual?.id === id)
|
|
53514
|
+
stopVisual().catch(() => {});
|
|
53063
53515
|
},
|
|
53064
53516
|
onError(error) {
|
|
53065
53517
|
if (valid())
|
|
@@ -53104,7 +53556,7 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53104
53556
|
if (preference)
|
|
53105
53557
|
call2.media.setPeerAudio(peer.holder, { volume: preference.volume / 100, muted: preference.muted }).catch((error) => diag?.("call.peer.audio.error", { code: error?.code || "calls/audio" }));
|
|
53106
53558
|
}
|
|
53107
|
-
emit({ participants: value,
|
|
53559
|
+
emit({ participants: value, ...sourceState(call2) });
|
|
53108
53560
|
available();
|
|
53109
53561
|
call2.refresh?.();
|
|
53110
53562
|
if (call2.mediaReady)
|
|
@@ -53135,6 +53587,8 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53135
53587
|
checkCall();
|
|
53136
53588
|
await call2.room.start({ create });
|
|
53137
53589
|
checkCall();
|
|
53590
|
+
if (operation.visual)
|
|
53591
|
+
assertCallVideoSources(call2.participants, callEndpoint.holder, { sources: [{ ...operation.visual, contextId: 1 }] });
|
|
53138
53592
|
step("admission");
|
|
53139
53593
|
if (create) {
|
|
53140
53594
|
try {
|
|
@@ -53236,12 +53690,14 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53236
53690
|
};
|
|
53237
53691
|
call2.refresh = refresh;
|
|
53238
53692
|
refresh(true);
|
|
53239
|
-
|
|
53240
|
-
await operation
|
|
53693
|
+
while (operation.visual) {
|
|
53694
|
+
await captureVisual(operation);
|
|
53241
53695
|
check();
|
|
53242
|
-
|
|
53696
|
+
const source = operation.visual;
|
|
53697
|
+
await publishVisual(call2, source);
|
|
53243
53698
|
check();
|
|
53244
|
-
operation.
|
|
53699
|
+
if (operation.visual === source)
|
|
53700
|
+
operation.visual = null;
|
|
53245
53701
|
}
|
|
53246
53702
|
return chatId;
|
|
53247
53703
|
} catch (error) {
|
|
@@ -53274,20 +53730,21 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53274
53730
|
throw new Error("open veyl to join a call");
|
|
53275
53731
|
return runJoin(chatId, expectedCallId, onlyExisting);
|
|
53276
53732
|
}
|
|
53277
|
-
function runJoin(chatId, expectedCallId = null, onlyExisting = false, continuation = null,
|
|
53733
|
+
function runJoin(chatId, expectedCallId = null, onlyExisting = false, continuation = null, visual = null) {
|
|
53278
53734
|
const intent = ++joinIntent;
|
|
53279
|
-
const operation = { predecessor: active, chatId, candidate: null, continuation,
|
|
53735
|
+
const operation = { predecessor: active, chatId, candidate: null, continuation, visual, controller: new AbortController };
|
|
53280
53736
|
let resolve, reject;
|
|
53281
53737
|
operation.promise = new Promise((done, fail2) => {
|
|
53282
53738
|
resolve = done;
|
|
53283
53739
|
reject = fail2;
|
|
53284
53740
|
});
|
|
53285
|
-
if (
|
|
53286
|
-
|
|
53741
|
+
if (visual)
|
|
53742
|
+
visual.promise = operation.promise;
|
|
53287
53743
|
const previous = joining;
|
|
53288
53744
|
joining = operation;
|
|
53745
|
+
previous?.controller.abort();
|
|
53289
53746
|
previous?.preparation?.close();
|
|
53290
|
-
previous?.
|
|
53747
|
+
previous?.visual?.stop();
|
|
53291
53748
|
emit({ joiningChatId: chatId });
|
|
53292
53749
|
if (previous?.candidate && previous.candidate !== active)
|
|
53293
53750
|
disposeCall(previous.candidate);
|
|
@@ -53311,7 +53768,8 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53311
53768
|
}
|
|
53312
53769
|
}
|
|
53313
53770
|
} finally {
|
|
53314
|
-
operation.
|
|
53771
|
+
operation.controller.abort();
|
|
53772
|
+
operation.visual?.stop();
|
|
53315
53773
|
if (joining === operation) {
|
|
53316
53774
|
joining = null;
|
|
53317
53775
|
emit({ joiningChatId: null });
|
|
@@ -53323,8 +53781,9 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53323
53781
|
}
|
|
53324
53782
|
function leave() {
|
|
53325
53783
|
joinIntent += 1;
|
|
53784
|
+
joining?.controller.abort();
|
|
53326
53785
|
joining?.preparation?.close();
|
|
53327
|
-
joining?.
|
|
53786
|
+
joining?.visual?.stop();
|
|
53328
53787
|
const candidate = joining?.candidate;
|
|
53329
53788
|
joining = null;
|
|
53330
53789
|
emit({ joiningChatId: null });
|
|
@@ -53339,8 +53798,9 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53339
53798
|
if (!operation)
|
|
53340
53799
|
return Promise.resolve();
|
|
53341
53800
|
joinIntent += 1;
|
|
53801
|
+
operation.controller.abort();
|
|
53342
53802
|
operation.preparation?.close();
|
|
53343
|
-
operation.
|
|
53803
|
+
operation.visual?.stop();
|
|
53344
53804
|
joining = null;
|
|
53345
53805
|
emit({ joiningChatId: null });
|
|
53346
53806
|
const pending = operation.candidate && operation.candidate !== active ? disposeCall(operation.candidate) : null;
|
|
@@ -53359,15 +53819,17 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53359
53819
|
leaving = retired.length ? Promise.race([released, Promise.all(retired.map((room) => room.closing))]) : released;
|
|
53360
53820
|
}
|
|
53361
53821
|
if (!call2) {
|
|
53362
|
-
emit({ phase: "idle", chatId: null, callId: null, mode: null, transport: null, participants: [], speaking: [], videoSources: [], sharingScreen: false, screenShareAvailable: false });
|
|
53822
|
+
emit({ phase: "idle", chatId: null, callId: null, mode: null, transport: null, participants: [], speaking: [], videoSources: [], sharingScreen: false, screenShareAvailable: false, sharingCamera: false, cameraAvailable: false });
|
|
53363
53823
|
return leaving;
|
|
53364
53824
|
}
|
|
53825
|
+
if (call2.discovery.descriptor?.callId === call2.callId)
|
|
53826
|
+
call2.discovery.roster = call2.participants;
|
|
53365
53827
|
active = null;
|
|
53366
53828
|
leaving = disposeCall(call2);
|
|
53367
53829
|
if (identity && discoveries.get(call2.chatId) === call2.discovery)
|
|
53368
53830
|
observeRoster(call2.discovery);
|
|
53369
53831
|
available();
|
|
53370
|
-
emit({ phase: "idle", chatId: null, callId: null, mode: null, transport: null, participants: [], speaking: [], videoSources: [], sharingScreen: false, screenShareAvailable: false });
|
|
53832
|
+
emit({ phase: "idle", chatId: null, callId: null, mode: null, transport: null, participants: [], speaking: [], videoSources: [], sharingScreen: false, screenShareAvailable: false, sharingCamera: false, cameraAvailable: false });
|
|
53371
53833
|
return leaving;
|
|
53372
53834
|
}
|
|
53373
53835
|
async function retireDiscovery(call2) {
|
|
@@ -53388,15 +53850,20 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53388
53850
|
function finishDiscovery(entry, descriptor) {
|
|
53389
53851
|
if (entry.finishing.has(descriptor.callId))
|
|
53390
53852
|
return;
|
|
53391
|
-
const finishing = retireDiscovery({ discovery: entry, callId: descriptor.callId, chatId: entry.epochState.manifest.chatId }).catch((error) => diag?.("call.close.error", { code: error?.code || "calls/discovery" })).finally(() =>
|
|
53853
|
+
const finishing = retireDiscovery({ discovery: entry, callId: descriptor.callId, chatId: entry.epochState.manifest.chatId }).catch((error) => diag?.("call.close.error", { code: error?.code || "calls/discovery" })).finally(() => {
|
|
53854
|
+
entry.finishing.delete(descriptor.callId);
|
|
53855
|
+
prune();
|
|
53856
|
+
});
|
|
53392
53857
|
entry.finishing.set(descriptor.callId, finishing);
|
|
53393
53858
|
}
|
|
53394
53859
|
function disposeCall(call2) {
|
|
53395
53860
|
if (call2.closing)
|
|
53396
53861
|
return call2.closing;
|
|
53397
53862
|
call2.leaving = true;
|
|
53398
|
-
call2.
|
|
53399
|
-
call2.
|
|
53863
|
+
call2.capturing?.stop();
|
|
53864
|
+
call2.capturing = null;
|
|
53865
|
+
call2.visual?.stop();
|
|
53866
|
+
call2.visual = null;
|
|
53400
53867
|
retiring.add(call2);
|
|
53401
53868
|
clearTimeout(call2.timer);
|
|
53402
53869
|
clearTimeout(call2.joinTimer);
|
|
@@ -53438,28 +53905,40 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53438
53905
|
});
|
|
53439
53906
|
return call2.closing;
|
|
53440
53907
|
}
|
|
53441
|
-
function
|
|
53442
|
-
if (!identity || closed || !
|
|
53443
|
-
return Promise.reject(new Error("screen sharing is unavailable in this call
|
|
53908
|
+
function startVisual(kind, chatId = active?.chatId) {
|
|
53909
|
+
if (!identity || closed || !captureFor(kind) || !mediaPort.capabilities?.[kind] || !mediaPort.capabilities?.video || !mls || !cloud.calls || !chatId)
|
|
53910
|
+
return Promise.reject(new Error(`${kind === "camera" ? "camera" : "screen sharing"} is unavailable in this call`));
|
|
53444
53911
|
const operation = joining && [joining.chatId, state.joiningChatId].includes(chatId) ? joining : null;
|
|
53445
|
-
if (operation?.screen)
|
|
53446
|
-
return operation.screen.promise;
|
|
53447
53912
|
const call2 = active?.chatId === chatId ? active : null;
|
|
53448
|
-
|
|
53449
|
-
|
|
53913
|
+
const pending = operation?.visual || call2?.capturing;
|
|
53914
|
+
if (pending?.kind === kind)
|
|
53915
|
+
return pending.promise;
|
|
53916
|
+
if (!pending && call2?.visual?.kind === kind)
|
|
53917
|
+
return call2.visual.promise;
|
|
53918
|
+
if (call2 && !operation && !canShare(call2, kind)) {
|
|
53919
|
+
try {
|
|
53920
|
+
assertCallVideoSources(call2.participants, call2.endpoint.holder, { sources: [{ id: "capture", kind, contextId: 1 }] });
|
|
53921
|
+
} catch (error) {
|
|
53922
|
+
return Promise.reject(error);
|
|
53923
|
+
}
|
|
53924
|
+
return Promise.reject(new Error(`${kind} is unavailable in this call`));
|
|
53925
|
+
}
|
|
53450
53926
|
if (!call2 && !foreground)
|
|
53451
53927
|
return Promise.reject(new Error("open veyl to join a call"));
|
|
53452
|
-
|
|
53453
|
-
|
|
53928
|
+
let rejectCapture;
|
|
53929
|
+
const cancellation = new Promise((_, reject) => {
|
|
53930
|
+
rejectCapture = reject;
|
|
53931
|
+
});
|
|
53454
53932
|
const source = {
|
|
53455
53933
|
id: toHex(randomBytes3(16)),
|
|
53456
53934
|
contextId: null,
|
|
53457
|
-
kind
|
|
53935
|
+
kind,
|
|
53458
53936
|
stream: null,
|
|
53459
53937
|
promise: null,
|
|
53460
53938
|
cancelled: false,
|
|
53461
53939
|
stop() {
|
|
53462
53940
|
source.cancelled = true;
|
|
53941
|
+
rejectCapture(cancelled2());
|
|
53463
53942
|
for (const track of source.stream?.getTracks() || []) {
|
|
53464
53943
|
track.removeEventListener?.("ended", sourceEnded);
|
|
53465
53944
|
track.stop();
|
|
@@ -53467,18 +53946,18 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53467
53946
|
}
|
|
53468
53947
|
};
|
|
53469
53948
|
const sourceEnded = () => {
|
|
53470
|
-
if (joining?.
|
|
53949
|
+
if (joining?.visual === source)
|
|
53471
53950
|
cancelJoin();
|
|
53472
|
-
else if (active?.
|
|
53473
|
-
|
|
53951
|
+
else if (active?.visual === source || active?.capturing === source)
|
|
53952
|
+
stopVisual().catch(() => {});
|
|
53474
53953
|
};
|
|
53475
53954
|
let capturing;
|
|
53476
53955
|
try {
|
|
53477
|
-
capturing =
|
|
53956
|
+
capturing = captureFor(kind).call(mediaPort);
|
|
53478
53957
|
} catch (error) {
|
|
53479
53958
|
return Promise.reject(error);
|
|
53480
53959
|
}
|
|
53481
|
-
source.capture = Promise.resolve(capturing).then((stream) => {
|
|
53960
|
+
source.capture = Promise.race([cancellation, Promise.resolve(capturing).then((stream) => {
|
|
53482
53961
|
if (source.cancelled || stream.getTracks().some((track) => track.readyState === "ended")) {
|
|
53483
53962
|
stream.getTracks().forEach((track) => track.stop());
|
|
53484
53963
|
throw cancelled2();
|
|
@@ -53486,64 +53965,78 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53486
53965
|
source.stream = stream;
|
|
53487
53966
|
for (const track of stream.getTracks())
|
|
53488
53967
|
track.addEventListener?.("ended", sourceEnded, { once: true });
|
|
53489
|
-
});
|
|
53968
|
+
})]);
|
|
53490
53969
|
if (operation) {
|
|
53970
|
+
operation.visual = source;
|
|
53971
|
+
if (pending !== call2?.visual)
|
|
53972
|
+
pending?.stop();
|
|
53491
53973
|
source.promise = Promise.all([source.capture, operation.promise]).then(() => {
|
|
53492
53974
|
return;
|
|
53493
53975
|
});
|
|
53494
|
-
operation.screen = source;
|
|
53495
53976
|
return source.promise;
|
|
53496
53977
|
}
|
|
53497
53978
|
if (!call2) {
|
|
53498
53979
|
source.promise = runJoin(chatId, null, false, null, source);
|
|
53499
53980
|
return source.promise;
|
|
53500
53981
|
}
|
|
53501
|
-
call2.
|
|
53982
|
+
call2.capturing = source;
|
|
53983
|
+
if (pending !== call2.visual)
|
|
53984
|
+
pending?.stop();
|
|
53502
53985
|
source.promise = source.capture.then(() => {
|
|
53503
|
-
if (!
|
|
53986
|
+
if (source.cancelled || active !== call2 || call2.capturing !== source || call2.leaving || !call2.mediaReady)
|
|
53504
53987
|
throw cancelled2();
|
|
53505
|
-
return
|
|
53988
|
+
return publishVisual(call2, source);
|
|
53506
53989
|
}).catch(async (error) => {
|
|
53507
53990
|
source.stop();
|
|
53508
|
-
if (call2.
|
|
53509
|
-
call2.
|
|
53991
|
+
if (call2.visual === source) {
|
|
53992
|
+
call2.visual = null;
|
|
53510
53993
|
if (active === call2)
|
|
53511
|
-
emit(
|
|
53994
|
+
emit(sourceState(call2));
|
|
53512
53995
|
if (!call2.leaving && source.stream)
|
|
53513
53996
|
await call2.media.removeSource(source.id).catch(() => {});
|
|
53514
53997
|
}
|
|
53515
53998
|
throw error;
|
|
53999
|
+
}).finally(() => {
|
|
54000
|
+
if (call2.capturing === source)
|
|
54001
|
+
call2.capturing = null;
|
|
53516
54002
|
});
|
|
53517
54003
|
return source.promise;
|
|
53518
54004
|
}
|
|
53519
|
-
async function
|
|
53520
|
-
if (source.cancelled || active !== call2 || call2.leaving || !call2.mediaReady || !call2.media.
|
|
54005
|
+
async function publishVisual(call2, source) {
|
|
54006
|
+
if (source.cancelled || active !== call2 || call2.leaving || !call2.mediaReady || !call2.media.supportsSource(source.kind))
|
|
53521
54007
|
throw cancelled2();
|
|
54008
|
+
const previous = call2.visual;
|
|
53522
54009
|
source.contextId = ++call2.sourceContext;
|
|
53523
|
-
|
|
53524
|
-
|
|
53525
|
-
|
|
53526
|
-
|
|
54010
|
+
previous?.stop();
|
|
54011
|
+
call2.visual = source;
|
|
54012
|
+
emit(sourceState(call2));
|
|
54013
|
+
await call2.media.addSource(source, previous?.id);
|
|
54014
|
+
if (active !== call2 || call2.visual !== source)
|
|
53527
54015
|
throw cancelled2();
|
|
53528
54016
|
}
|
|
53529
|
-
async function
|
|
53530
|
-
if (joining?.
|
|
54017
|
+
async function stopVisual(kind) {
|
|
54018
|
+
if (joining?.visual && (!kind || joining.visual.kind === kind))
|
|
53531
54019
|
return cancelJoin();
|
|
53532
54020
|
const call2 = active;
|
|
53533
|
-
|
|
53534
|
-
|
|
54021
|
+
if (call2?.capturing && (!kind || call2.capturing.kind === kind || call2.visual?.kind === kind)) {
|
|
54022
|
+
call2.capturing.stop();
|
|
54023
|
+
call2.capturing = null;
|
|
54024
|
+
}
|
|
54025
|
+
const source = call2?.visual;
|
|
54026
|
+
if (!source || kind && source.kind !== kind)
|
|
53535
54027
|
return;
|
|
53536
|
-
call2.
|
|
54028
|
+
call2.visual = null;
|
|
53537
54029
|
source.stop();
|
|
53538
|
-
emit(
|
|
54030
|
+
emit(sourceState(call2));
|
|
53539
54031
|
if (source.stream)
|
|
53540
54032
|
await call2.media.removeSource(source.id);
|
|
53541
54033
|
}
|
|
53542
54034
|
function applyAudio(call2) {
|
|
53543
54035
|
const audio = { muted: state.muted || state.deafened, deafened: state.deafened };
|
|
53544
54036
|
const applied = Promise.all([call2.media.mute(audio.muted), call2.media.deafen(audio.deafened)]);
|
|
54037
|
+
call2.pendingAudio = audio;
|
|
53545
54038
|
const result = Promise.all([applied, call2.muteTail]).then(() => {
|
|
53546
|
-
if (!call2.leaving)
|
|
54039
|
+
if (!call2.leaving && call2.pendingAudio === audio)
|
|
53547
54040
|
return call2.room.setAudio(audio);
|
|
53548
54041
|
});
|
|
53549
54042
|
call2.muteTail = result.catch(() => {});
|
|
@@ -53555,14 +54048,16 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53555
54048
|
return;
|
|
53556
54049
|
const token = generation;
|
|
53557
54050
|
pendingAudio = value;
|
|
53558
|
-
const saving =
|
|
54051
|
+
const saving = audioSaveTail.then(async () => {
|
|
53559
54052
|
if (generation !== token)
|
|
53560
54053
|
throw cancelled2();
|
|
53561
|
-
|
|
53562
|
-
|
|
54054
|
+
if (pendingAudio !== value)
|
|
54055
|
+
return;
|
|
54056
|
+
await saveAudio(value);
|
|
53563
54057
|
if (generation === token)
|
|
53564
54058
|
audioSource = value;
|
|
53565
54059
|
});
|
|
54060
|
+
audioSaveTail = saving.catch(() => {});
|
|
53566
54061
|
try {
|
|
53567
54062
|
await Promise.all([saving, applyAudioPreferences(value)]);
|
|
53568
54063
|
} finally {
|
|
@@ -53733,6 +54228,7 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53733
54228
|
endpoint = null;
|
|
53734
54229
|
discoveries.clear();
|
|
53735
54230
|
discoveryReads.clear();
|
|
54231
|
+
messageHints.clear();
|
|
53736
54232
|
identity = session;
|
|
53737
54233
|
const stopped = leave();
|
|
53738
54234
|
Promise.resolve(stopped).finally(() => {
|
|
@@ -53747,6 +54243,7 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53747
54243
|
confirmedPeerAudio.clear();
|
|
53748
54244
|
pendingPeerAudio.clear();
|
|
53749
54245
|
pendingAudio = null;
|
|
54246
|
+
audioSaveTail = Promise.resolve();
|
|
53750
54247
|
audioSource = normalizeCallAudio(settings.callAudio);
|
|
53751
54248
|
peerAudioSource = session ? peerAudio : null;
|
|
53752
54249
|
for (const [chatPK, value] of Object.entries(peerAudioSource || {}))
|
|
@@ -53764,10 +54261,41 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53764
54261
|
return retirement;
|
|
53765
54262
|
}
|
|
53766
54263
|
function refreshObserved() {
|
|
53767
|
-
for (const chatId of
|
|
54264
|
+
for (const chatId of observedChatIds()) {
|
|
53768
54265
|
readDiscovery(chatId).catch((error) => diag?.("call.discovery.error", { stage: "prepare", code: error?.code || "calls/admission" }));
|
|
53769
54266
|
}
|
|
53770
54267
|
}
|
|
54268
|
+
function observedChatIds() {
|
|
54269
|
+
return new Set([...observers.values()].map((item) => item.chatId).concat([...messageHints].filter(([, hint]) => hint.watch).map(([chatId]) => chatId)));
|
|
54270
|
+
}
|
|
54271
|
+
const unsubscribeMessages = chat.subscribeMessages((chatId, messages) => {
|
|
54272
|
+
if (!identity || closed)
|
|
54273
|
+
return;
|
|
54274
|
+
let latest = null;
|
|
54275
|
+
for (const message of messages) {
|
|
54276
|
+
if (!isServerConfirmedMsg(message) || !readCallMessage(message))
|
|
54277
|
+
continue;
|
|
54278
|
+
if (!latest || getMessageOrderMs(message) > getMessageOrderMs(latest) || getMessageOrderMs(message) === getMessageOrderMs(latest) && getMessageKey(message) > getMessageKey(latest))
|
|
54279
|
+
latest = message;
|
|
54280
|
+
}
|
|
54281
|
+
if (!latest)
|
|
54282
|
+
return;
|
|
54283
|
+
const at = getMessageOrderMs(latest), key = getMessageKey(latest);
|
|
54284
|
+
if (!Number.isFinite(at) || !key)
|
|
54285
|
+
return;
|
|
54286
|
+
const previous = messageHints.get(chatId);
|
|
54287
|
+
if (previous && (previous.at > at || previous.at === at && previous.key >= key))
|
|
54288
|
+
return;
|
|
54289
|
+
const entry = discoveries.get(chatId);
|
|
54290
|
+
const watch = latest.event === "started" || previous?.watch === true || !!entry?.descriptor;
|
|
54291
|
+
messageHints.delete(chatId);
|
|
54292
|
+
messageHints.set(chatId, { at, key, watch });
|
|
54293
|
+
while (messageHints.size > RECENT_CALL_HINT_LIMIT)
|
|
54294
|
+
messageHints.delete(messageHints.keys().next().value);
|
|
54295
|
+
prune();
|
|
54296
|
+
if (watch && online)
|
|
54297
|
+
readDiscovery(chatId).catch((error) => diag?.("call.discovery.error", { stage: "message", code: error?.code || "calls/discovery" }));
|
|
54298
|
+
});
|
|
53771
54299
|
const unsubscribe = chat.subscribe(() => {
|
|
53772
54300
|
if (!identity)
|
|
53773
54301
|
return;
|
|
@@ -53781,6 +54309,7 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53781
54309
|
const intent = ++joinIntent;
|
|
53782
54310
|
const previous = active || joining?.candidate;
|
|
53783
54311
|
active = null;
|
|
54312
|
+
joining?.controller.abort();
|
|
53784
54313
|
joining = null;
|
|
53785
54314
|
if (previous)
|
|
53786
54315
|
disposeCall(previous);
|
|
@@ -53792,6 +54321,8 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53792
54321
|
videoSources: [],
|
|
53793
54322
|
sharingScreen: false,
|
|
53794
54323
|
screenShareAvailable: false,
|
|
54324
|
+
sharingCamera: false,
|
|
54325
|
+
cameraAvailable: false,
|
|
53795
54326
|
participants: state.participants.filter((member) => members.has(member.chatPK))
|
|
53796
54327
|
});
|
|
53797
54328
|
diag?.("call.epoch.transition", { stage: "starting", participants: state.participants.length });
|
|
@@ -53810,11 +54341,11 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53810
54341
|
closeDiscovery(entry);
|
|
53811
54342
|
discoveries.delete(chatId);
|
|
53812
54343
|
available();
|
|
53813
|
-
if (online &&
|
|
54344
|
+
if (online && observedChatIds().has(chatId))
|
|
53814
54345
|
readDiscovery(chatId).catch(() => {});
|
|
53815
54346
|
}
|
|
53816
54347
|
if (online)
|
|
53817
|
-
for (const chatId of
|
|
54348
|
+
for (const chatId of observedChatIds()) {
|
|
53818
54349
|
if (!discoveries.has(chatId) && !discoveryReads.has(chatId) && chat.getSnapshot().getOwnerChat(chatId)?.epochState) {
|
|
53819
54350
|
readDiscovery(chatId).catch((error) => diag?.("call.discovery.error", { stage: "prepare", code: error?.code || "calls/admission" }));
|
|
53820
54351
|
}
|
|
@@ -53832,8 +54363,10 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53832
54363
|
leave,
|
|
53833
54364
|
cancelJoin,
|
|
53834
54365
|
setMicrophoneVolume,
|
|
53835
|
-
startScreenShare,
|
|
53836
|
-
stopScreenShare,
|
|
54366
|
+
startScreenShare: (chatId) => startVisual("screen", chatId),
|
|
54367
|
+
stopScreenShare: () => stopVisual("screen"),
|
|
54368
|
+
startCamera: (chatId) => startVisual("camera", chatId),
|
|
54369
|
+
stopCamera: () => stopVisual("camera"),
|
|
53837
54370
|
async resolveActiveCall(chatId) {
|
|
53838
54371
|
const entry = await readDiscovery(chatId);
|
|
53839
54372
|
return entry.descriptor?.callId || null;
|
|
@@ -53848,6 +54381,7 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53848
54381
|
return Promise.reject(ended());
|
|
53849
54382
|
return join(chatId, callId, true);
|
|
53850
54383
|
},
|
|
54384
|
+
setAudio,
|
|
53851
54385
|
setMuted(value) {
|
|
53852
54386
|
return setAudio({ muted: value === true });
|
|
53853
54387
|
},
|
|
@@ -53881,6 +54415,7 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53881
54415
|
});
|
|
53882
54416
|
closed = true;
|
|
53883
54417
|
const stopped = setSources(null);
|
|
54418
|
+
unsubscribeMessages();
|
|
53884
54419
|
unsubscribe();
|
|
53885
54420
|
listeners.clear();
|
|
53886
54421
|
Promise.resolve(stopped).finally(() => Promise.all([mls?.close?.(), mediaPort?.close?.()])).then(finish, reject);
|
|
@@ -53888,7 +54423,7 @@ function createCallSession({ cloud, chat, media: mediaPort, mls, savePeerAudio,
|
|
|
53888
54423
|
}
|
|
53889
54424
|
});
|
|
53890
54425
|
}
|
|
53891
|
-
var initial = () => ({ phase: "idle", chatId: null, callId: null, joiningChatId: null, mode: null, transport: null, available: null, elsewhere: null, participants: [], speaking: [], videoSources: [], sharingScreen: false, screenShareAvailable: false, peerAudio: {}, muted: false, deafened: false, microphoneVolume: 100, error: null }), cancelled2 = () => Object.assign(new Error("call cancelled"), { code: "calls/cancelled" }), ended = () => Object.assign(new Error("this call has ended"), { code: "calls/ended" }), callError = (chatId, error) => ({ chatId, code: error?.code || "calls/failed", message: error?.message || "could not connect the call" });
|
|
54426
|
+
var initial = () => ({ phase: "idle", chatId: null, callId: null, joiningChatId: null, mode: null, transport: null, available: null, elsewhere: null, participants: [], speaking: [], videoSources: [], sharingScreen: false, screenShareAvailable: false, sharingCamera: false, cameraAvailable: false, peerAudio: {}, muted: false, deafened: false, microphoneVolume: 100, error: null }), cancelled2 = () => Object.assign(new Error("call cancelled"), { code: "calls/cancelled" }), ended = () => Object.assign(new Error("this call has ended"), { code: "calls/ended" }), RECENT_CALL_HINT_LIMIT = 32, callError = (chatId, error) => ({ chatId, code: error?.code || "calls/failed", message: error?.message || "could not connect the call" });
|
|
53892
54427
|
var init_session5 = __esm(() => {
|
|
53893
54428
|
init_core();
|
|
53894
54429
|
init_identity();
|
|
@@ -53899,9 +54434,11 @@ var init_session5 = __esm(() => {
|
|
|
53899
54434
|
init_media5();
|
|
53900
54435
|
init_observation();
|
|
53901
54436
|
init_wire();
|
|
53902
|
-
|
|
54437
|
+
init_state();
|
|
54438
|
+
init_control();
|
|
53903
54439
|
init_ownership();
|
|
53904
54440
|
init_settings();
|
|
54441
|
+
init_sources();
|
|
53905
54442
|
});
|
|
53906
54443
|
|
|
53907
54444
|
// ../../node_modules/.bun/@zxcvbn-ts+core@4.2.0/node_modules/@zxcvbn-ts/core/dist/utils/helper.cjs
|
|
@@ -57525,6 +58062,18 @@ function stablePeerMap(previous, next) {
|
|
|
57525
58062
|
}
|
|
57526
58063
|
return previous;
|
|
57527
58064
|
}
|
|
58065
|
+
function replaceIndexedPeers(index2, updates, field) {
|
|
58066
|
+
let next = index2;
|
|
58067
|
+
for (const peer of updates.values()) {
|
|
58068
|
+
const key = peer[field];
|
|
58069
|
+
if (!key || index2.get(key)?.uid !== peer.uid)
|
|
58070
|
+
continue;
|
|
58071
|
+
if (next === index2)
|
|
58072
|
+
next = new Map(index2);
|
|
58073
|
+
next.set(key, peer);
|
|
58074
|
+
}
|
|
58075
|
+
return next;
|
|
58076
|
+
}
|
|
57528
58077
|
function stableValueSet(previous, next) {
|
|
57529
58078
|
if (previous === next)
|
|
57530
58079
|
return previous;
|
|
@@ -57552,12 +58101,11 @@ function peerProfileSnapshot(peer) {
|
|
|
57552
58101
|
active: !!peer.active,
|
|
57553
58102
|
presence: peer.presence,
|
|
57554
58103
|
accountType: peer.accountType || null,
|
|
57555
|
-
blocked: !!peer.blocked
|
|
57556
|
-
hidden: !!peer.hidden
|
|
58104
|
+
blocked: !!peer.blocked
|
|
57557
58105
|
};
|
|
57558
58106
|
}
|
|
57559
58107
|
function samePeerProfile(left, right) {
|
|
57560
|
-
return left === right || !!left && !!right && left.uid === right.uid && left.username === right.username && left.chatPK === right.chatPK && left.chatSigningPK === right.chatSigningPK && left.notificationPK === right.notificationPK && sameChatAdmission(left.chatAdmission, right.chatAdmission) && left.walletPK === right.walletPK && left.avatar === right.avatar && left.avatarVersion === right.avatarVersion && left.active === right.active && left.presence === right.presence && left.accountType === right.accountType && left.blocked === right.blocked
|
|
58108
|
+
return left === right || !!left && !!right && left.uid === right.uid && left.username === right.username && left.chatPK === right.chatPK && left.chatSigningPK === right.chatSigningPK && left.notificationPK === right.notificationPK && sameChatAdmission(left.chatAdmission, right.chatAdmission) && left.walletPK === right.walletPK && left.avatar === right.avatar && left.avatarVersion === right.avatarVersion && left.active === right.active && left.presence === right.presence && left.accountType === right.accountType && left.blocked === right.blocked;
|
|
57561
58109
|
}
|
|
57562
58110
|
function normalizeProfileIdentity(identity = {}) {
|
|
57563
58111
|
const uid = String(identity?.uid || "").trim();
|
|
@@ -57647,7 +58195,6 @@ function createPeerList({ peerApi, presence, diag, onMissingPeers }) {
|
|
|
57647
58195
|
let blockedUids = [];
|
|
57648
58196
|
let blockedUidsKey = "";
|
|
57649
58197
|
let blockedUidSet = new Set;
|
|
57650
|
-
let hiddenPeerUidSet = new Set;
|
|
57651
58198
|
let seenWalletPKs = new Set;
|
|
57652
58199
|
let seenChatPKs = new Set;
|
|
57653
58200
|
let walletPKs = [];
|
|
@@ -57681,7 +58228,7 @@ function createPeerList({ peerApi, presence, diag, onMissingPeers }) {
|
|
|
57681
58228
|
subscriberCount += 1;
|
|
57682
58229
|
if (!firstSubscriber)
|
|
57683
58230
|
return;
|
|
57684
|
-
unsubscribePresence = presence.subscribePeers(
|
|
58231
|
+
unsubscribePresence = presence.subscribePeers(updatePresence);
|
|
57685
58232
|
hydrateLocalAvatars();
|
|
57686
58233
|
const becameReady = startProfileLoad();
|
|
57687
58234
|
if (becameReady) {
|
|
@@ -57713,7 +58260,7 @@ function createPeerList({ peerApi, presence, diag, onMissingPeers }) {
|
|
|
57713
58260
|
function resolveProfile(identity) {
|
|
57714
58261
|
if (!identity)
|
|
57715
58262
|
return null;
|
|
57716
|
-
const peer = (identity.uid ?
|
|
58263
|
+
const peer = (identity.uid ? allPeerByUid.get(identity.uid) : null) ?? (identity.username ? peerByUsername.get(identity.username) : null) ?? (identity.chatPK ? peerByChatPK.get(identity.chatPK) : null) ?? (identity.walletPK ? peerByWalletPK.get(identity.walletPK) : null) ?? null;
|
|
57717
58264
|
return peer?.uid ? peerProfilesByUid.get(peer.uid) || null : null;
|
|
57718
58265
|
}
|
|
57719
58266
|
function emitProfiles() {
|
|
@@ -57744,8 +58291,6 @@ function createPeerList({ peerApi, presence, diag, onMissingPeers }) {
|
|
|
57744
58291
|
primePeer,
|
|
57745
58292
|
findPeer,
|
|
57746
58293
|
updatePeer,
|
|
57747
|
-
dropPeer,
|
|
57748
|
-
restorePeer,
|
|
57749
58294
|
loadBlockedPeers
|
|
57750
58295
|
};
|
|
57751
58296
|
}
|
|
@@ -57823,7 +58368,7 @@ function createPeerList({ peerApi, presence, diag, onMissingPeers }) {
|
|
|
57823
58368
|
function syncPresencePeers() {
|
|
57824
58369
|
const candidates = new Map;
|
|
57825
58370
|
const add2 = (profile) => {
|
|
57826
|
-
if (!profile?.uid || profile.blocked ||
|
|
58371
|
+
if (!profile?.uid || profile.blocked || candidates.size >= RECENT_PEER_REFRESH_LIMIT)
|
|
57827
58372
|
return;
|
|
57828
58373
|
candidates.set(profile.uid, profile);
|
|
57829
58374
|
};
|
|
@@ -57847,33 +58392,61 @@ function createPeerList({ peerApi, presence, diag, onMissingPeers }) {
|
|
|
57847
58392
|
function buildBasePeers() {
|
|
57848
58393
|
allPeers = assemblePeers(walletPeerSet, sources.chatPeers, discoveredPeerUids);
|
|
57849
58394
|
}
|
|
57850
|
-
function
|
|
57851
|
-
const
|
|
57852
|
-
|
|
57853
|
-
|
|
57854
|
-
|
|
58395
|
+
function projectPeer(peer) {
|
|
58396
|
+
const activity = presence.getPeer(peer);
|
|
58397
|
+
const next = {
|
|
58398
|
+
...peer,
|
|
58399
|
+
active: activity.availability === "online",
|
|
58400
|
+
presence: activity,
|
|
58401
|
+
blocked: blockedUidSet.has(peer.uid)
|
|
58402
|
+
};
|
|
58403
|
+
const previous = allPeerByUid.get(peer.uid);
|
|
58404
|
+
return samePeerRecord(previous, next) ? previous : next;
|
|
58405
|
+
}
|
|
58406
|
+
function updatePresence(changedUids) {
|
|
58407
|
+
const updates = new Map;
|
|
58408
|
+
for (const uid of changedUids) {
|
|
58409
|
+
const previous = allPeerByUid.get(uid);
|
|
58410
|
+
if (!previous)
|
|
58411
|
+
continue;
|
|
58412
|
+
const next = projectPeer(previous);
|
|
58413
|
+
if (next !== previous)
|
|
58414
|
+
updates.set(uid, next);
|
|
57855
58415
|
}
|
|
58416
|
+
if (!updates.size)
|
|
58417
|
+
return;
|
|
58418
|
+
allPeerByUid = replaceIndexedPeers(allPeerByUid, updates, "uid");
|
|
58419
|
+
peerByUid = replaceIndexedPeers(peerByUid, updates, "uid");
|
|
58420
|
+
peerByWalletPK = replaceIndexedPeers(peerByWalletPK, updates, "walletPK");
|
|
58421
|
+
peerByChatPK = replaceIndexedPeers(peerByChatPK, updates, "chatPK");
|
|
58422
|
+
peerByUsername = replaceIndexedPeers(peerByUsername, updates, "username");
|
|
58423
|
+
const replaceList = (list) => stablePeerList(list, list.map((peer) => updates.get(peer.uid) || peer));
|
|
58424
|
+
visiblePeers = replaceList(visiblePeers);
|
|
58425
|
+
blockedPeerItems = replaceList(blockedPeerItems);
|
|
58426
|
+
const all = replaceList(recentPeers.all);
|
|
58427
|
+
const wallet = replaceList(recentPeers.wallet);
|
|
58428
|
+
const chat = replaceList(recentPeers.chat);
|
|
58429
|
+
if (all !== recentPeers.all || wallet !== recentPeers.wallet || chat !== recentPeers.chat)
|
|
58430
|
+
recentPeers = { all, wallet, chat };
|
|
58431
|
+
peerProfilesByUid = new Map(peerProfilesByUid);
|
|
58432
|
+
for (const peer of updates.values()) {
|
|
58433
|
+
const next = peerProfileSnapshot(peer);
|
|
58434
|
+
const previous = peerProfilesByUid.get(peer.uid);
|
|
58435
|
+
peerProfilesByUid.set(peer.uid, samePeerProfile(previous, next) ? previous : next);
|
|
58436
|
+
}
|
|
58437
|
+
publish();
|
|
58438
|
+
}
|
|
58439
|
+
function buildPeerProjection() {
|
|
57856
58440
|
const peerMap = new Map;
|
|
57857
58441
|
for (const peer of allPeers)
|
|
57858
58442
|
mergePeerRecord(peerMap, peer);
|
|
57859
58443
|
for (const peer of blockedPeers)
|
|
57860
58444
|
mergePeerRecord(peerMap, peer);
|
|
57861
|
-
const taggedPeers = Array.from(peerMap.values()).map(
|
|
57862
|
-
|
|
57863
|
-
const next = {
|
|
57864
|
-
...peer,
|
|
57865
|
-
active: activity.availability === "online",
|
|
57866
|
-
presence: activity,
|
|
57867
|
-
blocked: blockedPeerUidSet.has(peer.uid) || hiddenPeerUidSet.has(peer.uid),
|
|
57868
|
-
hidden: hiddenPeerUidSet.has(peer.uid)
|
|
57869
|
-
};
|
|
57870
|
-
const previous = allPeerByUid.get(peer.uid);
|
|
57871
|
-
return samePeerRecord(previous, next) ? previous : next;
|
|
57872
|
-
});
|
|
57873
|
-
visiblePeers = stablePeerList(visiblePeers, taggedPeers.filter((peer) => !peer.blocked && !peer.hidden));
|
|
58445
|
+
const taggedPeers = Array.from(peerMap.values()).map(projectPeer);
|
|
58446
|
+
visiblePeers = stablePeerList(visiblePeers, taggedPeers.filter((peer) => !peer.blocked));
|
|
57874
58447
|
const blockedByUid = new Map(taggedPeers.filter((peer) => peer?.uid && peer.blocked).map((peer) => [peer.uid, peer]));
|
|
57875
58448
|
const orderedBlocked = blockedUids.map((uid) => blockedByUid.get(uid)).filter((peer) => peer?.uid);
|
|
57876
|
-
blockedPeerItems = stablePeerList(blockedPeerItems, orderedBlocked
|
|
58449
|
+
blockedPeerItems = stablePeerList(blockedPeerItems, orderedBlocked);
|
|
57877
58450
|
blockedChatPKSet = stableValueSet(blockedChatPKSet, new Set(taggedPeers.filter((peer) => peer.blocked && peer.chatPK).map((peer) => peer.chatPK)));
|
|
57878
58451
|
const allIndexes = indexPeers(taggedPeers);
|
|
57879
58452
|
const visibleIndexes = indexPeers(visiblePeers);
|
|
@@ -57986,7 +58559,6 @@ function createPeerList({ peerApi, presence, diag, onMissingPeers }) {
|
|
|
57986
58559
|
blockedPeersReady = false;
|
|
57987
58560
|
discoveredPeerUids = new Set;
|
|
57988
58561
|
blockedPeers = [];
|
|
57989
|
-
hiddenPeerUidSet = new Set;
|
|
57990
58562
|
seenWalletPKs = new Set;
|
|
57991
58563
|
seenChatPKs = new Set;
|
|
57992
58564
|
allPeers = [];
|
|
@@ -58099,6 +58671,10 @@ function createPeerList({ peerApi, presence, diag, onMissingPeers }) {
|
|
|
58099
58671
|
blockedUids = nextBlockedUids;
|
|
58100
58672
|
blockedUidsKey = nextBlockedUidsKey;
|
|
58101
58673
|
blockedUidSet = new Set(blockedUids);
|
|
58674
|
+
for (const uid of blockedUids) {
|
|
58675
|
+
if (allPeerByUid.has(uid))
|
|
58676
|
+
discoveredPeerUids.add(uid);
|
|
58677
|
+
}
|
|
58102
58678
|
if (blockedPeersReady) {
|
|
58103
58679
|
blockedPeers = blockedPeers.filter((peer) => blockedUidSet.has(peer?.uid));
|
|
58104
58680
|
}
|
|
@@ -58140,7 +58716,7 @@ function createPeerList({ peerApi, presence, diag, onMissingPeers }) {
|
|
|
58140
58716
|
const enrichedPeer = await fetchAndCachePeer(partialProfile);
|
|
58141
58717
|
if (!isCurrent(session) || !enrichedPeer)
|
|
58142
58718
|
return null;
|
|
58143
|
-
if (blockedUidSet.has(enrichedPeer.uid)
|
|
58719
|
+
if (blockedUidSet.has(enrichedPeer.uid))
|
|
58144
58720
|
return null;
|
|
58145
58721
|
if (!discoveredPeerUids.has(enrichedPeer.uid)) {
|
|
58146
58722
|
discoveredPeerUids.add(enrichedPeer.uid);
|
|
@@ -58199,37 +58775,14 @@ function createPeerList({ peerApi, presence, diag, onMissingPeers }) {
|
|
|
58199
58775
|
if (requestedKey !== blockedUidsKey)
|
|
58200
58776
|
continue;
|
|
58201
58777
|
blockedPeers = requestedUids.map((uid) => nextByUid.get(uid)).filter((peer) => peer?.uid);
|
|
58778
|
+
for (const peer of blockedPeers)
|
|
58779
|
+
discoveredPeerUids.add(peer.uid);
|
|
58202
58780
|
blockedPeersReady = true;
|
|
58203
|
-
rebuild({
|
|
58781
|
+
rebuild({ base: true });
|
|
58204
58782
|
return blockedPeers;
|
|
58205
58783
|
}
|
|
58206
58784
|
return blockedPeers;
|
|
58207
58785
|
}
|
|
58208
|
-
function dropPeer(peer) {
|
|
58209
|
-
const uid = peerUid(peer);
|
|
58210
|
-
if (!uid)
|
|
58211
|
-
return;
|
|
58212
|
-
const discovered = discoveredPeerUids.has(uid);
|
|
58213
|
-
if (!hiddenPeerUidSet.has(uid)) {
|
|
58214
|
-
hiddenPeerUidSet.add(uid);
|
|
58215
|
-
}
|
|
58216
|
-
if (discovered) {
|
|
58217
|
-
discoveredPeerUids.delete(uid);
|
|
58218
|
-
}
|
|
58219
|
-
if (typeof peer === "object" && peer?.uid) {
|
|
58220
|
-
blockedPeers = [...blockedPeers.filter((item) => item?.uid !== uid), peer];
|
|
58221
|
-
}
|
|
58222
|
-
rebuild(discovered ? { base: true } : { projection: true });
|
|
58223
|
-
}
|
|
58224
|
-
function restorePeer(peer) {
|
|
58225
|
-
const uid = peerUid(peer);
|
|
58226
|
-
if (!uid)
|
|
58227
|
-
return;
|
|
58228
|
-
hiddenPeerUidSet.delete(uid);
|
|
58229
|
-
blockedPeers = blockedPeers.filter((item) => item?.uid !== uid);
|
|
58230
|
-
discoveredPeerUids.add(uid);
|
|
58231
|
-
rebuild({ base: true });
|
|
58232
|
-
}
|
|
58233
58786
|
snapshot = makeSnapshot();
|
|
58234
58787
|
return {
|
|
58235
58788
|
getSnapshot() {
|
|
@@ -60973,7 +61526,7 @@ function compareRecentTransfer(a, b) {
|
|
|
60973
61526
|
return String(b?.id || "").localeCompare(String(a?.id || ""));
|
|
60974
61527
|
}
|
|
60975
61528
|
function sameTransfer(left, right) {
|
|
60976
|
-
return left?.id === right?.id && left?.status === right?.status && txCreatedMs(left) === txCreatedMs(right) && txUpdatedMs(left) === txUpdatedMs(right) && left?.totalValue === right?.totalValue && left?.type === right?.type && left?.transferDirection === right?.transferDirection && left?.senderIdentityPublicKey === right?.senderIdentityPublicKey && left?.receiverIdentityPublicKey === right?.receiverIdentityPublicKey && left?.bitcoinAddress === right?.bitcoinAddress && left?.bitcoinTxid === right?.bitcoinTxid && left?.lightningAddress === right?.lightningAddress;
|
|
61529
|
+
return left?.id === right?.id && left?.status === right?.status && txCreatedMs(left) === txCreatedMs(right) && txUpdatedMs(left) === txUpdatedMs(right) && left?.totalValue === right?.totalValue && left?.tokenIdentifier === right?.tokenIdentifier && left?.amountUnits === right?.amountUnits && left?.network === right?.network && left?.type === right?.type && left?.transferDirection === right?.transferDirection && left?.senderIdentityPublicKey === right?.senderIdentityPublicKey && left?.receiverIdentityPublicKey === right?.receiverIdentityPublicKey && left?.bitcoinAddress === right?.bitcoinAddress && left?.bitcoinTxid === right?.bitcoinTxid && left?.lightningAddress === right?.lightningAddress;
|
|
60977
61530
|
}
|
|
60978
61531
|
function sameTransfers(a = [], b = []) {
|
|
60979
61532
|
if (a === b) {
|
|
@@ -61031,7 +61584,7 @@ function compactTransfer(tx, { incoming = false, bitcoinAddress = null, bitcoinT
|
|
|
61031
61584
|
senderIdentityPublicKey: hexKey(tx.senderIdentityPublicKey),
|
|
61032
61585
|
receiverIdentityPublicKey: hexKey(tx.receiverIdentityPublicKey),
|
|
61033
61586
|
status: enumName(tx.status, TRANSFER_STATUS_BY_CODE),
|
|
61034
|
-
totalValue: satValue(tx.totalValue),
|
|
61587
|
+
...type === "TOKEN_TRANSFER" ? { tokenIdentifier: tx.tokenIdentifier, amountUnits: tx.amountUnits, network: tx.network } : { totalValue: satValue(tx.totalValue) },
|
|
61035
61588
|
createdTime: tx.createdTime,
|
|
61036
61589
|
updatedTime: tx.updatedTime,
|
|
61037
61590
|
type,
|
|
@@ -61265,32 +61818,6 @@ function mergeTransferPageForWallet(currentTransfers = [], pageTransfers = [], w
|
|
|
61265
61818
|
const page = compactTransfersForWallet(pageTransfers, walletPK);
|
|
61266
61819
|
return mergeCompactedTransferPage(current, page, position);
|
|
61267
61820
|
}
|
|
61268
|
-
function mergeRecentSnapshotForWallet(currentTransfers = [], latestTransfers = [], walletPK) {
|
|
61269
|
-
const currentById = new Map(currentTransfers.map((tx) => [String(tx?.id || ""), tx]));
|
|
61270
|
-
const latest = mergeTransferPageForWallet([], latestTransfers, walletPK, "append").map((tx) => mergeTransferDetails(currentById.get(String(tx?.id || "")), tx));
|
|
61271
|
-
if (!latest.length) {
|
|
61272
|
-
return filterTransfersForWallet(currentTransfers, walletPK);
|
|
61273
|
-
}
|
|
61274
|
-
const latestIds = new Set(latest.map((tx) => String(tx.id)));
|
|
61275
|
-
const latestById = new Map(latest.map((tx) => [String(tx.id), tx]));
|
|
61276
|
-
const oldestLatestMs = getOldestTransferMs(latest);
|
|
61277
|
-
const olderCurrent = filterTransfersForWallet(currentTransfers, walletPK).filter((tx) => {
|
|
61278
|
-
const id = String(tx?.id || "");
|
|
61279
|
-
if (latestIds.has(id)) {
|
|
61280
|
-
const latestTx = latestById.get(id);
|
|
61281
|
-
if (isPendingTransfer(latestTx) && !isPendingTransfer(tx)) {
|
|
61282
|
-
return true;
|
|
61283
|
-
}
|
|
61284
|
-
return false;
|
|
61285
|
-
}
|
|
61286
|
-
if (isPendingTransfer(tx)) {
|
|
61287
|
-
return false;
|
|
61288
|
-
}
|
|
61289
|
-
const createdMs = txCreatedMs(tx);
|
|
61290
|
-
return oldestLatestMs == null || createdMs > 0 && createdMs < oldestLatestMs;
|
|
61291
|
-
});
|
|
61292
|
-
return mergeTransferPage(latest, olderCurrent, "append");
|
|
61293
|
-
}
|
|
61294
61821
|
function markClaimedTransfersCompleted(transfers = [], ids = []) {
|
|
61295
61822
|
const claimedIds = new Set((Array.isArray(ids) ? ids : [ids]).map((id) => String(id || "")).filter(Boolean));
|
|
61296
61823
|
if (!claimedIds.size)
|
|
@@ -61540,7 +62067,7 @@ async function refreshRequestedTransfers(owner, ids = []) {
|
|
|
61540
62067
|
const resolvedIds = requestedTransfers.filter((transfer) => !isPendingTransfer(transfer)).map((transfer) => String(transfer.id));
|
|
61541
62068
|
forgetPending(owner, resolvedIds);
|
|
61542
62069
|
if (nextTransfers !== currentTransfers) {
|
|
61543
|
-
await owner.snapshot.
|
|
62070
|
+
await owner.snapshot.commitHistory(overlayPending(owner, updates.map((transfer) => compactTransfer(transfer))), { position: "prepend" });
|
|
61544
62071
|
if (!isCurrentGeneration(owner, generation))
|
|
61545
62072
|
return [];
|
|
61546
62073
|
owner.snapshot.replace(mergeTransferPageForWallet(owner.snapshot.getTransfers(), updates, owner.walletPK, "append"));
|
|
@@ -61658,8 +62185,7 @@ async function commitPendingReconciliation(owner, plan, result, generation) {
|
|
|
61658
62185
|
const nextTransfers = result.updates.length ? overlayPending(owner, mergeTransferPageForWallet(current, result.updates, owner.walletPK, "append")) : current;
|
|
61659
62186
|
forgetPending(owner, result.resolvedIds);
|
|
61660
62187
|
if (nextTransfers !== current) {
|
|
61661
|
-
|
|
61662
|
-
await owner.snapshot.commitConfirmed(nextTransfers.filter((transfer) => selectedIds.has(String(transfer?.id)) && !isPendingTransfer(transfer)), { position: "prepend" });
|
|
62188
|
+
await owner.snapshot.commitHistory(overlayPending(owner, result.updates.map((transfer) => compactTransfer(transfer))), { position: "prepend" });
|
|
61663
62189
|
if (!isCurrentGeneration(owner, generation))
|
|
61664
62190
|
return null;
|
|
61665
62191
|
owner.snapshot.replace(mergeTransferPageForWallet(owner.snapshot.getTransfers(), result.updates, owner.walletPK, "append"));
|
|
@@ -61870,13 +62396,17 @@ function hydrateRecentHistory(owner) {
|
|
|
61870
62396
|
return owner.hydratePromise;
|
|
61871
62397
|
const generation = owner.generation;
|
|
61872
62398
|
const startedAt = Date.now();
|
|
61873
|
-
const request2 = owner.historyStore.query({ limit: WORKING_CONFIRMED_TRANSFER_LIMIT }).then((result) => {
|
|
61874
|
-
|
|
62399
|
+
const request2 = owner.historyStore.query({ limit: WORKING_CONFIRMED_TRANSFER_LIMIT }).then(async (result) => {
|
|
62400
|
+
const loadedIds = new Set(result.transfers.map((tx) => String(tx.id)));
|
|
62401
|
+
const missingPending = Object.keys(owner.historyStore.getSnapshot().projection?.pending || {}).filter((id) => !loadedIds.has(id));
|
|
62402
|
+
const pending = await Promise.all(missingPending.map((id) => owner.historyStore.getById(id)));
|
|
62403
|
+
const transfers = pending.length ? mergeTransferPage(result.transfers, pending.filter(Boolean), "prepend") : result.transfers;
|
|
62404
|
+
if (!isCurrentGeneration2(owner, generation) || owner.snapshot.getTransfers().length || !transfers.length)
|
|
61875
62405
|
return false;
|
|
61876
62406
|
owner.nextOffset = owner.historyStore.getSnapshot().nextOffset;
|
|
61877
|
-
owner.snapshot.replace(
|
|
62407
|
+
owner.snapshot.replace(transfers);
|
|
61878
62408
|
owner.snapshot.setReady();
|
|
61879
|
-
markDone(owner.diag, "wallet.history.hydrate", startedAt, { count:
|
|
62409
|
+
markDone(owner.diag, "wallet.history.hydrate", startedAt, { count: transfers.length, pending: pendingTransfers(transfers).length });
|
|
61880
62410
|
return true;
|
|
61881
62411
|
}).catch((error) => {
|
|
61882
62412
|
if (isCurrentGeneration2(owner, generation))
|
|
@@ -61935,13 +62465,12 @@ async function fetchHistoryPages(owner, options, generation) {
|
|
|
61935
62465
|
completeOnStop: !!completeOnStop
|
|
61936
62466
|
});
|
|
61937
62467
|
try {
|
|
61938
|
-
let stagedTransfers = owner.snapshot.getTransfers();
|
|
61939
62468
|
let oldestFetchedMs = null;
|
|
61940
62469
|
let stoppedAtKnownTransfer = false;
|
|
61941
62470
|
while ((!owner.historyStore.getSnapshot().complete || force) && pages < maxPages) {
|
|
61942
62471
|
if (!isCurrentGeneration2(owner, generation) || owner.backgroundSignal?.aborted === true)
|
|
61943
62472
|
break;
|
|
61944
|
-
if (!allHistory && Number.isFinite(sinceMs) && stagedHistoryCoverage(owner,
|
|
62473
|
+
if (!allHistory && Number.isFinite(sinceMs) && stagedHistoryCoverage(owner, owner.snapshot.getTransfers(), oldestFetchedMs, sinceMs, force))
|
|
61945
62474
|
break;
|
|
61946
62475
|
const offset = owner.nextOffset;
|
|
61947
62476
|
const rawPage = await owner.readPage(TRANSFER_PAGE_LIMIT, offset, undefined, new Date(boundaryMs));
|
|
@@ -61952,12 +62481,11 @@ async function fetchHistoryPages(owner, options, generation) {
|
|
|
61952
62481
|
const pageHitStopId = stopIds.size > 0 && page.transfers.some((transfer) => stopIds.has(String(transfer?.id)));
|
|
61953
62482
|
if (page.transfers.length) {
|
|
61954
62483
|
owner.pending.forgetResolvedFetched(page.transfers);
|
|
61955
|
-
stagedTransfers = boundedWorkingTransfers(mergeTransferPageForWallet(stagedTransfers, page.transfers, owner.walletPK, "append"));
|
|
61956
62484
|
const oldestPageMs = getOldestTransferMs(page.transfers);
|
|
61957
62485
|
if (oldestPageMs != null)
|
|
61958
62486
|
oldestFetchedMs = oldestFetchedMs == null ? oldestPageMs : Math.min(oldestFetchedMs, oldestPageMs);
|
|
61959
62487
|
}
|
|
61960
|
-
await owner.
|
|
62488
|
+
await owner.commitHistory(page.transfers, {
|
|
61961
62489
|
position: "append",
|
|
61962
62490
|
nextOffset: page.nextOffset,
|
|
61963
62491
|
boundaryMs,
|
|
@@ -61965,6 +62493,7 @@ async function fetchHistoryPages(owner, options, generation) {
|
|
|
61965
62493
|
});
|
|
61966
62494
|
if (!isCurrentGeneration2(owner, generation))
|
|
61967
62495
|
break;
|
|
62496
|
+
owner.snapshot.replace(mergeTransferPageForWallet(owner.snapshot.getTransfers(), page.transfers, owner.walletPK, "append"));
|
|
61968
62497
|
owner.nextOffset = Math.max(owner.nextOffset, page.nextOffset);
|
|
61969
62498
|
await yieldToUi();
|
|
61970
62499
|
if (pageHitStopId) {
|
|
@@ -61975,7 +62504,6 @@ async function fetchHistoryPages(owner, options, generation) {
|
|
|
61975
62504
|
break;
|
|
61976
62505
|
}
|
|
61977
62506
|
if (isCurrentGeneration2(owner, generation)) {
|
|
61978
|
-
owner.snapshot.replace(stagedTransfers);
|
|
61979
62507
|
markDone(owner.diag, label, startedAt, {
|
|
61980
62508
|
pages,
|
|
61981
62509
|
loaded: owner.snapshot.getTransfers().length,
|
|
@@ -62072,13 +62600,12 @@ async function refreshCachedHistory(owner, includePending = true, reason = "cach
|
|
|
62072
62600
|
deltaAfterMs = Math.max(0, deltaState.newestMs - 1);
|
|
62073
62601
|
deltaBeforeMs = Date.now();
|
|
62074
62602
|
offset = 0;
|
|
62075
|
-
await owner.
|
|
62603
|
+
await owner.commitHistory([], { deltaAfterMs, deltaBeforeMs, deltaNextOffset: 0 });
|
|
62076
62604
|
if (!isCurrentGeneration2(owner, generation))
|
|
62077
62605
|
return null;
|
|
62078
62606
|
}
|
|
62079
62607
|
const createdAfter = new Date(deltaAfterMs);
|
|
62080
62608
|
const createdBefore = new Date(deltaBeforeMs);
|
|
62081
|
-
let stagedTransfers = owner.snapshot.getTransfers();
|
|
62082
62609
|
let rawCount = 0;
|
|
62083
62610
|
let pages = 0;
|
|
62084
62611
|
let complete = false;
|
|
@@ -62090,10 +62617,7 @@ async function refreshCachedHistory(owner, includePending = true, reason = "cach
|
|
|
62090
62617
|
rawCount += page.transfers.length;
|
|
62091
62618
|
pages += 1;
|
|
62092
62619
|
complete = !page.hasMore;
|
|
62093
|
-
|
|
62094
|
-
stagedTransfers = boundedWorkingTransfers(mergeTransferPageForWallet(stagedTransfers, page.transfers, walletKey, "prepend"));
|
|
62095
|
-
}
|
|
62096
|
-
await owner.commitConfirmed(page.transfers, {
|
|
62620
|
+
await owner.commitHistory(page.transfers, {
|
|
62097
62621
|
position: "prepend",
|
|
62098
62622
|
deltaAfterMs: complete ? null : deltaAfterMs,
|
|
62099
62623
|
deltaBeforeMs: complete ? null : deltaBeforeMs,
|
|
@@ -62101,6 +62625,8 @@ async function refreshCachedHistory(owner, includePending = true, reason = "cach
|
|
|
62101
62625
|
});
|
|
62102
62626
|
if (!isCurrentGeneration2(owner, generation))
|
|
62103
62627
|
return null;
|
|
62628
|
+
owner.pending.forgetResolvedFetched(page.transfers);
|
|
62629
|
+
owner.snapshot.replace(mergeTransferPageForWallet(owner.snapshot.getTransfers(), page.transfers, walletKey, "prepend"));
|
|
62104
62630
|
offset = page.nextOffset;
|
|
62105
62631
|
if (complete)
|
|
62106
62632
|
break;
|
|
@@ -62109,10 +62635,14 @@ async function refreshCachedHistory(owner, includePending = true, reason = "cach
|
|
|
62109
62635
|
if (!isCurrentGeneration2(owner, generation) || owner.backgroundSignal?.aborted === true)
|
|
62110
62636
|
return null;
|
|
62111
62637
|
const pending = includePending ? await owner.pending.query() : [];
|
|
62638
|
+
if (!isCurrentGeneration2(owner, generation))
|
|
62639
|
+
return null;
|
|
62640
|
+
if (pending.length)
|
|
62641
|
+
await owner.commitHistory(pending, { position: "prepend" });
|
|
62112
62642
|
if (!isCurrentGeneration2(owner, generation))
|
|
62113
62643
|
return null;
|
|
62114
62644
|
owner.pending.remember(pending.map((transfer) => transfer.id));
|
|
62115
|
-
const nextTransfers = pending.length ? mergeTransferPageForWallet(
|
|
62645
|
+
const nextTransfers = pending.length ? mergeTransferPageForWallet(owner.snapshot.getTransfers(), pending, walletKey, "prepend") : owner.snapshot.getTransfers();
|
|
62116
62646
|
owner.snapshot.replace(nextTransfers);
|
|
62117
62647
|
const visibleCount = Math.min(nextTransfers.length, Math.max(owner.snapshot.getTransfers().length, RECENT_TRANSFER_LIMIT));
|
|
62118
62648
|
owner.snapshot.publish(nextTransfers.slice(0, visibleCount));
|
|
@@ -62201,7 +62731,7 @@ async function loadRecentTransfers(owner, options = {}, generation = owner.gener
|
|
|
62201
62731
|
const loadedPendingIncoming = includePending ? owner.pending.claimCandidates(latestTransfers) : [];
|
|
62202
62732
|
const latestIds = new Set(latestTransfers.map((transfer) => String(transfer?.id)).filter(Boolean));
|
|
62203
62733
|
const reconcileIds = previousHistoryComplete ? [] : previousTransfers.filter((transfer) => transfer?.id && !latestIds.has(String(transfer.id)) && !isPendingTransfer(transfer)).map((transfer) => String(transfer.id));
|
|
62204
|
-
await owner.
|
|
62734
|
+
await owner.commitHistory([...pending, ...page.transfers], {
|
|
62205
62735
|
position: "prepend",
|
|
62206
62736
|
nextOffset: Math.max(owner.historyStore.getSnapshot().nextOffset, page.nextOffset),
|
|
62207
62737
|
boundaryMs,
|
|
@@ -62209,12 +62739,11 @@ async function loadRecentTransfers(owner, options = {}, generation = owner.gener
|
|
|
62209
62739
|
});
|
|
62210
62740
|
if (!isCurrentGeneration2(owner, generation))
|
|
62211
62741
|
return null;
|
|
62212
|
-
const fetchedTransfers = page.hasMore ? mergeRecentSnapshotForWallet(previousTransfers, latestTransfers, owner.walletPK) : mergeTransferPageForWallet([], latestTransfers, owner.walletPK);
|
|
62213
|
-
const committedTransfers = boundedWorkingTransfers(mergeTransferPageForWallet(owner.snapshot.getTransfers(), fetchedTransfers, owner.walletPK, "append"));
|
|
62214
62742
|
if (!showLoading)
|
|
62215
62743
|
await yieldToUi();
|
|
62216
62744
|
if (!isCurrentGeneration2(owner, generation))
|
|
62217
62745
|
return null;
|
|
62746
|
+
const committedTransfers = boundedWorkingTransfers(mergeTransferPageForWallet(owner.snapshot.getTransfers(), [...pending, ...page.transfers], owner.walletPK, "append"));
|
|
62218
62747
|
owner.nextOffset = Math.max(owner.nextOffset, page.nextOffset);
|
|
62219
62748
|
const committedHistory = owner.historyStore.getSnapshot();
|
|
62220
62749
|
const visibleCount = Math.min(committedTransfers.length, Math.max(owner.snapshot.getTransfers().length, RECENT_TRANSFER_LIMIT));
|
|
@@ -62264,8 +62793,8 @@ function getRecentTransfers(owner, options = {}) {
|
|
|
62264
62793
|
owner.recentPromise = request2;
|
|
62265
62794
|
return request2;
|
|
62266
62795
|
}
|
|
62267
|
-
function createTransferHistorySync({ walletPK, historyStore, initialOffset = 0, snapshot, readPage,
|
|
62268
|
-
if (!walletPK || !historyStore || !snapshot || typeof readPage !== "function" || typeof
|
|
62796
|
+
function createTransferHistorySync({ walletPK, historyStore, initialOffset = 0, snapshot, readPage, commitHistory, pending, waitForWork, backgroundSignal = null, isClosed, diag = null } = {}) {
|
|
62797
|
+
if (!walletPK || !historyStore || !snapshot || typeof readPage !== "function" || typeof commitHistory !== "function" || !pending || typeof waitForWork !== "function" || typeof isClosed !== "function") {
|
|
62269
62798
|
throw new Error("transfer history sync requires history, snapshot, pending, and read ports");
|
|
62270
62799
|
}
|
|
62271
62800
|
const owner = {
|
|
@@ -62273,7 +62802,7 @@ function createTransferHistorySync({ walletPK, historyStore, initialOffset = 0,
|
|
|
62273
62802
|
historyStore,
|
|
62274
62803
|
snapshot,
|
|
62275
62804
|
readPage,
|
|
62276
|
-
|
|
62805
|
+
commitHistory,
|
|
62277
62806
|
pending,
|
|
62278
62807
|
waitForWork,
|
|
62279
62808
|
backgroundSignal,
|
|
@@ -62363,13 +62892,20 @@ function compareTransfers(left, right) {
|
|
|
62363
62892
|
const delta = txCreatedMs(right) - txCreatedMs(left);
|
|
62364
62893
|
return delta || transferId(right).localeCompare(transferId(left));
|
|
62365
62894
|
}
|
|
62895
|
+
function mergeHistoryTransfer(current, next) {
|
|
62896
|
+
if (!current)
|
|
62897
|
+
return next;
|
|
62898
|
+
if (!isPendingTransfer(current) && isPendingTransfer(next) || txUpdatedMs(next) < txUpdatedMs(current))
|
|
62899
|
+
return current;
|
|
62900
|
+
return { ...current, ...next };
|
|
62901
|
+
}
|
|
62366
62902
|
function normalizeTransfers(transfers, walletPK) {
|
|
62367
62903
|
const byId = new Map;
|
|
62368
62904
|
for (const transfer of Array.isArray(transfers) ? transfers : EMPTY_TRANSFERS) {
|
|
62369
62905
|
const id = transferId(transfer);
|
|
62370
62906
|
if (!id || !isVisibleTransfer(transfer) || !transferBelongsToWallet(transfer, walletPK))
|
|
62371
62907
|
continue;
|
|
62372
|
-
byId.set(id, jsonClone(transfer));
|
|
62908
|
+
byId.set(id, mergeHistoryTransfer(byId.get(id), jsonClone(transfer)));
|
|
62373
62909
|
}
|
|
62374
62910
|
return [...byId.values()].sort(compareTransfers);
|
|
62375
62911
|
}
|
|
@@ -62394,7 +62930,8 @@ function emptyProjection() {
|
|
|
62394
62930
|
days: {},
|
|
62395
62931
|
hours: {},
|
|
62396
62932
|
months: {},
|
|
62397
|
-
peers: {}
|
|
62933
|
+
peers: {},
|
|
62934
|
+
pending: {}
|
|
62398
62935
|
};
|
|
62399
62936
|
}
|
|
62400
62937
|
function cleanStats(value) {
|
|
@@ -62409,6 +62946,7 @@ function cleanStats(value) {
|
|
|
62409
62946
|
}
|
|
62410
62947
|
function cleanProjection(value) {
|
|
62411
62948
|
const projection = emptyProjection();
|
|
62949
|
+
projection.pending = { ...value?.pending };
|
|
62412
62950
|
projection.all = cleanStats(value?.all);
|
|
62413
62951
|
for (const [key, stats] of Object.entries(value?.days || {}))
|
|
62414
62952
|
projection.days[key] = cleanStats(stats);
|
|
@@ -62445,6 +62983,8 @@ function applyStats(stats, transfer, sign2, { excludeFunding = false } = {}) {
|
|
|
62445
62983
|
const amount = Number(transfer?.totalValue) || 0;
|
|
62446
62984
|
const incoming = isIncomingTransfer(transfer);
|
|
62447
62985
|
stats.count += sign2;
|
|
62986
|
+
if (incoming && isPendingTransfer(transfer))
|
|
62987
|
+
return;
|
|
62448
62988
|
stats.vol += sign2 * amount;
|
|
62449
62989
|
if (incoming) {
|
|
62450
62990
|
stats.received += sign2 * amount;
|
|
@@ -62463,6 +63003,11 @@ function normalizeStats(stats) {
|
|
|
62463
63003
|
return stats;
|
|
62464
63004
|
}
|
|
62465
63005
|
function applyProjection(projection, transfer, sign2) {
|
|
63006
|
+
const id = transferId(transfer);
|
|
63007
|
+
if (sign2 > 0 && isPendingTransfer(transfer))
|
|
63008
|
+
projection.pending[id] = txCreatedMs(transfer);
|
|
63009
|
+
else
|
|
63010
|
+
delete projection.pending[id];
|
|
62466
63011
|
const { day, hour, month } = timeKeys(transfer);
|
|
62467
63012
|
projection.days[day] ||= emptyStats();
|
|
62468
63013
|
projection.hours[hour] ||= emptyStats();
|
|
@@ -62548,11 +63093,12 @@ function normalizeManifest(value, walletPK) {
|
|
|
62548
63093
|
projection: cleanProjection(value.projection)
|
|
62549
63094
|
};
|
|
62550
63095
|
}
|
|
62551
|
-
function repositorySnapshot(manifest, ready, syncing = false) {
|
|
63096
|
+
function repositorySnapshot(manifest, ready, syncing = false, head = EMPTY_TRANSFERS) {
|
|
62552
63097
|
return Object.freeze({
|
|
62553
63098
|
walletPK: manifest.walletPK,
|
|
62554
63099
|
ready,
|
|
62555
63100
|
syncing,
|
|
63101
|
+
head,
|
|
62556
63102
|
count: manifest.count,
|
|
62557
63103
|
complete: manifest.complete,
|
|
62558
63104
|
bitcoinOldestMs: manifest.bitcoinOldestMs,
|
|
@@ -62600,8 +63146,14 @@ function buildSegmentCommitPlan({ manifest, incoming, removedIds, mappings, oldS
|
|
|
62600
63146
|
const incomingById = new Map(incoming.map((transfer) => [transferId(transfer), transfer]));
|
|
62601
63147
|
const working = new Map;
|
|
62602
63148
|
for (const [segmentId, segmentTransfers] of oldSegments) {
|
|
62603
|
-
for (const transfer of segmentTransfers)
|
|
62604
|
-
|
|
63149
|
+
for (const transfer of segmentTransfers) {
|
|
63150
|
+
const id = transferId(transfer);
|
|
63151
|
+
previousById.set(id, transfer);
|
|
63152
|
+
const next2 = incomingById.get(id);
|
|
63153
|
+
if (!next2)
|
|
63154
|
+
continue;
|
|
63155
|
+
incomingById.set(id, mergeHistoryTransfer(transfer, next2));
|
|
63156
|
+
}
|
|
62605
63157
|
const next = segmentTransfers.filter((transfer) => !removedIds.has(transferId(transfer))).map((transfer) => incomingById.get(transferId(transfer)) || transfer);
|
|
62606
63158
|
working.set(segmentId, normalizeTransfers(next, walletPK));
|
|
62607
63159
|
}
|
|
@@ -62817,6 +63369,15 @@ async function openWalletHistoryRepository({ localCache, walletPK } = {}) {
|
|
|
62817
63369
|
}
|
|
62818
63370
|
return rememberSegment(id, transfers);
|
|
62819
63371
|
};
|
|
63372
|
+
const readHead = async () => {
|
|
63373
|
+
const transfers = [];
|
|
63374
|
+
for (const segment of manifest.segments) {
|
|
63375
|
+
transfers.push(...await readSegment(segment.id));
|
|
63376
|
+
if (transfers.length >= WALLET_HISTORY_HEAD_SIZE)
|
|
63377
|
+
break;
|
|
63378
|
+
}
|
|
63379
|
+
return transfers.length ? Object.freeze(transfers.slice(0, WALLET_HISTORY_HEAD_SIZE)) : EMPTY_TRANSFERS;
|
|
63380
|
+
};
|
|
62820
63381
|
const readIdMappings = async (ids, generation = manifest.generation) => {
|
|
62821
63382
|
const grouped = new Map;
|
|
62822
63383
|
for (const id of ids) {
|
|
@@ -62861,7 +63422,7 @@ async function openWalletHistoryRepository({ localCache, walletPK } = {}) {
|
|
|
62861
63422
|
if (!wrote)
|
|
62862
63423
|
throw new Error("wallet history checkpoint write failed");
|
|
62863
63424
|
manifest = normalizeManifest(nextManifest, requestedWalletPK);
|
|
62864
|
-
snapshot = repositorySnapshot(manifest, true);
|
|
63425
|
+
snapshot = repositorySnapshot(manifest, true, false, await readHead());
|
|
62865
63426
|
emit(listeners);
|
|
62866
63427
|
};
|
|
62867
63428
|
const pruneSegments = async () => {
|
|
@@ -62879,6 +63440,13 @@ async function openWalletHistoryRepository({ localCache, walletPK } = {}) {
|
|
|
62879
63440
|
};
|
|
62880
63441
|
const prunePromise = pruneSegments();
|
|
62881
63442
|
await prunePromise;
|
|
63443
|
+
const initialHead = await readHead().catch(async (error) => {
|
|
63444
|
+
if (!recoveryPromise)
|
|
63445
|
+
throw error;
|
|
63446
|
+
await recoveryPromise;
|
|
63447
|
+
return EMPTY_TRANSFERS;
|
|
63448
|
+
});
|
|
63449
|
+
snapshot = repositorySnapshot(manifest, true, false, initialHead);
|
|
62882
63450
|
const readCommitSource = async (incoming, removedIds, position) => {
|
|
62883
63451
|
const ids = [...new Set([...incoming.map(transferId), ...removedIds])];
|
|
62884
63452
|
const { mappings, known, buckets: idBuckets } = await readIdMappings(ids);
|
|
@@ -62950,7 +63518,9 @@ async function openWalletHistoryRepository({ localCache, walletPK } = {}) {
|
|
|
62950
63518
|
if (!wrote)
|
|
62951
63519
|
throw new Error("wallet history checkpoint write failed");
|
|
62952
63520
|
manifest = normalizeManifest(nextManifest, requestedWalletPK);
|
|
62953
|
-
|
|
63521
|
+
for (const record of plan.newSegmentRecords)
|
|
63522
|
+
rememberSegment(record.descriptor.id, record.transfers);
|
|
63523
|
+
snapshot = repositorySnapshot(manifest, true, false, await readHead());
|
|
62954
63524
|
emit(listeners);
|
|
62955
63525
|
} else {
|
|
62956
63526
|
for (const record of plan.newSegmentRecords) {
|
|
@@ -62977,6 +63547,10 @@ async function openWalletHistoryRepository({ localCache, walletPK } = {}) {
|
|
|
62977
63547
|
await prunePromise;
|
|
62978
63548
|
const incoming = normalizeTransfers(transfers, requestedWalletPK);
|
|
62979
63549
|
const removedIds = new Set((Array.isArray(options.removedIds) ? options.removedIds : []).map(String).filter(Boolean));
|
|
63550
|
+
for (const transfer of transfers) {
|
|
63551
|
+
if (transfer?.id && transferBelongsToWallet(transfer, requestedWalletPK) && !isVisibleTransfer(transfer))
|
|
63552
|
+
removedIds.add(String(transfer.id));
|
|
63553
|
+
}
|
|
62980
63554
|
const bitcoinTimes = incoming.filter((tx) => !tx.tokenIdentifier).map(txCreatedMs);
|
|
62981
63555
|
if (bitcoinTimes.length)
|
|
62982
63556
|
options = { ...options, bitcoinOldestMs: Math.min(manifest.bitcoinOldestMs ?? Infinity, ...bitcoinTimes) };
|
|
@@ -63010,13 +63584,13 @@ async function openWalletHistoryRepository({ localCache, walletPK } = {}) {
|
|
|
63010
63584
|
commitPage(transfers, options = {}) {
|
|
63011
63585
|
if (corruptionDetected)
|
|
63012
63586
|
return Promise.reject(new Error("wallet history cache recovery in progress"));
|
|
63013
|
-
snapshot = repositorySnapshot(manifest, true, true);
|
|
63587
|
+
snapshot = repositorySnapshot(manifest, true, true, snapshot.head);
|
|
63014
63588
|
emit(listeners);
|
|
63015
63589
|
const run2 = () => commit(transfers, options);
|
|
63016
63590
|
const result = writeTail.then(run2, run2);
|
|
63017
63591
|
writeTail = result.catch(() => {}).finally(() => {
|
|
63018
63592
|
if (!closed) {
|
|
63019
|
-
snapshot = repositorySnapshot(manifest, true, false);
|
|
63593
|
+
snapshot = repositorySnapshot(manifest, true, false, snapshot.head);
|
|
63020
63594
|
emit(listeners);
|
|
63021
63595
|
}
|
|
63022
63596
|
});
|
|
@@ -63240,11 +63814,12 @@ function createWalletHistoryStore() {
|
|
|
63240
63814
|
}
|
|
63241
63815
|
});
|
|
63242
63816
|
}
|
|
63243
|
-
var MANIFEST_PART = "wallet-history-manifest", SEGMENT_PREFIX = "whs", ID_INDEX_PREFIX = "whi", PEER_INDEX_PREFIX = "whp", REPOSITORY_VERSION = 2, SEGMENT_SIZE = 100, SEGMENT_CACHE_LIMIT = 5, ID_BUCKET_COUNT = 4096, PEER_BUCKET_COUNT = 256, EMPTY_TRANSFERS;
|
|
63817
|
+
var MANIFEST_PART = "wallet-history-manifest", SEGMENT_PREFIX = "whs", ID_INDEX_PREFIX = "whi", PEER_INDEX_PREFIX = "whp", REPOSITORY_VERSION = 2, SEGMENT_SIZE = 100, WALLET_HISTORY_HEAD_SIZE, SEGMENT_CACHE_LIMIT = 5, ID_BUCKET_COUNT = 4096, PEER_BUCKET_COUNT = 256, EMPTY_TRANSFERS;
|
|
63244
63818
|
var init_historyrepository = __esm(() => {
|
|
63245
63819
|
init_core();
|
|
63246
63820
|
init_time();
|
|
63247
63821
|
init_tx();
|
|
63822
|
+
WALLET_HISTORY_HEAD_SIZE = SEGMENT_SIZE;
|
|
63248
63823
|
EMPTY_TRANSFERS = Object.freeze([]);
|
|
63249
63824
|
});
|
|
63250
63825
|
|
|
@@ -63317,9 +63892,9 @@ function createWalletTransferSession({ wallet, walletPK, claimIncomingTransfers,
|
|
|
63317
63892
|
const snapshot = transferStore.getSnapshot();
|
|
63318
63893
|
return lowerText(snapshot.walletPK) === walletKey && snapshot.ready === true;
|
|
63319
63894
|
};
|
|
63320
|
-
const
|
|
63321
|
-
const
|
|
63322
|
-
return historyStore.commitPage(
|
|
63895
|
+
const commitHistoryPage = (pageTransfers, options = {}) => {
|
|
63896
|
+
const updates = pageTransfers.map((transfer) => compactTransfer(transfer)).filter((transfer) => transfer && transferBelongsToWallet(transfer, walletKey));
|
|
63897
|
+
return historyStore.commitPage(updates, options);
|
|
63323
63898
|
};
|
|
63324
63899
|
const forgetPendingLookupIds = (ids = []) => pendingReconciliation.forget(ids);
|
|
63325
63900
|
const rememberPendingTransferIds = (ids = []) => pendingReconciliation.remember(ids);
|
|
@@ -63355,9 +63930,6 @@ function createWalletTransferSession({ wallet, walletPK, claimIncomingTransfers,
|
|
|
63355
63930
|
if (closed)
|
|
63356
63931
|
return current;
|
|
63357
63932
|
let ownedTransfers = owned ? nextTransfers : filterTransfersForWallet(nextTransfers, walletKey);
|
|
63358
|
-
if (current.length && ownedTransfers.length && ownedTransfers.length < current.length) {
|
|
63359
|
-
ownedTransfers = mergeTransferPageForWallet(ownedTransfers, current, walletKey, "append");
|
|
63360
|
-
}
|
|
63361
63933
|
ownedTransfers = boundedWorkingTransfers(applyTransferStatusOverlays(ownedTransfers));
|
|
63362
63934
|
const committed = sameTransfers(current, ownedTransfers) ? current : ownedTransfers;
|
|
63363
63935
|
transferStore.setHistory({ transfers: committed, walletPK: walletKey });
|
|
@@ -63494,7 +64066,7 @@ function createWalletTransferSession({ wallet, walletPK, claimIncomingTransfers,
|
|
|
63494
64066
|
return [];
|
|
63495
64067
|
const nextTransfers = markClaimedTransfersCompleted(current, knownIds);
|
|
63496
64068
|
if (nextTransfers !== current) {
|
|
63497
|
-
|
|
64069
|
+
commitHistoryPage(nextTransfers.filter((transfer) => knownIds.includes(String(transfer?.id))), { position: "prepend" });
|
|
63498
64070
|
commitTransferSnapshot(nextTransfers, { publish: true, owned: true });
|
|
63499
64071
|
setTxReadyValue(true);
|
|
63500
64072
|
}
|
|
@@ -63519,7 +64091,7 @@ function createWalletTransferSession({ wallet, walletPK, claimIncomingTransfers,
|
|
|
63519
64091
|
snapshot: {
|
|
63520
64092
|
getTransfers: currentTransfers,
|
|
63521
64093
|
replace: (transfers) => commitTransferSnapshot(transfers, { publish: true, owned: true }),
|
|
63522
|
-
|
|
64094
|
+
commitHistory: commitHistoryPage,
|
|
63523
64095
|
setReady: () => setTxReadyValue(true)
|
|
63524
64096
|
},
|
|
63525
64097
|
reads: {
|
|
@@ -63543,7 +64115,7 @@ function createWalletTransferSession({ wallet, walletPK, claimIncomingTransfers,
|
|
|
63543
64115
|
setLoading: setIsTxLoading
|
|
63544
64116
|
},
|
|
63545
64117
|
readPage: fetchTransfersPage,
|
|
63546
|
-
|
|
64118
|
+
commitHistory: commitHistoryPage,
|
|
63547
64119
|
pending: {
|
|
63548
64120
|
query: getPendingTransfersSnapshot,
|
|
63549
64121
|
isQuerying: () => !!pendingQueryPromiseRef.current,
|
|
@@ -63600,7 +64172,10 @@ function createWalletTransferSession({ wallet, walletPK, claimIncomingTransfers,
|
|
|
63600
64172
|
const current = currentTransfers();
|
|
63601
64173
|
const nextTransfers = mergeTransferPageForWallet(current, incomingTransfers, walletKey, "prepend");
|
|
63602
64174
|
if (nextTransfers !== current) {
|
|
63603
|
-
|
|
64175
|
+
await commitHistoryPage(incomingTransfers, { position: "prepend" });
|
|
64176
|
+
if (closed)
|
|
64177
|
+
return [];
|
|
64178
|
+
commitTransferSnapshot(mergeTransferPageForWallet(currentTransfers(), incomingTransfers, walletKey, "prepend"), { publish: true, owned: true });
|
|
63604
64179
|
setTxReadyValue(true);
|
|
63605
64180
|
}
|
|
63606
64181
|
}
|
|
@@ -63642,10 +64217,8 @@ function createWalletTransferSession({ wallet, walletPK, claimIncomingTransfers,
|
|
|
63642
64217
|
forgetPendingLookupIds(resolvedUpdateIds);
|
|
63643
64218
|
}
|
|
63644
64219
|
}
|
|
63645
|
-
|
|
63646
|
-
|
|
63647
|
-
await commitConfirmedHistoryPage(updates, { position: "prepend" });
|
|
63648
|
-
commitTransferSnapshot(nextTransfers, { publish: true, owned: true });
|
|
64220
|
+
await commitHistoryPage(updates, { position: "prepend" });
|
|
64221
|
+
commitTransferSnapshot(mergeTransferPageForWallet(currentTransfers(), updates, walletKey, "prepend"), { publish: true, owned: true });
|
|
63649
64222
|
setTxReadyValue(true);
|
|
63650
64223
|
markDone(diag, "wallet.claimedTxs", startedAt2, { found: true, count: updates.length });
|
|
63651
64224
|
return updates;
|
|
@@ -63670,10 +64243,8 @@ function createWalletTransferSession({ wallet, walletPK, claimIncomingTransfers,
|
|
|
63670
64243
|
forgetPendingLookupIds([compact2.id]);
|
|
63671
64244
|
}
|
|
63672
64245
|
}
|
|
63673
|
-
|
|
63674
|
-
|
|
63675
|
-
await commitConfirmedHistoryPage([compact2], { position: "prepend" });
|
|
63676
|
-
commitTransferSnapshot(nextTransfers, { publish: true, owned: true });
|
|
64246
|
+
await commitHistoryPage([compact2], { position: "prepend" });
|
|
64247
|
+
commitTransferSnapshot(mergeTransferPageForWallet(currentTransfers(), [compact2], walletKey, "prepend"), { publish: true, owned: true });
|
|
63677
64248
|
setTxReadyValue(true);
|
|
63678
64249
|
markDone(diag, "wallet.claimedTx", startedAt, { found: true });
|
|
63679
64250
|
return compact2;
|
|
@@ -64521,27 +65092,21 @@ var init_portfolio = __esm(() => {
|
|
|
64521
65092
|
});
|
|
64522
65093
|
|
|
64523
65094
|
// ../../core/wallet/txlist.js
|
|
64524
|
-
async function readTxListPage(
|
|
65095
|
+
async function readTxListPage(queryHistory, { offset = 0, limit = 100 } = {}) {
|
|
64525
65096
|
const start2 = Math.max(0, Math.floor(offset));
|
|
64526
65097
|
const count = Math.max(1, Math.min(500, Math.floor(limit)));
|
|
64527
|
-
const
|
|
64528
|
-
|
|
64529
|
-
|
|
64530
|
-
|
|
64531
|
-
}
|
|
64532
|
-
async function findTxListPeriod(pending, history, queryHistory, { sinceMs, untilMs, hourly }) {
|
|
64533
|
-
const pendingIndex = pending.findIndex((tx) => tx.createdMs >= sinceMs && tx.createdMs < untilMs);
|
|
64534
|
-
if (pendingIndex >= 0)
|
|
64535
|
-
return pendingIndex;
|
|
65098
|
+
const history = await queryHistory({ offset: start2, limit: count });
|
|
65099
|
+
return { offset: start2, transfers: history.transfers };
|
|
65100
|
+
}
|
|
65101
|
+
async function findTxListPeriod(history, queryHistory, { sinceMs, hourly }) {
|
|
64536
65102
|
const key = (hourly ? dayHourKey : dayKey)(new Date(sinceMs));
|
|
64537
65103
|
const buckets = history.projection[hourly ? "hours" : "days"];
|
|
64538
65104
|
const newer = Object.entries(buckets).reduce((count, [period, stats]) => count + (period > key ? stats.count : 0), 0);
|
|
64539
65105
|
if (buckets[key]?.count)
|
|
64540
|
-
return
|
|
65106
|
+
return newer;
|
|
64541
65107
|
const offset = Math.max(0, newer - 1);
|
|
64542
65108
|
const { transfers } = await queryHistory({ offset, limit: 2 });
|
|
64543
|
-
const candidates =
|
|
64544
|
-
transfers.forEach((tx, index2) => candidates.push({ tx, index: pending.length + offset + index2 }));
|
|
65109
|
+
const candidates = transfers.map((tx, index2) => ({ tx, index: offset + index2 }));
|
|
64545
65110
|
candidates.sort((a, b) => Math.abs(a.tx.createdMs - sinceMs) - Math.abs(b.tx.createdMs - sinceMs));
|
|
64546
65111
|
return candidates[0]?.index ?? -1;
|
|
64547
65112
|
}
|
|
@@ -64640,14 +65205,14 @@ function mergeTxSearchResults(loaded, found, limit = TX_SEARCH_DEFAULT_LIMIT) {
|
|
|
64640
65205
|
}
|
|
64641
65206
|
return [...byId.values()].sort(byRecentTx).slice(0, max2);
|
|
64642
65207
|
}
|
|
64643
|
-
function getDailySeries({ days, transactions, aggregatedData, balance, historyComplete, oldestMs, cache }) {
|
|
65208
|
+
function getDailySeries({ days, transactions, aggregatedData, balance, historyComplete, oldestMs, cache, nowMs: nowMs2 = Date.now() }) {
|
|
64644
65209
|
if (!transactions.length || balance == null)
|
|
64645
65210
|
return [];
|
|
64646
65211
|
const cacheKey = `daily-${days}`;
|
|
64647
|
-
|
|
64648
|
-
|
|
64649
|
-
|
|
64650
|
-
const today = new Date;
|
|
65212
|
+
const clock = Math.floor(nowMs2 / 60000);
|
|
65213
|
+
if (cache.get(cacheKey)?.clock === clock)
|
|
65214
|
+
return cache.get(cacheKey).series;
|
|
65215
|
+
const today = new Date(nowMs2);
|
|
64651
65216
|
today.setHours(0, 0, 0, 0);
|
|
64652
65217
|
const series = [];
|
|
64653
65218
|
let runningBalance = Number(balance);
|
|
@@ -64658,84 +65223,44 @@ function getDailySeries({ days, transactions, aggregatedData, balance, historyCo
|
|
|
64658
65223
|
const d = new Date(today);
|
|
64659
65224
|
d.setDate(d.getDate() - i2);
|
|
64660
65225
|
const key = dayKey(d);
|
|
64661
|
-
|
|
65226
|
+
const end = new Date(d);
|
|
65227
|
+
end.setDate(end.getDate() + 1);
|
|
65228
|
+
series.push({ date: key, timestamp: i2 === 0 ? nowMs2 : end.getTime(), now: i2 === 0, balance: runningBalance });
|
|
64662
65229
|
const dayData = aggregatedData.dayMap.get(key);
|
|
64663
65230
|
if (dayData) {
|
|
64664
65231
|
runningBalance -= dayData.net;
|
|
64665
65232
|
}
|
|
64666
65233
|
}
|
|
64667
65234
|
const result = series.reverse();
|
|
64668
|
-
cache.set(cacheKey, result);
|
|
65235
|
+
cache.set(cacheKey, { clock, series: result });
|
|
64669
65236
|
return result;
|
|
64670
65237
|
}
|
|
64671
|
-
function getHourlySeriesForData({ hours, prefix = "today", transactions, aggregatedData, balance, historyComplete, oldestMs, cache }) {
|
|
65238
|
+
function getHourlySeriesForData({ hours, prefix = "today", transactions, aggregatedData, balance, historyComplete, oldestMs, cache, nowMs: nowMs2 = Date.now() }) {
|
|
64672
65239
|
if (!transactions.length || balance == null)
|
|
64673
65240
|
return [];
|
|
64674
65241
|
const cacheKey = `hourly-${hours}-${prefix}`;
|
|
64675
|
-
|
|
64676
|
-
|
|
64677
|
-
|
|
64678
|
-
const now = new Date;
|
|
64679
|
-
const series = [];
|
|
65242
|
+
const clock = Math.floor(nowMs2 / 60000);
|
|
65243
|
+
if (cache.get(cacheKey)?.clock === clock)
|
|
65244
|
+
return cache.get(cacheKey).series;
|
|
65245
|
+
const now = new Date(nowMs2);
|
|
65246
|
+
const series = [{ hour: "now", timestamp: nowMs2, now: true, balance: Number(balance) }];
|
|
64680
65247
|
let runningBalance = Number(balance);
|
|
64681
|
-
const today =
|
|
64682
|
-
|
|
65248
|
+
const today = new Date(now);
|
|
65249
|
+
today.setHours(0, 0, 0, 0);
|
|
64683
65250
|
const coveredHours = historyComplete ? hours : coveredUnitsSince(oldestMs, HOUR_MS, now.getTime());
|
|
64684
65251
|
const actualHours = Math.min(hours, coveredHours);
|
|
64685
|
-
|
|
64686
|
-
|
|
64687
|
-
|
|
64688
|
-
|
|
64689
|
-
|
|
64690
|
-
|
|
64691
|
-
|
|
64692
|
-
|
|
64693
|
-
|
|
64694
|
-
|
|
64695
|
-
|
|
64696
|
-
|
|
64697
|
-
series.push({ hour, balance: runningBalance });
|
|
64698
|
-
const key = `${today}-${hour}`;
|
|
64699
|
-
const hourData = aggregatedData.hourMap.get(key);
|
|
64700
|
-
if (hourData) {
|
|
64701
|
-
runningBalance += hourData.net;
|
|
64702
|
-
}
|
|
64703
|
-
}
|
|
64704
|
-
series.push({ hour: "now", balance: Number(balance) });
|
|
64705
|
-
} else if (prefix === "24h") {
|
|
64706
|
-
const currentHour = now.getHours();
|
|
64707
|
-
const firstOffset = Math.max(0, 24 - actualHours);
|
|
64708
|
-
for (let i2 = 0;i2 <= currentHour; i2++) {
|
|
64709
|
-
const key = `${today}-${hourKey(i2)}`;
|
|
64710
|
-
const hourData = aggregatedData.hourMap.get(key);
|
|
64711
|
-
if (hourData)
|
|
64712
|
-
runningBalance -= hourData.net;
|
|
64713
|
-
}
|
|
64714
|
-
for (let i2 = Math.max(currentHour + 1, firstOffset);i2 < 24; i2++) {
|
|
64715
|
-
const key = `${yesterday}-${hourKey(i2)}`;
|
|
64716
|
-
const hourData = aggregatedData.hourMap.get(key);
|
|
64717
|
-
if (hourData)
|
|
64718
|
-
runningBalance -= hourData.net;
|
|
64719
|
-
}
|
|
64720
|
-
for (let i2 = Math.max(currentHour + 1, firstOffset);i2 < 24; i2++) {
|
|
64721
|
-
const hour = hourKey(i2);
|
|
64722
|
-
series.push({ hour, balance: runningBalance });
|
|
64723
|
-
const key = `${yesterday}-${hour}`;
|
|
64724
|
-
const hourData = aggregatedData.hourMap.get(key);
|
|
64725
|
-
if (hourData)
|
|
64726
|
-
runningBalance += hourData.net;
|
|
64727
|
-
}
|
|
64728
|
-
for (let i2 = firstOffset > currentHour ? firstOffset : 0;i2 <= currentHour; i2++) {
|
|
64729
|
-
const hour = hourKey(i2);
|
|
64730
|
-
series.push({ hour, balance: runningBalance });
|
|
64731
|
-
const key = `${today}-${hour}`;
|
|
64732
|
-
const hourData = aggregatedData.hourMap.get(key);
|
|
64733
|
-
if (hourData)
|
|
64734
|
-
runningBalance += hourData.net;
|
|
64735
|
-
}
|
|
64736
|
-
}
|
|
64737
|
-
cache.set(cacheKey, series);
|
|
64738
|
-
return series;
|
|
65252
|
+
const endHour = new Date(now);
|
|
65253
|
+
endHour.setMinutes(0, 0, 0);
|
|
65254
|
+
for (let offset = 0;offset < actualHours; offset++) {
|
|
65255
|
+
const date = new Date(endHour.getTime() - offset * HOUR_MS);
|
|
65256
|
+
if (prefix === "today" && date < today)
|
|
65257
|
+
break;
|
|
65258
|
+
runningBalance -= aggregatedData.hourMap.get(dayHourKey(date))?.net || 0;
|
|
65259
|
+
series.push({ hour: hourKey(date), timestamp: date.getTime(), balance: runningBalance });
|
|
65260
|
+
}
|
|
65261
|
+
const result = series.reverse();
|
|
65262
|
+
cache.set(cacheKey, { clock, series: result });
|
|
65263
|
+
return result;
|
|
64739
65264
|
}
|
|
64740
65265
|
function getTxsInRangeFrom(sortedTransactions, timeRange) {
|
|
64741
65266
|
if (!sortedTransactions.length)
|
|
@@ -64922,11 +65447,8 @@ function createTxData({ transferStore, historyStore, diag = null, deferAggregati
|
|
|
64922
65447
|
transfers: result.transfers.map((transfer) => getVisibleWalletTransfer(transfer, sources.walletPK)).filter(Boolean)
|
|
64923
65448
|
};
|
|
64924
65449
|
}
|
|
64925
|
-
function pendingList() {
|
|
64926
|
-
return readCurrentAggregatedData(sources.walletPK).sortedTxs.filter((tx) => tx.pending);
|
|
64927
|
-
}
|
|
64928
65450
|
function getTxListPage(options) {
|
|
64929
|
-
return readTxListPage(
|
|
65451
|
+
return readTxListPage(queryHistory, options);
|
|
64930
65452
|
}
|
|
64931
65453
|
async function findTxListPeriod2(period) {
|
|
64932
65454
|
const { sinceMs, untilMs } = period;
|
|
@@ -64944,7 +65466,7 @@ function createTxData({ transferStore, historyStore, diag = null, deferAggregati
|
|
|
64944
65466
|
return -1;
|
|
64945
65467
|
if (!covered())
|
|
64946
65468
|
throw new Error("transaction history is unavailable for this date");
|
|
64947
|
-
return findTxListPeriod(
|
|
65469
|
+
return findTxListPeriod(historyStore.getSnapshot(), queryHistory, period);
|
|
64948
65470
|
}
|
|
64949
65471
|
async function getHistoryById(txId, tokenIdentifier) {
|
|
64950
65472
|
const { walletPK, lookupTxById } = sources;
|
|
@@ -64964,31 +65486,17 @@ function createTxData({ transferStore, historyStore, diag = null, deferAggregati
|
|
|
64964
65486
|
const peerKey = lowerText(peerPK);
|
|
64965
65487
|
const limit = Math.max(1, Math.min(500, Math.floor(Number(options.limit) || 100)));
|
|
64966
65488
|
const offset = Math.max(0, Math.floor(Number(options.offset) || 0));
|
|
64967
|
-
const
|
|
64968
|
-
const pending = pendingData.txs;
|
|
64969
|
-
const pendingPage = pending.slice(offset, offset + limit);
|
|
64970
|
-
const historyOffset = Math.max(0, offset - pending.length);
|
|
64971
|
-
const historyLimit = Math.max(0, limit - pendingPage.length);
|
|
64972
|
-
const result = historyLimit ? await queryHistory({ ...options, peerPK: peerKey, offset: historyOffset, limit: historyLimit }) : { transfers: [], cursor: null };
|
|
65489
|
+
const result = await queryHistory({ ...options, peerPK: peerKey, offset, limit });
|
|
64973
65490
|
const storedStats = historyStore.getSnapshot().projection?.peers?.[peerKey];
|
|
64974
|
-
const
|
|
65491
|
+
const stats = {
|
|
64975
65492
|
sent: Number(storedStats?.sent) || 0,
|
|
64976
65493
|
received: Number(storedStats?.received) || 0,
|
|
64977
65494
|
net: Number(storedStats?.net) || 0,
|
|
64978
65495
|
vol: Number(storedStats?.vol) || 0,
|
|
64979
65496
|
cnt: Number(storedStats?.count) || 0
|
|
64980
65497
|
};
|
|
64981
|
-
const
|
|
64982
|
-
|
|
64983
|
-
sent: confirmedStats.sent + pendingStats.sent,
|
|
64984
|
-
received: confirmedStats.received + pendingStats.received,
|
|
64985
|
-
net: confirmedStats.net + pendingStats.net,
|
|
64986
|
-
vol: confirmedStats.vol + pendingStats.vol,
|
|
64987
|
-
cnt: confirmedStats.cnt + pendingStats.cnt
|
|
64988
|
-
} : null;
|
|
64989
|
-
const txs = [...pendingPage, ...result.transfers];
|
|
64990
|
-
const total = confirmedStats.cnt + pending.length;
|
|
64991
|
-
return { ...result, transfers: txs, txs, stats, total, hasMore: offset + txs.length < total };
|
|
65498
|
+
const txs = result.transfers;
|
|
65499
|
+
return { ...result, txs, stats: stats.cnt ? stats : null, total: stats.cnt, hasMore: offset + txs.length < stats.cnt };
|
|
64992
65500
|
}
|
|
64993
65501
|
async function getHistorySummary(timeRange = "all-time") {
|
|
64994
65502
|
const result = await historyStore.summarize({ sinceMs: getTxRangeStartMs(timeRange) });
|
|
@@ -65044,31 +65552,16 @@ function createTxData({ transferStore, historyStore, diag = null, deferAggregati
|
|
|
65044
65552
|
}
|
|
65045
65553
|
return [...combined.values()].sort(byRecentTx).slice(0, limit);
|
|
65046
65554
|
}
|
|
65047
|
-
function addCashSeries(series, hourly,
|
|
65048
|
-
const history = historyStore.getSnapshot();
|
|
65049
|
-
const tokens = sources.tokens;
|
|
65555
|
+
function addCashSeries(series, hourly, history, tokens) {
|
|
65050
65556
|
const coverage = !tokens.length || history.tokenComplete;
|
|
65051
|
-
const now = new Date;
|
|
65052
65557
|
const buckets = history.projection[hourly ? "hours" : "days"];
|
|
65053
65558
|
return series.map((point) => {
|
|
65054
|
-
|
|
65055
|
-
|
|
65056
|
-
timestamp = new Date(now);
|
|
65057
|
-
if (point.hour !== "now") {
|
|
65058
|
-
const hour = Number(point.hour.slice(0, 2));
|
|
65059
|
-
timestamp.setHours(hour, 0, 0, 0);
|
|
65060
|
-
if (prefix === "24h" && hour > now.getHours())
|
|
65061
|
-
timestamp.setDate(timestamp.getDate() - 1);
|
|
65062
|
-
}
|
|
65063
|
-
} else {
|
|
65064
|
-
timestamp = new Date(`${point.date}T00:00:00`);
|
|
65065
|
-
timestamp.setDate(timestamp.getDate() + 1);
|
|
65066
|
-
}
|
|
65067
|
-
const known = coverage || history.tokenOldestMs != null && timestamp.getTime() > history.tokenOldestMs;
|
|
65559
|
+
const timestamp = new Date(point.timestamp);
|
|
65560
|
+
const known = point.now || coverage || history.tokenOldestMs != null && timestamp.getTime() > history.tokenOldestMs;
|
|
65068
65561
|
const key = (hourly ? dayHourKey : dayKey)(timestamp);
|
|
65069
65562
|
const deltas = {};
|
|
65070
|
-
if (point.
|
|
65071
|
-
for (const [bucketKey, stats] of Object.entries(buckets)) {
|
|
65563
|
+
if (!point.now)
|
|
65564
|
+
for (const [bucketKey, stats] of Object.entries(buckets || {})) {
|
|
65072
65565
|
if (bucketKey < key)
|
|
65073
65566
|
continue;
|
|
65074
65567
|
for (const [id, units] of Object.entries(stats.tokens || {}))
|
|
@@ -65078,14 +65571,14 @@ function createTxData({ transferStore, historyStore, diag = null, deferAggregati
|
|
|
65078
65571
|
});
|
|
65079
65572
|
}
|
|
65080
65573
|
function buildSnapshot() {
|
|
65081
|
-
const { balance, walletPK, oldestTxMs, txReady, isTxLoading, ensureTxCoverage, loadMoreTxs } = sources;
|
|
65574
|
+
const { balance, tokens, walletPK, oldestTxMs, txReady, isTxLoading, ensureTxCoverage, loadMoreTxs } = sources;
|
|
65082
65575
|
const storeSnapshot = transferStore.getSnapshot();
|
|
65083
65576
|
const historySnapshot = historyStore.getSnapshot();
|
|
65084
65577
|
observedStoreSnapshot = storeSnapshot;
|
|
65085
65578
|
observedHistorySnapshot = historySnapshot;
|
|
65086
65579
|
const transfers = storeSnapshot.transfers;
|
|
65087
65580
|
const aggregatedData = deferAggregation ? EMPTY_AGG : readCurrentAggregatedData(walletPK);
|
|
65088
|
-
const projectedData = projectionData(historySnapshot);
|
|
65581
|
+
const projectedData = historySnapshot.walletPK === walletPK ? projectionData(historySnapshot) : null;
|
|
65089
65582
|
const transactions = aggregatedData.enrichedTxs;
|
|
65090
65583
|
const sortedTransactions = aggregatedData.sortedTxs;
|
|
65091
65584
|
const readLiveTransfers = () => deferAggregation ? transferStore.getSnapshot().transfers : transfers;
|
|
@@ -65155,16 +65648,17 @@ function createTxData({ transferStore, historyStore, diag = null, deferAggregati
|
|
|
65155
65648
|
cache: seriesCache.current
|
|
65156
65649
|
});
|
|
65157
65650
|
};
|
|
65158
|
-
const getHistorySeries = (days) => addCashSeries(getDailySeries({
|
|
65651
|
+
const getHistorySeries = (days, nowMs2 = Date.now()) => !projectedData ? [] : addCashSeries(getDailySeries({
|
|
65159
65652
|
days,
|
|
65160
65653
|
transactions: projectedData.enrichedTxs,
|
|
65161
65654
|
aggregatedData: projectedData,
|
|
65162
65655
|
balance,
|
|
65163
65656
|
historyComplete: historySnapshot.complete,
|
|
65164
65657
|
oldestMs: historySnapshot.bitcoinOldestMs,
|
|
65165
|
-
cache: historySeriesCache.current
|
|
65166
|
-
|
|
65167
|
-
|
|
65658
|
+
cache: historySeriesCache.current,
|
|
65659
|
+
nowMs: nowMs2
|
|
65660
|
+
}), false, historySnapshot, tokens);
|
|
65661
|
+
const getHistoryHourlySeries = (hours, prefix = "today", nowMs2 = Date.now()) => !projectedData ? [] : addCashSeries(getHourlySeriesForData({
|
|
65168
65662
|
hours,
|
|
65169
65663
|
prefix,
|
|
65170
65664
|
transactions: projectedData.enrichedTxs,
|
|
@@ -65172,8 +65666,9 @@ function createTxData({ transferStore, historyStore, diag = null, deferAggregati
|
|
|
65172
65666
|
balance,
|
|
65173
65667
|
historyComplete: historySnapshot.complete,
|
|
65174
65668
|
oldestMs: historySnapshot.bitcoinOldestMs,
|
|
65175
|
-
cache: historySeriesCache.current
|
|
65176
|
-
|
|
65669
|
+
cache: historySeriesCache.current,
|
|
65670
|
+
nowMs: nowMs2
|
|
65671
|
+
}), true, historySnapshot, tokens);
|
|
65177
65672
|
const getTxById = (txId) => getVisibleTransferById(readLiveTransfers(), walletPK, txId, transferByIdRef);
|
|
65178
65673
|
const getPeerTxs = (peerPK) => peerPK ? readPeerData(peerPK).txs : EMPTY_TXS2;
|
|
65179
65674
|
const getPeerStats = (peerPK) => peerPK ? readPeerData(peerPK).stats : null;
|
|
@@ -65206,7 +65701,7 @@ function createTxData({ transferStore, historyStore, diag = null, deferAggregati
|
|
|
65206
65701
|
queryHistory,
|
|
65207
65702
|
getTxListPage,
|
|
65208
65703
|
findTxListPeriod: findTxListPeriod2,
|
|
65209
|
-
txListCount: historySnapshot.
|
|
65704
|
+
txListCount: historySnapshot.walletPK === walletPK ? historySnapshot.count : 0,
|
|
65210
65705
|
getTxById,
|
|
65211
65706
|
searchTxs,
|
|
65212
65707
|
getPeerTxs,
|
|
@@ -65986,8 +66481,7 @@ function openAccount(options = {}) {
|
|
|
65986
66481
|
getPeerProfile: getChatPeerProfile,
|
|
65987
66482
|
refreshPeerProfile: refreshChatPeerProfile,
|
|
65988
66483
|
resolvePeerProfile: resolveChatPeerProfile,
|
|
65989
|
-
maintenance: messageMaintenance
|
|
65990
|
-
resolveActiveCall: (chatId) => calls.resolveActiveCall(chatId)
|
|
66484
|
+
maintenance: messageMaintenance
|
|
65991
66485
|
}, chatSources());
|
|
65992
66486
|
const calls = createCallSession({
|
|
65993
66487
|
cloud,
|
|
@@ -66014,11 +66508,11 @@ function openAccount(options = {}) {
|
|
|
66014
66508
|
function chatSources(sessionOverride = chatSession) {
|
|
66015
66509
|
const currentUser = state.user || {};
|
|
66016
66510
|
const session = sessionOverride;
|
|
66017
|
-
const cachedBlacklist = session?.localCache?.read?.()?.blacklist;
|
|
66018
66511
|
return {
|
|
66019
66512
|
uid: currentUser.uid || "",
|
|
66020
|
-
blocked: currentUser.
|
|
66021
|
-
|
|
66513
|
+
blocked: currentUser.blocked,
|
|
66514
|
+
blockedPending: currentUser.blockedPending,
|
|
66515
|
+
blockedReady: currentUser.blockedReady,
|
|
66022
66516
|
online: state.online,
|
|
66023
66517
|
chatPK: session?.chatPK || currentUser.chatPK || "",
|
|
66024
66518
|
chatSigningPK: session?.chatSigningPK || currentUser.chatSigningPK || "",
|
|
@@ -66051,13 +66545,12 @@ function openAccount(options = {}) {
|
|
|
66051
66545
|
}
|
|
66052
66546
|
function syncPeers() {
|
|
66053
66547
|
const currentUser = state.user || {};
|
|
66054
|
-
const cachedBlacklist = state.session?.localCache?.read?.()?.blacklist;
|
|
66055
66548
|
const chatSnapshot = chat.getSnapshot();
|
|
66056
66549
|
peers.setSources({
|
|
66057
66550
|
chatPeers: chatSnapshot.peers,
|
|
66058
66551
|
chats: chatSnapshot.chats,
|
|
66059
|
-
blocked: currentUser.
|
|
66060
|
-
blockedReady: currentUser.blockedReady
|
|
66552
|
+
blocked: currentUser.blocked,
|
|
66553
|
+
blockedReady: currentUser.blockedReady,
|
|
66061
66554
|
chatPK: state.session?.chatPK || currentUser.chatPK || "",
|
|
66062
66555
|
chatSigningPK: state.session?.chatSigningPK || currentUser.chatSigningPK || "",
|
|
66063
66556
|
walletPeers: wallet.getPeers(),
|
|
@@ -66342,8 +66835,9 @@ function openAccount(options = {}) {
|
|
|
66342
66835
|
hasVault: !!vault,
|
|
66343
66836
|
fromCache: !!info.fromCache
|
|
66344
66837
|
});
|
|
66838
|
+
const currentVault = state.vault && vault && equalBytes(state.vault, vault) ? state.vault : vault || null;
|
|
66345
66839
|
emit2({
|
|
66346
|
-
vault:
|
|
66840
|
+
vault: currentVault,
|
|
66347
66841
|
vaultFromCache: false,
|
|
66348
66842
|
vaultReady: state.vaultReady || !info.fromCache,
|
|
66349
66843
|
vaultError: info.fromCache ? state.vaultError : null
|
|
@@ -66453,7 +66947,7 @@ function openAccount(options = {}) {
|
|
|
66453
66947
|
function syncBootstrap() {
|
|
66454
66948
|
if (closed || !state.session || state.session.closed || !serverReady())
|
|
66455
66949
|
return;
|
|
66456
|
-
if (state.user.blockedReady) {
|
|
66950
|
+
if (state.user.blockedReady && !state.user.blockedPending?.length) {
|
|
66457
66951
|
const cache = state.session.localCache;
|
|
66458
66952
|
const blocked = state.user.blocked;
|
|
66459
66953
|
if (JSON.stringify(cache?.read?.()?.blacklist) !== JSON.stringify(blocked)) {
|
|
@@ -66593,6 +67087,7 @@ function openAccount(options = {}) {
|
|
|
66593
67087
|
});
|
|
66594
67088
|
if (!isCurrent())
|
|
66595
67089
|
throw new Error("account changed during unlock");
|
|
67090
|
+
user.restoreBlocked({ uid, blocked: session.localCache?.read?.()?.blacklist });
|
|
66596
67091
|
chatSession = session;
|
|
66597
67092
|
chat.setSources(chatSources());
|
|
66598
67093
|
unlockStage = "launching";
|
|
@@ -66787,15 +67282,21 @@ function openAccount(options = {}) {
|
|
|
66787
67282
|
if (!observedVault)
|
|
66788
67283
|
throw new Error("vault not ready");
|
|
66789
67284
|
const isCurrent = () => !closed && !operationBarrier.blocksOperations && state.uid === uid && state.session === session && state.vault === observedVault && !session.closed && cloud.auth.user?.uid === uid;
|
|
67285
|
+
markDiag(diag, "account.delete.phase", { phase: "verify-vault" });
|
|
66790
67286
|
const verified = await vaultCrypto.verifyVaultPassword(observedVault, password);
|
|
66791
67287
|
if (!verified)
|
|
66792
67288
|
throw new Error("incorrect password");
|
|
66793
67289
|
if (!isCurrent())
|
|
66794
67290
|
throw new Error("account changed during deletion");
|
|
67291
|
+
await waitForOnlineDeletion(isCurrent);
|
|
67292
|
+
markDiag(diag, "account.delete.phase", { phase: "retire-chats" });
|
|
66795
67293
|
const chats = await chat.getSnapshot().deleteAllChats();
|
|
66796
67294
|
if (!isCurrent())
|
|
66797
67295
|
throw new Error("account changed during deletion");
|
|
67296
|
+
await waitForOnlineDeletion(isCurrent);
|
|
67297
|
+
markDiag(diag, "account.delete.phase", { phase: "delete-account" });
|
|
66798
67298
|
await cloud.user.delete();
|
|
67299
|
+
markDiag(diag, "account.delete.phase", { phase: "clear-local-state" });
|
|
66799
67300
|
try {
|
|
66800
67301
|
await localCache?.clear?.();
|
|
66801
67302
|
} catch (error) {
|
|
@@ -66819,6 +67320,31 @@ function openAccount(options = {}) {
|
|
|
66819
67320
|
finishOperation();
|
|
66820
67321
|
}
|
|
66821
67322
|
}
|
|
67323
|
+
function waitForOnlineDeletion(isCurrent) {
|
|
67324
|
+
if (!isCurrent())
|
|
67325
|
+
return Promise.reject(new Error("account changed during deletion"));
|
|
67326
|
+
if (state.online)
|
|
67327
|
+
return Promise.resolve();
|
|
67328
|
+
return new Promise((resolve, reject) => {
|
|
67329
|
+
const finish = (error) => {
|
|
67330
|
+
clearTimeout(timer);
|
|
67331
|
+
listeners.delete(check);
|
|
67332
|
+
if (error)
|
|
67333
|
+
reject(error);
|
|
67334
|
+
else
|
|
67335
|
+
resolve();
|
|
67336
|
+
};
|
|
67337
|
+
const check = () => {
|
|
67338
|
+
if (!isCurrent())
|
|
67339
|
+
finish(new Error("account changed during deletion"));
|
|
67340
|
+
else if (state.online)
|
|
67341
|
+
finish();
|
|
67342
|
+
};
|
|
67343
|
+
const timer = setTimeout(() => finish(new Error("couldnt connect to server")), ACCOUNT_CONNECTION_UNAVAILABLE_MS);
|
|
67344
|
+
listeners.add(check);
|
|
67345
|
+
check();
|
|
67346
|
+
});
|
|
67347
|
+
}
|
|
66822
67348
|
async function switchNetwork(network) {
|
|
66823
67349
|
if (state.lockState !== "unlocked") {
|
|
66824
67350
|
throw new Error("unlock the vault before switching networks");
|
|
@@ -66948,6 +67474,7 @@ var init_account = __esm(() => {
|
|
|
66948
67474
|
init_peers2();
|
|
66949
67475
|
init_support();
|
|
66950
67476
|
init_wallet();
|
|
67477
|
+
init_utils2();
|
|
66951
67478
|
});
|
|
66952
67479
|
|
|
66953
67480
|
// ../../core/passkey.js
|
|
@@ -68575,7 +69102,6 @@ var init_listen = __esm(() => {
|
|
|
68575
69102
|
init_messages();
|
|
68576
69103
|
init_presentation();
|
|
68577
69104
|
init_waveform();
|
|
68578
|
-
init_call();
|
|
68579
69105
|
init_messages3();
|
|
68580
69106
|
});
|
|
68581
69107
|
|
|
@@ -68723,7 +69249,7 @@ function parseUrl(value) {
|
|
|
68723
69249
|
return null;
|
|
68724
69250
|
}
|
|
68725
69251
|
}
|
|
68726
|
-
function
|
|
69252
|
+
function readInviteParams(params) {
|
|
68727
69253
|
const compact2 = compactInvite(params);
|
|
68728
69254
|
if (compact2)
|
|
68729
69255
|
return compact2;
|
|
@@ -68758,7 +69284,7 @@ function makeInvite(value = {}) {
|
|
|
68758
69284
|
const toIsUsername = !!cleanUsername2(rawTo);
|
|
68759
69285
|
const paymentKind2 = cleanedKind === invite.send || cleanedKind === invite.request;
|
|
68760
69286
|
const receiver = value.r ?? value.walletPK ?? value.receiver ?? (paymentKind2 && !toIsUsername ? rawTo : null);
|
|
68761
|
-
const data =
|
|
69287
|
+
const data = readInviteParams({
|
|
68762
69288
|
kind,
|
|
68763
69289
|
from: value.from ?? value.sender,
|
|
68764
69290
|
to: rawTo,
|
|
@@ -68808,7 +69334,7 @@ function readInvite(raw) {
|
|
|
68808
69334
|
return makeInvite({ kind: invite.join });
|
|
68809
69335
|
if (url.pathname !== "/")
|
|
68810
69336
|
return null;
|
|
68811
|
-
return
|
|
69337
|
+
return readInviteParams(url.searchParams);
|
|
68812
69338
|
}
|
|
68813
69339
|
var invite, inviteKinds, tokenKinds;
|
|
68814
69340
|
var init_invite = __esm(() => {
|
|
@@ -69053,23 +69579,20 @@ function createRuntimeProductActions({
|
|
|
69053
69579
|
const chatSnapshot = runtime.chat.getSnapshot();
|
|
69054
69580
|
const result = await executeUserBlock({
|
|
69055
69581
|
report: () => supportOwner.report(peer.uid),
|
|
69056
|
-
block: () =>
|
|
69057
|
-
|
|
69582
|
+
block: () => user.blockPeer(peer, {
|
|
69583
|
+
beforeCommit: () => chatSnapshot.retirePeerChat(peer.chatPK)
|
|
69058
69584
|
})
|
|
69059
69585
|
});
|
|
69060
69586
|
if (!result.blocked) {
|
|
69061
69587
|
throw new Error(`block failed during private route retirement: ${result.blockError?.message || "unknown error"}`, { cause: result.blockError });
|
|
69062
69588
|
}
|
|
69063
|
-
runtime.peers.getSnapshot().dropPeer(peer);
|
|
69064
69589
|
return { blocked: true, reported: result.reported, peer: projectPeer({ ...peer, blocked: true }) };
|
|
69065
69590
|
},
|
|
69066
69591
|
async unblock(value) {
|
|
69067
69592
|
const runtime = await readyRuntime();
|
|
69068
69593
|
const user = await getUser();
|
|
69069
69594
|
const peer = await resolvePeer(value);
|
|
69070
|
-
await runtime.chat.getSnapshot().retirePeerChat(peer.chatPK);
|
|
69071
|
-
await user.unblockPeer(peer);
|
|
69072
|
-
runtime.peers.getSnapshot().restorePeer(peer);
|
|
69595
|
+
await user.unblockPeer(peer, { beforeCommit: () => runtime.chat.getSnapshot().retirePeerChat(peer.chatPK) });
|
|
69073
69596
|
return { blocked: false, peer: projectPeer({ ...peer, blocked: false }) };
|
|
69074
69597
|
}
|
|
69075
69598
|
};
|
|
@@ -69601,7 +70124,6 @@ function deriveMessageRouteProjection({
|
|
|
69601
70124
|
conversationStateKey,
|
|
69602
70125
|
deleteMessageDocs,
|
|
69603
70126
|
chatActivity,
|
|
69604
|
-
resolveActiveCall,
|
|
69605
70127
|
epochId,
|
|
69606
70128
|
exists,
|
|
69607
70129
|
expireMessageBatch: expireMessageBatch2,
|
|
@@ -69708,7 +70230,6 @@ function deriveMessageRouteProjection({
|
|
|
69708
70230
|
expireMessageBatch: expireMessageBatch2,
|
|
69709
70231
|
deleteMessageDocs,
|
|
69710
70232
|
chatActivity,
|
|
69711
|
-
resolveActiveCall,
|
|
69712
70233
|
pruneMessageCache: pruneMessageCache2
|
|
69713
70234
|
},
|
|
69714
70235
|
liveWindowKey,
|
|
@@ -70765,11 +71286,11 @@ function messageStateHasKey(state, key) {
|
|
|
70765
71286
|
}
|
|
70766
71287
|
return false;
|
|
70767
71288
|
}
|
|
70768
|
-
function shouldKeepInitialWindowHidden(currentlyHidden, hasOlder, messageGroups
|
|
71289
|
+
function shouldKeepInitialWindowHidden(currentlyHidden, hasOlder, messageGroups) {
|
|
70769
71290
|
if (!currentlyHidden || hasOlder !== true)
|
|
70770
71291
|
return false;
|
|
70771
71292
|
const visible = (messageGroups || []).reduce((total, messages) => total + countShowableRows(messages), 0);
|
|
70772
|
-
return visible
|
|
71293
|
+
return visible === 0;
|
|
70773
71294
|
}
|
|
70774
71295
|
function queueRouteCachePrune(refs, messageCacheWriter, config, messages, options) {
|
|
70775
71296
|
const {
|
|
@@ -71036,11 +71557,11 @@ function currentHydrationWindow(refs) {
|
|
|
71036
71557
|
visible: countShowableRows(historyMessages) + countShowableRows(latestMessages)
|
|
71037
71558
|
};
|
|
71038
71559
|
}
|
|
71039
|
-
function shouldGateInitialCache(refs, window2
|
|
71040
|
-
return refs.initialHydratingCache.current && window2.count > 0 && window2.visible
|
|
71560
|
+
function shouldGateInitialCache(refs, window2) {
|
|
71561
|
+
return refs.initialHydratingCache.current && window2.count > 0 && window2.visible === 0 && refs.readMessageState().hasOlder && !refs.olderLoaded.current;
|
|
71041
71562
|
}
|
|
71042
71563
|
function adoptCurrentRouteWindow(refs, config, window2) {
|
|
71043
|
-
if (!refs.olderLoaded.current || window2.count <= 0)
|
|
71564
|
+
if (!refs.olderLoaded.current || window2.count <= 0 || refs.readMessageState().hasOlder)
|
|
71044
71565
|
return false;
|
|
71045
71566
|
refs.messageCachePartialHydrate.current = !!refs.readMessageState().hasOlder;
|
|
71046
71567
|
config.setInitialHydratingCache(false);
|
|
@@ -71263,7 +71784,7 @@ function finishMessageCacheHydration(refs, config, hydrateTask) {
|
|
|
71263
71784
|
refs.messageCacheHydratePromise.current = null;
|
|
71264
71785
|
}
|
|
71265
71786
|
const currentVisible = countShowableRows(refs.readMessageState().historyMessages) + countShowableRows(refs.readMessageState().latestMessages) + countShowableRows(refs.locals.current);
|
|
71266
|
-
const cacheWindowCanReveal = refs.readMessageState()?.ready === true && currentVisible
|
|
71787
|
+
const cacheWindowCanReveal = refs.readMessageState()?.ready === true && (currentVisible > 0 || refs.readMessageState().hasOlder !== true);
|
|
71267
71788
|
if (!config.chatId || cacheWindowCanReveal || refs.routeActive.current !== true) {
|
|
71268
71789
|
config.setInitialHydratingCache(false);
|
|
71269
71790
|
}
|
|
@@ -71277,7 +71798,7 @@ function hydrateRouteMessageCache(refs, config) {
|
|
|
71277
71798
|
refs.messageCacheHydrateKey.current = hydrateKey;
|
|
71278
71799
|
const window2 = currentHydrationWindow(refs);
|
|
71279
71800
|
if (window2.count > 0 && refs.initialHydratingCache.current) {
|
|
71280
|
-
config.setInitialHydratingCache(shouldGateInitialCache(refs, window2
|
|
71801
|
+
config.setInitialHydratingCache(shouldGateInitialCache(refs, window2));
|
|
71281
71802
|
}
|
|
71282
71803
|
if (adoptCurrentRouteWindow(refs, config, window2))
|
|
71283
71804
|
return;
|
|
@@ -71524,7 +72045,7 @@ function applyResolvedBatch(config, refs, msgBatch, prepared, context) {
|
|
|
71524
72045
|
fast: true
|
|
71525
72046
|
});
|
|
71526
72047
|
}
|
|
71527
|
-
config.setInitialHydratingCache(shouldKeepInitialWindowHidden(refs.initialHydratingCache.current || !refs.initialServerWindowApplied.current, merged.nextHasOlder, [merged.nextHistory, merged.nextLive, refs.locals.current]
|
|
72048
|
+
config.setInitialHydratingCache(shouldKeepInitialWindowHidden(refs.initialHydratingCache.current || !refs.initialServerWindowApplied.current, merged.nextHasOlder, [merged.nextHistory, merged.nextLive, refs.locals.current]));
|
|
71528
72049
|
if (context.applyingServerBatch)
|
|
71529
72050
|
refs.initialServerWindowApplied.current = true;
|
|
71530
72051
|
config.ackMessages(config.chatId, merged.nextBatchMessages);
|
|
@@ -72615,7 +73136,6 @@ function readRouteExpiry(current, chatId, options = {}) {
|
|
|
72615
73136
|
const messages = current.rawMessages?.length ? current.rawMessages : current.messages;
|
|
72616
73137
|
return analyzeMessageExpiry(messages, current.selfChatPublicKey, current.peerChatPublicKey, {
|
|
72617
73138
|
now: options.now,
|
|
72618
|
-
activeCallId: options.activeCallId,
|
|
72619
73139
|
keepKeys: options.release ? new Set : current.keepKeys,
|
|
72620
73140
|
memberChatPKs: current.memberChatPKs,
|
|
72621
73141
|
memberStatesByEpoch: current.memberStatesByEpoch
|
|
@@ -72738,32 +73258,6 @@ function expireRouteMessages(current, chatId, options = {}, diag = null, data =
|
|
|
72738
73258
|
return false;
|
|
72739
73259
|
if (options.release === true)
|
|
72740
73260
|
scheduleLoadedControlCompaction(current, chatId, diag, data);
|
|
72741
|
-
if (options.release === true && typeof current.resolveActiveCall === "function" && ((current.rawMessages?.length) ? current.rawMessages : current.messages || []).some((message) => readCallMessage(message)?.event === "started")) {
|
|
72742
|
-
const lease = messageMaintenance(current).claim("call-expiry", chatId);
|
|
72743
|
-
if (lease) {
|
|
72744
|
-
const work = Promise.resolve().then(() => {
|
|
72745
|
-
lease.assertCurrent();
|
|
72746
|
-
return current.resolveActiveCall(chatId).catch((error) => {
|
|
72747
|
-
markError(diag, "chat.call.expiry", Date.now(), error);
|
|
72748
|
-
return;
|
|
72749
|
-
});
|
|
72750
|
-
}).then((activeCallId) => {
|
|
72751
|
-
lease.assertCurrent();
|
|
72752
|
-
if (current.routeMemory?.isActive(chatId))
|
|
72753
|
-
return;
|
|
72754
|
-
const expired = readRouteExpiry(current, chatId, { ...options, activeCallId })?.expired || [];
|
|
72755
|
-
if (!expired.length)
|
|
72756
|
-
return;
|
|
72757
|
-
hideExpiredRouteMessages(current, chatId, expired, diag, data);
|
|
72758
|
-
return deleteExpiredMessages(current, chatId, expired, diag, data);
|
|
72759
|
-
}).catch((error) => {
|
|
72760
|
-
if (!isStaleMessageMaintenance(error))
|
|
72761
|
-
markError(diag, "chat.call.expiry", Date.now(), error);
|
|
72762
|
-
}).finally(() => lease.release());
|
|
72763
|
-
messageMaintenance(current).track(work);
|
|
72764
|
-
}
|
|
72765
|
-
return true;
|
|
72766
|
-
}
|
|
72767
73261
|
const analysis = readRouteExpiry(current, chatId, { now: options.now, release: options.release === true });
|
|
72768
73262
|
const expiredMessages = analysis?.expired || [];
|
|
72769
73263
|
if (!expiredMessages.length) {
|
|
@@ -72786,7 +73280,6 @@ var init_expirycleanup = __esm(() => {
|
|
|
72786
73280
|
init_maintenance();
|
|
72787
73281
|
init_window();
|
|
72788
73282
|
init_control();
|
|
72789
|
-
init_call();
|
|
72790
73283
|
});
|
|
72791
73284
|
|
|
72792
73285
|
// ../../core/chat/messages/routerelease.js
|
|
@@ -74285,7 +74778,7 @@ function createRouteMessageHistory({
|
|
|
74285
74778
|
return;
|
|
74286
74779
|
}
|
|
74287
74780
|
const before = countShowableRows(refs.readMessageState().historyMessages) + countShowableRows(refs.readMessageState().latestMessages) + countShowableRows(localsRef.current);
|
|
74288
|
-
if (before
|
|
74781
|
+
if (before > 0 || routeActiveRef.current !== true) {
|
|
74289
74782
|
setInitialHydratingCache(false);
|
|
74290
74783
|
return;
|
|
74291
74784
|
}
|
|
@@ -74295,7 +74788,7 @@ function createRouteMessageHistory({
|
|
|
74295
74788
|
return;
|
|
74296
74789
|
}
|
|
74297
74790
|
const after = countShowableRows(refs.readMessageState().historyMessages) + countShowableRows(refs.readMessageState().latestMessages) + countShowableRows(localsRef.current);
|
|
74298
|
-
if (!refs.readMessageState().hasOlder || after
|
|
74791
|
+
if (!refs.readMessageState().hasOlder || after > 0 || after <= before) {
|
|
74299
74792
|
setInitialHydratingCache(false);
|
|
74300
74793
|
}
|
|
74301
74794
|
}).catch((error) => {
|
|
@@ -74853,7 +75346,6 @@ function deriveProjection(owner, input = owner.input) {
|
|
|
74853
75346
|
conversationStateKey: input.conversationStateKey,
|
|
74854
75347
|
deleteMessageDocs: input.deleteMessageDocs,
|
|
74855
75348
|
chatActivity: input.chatActivity,
|
|
74856
|
-
resolveActiveCall: input.resolveActiveCall,
|
|
74857
75349
|
epochId: input.epochId,
|
|
74858
75350
|
exists: state.exists,
|
|
74859
75351
|
expireMessageBatch: input.messageRuntime.expireMessageBatch,
|
|
@@ -75114,7 +75606,6 @@ var init_routereconcile = __esm(() => {
|
|
|
75114
75606
|
"conversationStateKey",
|
|
75115
75607
|
"deleteMessageDocs",
|
|
75116
75608
|
"chatActivity",
|
|
75117
|
-
"resolveActiveCall",
|
|
75118
75609
|
"epochId",
|
|
75119
75610
|
"hasCurrentChat",
|
|
75120
75611
|
"initialState",
|
|
@@ -75228,7 +75719,7 @@ function createMessageRouteState(initialInput) {
|
|
|
75228
75719
|
yieldToMain: yieldToMain2
|
|
75229
75720
|
} = routeStateOptions(initialInput);
|
|
75230
75721
|
const messageStore = createMessageStore(messageStoreState(initialState, chatId, conversationStateKey));
|
|
75231
|
-
const initialHydratingCache = !!chatId && (initialState?.ready !== true || initialState?.exists === true && initialState?.hasOlder === true);
|
|
75722
|
+
const initialHydratingCache = !!chatId && (initialState?.ready !== true || initialState?.exists === true && initialState?.hasOlder === true && countShowableRows(initialState.historyMessages) + countShowableRows(initialState.latestMessages) === 0);
|
|
75232
75723
|
const refs = routeRefs(messageStore, {
|
|
75233
75724
|
chatPreviewKey,
|
|
75234
75725
|
foreground: foreground === true,
|
|
@@ -75376,6 +75867,7 @@ var init_routestate = __esm(() => {
|
|
|
75376
75867
|
init_routerelease();
|
|
75377
75868
|
init_routereconcile();
|
|
75378
75869
|
init_async();
|
|
75870
|
+
init_history();
|
|
75379
75871
|
ROUTE_STATE_DEFAULTS = Object.freeze({
|
|
75380
75872
|
cancelSchedule: clearTimeout,
|
|
75381
75873
|
chatPreviewKey: null,
|
|
@@ -76066,12 +76558,9 @@ function createRuntimeChatActions({
|
|
|
76066
76558
|
throw new Error("chat id required");
|
|
76067
76559
|
const runtime = await readyChatRuntime();
|
|
76068
76560
|
let snapshot2 = runtime.chat.getSnapshot();
|
|
76069
|
-
|
|
76070
|
-
|
|
76071
|
-
|
|
76072
|
-
snapshot2 = runtime.chat.getSnapshot();
|
|
76073
|
-
chat = snapshot2.chats.find((item) => item?.id === id) || null;
|
|
76074
|
-
}
|
|
76561
|
+
await snapshot2.ensureChat(id);
|
|
76562
|
+
snapshot2 = runtime.chat.getSnapshot();
|
|
76563
|
+
let chat = snapshot2.getOwnerChat(id);
|
|
76075
76564
|
if (!chat && options.allowPending === true && snapshot2.pendingChatLaunch?.id === id) {
|
|
76076
76565
|
chat = snapshot2.pendingChatLaunch.chat || null;
|
|
76077
76566
|
}
|
|
@@ -76430,8 +76919,18 @@ function createRuntimeChatActions({
|
|
|
76430
76919
|
if (!["accept", "reject"].includes(nextDecision)) {
|
|
76431
76920
|
throw new Error("chat request decision must be accept or reject");
|
|
76432
76921
|
}
|
|
76433
|
-
const
|
|
76434
|
-
|
|
76922
|
+
const runtime = await readyChatRuntime();
|
|
76923
|
+
const id = cleanText(chatId).toLowerCase();
|
|
76924
|
+
if (!/^[0-9a-f]{64}$/u.test(id))
|
|
76925
|
+
throw new Error("chat id required");
|
|
76926
|
+
let snapshot2 = runtime.chat.getSnapshot();
|
|
76927
|
+
let chat = snapshot2.chats.find((item) => item.id === id);
|
|
76928
|
+
while (!chat && snapshot2.hasMoreChats && !snapshot2.loadingMoreChats) {
|
|
76929
|
+
await snapshot2.loadMoreChats();
|
|
76930
|
+
snapshot2 = runtime.chat.getSnapshot();
|
|
76931
|
+
chat = snapshot2.chats.find((item) => item.id === id);
|
|
76932
|
+
}
|
|
76933
|
+
if (chat?.messageRequest !== true)
|
|
76435
76934
|
throw new Error("message request not found");
|
|
76436
76935
|
const resolved = await runtime.chat.getSnapshot().resolveChatRequest(chat.id, nextDecision);
|
|
76437
76936
|
if (resolved !== true)
|
|
@@ -77389,7 +77888,7 @@ var package_default;
|
|
|
77389
77888
|
var init_package2 = __esm(() => {
|
|
77390
77889
|
package_default = {
|
|
77391
77890
|
name: "veyl",
|
|
77392
|
-
version: "0.
|
|
77891
|
+
version: "0.78.0",
|
|
77393
77892
|
packageManager: "bun@1.4.0",
|
|
77394
77893
|
private: true,
|
|
77395
77894
|
license: "UNLICENSED",
|
|
@@ -78039,6 +78538,11 @@ class AccountRuntime {
|
|
|
78039
78538
|
if (!this.session) {
|
|
78040
78539
|
await this.unlockVault({ key: vaultSecret, saveKey: false });
|
|
78041
78540
|
}
|
|
78541
|
+
const session = this.session;
|
|
78542
|
+
const ready = await waitForOwner(this.accountOwner, (state) => state.online || state.uid !== profile.uid || state.session !== session, options.timeoutMs);
|
|
78543
|
+
if (ready.uid !== profile.uid || ready.session !== session || session.closed) {
|
|
78544
|
+
throw new Error("account changed before deletion");
|
|
78545
|
+
}
|
|
78042
78546
|
const profileName = profile.profile || "";
|
|
78043
78547
|
const localCache = this.session?.localCache;
|
|
78044
78548
|
const result = await this.accountOwner.delete({
|