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