This repository has been archived on 2022-08-19. You can view files and clone it, but cannot push or open issues or pull requests.
hydrogen-web/assets/index.baaa0776.js.map

1 line
2.1 MiB
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{"version":3,"file":"index.baaa0776.js","sources":["../../src/utils/enum.ts","../../src/matrix/well-known.js","../../src/utils/error.ts","../../src/observable/BaseObservable.ts","../../src/observable/ObservableValue.ts","../../src/utils/AbortableOperation.ts","../../src/platform/web/dom/BlobHandle.js","../../src/matrix/net/common.ts","../../src/matrix/error.js","../../src/matrix/net/HomeServerRequest.ts","../../src/matrix/net/HomeServerApi.ts","../../src/matrix/net/ExponentialRetryDelay.ts","../../src/matrix/net/Reconnector.ts","../../src/matrix/e2ee/attachment.js","../../src/matrix/net/MediaRepository.ts","../../src/matrix/net/RequestScheduler.ts","../../src/matrix/Sync.js","../../src/utils/EventEmitter.ts","../../node_modules/another-json/another-json.js","../../src/matrix/e2ee/common.js","../../src/matrix/room/RoomSummary.js","../../src/matrix/storage/common.ts","../../src/matrix/room/timeline/EventKey.ts","../../src/matrix/room/timeline/entries/BaseEntry.ts","../../src/matrix/room/common.ts","../../src/matrix/room/timeline/relations.js","../../src/matrix/room/timeline/PendingAnnotation.js","../../src/matrix/room/timeline/entries/reply.js","../../src/matrix/room/timeline/entries/BaseEventEntry.js","../../src/matrix/room/timeline/entries/PendingEventEntry.js","../../src/matrix/room/sending/PendingEvent.js","../../src/matrix/room/timeline/entries/EventEntry.js","../../src/matrix/room/timeline/persistence/common.js","../../src/matrix/room/members/RoomMember.js","../../src/matrix/room/timeline/common.js","../../src/matrix/room/timeline/FragmentIdComparer.js","../../vite/preload-helper","../../src/matrix/storage/idb/error.ts","../../src/matrix/storage/idb/utils.ts","../../src/matrix/storage/idb/QueryTarget.ts","../../src/matrix/storage/idb/Store.ts","../../src/utils/typedJSON.ts","../../src/matrix/storage/idb/stores/SessionStore.ts","../../src/matrix/storage/idb/stores/RoomSummaryStore.ts","../../src/matrix/storage/idb/stores/InviteStore.ts","../../src/logging/LogFilter.ts","../../src/logging/NullLogger.ts","../../src/matrix/storage/idb/stores/TimelineEventStore.ts","../../src/matrix/storage/idb/stores/common.ts","../../src/matrix/storage/idb/stores/TimelineRelationStore.ts","../../src/matrix/storage/idb/stores/RoomStateStore.ts","../../src/matrix/storage/idb/stores/RoomMemberStore.ts","../../src/matrix/storage/idb/stores/TimelineFragmentStore.ts","../../src/matrix/storage/idb/stores/PendingEventStore.ts","../../src/matrix/storage/idb/stores/UserIdentityStore.ts","../../src/matrix/storage/idb/stores/DeviceIdentityStore.ts","../../src/matrix/storage/idb/stores/OlmSessionStore.ts","../../src/matrix/storage/idb/stores/InboundGroupSessionStore.ts","../../src/matrix/storage/idb/stores/OutboundGroupSessionStore.ts","../../src/matrix/storage/idb/stores/GroupSessionDecryptionStore.ts","../../src/matrix/storage/idb/stores/OperationStore.ts","../../src/matrix/storage/idb/stores/AccountDataStore.ts","../../src/matrix/storage/idb/Transaction.ts","../../src/matrix/storage/idb/Storage.ts","../../src/matrix/storage/idb/export.ts","../../src/matrix/storage/idb/schema.ts","../../src/matrix/storage/idb/quirks.ts","../../src/matrix/storage/idb/StorageFactory.ts","../../src/matrix/room/timeline/persistence/RelationWriter.js","../../src/matrix/room/timeline/Direction.ts","../../src/matrix/room/timeline/entries/FragmentBoundaryEntry.js","../../src/matrix/room/timeline/persistence/SyncWriter.js","../../src/utils/LRUCache.ts","../../src/matrix/room/timeline/persistence/MemberWriter.js","../../src/matrix/room/timeline/persistence/GapWriter.js","../../src/observable/list/BaseObservableList.ts","../../src/utils/sortedIndex.ts","../../src/observable/map/BaseObservableMap.ts","../../src/observable/map/ObservableMap.ts","../../src/observable/list/SortedMapList.js","../../src/observable/map/FilteredMap.js","../../src/observable/map/MappedMap.js","../../src/observable/map/JoinedMap.js","../../src/observable/list/ObservableArray.ts","../../src/observable/list/common.ts","../../src/observable/list/SortedArray.ts","../../src/observable/list/BaseMappedList.ts","../../src/observable/list/AsyncMappedList.ts","../../src/observable/list/ConcatList.ts","../../src/observable/index.js","../../src/utils/Disposables.ts","../../src/matrix/room/timeline/persistence/TimelineReader.js","../../src/matrix/room/timeline/entries/NonPersistedEventEntry.js","../../src/matrix/User.js","../../src/matrix/room/timeline/Timeline.js","../../src/matrix/room/members/load.js","../../src/utils/RetainedValue.ts","../../src/matrix/room/members/MemberList.js","../../src/matrix/room/members/Heroes.js","../../src/matrix/room/ObservedEventMap.js","../../src/logging/utils.ts","../../src/matrix/room/PowerLevels.js","../../src/matrix/room/BaseRoom.js","../../src/matrix/common.js","../../src/matrix/room/sending/SendQueue.js","../../src/matrix/room/AttachmentUpload.js","../../src/matrix/room/Room.js","../../src/matrix/room/ArchivedRoom.js","../../src/matrix/profile.ts","../../src/matrix/room/RoomBeingCreated.ts","../../src/matrix/room/Invite.js","../../src/matrix/push/Pusher.ts","../../src/utils/groupBy.ts","../../src/matrix/DeviceMessageHandler.js","../../src/matrix/e2ee/Account.js","../../src/matrix/ssss/common.ts","../../src/matrix/ssss/passphrase.ts","../../src/matrix/ssss/recoveryKey.ts","../../src/matrix/ssss/index.ts","../../src/matrix/e2ee/Dehydration.js","../../src/utils/Lock.ts","../../src/matrix/e2ee/olm/Session.ts","../../src/matrix/e2ee/DecryptionResult.ts","../../src/matrix/e2ee/olm/types.ts","../../src/matrix/e2ee/olm/Decryption.ts","../../src/matrix/e2ee/olm/Encryption.ts","../../src/matrix/e2ee/megolm/decryption/DecryptionChanges.js","../../src/utils/mergeMap.ts","../../src/matrix/e2ee/megolm/decryption/DecryptionPreparation.js","../../src/matrix/e2ee/megolm/decryption/ReplayDetectionEntry.ts","../../src/matrix/e2ee/megolm/decryption/SessionDecryption.ts","../../src/matrix/e2ee/megolm/decryption/utils.ts","../../src/matrix/e2ee/megolm/decryption/RoomKey.ts","../../src/matrix/e2ee/megolm/Decryption.ts","../../src/matrix/e2ee/megolm/decryption/KeyLoader.ts","../../src/matrix/e2ee/megolm/keybackup/Curve25519.ts","../../src/matrix/e2ee/megolm/keybackup/KeyBackup.ts","../../src/matrix/e2ee/megolm/Encryption.js","../../src/logging/LogItem.ts","../../src/logging/BaseLogger.ts","../../src/logging/ConsoleLogger.ts","../../src/matrix/e2ee/RoomEncryption.js","../../src/matrix/e2ee/DeviceTracker.js","../../src/utils/LockMap.ts","../../src/matrix/ssss/SecretStorage.ts","../../src/matrix/Session.js","../../src/matrix/login/PasswordLoginMethod.ts","../../src/matrix/login/TokenLoginMethod.ts","../../src/matrix/login/SSOLoginHelper.ts","../../src/matrix/registration/stages/BaseRegistrationStage.ts","../../src/matrix/registration/stages/DummyAuth.ts","../../src/matrix/registration/stages/TermsAuth.ts","../../src/matrix/registration/stages/TokenAuth.ts","../../src/matrix/registration/Registration.ts","../../src/matrix/Client.js","../../src/domain/ViewModel.ts","../../src/domain/avatar.ts","../../src/domain/session/leftpanel/BaseTileViewModel.js","../../src/domain/session/leftpanel/RoomTileViewModel.js","../../src/domain/session/leftpanel/common.js","../../src/domain/session/leftpanel/InviteTileViewModel.js","../../src/domain/session/leftpanel/RoomBeingCreatedTileViewModel.js","../../src/domain/session/leftpanel/RoomFilter.js","../../src/observable/map/ApplyMap.js","../../src/domain/navigation/Navigation.ts","../../src/domain/navigation/URLRouter.ts","../../src/domain/navigation/index.ts","../../src/domain/session/leftpanel/LeftPanelViewModel.js","../../src/domain/session/room/timeline/UpdateAction.js","../../src/domain/session/room/timeline/TilesCollection.js","../../src/domain/session/room/timeline/TimelineViewModel.js","../../src/domain/session/room/ComposerViewModel.js","../../src/domain/session/common.js","../../src/domain/session/room/timeline/tiles/SimpleTile.js","../../src/domain/session/room/timeline/tiles/GapTile.js","../../src/domain/session/room/timeline/ReactionsViewModel.js","../../src/domain/session/room/timeline/tiles/BaseMessageTile.js","../../src/domain/session/room/timeline/linkify/regex.js","../../src/domain/session/room/timeline/linkify/linkify.js","../../src/domain/session/room/timeline/MessageBody.js","../../src/domain/session/room/timeline/tiles/BaseTextTile.js","../../src/domain/session/room/timeline/deserialize.js","../../src/domain/session/room/timeline/tiles/TextTile.js","../../src/domain/session/room/timeline/tiles/RedactedTile.js","../../src/domain/session/room/timeline/tiles/BaseMediaTile.js","../../src/domain/session/room/timeline/tiles/ImageTile.js","../../src/domain/session/room/timeline/tiles/VideoTile.js","../../src/utils/formatSize.ts","../../src/domain/session/room/timeline/tiles/FileTile.js","../../src/domain/session/room/timeline/tiles/LocationTile.js","../../src/domain/session/room/timeline/tiles/RoomNameTile.js","../../src/domain/session/room/timeline/tiles/RoomMemberTile.js","../../src/domain/session/room/timeline/tiles/EncryptedEventTile.js","../../src/domain/session/room/timeline/tiles/EncryptionEnabledTile.js","../../src/domain/session/room/timeline/tiles/MissingAttachmentTile.js","../../src/domain/session/room/timeline/tiles/index.ts","../../src/domain/session/room/RoomViewModel.js","../../src/domain/session/room/UnknownRoomViewModel.js","../../src/domain/session/room/InviteViewModel.js","../../src/domain/session/room/RoomBeingCreatedViewModel.js","../../src/domain/session/room/LightboxViewModel.js","../../src/domain/session/SessionStatusViewModel.js","../../src/domain/session/RoomGridViewModel.js","../../src/domain/session/settings/KeyBackupViewModel.js","../../src/domain/rageshake.ts","../../src/domain/session/settings/SettingsViewModel.js","../../src/domain/session/CreateRoomViewModel.js","../../src/domain/session/RoomViewModelObservable.js","../../src/domain/session/rightpanel/RoomDetailsViewModel.js","../../src/domain/session/rightpanel/MemberTileViewModel.js","../../src/domain/session/rightpanel/members/comparator.js","../../src/domain/session/rightpanel/members/disambiguator.js","../../src/domain/session/rightpanel/MemberListViewModel.js","../../src/domain/session/rightpanel/MemberDetailsViewModel.js","../../src/domain/session/rightpanel/RightPanelViewModel.js","../../src/domain/session/SessionViewModel.js","../../src/domain/AccountSetupViewModel.js","../../src/domain/SessionLoadViewModel.js","../../src/domain/login/PasswordLoginViewModel.js","../../src/domain/login/StartSSOLoginViewModel.js","../../src/domain/login/CompleteSSOLoginViewModel.js","../../src/domain/login/LoginViewModel.ts","../../src/domain/LogoutViewModel.ts","../../src/domain/SessionPickerViewModel.js","../../src/domain/RootViewModel.js","../../src/platform/web/main.js","../../src/utils/timeout.ts","../../src/platform/web/dom/request/common.js","../../src/platform/web/dom/request/xhr.js","../../src/platform/web/dom/request/fetch.js","../../src/matrix/sessioninfo/localstorage/SessionInfoStorage.ts","../../src/platform/web/dom/SettingsStorage.js","../../src/platform/web/dom/UTF8.js","../../node_modules/base64-arraybuffer/lib/base64-arraybuffer.js","../../src/platform/web/utils/Base64.js","../../scripts/package-overrides/buffer/index.js","../../node_modules/base-x/src/index.js","../../node_modules/bs58/index.js","../../src/platform/web/utils/Base58.js","../../src/platform/web/utils/Encoding.js","../../src/matrix/e2ee/OlmWorker.js","../../src/logging/IDBLogger.ts","../../src/platform/web/ui/general/html.ts","../../src/platform/web/ui/general/utils.ts","../../src/platform/web/ui/general/ListView.ts","../../src/platform/web/ui/general/BaseUpdateView.ts","../../src/platform/web/ui/general/TemplateView.ts","../../src/platform/web/ui/avatar.js","../../src/platform/web/ui/AvatarView.js","../../src/platform/web/ui/common.js","../../src/platform/web/ui/session/leftpanel/RoomTileView.js","../../src/platform/web/ui/session/leftpanel/LeftPanelView.js","../../src/platform/web/ui/general/Popup.js","../../src/platform/web/ui/general/Menu.js","../../src/platform/web/ui/session/room/TimelineView.ts","../../src/platform/web/ui/session/room/TimelineLoadingView.js","../../src/platform/web/ui/session/room/MessageComposer.js","../../src/platform/web/ui/session/room/DisabledComposerView.js","../../src/platform/web/ui/session/room/RoomView.js","../../src/platform/web/ui/session/room/UnknownRoomView.js","../../src/platform/web/ui/general/StaticView.js","../../src/platform/web/ui/general/LoadingView.js","../../src/platform/web/ui/session/room/RoomBeingCreatedView.js","../../src/platform/web/ui/session/room/InviteView.js","../../src/platform/web/ui/session/room/LightboxView.js","../../src/platform/web/ui/session/SessionStatusView.js","../../src/platform/web/ui/session/RoomGridView.js","../../src/platform/web/ui/session/settings/KeyBackupSettingsView.js","../../src/platform/web/ui/session/settings/SettingsView.js","../../src/platform/web/ui/session/CreateRoomView.js","../../src/platform/web/ui/session/rightpanel/RoomDetailsView.js","../../src/platform/web/ui/general/Range.ts","../../src/platform/web/ui/general/ListRange.ts","../../src/platform/web/ui/general/LazyListView.ts","../../src/platform/web/ui/session/rightpanel/MemberTileView.js","../../src/platform/web/ui/session/rightpanel/MemberListView.js","../../src/platform/web/ui/session/rightpanel/MemberDetailsView.js","../../src/platform/web/ui/session/rightpanel/RightPanelView.js","../../src/platform/web/ui/session/room/timeline/ReactionsView.js","../../src/platform/web/ui/session/room/timeline/BaseMessageView.js","../../src/platform/web/ui/session/room/timeline/ReplyPreviewView.js","../../src/platform/web/ui/session/room/timeline/TextMessageView.js","../../src/platform/web/ui/session/room/timeline/BaseMediaView.js","../../src/platform/web/ui/session/room/timeline/ImageView.js","../../src/platform/web/dom/utils.js","../../src/platform/web/ui/session/room/timeline/VideoView.js","../../src/platform/web/ui/session/room/timeline/FileView.js","../../src/platform/web/ui/session/room/timeline/LocationView.js","../../src/platform/web/ui/session/room/timeline/MissingAttachmentView.js","../../src/platform/web/ui/session/room/timeline/AnnouncementView.js","../../src/platform/web/ui/session/room/timeline/RedactedView.js","../../src/platform/web/ui/session/room/timeline/GapView.js","../../src/platform/web/ui/session/room/common.ts","../../src/platform/web/ui/session/SessionView.js","../../src/platform/web/ui/login/common.js","../../src/platform/web/ui/login/PasswordLoginView.js","../../src/platform/web/ui/login/AccountSetupView.js","../../src/platform/web/ui/login/SessionLoadStatusView.js","../../src/platform/web/ui/login/CompleteSSOView.js","../../src/platform/web/ui/login/LoginView.js","../../src/platform/web/ui/LogoutView.js","../../src/platform/web/ui/login/SessionLoadView.js","../../src/platform/web/ui/login/SessionPickerView.js","../../src/platform/web/ui/RootView.js","../../src/platform/web/dom/Clock.js","../../src/platform/web/dom/ServiceWorkerHandler.js","../../src/platform/web/dom/NotificationService.js","../../src/platform/web/dom/History.js","../../src/platform/web/dom/OnlineStatus.js","../../src/platform/web/dom/Crypto.js","../../src/platform/web/dom/StorageEstimate.js","../../src/platform/web/dom/WorkerPool.js","../../src/platform/web/dom/ImageHandle.js","../../src/platform/web/dom/download.js","../../node_modules/dompurify/dist/purify.es.js","../../src/platform/web/parsehtml.js","../../src/platform/web/theming/parsers/types.ts","../../src/platform/web/theming/shared/svg-colorizer.mjs","../../src/platform/web/theming/IconColorizer.ts","../../node_modules/off-color/cjs/index.min.js","../../node_modules/off-color/index.js","../../src/platform/web/theming/shared/color.mjs","../../src/platform/web/theming/DerivedVariables.ts","../../src/platform/web/theming/parsers/RuntimeThemeParser.ts","../../src/platform/web/theming/parsers/BuiltThemeParser.ts","../../src/platform/web/theming/ThemeLoader.ts","../../src/platform/web/Platform.js","../../src/platform/web/assets/config.json?url","../../src/platform/web/assets/download-sandbox.html?url","../../src/platform/web/worker/main.js?url","../../node_modules/@matrix-org/olm/olm.wasm?url","../../node_modules/@matrix-org/olm/olm.js?url","../../node_modules/@matrix-org/olm/olm_legacy.js?url","../../src/platform/web/sdk/paths/vite.js","../../src/platform/web/index.html?html-proxy&index=0.js"],"sourcesContent":["/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport function createEnum(...values: string[]): Readonly<{}> {\n const obj = {};\n for (const value of values) {\n obj[value] = value;\n }\n return Object.freeze(obj);\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nfunction normalizeHomeserver(homeserver) {\n try {\n return new URL(homeserver).origin;\n } catch (err) {\n return new URL(`https://${homeserver}`).origin;\n }\n}\n\nasync function getWellKnownResponse(homeserver, request) {\n const requestOptions = {format: \"json\", timeout: 30000, method: \"GET\"};\n try {\n const wellKnownUrl = `${homeserver}/.well-known/matrix/client`;\n return await request(wellKnownUrl, requestOptions).response();\n } catch (err) {\n if (err.name === \"ConnectionError\") {\n // don't fail lookup on a ConnectionError,\n // there might be a missing CORS header on a 404 response or something,\n // which won't be a problem necessarily with homeserver requests later on ...\n return null;\n } else {\n throw err;\n }\n }\n}\n\nexport async function lookupHomeserver(homeserver, request) {\n homeserver = normalizeHomeserver(homeserver);\n const wellKnownResponse = await getWellKnownResponse(homeserver, request);\n if (wellKnownResponse && wellKnownResponse.status === 200) {\n const {body} = wellKnownResponse;\n const wellKnownHomeserver = body[\"m.homeserver\"]?.[\"base_url\"];\n if (typeof wellKnownHomeserver === \"string\") {\n homeserver = normalizeHomeserver(wellKnownHomeserver);\n }\n }\n return homeserver;\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport class AbortError extends Error {\n get name(): string {\n return \"AbortError\";\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// we return undefined so you can reassign any member\n// that uses `member?: T` syntax in one statement.\nexport type SubscriptionHandle = () => undefined;\n\nexport abstract class BaseObservable<T> {\n protected _handlers: Set<T> = new Set<T>();\n\n onSubscribeFirst(): void {\n\n }\n\n onUnsubscribeLast(): void {\n\n }\n\n subscribe(handler: T): SubscriptionHandle {\n this._handlers.add(handler);\n if (this._handlers.size === 1) {\n this.onSubscribeFirst();\n }\n return () => {\n return this.unsubscribe(handler);\n };\n }\n\n unsubscribe(handler?: T): undefined {\n if (handler) {\n this._handlers.delete(handler);\n if (this._handlers.size === 0) {\n this.onUnsubscribeLast();\n }\n }\n return undefined;\n }\n\n unsubscribeAll(): void {\n if (this._handlers.size !== 0) {\n this._handlers.clear();\n this.onUnsubscribeLast();\n }\n }\n\n get hasSubscriptions(): boolean {\n return this._handlers.size !== 0;\n }\n\n // Add iterator over handlers here\n}\n\nexport function tests() {\n class Collection extends BaseObservable<{}> {\n firstSubscribeCalls: number = 0;\n firstUnsubscribeCalls: number = 0;\n\n onSubscribeFirst() { this.firstSubscribeCalls += 1; }\n onUnsubscribeLast() { this.firstUnsubscribeCalls += 1; }\n }\n\n return {\n test_unsubscribe(assert) {\n const c = new Collection();\n const unsubscribe = c.subscribe({});\n unsubscribe();\n assert.equal(c.firstSubscribeCalls, 1);\n assert.equal(c.firstUnsubscribeCalls, 1);\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {AbortError} from \"../utils/error\";\nimport {BaseObservable} from \"./BaseObservable\";\nimport type {SubscriptionHandle} from \"./BaseObservable\";\n\n// like an EventEmitter, but doesn't have an event type\nexport abstract class BaseObservableValue<T> extends BaseObservable<(value: T) => void> {\n emit(argument: T) {\n for (const h of this._handlers) {\n h(argument);\n }\n }\n\n abstract get(): T;\n\n waitFor(predicate: (value: T) => boolean): IWaitHandle<T> {\n if (predicate(this.get())) {\n return new ResolvedWaitForHandle(Promise.resolve(this.get()));\n } else {\n return new WaitForHandle(this, predicate);\n }\n }\n\n flatMap<C>(mapper: (value: T) => (BaseObservableValue<C> | undefined)): BaseObservableValue<C | undefined> {\n return new FlatMapObservableValue<T, C>(this, mapper);\n }\n}\n\ninterface IWaitHandle<T> {\n promise: Promise<T>;\n dispose(): void;\n}\n\nclass WaitForHandle<T> implements IWaitHandle<T> {\n private _promise: Promise<T>\n private _reject: ((reason?: any) => void) | null;\n private _subscription: (() => void) | null;\n\n constructor(observable: BaseObservableValue<T>, predicate: (value: T) => boolean) {\n this._promise = new Promise((resolve, reject) => {\n this._reject = reject;\n this._subscription = observable.subscribe(v => {\n if (predicate(v)) {\n this._reject = null;\n resolve(v);\n this.dispose();\n }\n });\n });\n }\n\n get promise(): Promise<T> {\n return this._promise;\n }\n\n dispose() {\n if (this._subscription) {\n this._subscription();\n this._subscription = null;\n }\n if (this._reject) {\n this._reject(new AbortError());\n this._reject = null;\n }\n }\n}\n\nclass ResolvedWaitForHandle<T> implements IWaitHandle<T> {\n constructor(public promise: Promise<T>) {}\n dispose() {}\n}\n\nexport class ObservableValue<T> extends BaseObservableValue<T> {\n private _value: T;\n\n constructor(initialValue: T) {\n super();\n this._value = initialValue;\n }\n\n get(): T {\n return this._value;\n }\n\n set(value: T): void {\n if (value !== this._value) {\n this._value = value;\n this.emit(this._value);\n }\n }\n}\n\nexport class RetainedObservableValue<T> extends ObservableValue<T> {\n private _freeCallback: () => void;\n\n constructor(initialValue: T, freeCallback: () => void) {\n super(initialValue);\n this._freeCallback = freeCallback;\n }\n\n onUnsubscribeLast() {\n super.onUnsubscribeLast();\n this._freeCallback();\n }\n}\n\nexport class FlatMapObservableValue<P, C> extends BaseObservableValue<C | undefined> {\n private sourceSubscription?: SubscriptionHandle;\n private targetSubscription?: SubscriptionHandle;\n\n constructor(\n private readonly source: BaseObservableValue<P>,\n private readonly mapper: (value: P) => (BaseObservableValue<C> | undefined)\n ) {\n super();\n }\n\n onUnsubscribeLast() {\n super.onUnsubscribeLast();\n this.sourceSubscription = this.sourceSubscription!();\n if (this.targetSubscription) {\n this.targetSubscription = this.targetSubscription();\n }\n }\n\n onSubscribeFirst() {\n super.onSubscribeFirst();\n this.sourceSubscription = this.source.subscribe(() => {\n this.updateTargetSubscription();\n this.emit(this.get());\n });\n this.updateTargetSubscription();\n }\n\n private updateTargetSubscription() {\n const sourceValue = this.source.get();\n if (sourceValue) {\n const target = this.mapper(sourceValue);\n if (target) {\n if (!this.targetSubscription) {\n this.targetSubscription = target.subscribe(() => this.emit(this.get()));\n }\n return;\n }\n }\n // if no sourceValue or target\n if (this.targetSubscription) {\n this.targetSubscription = this.targetSubscription();\n }\n }\n\n get(): C | undefined {\n const sourceValue = this.source.get();\n if (!sourceValue) {\n return undefined;\n }\n const mapped = this.mapper(sourceValue);\n return mapped?.get();\n }\n}\n\nexport function tests() {\n return {\n \"set emits an update\": assert => {\n const a = new ObservableValue<number>(0);\n let fired = false;\n const subscription = a.subscribe(v => {\n fired = true;\n assert.strictEqual(v, 5);\n });\n a.set(5);\n assert(fired);\n subscription();\n },\n \"set doesn't emit if value hasn't changed\": assert => {\n const a = new ObservableValue(5);\n let fired = false;\n const subscription = a.subscribe(() => {\n fired = true;\n });\n a.set(5);\n a.set(5);\n assert(!fired);\n subscription();\n },\n \"waitFor promise resolves on matching update\": async assert => {\n const a = new ObservableValue(5);\n const handle = a.waitFor(v => v === 6);\n Promise.resolve().then(() => {\n a.set(6);\n });\n await handle.promise;\n assert.strictEqual(a.get(), 6);\n },\n \"waitFor promise rejects when disposed\": async assert => {\n const a = new ObservableValue<number>(0);\n const handle = a.waitFor(() => false);\n Promise.resolve().then(() => {\n handle.dispose();\n });\n await assert.rejects(handle.promise, AbortError);\n },\n \"flatMap.get\": assert => {\n const a = new ObservableValue<undefined | {count: ObservableValue<number>}>(undefined);\n const countProxy = a.flatMap(a => a!.count);\n assert.strictEqual(countProxy.get(), undefined);\n const count = new ObservableValue<number>(0);\n a.set({count});\n assert.strictEqual(countProxy.get(), 0);\n },\n \"flatMap update from source\": assert => {\n const a = new ObservableValue<undefined | {count: ObservableValue<number>}>(undefined);\n const updates: (number | undefined)[] = [];\n a.flatMap(a => a!.count).subscribe(count => {\n updates.push(count);\n });\n const count = new ObservableValue<number>(0);\n a.set({count});\n assert.deepEqual(updates, [0]);\n },\n \"flatMap update from target\": assert => {\n const a = new ObservableValue<undefined | {count: ObservableValue<number>}>(undefined);\n const updates: (number | undefined)[] = [];\n a.flatMap(a => a!.count).subscribe(count => {\n updates.push(count);\n });\n const count = new ObservableValue<number>(0);\n a.set({count});\n count.set(5);\n assert.deepEqual(updates, [0, 5]);\n }\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseObservableValue, ObservableValue} from \"../observable/ObservableValue\";\n\nexport interface IAbortable {\n abort();\n}\n\nexport type SetAbortableFn = (a: IAbortable) => typeof a;\nexport type SetProgressFn<P> = (progress: P) => void;\ntype RunFn<T, P> = (setAbortable: SetAbortableFn, setProgress: SetProgressFn<P>) => T;\n\nexport class AbortableOperation<T, P = void> implements IAbortable {\n public readonly result: T;\n private _abortable?: IAbortable;\n private _progress: ObservableValue<P | undefined>;\n\n constructor(run: RunFn<T, P>) {\n this._abortable = undefined;\n const setAbortable: SetAbortableFn = abortable => {\n this._abortable = abortable;\n return abortable;\n };\n this._progress = new ObservableValue<P | undefined>(undefined);\n const setProgress: SetProgressFn<P> = (progress: P) => {\n this._progress.set(progress);\n };\n this.result = run(setAbortable, setProgress);\n }\n\n get progress(): BaseObservableValue<P | undefined> {\n return this._progress;\n }\n\n abort() {\n this._abortable?.abort();\n this._abortable = undefined;\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n\n// WARNING: We have to be very careful about what mime-types we allow into blobs.\n//\n// This means that the content is rendered using the origin of the script which\n// called createObjectURL(), and so if the content contains any scripting then it\n// will pose a XSS vulnerability when the browser renders it. This is particularly\n// bad if the user right-clicks the URI and pastes it into a new window or tab,\n// as the blob will then execute with access to Element's full JS environment(!)\n//\n// See https://github.com/matrix-org/matrix-react-sdk/pull/1820#issuecomment-385210647\n// for details.\n//\n// We mitigate this by only allowing mime-types into blobs which we know don't\n// contain any scripting, and instantiate all others as application/octet-stream\n// regardless of what mime-type the event claimed. Even if the payload itself\n// is some malicious HTML, the fact we instantiate it with a media mimetype or\n// application/octet-stream means the browser doesn't try to render it as such.\n//\n// One interesting edge case is image/svg+xml, which empirically *is* rendered\n// correctly if the blob is set to the src attribute of an img tag (for thumbnails)\n// *even if the mimetype is application/octet-stream*. However, empirically JS\n// in the SVG isn't executed in this scenario, so we seem to be okay.\n//\n// Tested on Chrome 65 and Firefox 60\n//\n// The list below is taken mainly from\n// https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats\n// N.B. Matrix doesn't currently specify which mimetypes are valid in given\n// events, so we pick the ones which HTML5 browsers should be able to display\n//\n// For the record, mime-types which must NEVER enter this list below include:\n// text/html, text/xhtml, image/svg, image/svg+xml, image/pdf, and similar.\n\nconst ALLOWED_BLOB_MIMETYPES = {\n 'image/jpeg': true,\n 'image/gif': true,\n 'image/png': true,\n\n 'video/mp4': true,\n 'video/webm': true,\n 'video/ogg': true,\n 'video/quicktime': true,\n 'video/VP8': true,\n\n 'audio/mp4': true,\n 'audio/webm': true,\n 'audio/aac': true,\n 'audio/mpeg': true,\n 'audio/ogg': true,\n 'audio/wave': true,\n 'audio/wav': true,\n 'audio/x-wav': true,\n 'audio/x-pn-wav': true,\n 'audio/flac': true,\n 'audio/x-flac': true,\n};\n\nconst DEFAULT_MIMETYPE = 'application/octet-stream';\n\nexport class BlobHandle {\n constructor(blob, buffer = null) {\n this._blob = blob;\n this._buffer = buffer;\n this._url = null;\n }\n\n static fromBuffer(buffer, mimetype) {\n mimetype = mimetype ? mimetype.split(\";\")[0].trim() : '';\n if (!ALLOWED_BLOB_MIMETYPES[mimetype]) {\n mimetype = DEFAULT_MIMETYPE;\n }\n return new BlobHandle(new Blob([buffer], {type: mimetype}), buffer);\n }\n\n static fromBlob(blob) {\n // ok to not filter mimetypes as these are local files\n return new BlobHandle(blob);\n }\n\n get nativeBlob() {\n return this._blob;\n }\n\n async readAsBuffer() {\n if (this._buffer) {\n return this._buffer;\n } else {\n const reader = new FileReader();\n const promise = new Promise((resolve, reject) => {\n reader.addEventListener(\"load\", evt => resolve(evt.target.result)); \n reader.addEventListener(\"error\", evt => reject(evt.target.error)); \n });\n reader.readAsArrayBuffer(this._blob);\n return promise;\n }\n }\n\n get url() {\n if (!this._url) {\n this._url = URL.createObjectURL(this._blob);\n }\n return this._url;\n }\n\n get size() {\n return this._blob.size;\n }\n\n get mimeType() {\n return this._blob.type || DEFAULT_MIMETYPE;\n }\n\n dispose() {\n if (this._url) {\n URL.revokeObjectURL(this._url);\n this._url = null;\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BlobHandle} from \"../../platform/web/dom/BlobHandle.js\";\n\nexport type RequestBody = BlobHandle | string | Map<string, string | {blob: BlobHandle, name: string}>;\n\nexport type EncodedBody = {\n mimeType: string;\n // the map gets transformed to a FormData object on the web\n body: RequestBody\n}\n\nexport function encodeQueryParams(queryParams?: object): string {\n return Object.entries(queryParams || {})\n .filter(([, value]) => value !== undefined)\n .map(([name, value]) => {\n if (typeof value === \"object\") {\n value = JSON.stringify(value);\n }\n return `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;\n })\n .join(\"&\");\n}\n\nexport function encodeBody(body: BlobHandle | object): EncodedBody {\n if (body instanceof BlobHandle) {\n const blob = body as BlobHandle;\n return {\n mimeType: blob.mimeType,\n body: blob // will be unwrapped in request fn\n };\n } else if (body instanceof Map) {\n return {\n mimeType: \"multipart/form-data\",\n body: body\n }\n } else if (typeof body === \"object\") {\n const json = JSON.stringify(body);\n return {\n mimeType: \"application/json\",\n body: json\n }\n } else {\n throw new Error(\"Unknown body type: \" + body);\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport class WrappedError extends Error {\n constructor(message, cause) {\n super(`${message}: ${cause.message}`);\n this.cause = cause;\n }\n\n get name() {\n return \"WrappedError\";\n }\n}\n\nexport class HomeServerError extends Error {\n constructor(method, url, body, status) {\n super(`${body ? body.error : status} on ${method} ${url}`);\n this.errcode = body ? body.errcode : null;\n this.retry_after_ms = body ? body.retry_after_ms : 0;\n this.statusCode = status;\n }\n\n get name() {\n return \"HomeServerError\";\n }\n}\n\nexport {AbortError} from \"../utils/error\";\n\nexport class ConnectionError extends Error {\n constructor(message, isTimeout) {\n super(message || \"ConnectionError\");\n this.isTimeout = isTimeout;\n }\n\n get name() {\n return \"ConnectionError\";\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {HomeServerError, ConnectionError} from \"../error.js\";\nimport type {RequestResult} from \"../../platform/web/dom/request/fetch.js\";\nimport type {ILogItem} from \"../../logging/types\";\n\nexport interface IHomeServerRequest {\n abort(): void;\n response(): Promise<any>;\n responseCode(): Promise<number>;\n}\n\ntype HomeServerRequestOptions = {\n log?: ILogItem;\n allowedStatusCodes?: number[];\n};\n\nexport class HomeServerRequest implements IHomeServerRequest {\n private readonly _log?: ILogItem;\n private _sourceRequest?: RequestResult;\n // as we add types for expected responses from hs, this could be a generic class instead\n private readonly _promise: Promise<any>;\n\n constructor(method: string, url: string, sourceRequest: RequestResult, options?: HomeServerRequestOptions) {\n let log: ILogItem | undefined;\n if (options?.log) {\n const parent = options?.log;\n log = parent.child({ t: \"network\", url, method, }, parent.level.Info);\n }\n this._log = log;\n this._sourceRequest = sourceRequest;\n this._promise = sourceRequest.response().then(response => {\n log?.set(\"status\", response.status);\n // ok?\n if (response.status >= 200 && response.status < 300 || options?.allowedStatusCodes?.includes(response.status)) {\n log?.finish();\n return response.body;\n } else {\n if (response.status >= 500) {\n const err = new ConnectionError(`Internal Server Error`);\n log?.catch(err);\n throw err;\n } else if (response.status >= 400 && !response.body?.errcode) {\n const err = new ConnectionError(`HTTP error status ${response.status} without errcode in body, assume this is a load balancer complaining the server is offline.`);\n log?.catch(err);\n throw err;\n } else {\n const err = new HomeServerError(method, url, response.body, response.status);\n log?.set(\"errcode\", err.errcode);\n log?.catch(err);\n throw err;\n }\n }\n }, err => {\n // if this._sourceRequest is still set,\n // the abort error came not from calling abort here\n if (err.name === \"AbortError\" && this._sourceRequest) {\n // The service worker sometimes (only on Firefox, on long, large request,\n // perhaps it has its own timeout?) aborts the request, see #187.\n // When it happens, the best thing to do seems to be to retry.\n // \n // In the service worker, we will also actively abort all\n // ongoing requests when trying to get a new service worker to activate\n // (this may surface in the app as a TypeError, which already gets mapped\n // to a ConnectionError in the request function, or an AbortError,\n // depending on the browser), as the service worker will only be\n // replaced when there are no more (fetch) events for the\n // current one to handle.\n // \n // In that case, the request function (in fetch.js) will check \n // the haltRequests flag on the service worker handler, and\n // block any new requests, as that would break the update process.\n // \n // So it is OK to return a ConnectionError here.\n // If we're updating the service worker, the /versions polling will\n // be blocked at the fetch level because haltRequests is set.\n // And for #187, retrying is the right thing to do.\n const err = new ConnectionError(`Service worker aborted, either updating or hit #187.`);\n log?.catch(err);\n throw err;\n } else {\n if (err.name === \"ConnectionError\") {\n log?.set(\"timeout\", err.isTimeout);\n }\n log?.catch(err);\n throw err;\n }\n });\n }\n\n abort(): void {\n if (this._sourceRequest) {\n this._log?.set(\"aborted\", true);\n this._sourceRequest.abort();\n // to mark that it was on purpose in above rejection handler\n this._sourceRequest = undefined;\n }\n }\n\n response(): Promise<any> {\n return this._promise;\n }\n\n async responseCode(): Promise<number> {\n const response = await this._sourceRequest.response();\n return response.status;\n }\n}\n\nimport {Request as MockRequest} from \"../../mocks/Request.js\";\nimport {AbortError} from \"../error.js\";\n\nexport function tests() {\n return {\n \"Response is passed through\": async assert => {\n const request = new MockRequest();\n const hsRequest = new HomeServerRequest(\"GET\", \"https://hs.tld/foo\", request);\n request.respond(200, \"foo\");\n assert.equal(await hsRequest.response(), \"foo\");\n },\n \"Unexpected AbortError is mapped to ConnectionError\": async assert => {\n const request = new MockRequest();\n const hsRequest = new HomeServerRequest(\"GET\", \"https://hs.tld/foo\", request);\n request.reject(new AbortError());\n await assert.rejects(hsRequest.response(), ConnectionError);\n },\n \"500 response is mapped to ConnectionError\": async assert => {\n const request = new MockRequest();\n const hsRequest = new HomeServerRequest(\"GET\", \"https://hs.tld/foo\", request);\n request.respond(500);\n await assert.rejects(hsRequest.response(), ConnectionError);\n },\n \"4xx response is mapped to HomeServerError\": async assert => {\n const request = new MockRequest();\n const hsRequest = new HomeServerRequest(\"GET\", \"https://hs.tld/foo\", request);\n request.respond(400, {errcode: \"FOO\"});\n await assert.rejects(hsRequest.response(), HomeServerError);\n },\n \"4xx response without errcode is mapped to ConnectionError\": async assert => {\n const request = new MockRequest();\n const hsRequest = new HomeServerRequest(\"GET\", \"https://hs.tld/foo\", request);\n request.respond(400);\n await assert.rejects(hsRequest.response(), ConnectionError);\n },\n \"Other errors are passed through\": async assert => {\n class MyError extends Error {}\n const request = new MockRequest();\n const hsRequest = new HomeServerRequest(\"GET\", \"https://hs.tld/foo\", request);\n request.reject(new MyError());\n await assert.rejects(hsRequest.response(), MyError);\n },\n };\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {encodeQueryParams, encodeBody} from \"./common\";\nimport {HomeServerRequest} from \"./HomeServerRequest\";\nimport type {IHomeServerRequest} from \"./HomeServerRequest\";\nimport type {Reconnector} from \"./Reconnector\";\nimport type {EncodedBody} from \"./common\";\nimport type {RequestFunction} from \"../../platform/types/types\";\nimport type {ILogItem} from \"../../logging/types\";\n\ntype RequestMethod = \"POST\" | \"GET\" | \"PUT\";\n\nconst CS_R0_PREFIX = \"/_matrix/client/r0\";\nconst CS_V3_PREFIX = \"/_matrix/client/v3\";\nconst DEHYDRATION_PREFIX = \"/_matrix/client/unstable/org.matrix.msc2697.v2\";\n\ntype Options = {\n homeserver: string;\n accessToken: string;\n request: RequestFunction;\n reconnector: Reconnector;\n};\n\ntype BaseRequestOptions = {\n log?: ILogItem;\n allowedStatusCodes?: number[];\n uploadProgress?: (loadedBytes: number) => void;\n timeout?: number;\n prefix?: string;\n};\n\nexport class HomeServerApi {\n private readonly _homeserver: string;\n private readonly _accessToken: string;\n private readonly _requestFn: RequestFunction;\n private readonly _reconnector: Reconnector;\n\n constructor({homeserver, accessToken, request, reconnector}: Options) {\n // store these both in a closure somehow so it's harder to get at in case of XSS?\n // one could change the homeserver as well so the token gets sent there, so both must be protected from read/write\n this._homeserver = homeserver;\n this._accessToken = accessToken;\n this._requestFn = request;\n this._reconnector = reconnector;\n }\n\n private _url(csPath: string, prefix: string = CS_R0_PREFIX): string {\n return this._homeserver + prefix + csPath;\n }\n\n private _baseRequest(method: RequestMethod, url: string, queryParams?: Record<string, any>, body?: Record<string, any>, options?: BaseRequestOptions, accessToken?: string): IHomeServerRequest {\n const queryString = encodeQueryParams(queryParams);\n url = `${url}?${queryString}`;\n let encodedBody: EncodedBody[\"body\"];\n const headers: Map<string, string | number> = new Map();\n if (accessToken) {\n headers.set(\"Authorization\", `Bearer ${accessToken}`);\n }\n headers.set(\"Accept\", \"application/json\");\n if (body) {\n const encoded = encodeBody(body);\n headers.set(\"Content-Type\", encoded.mimeType);\n encodedBody = encoded.body;\n }\n\n const requestResult = this._requestFn(url, {\n method,\n headers,\n body: encodedBody,\n timeout: options?.timeout,\n uploadProgress: options?.uploadProgress,\n format: \"json\", // response format\n cache: method !== \"GET\",\n });\n\n const hsRequest = new HomeServerRequest(method, url, requestResult, options);\n \n if (this._reconnector) {\n hsRequest.response().catch(err => {\n // Some endpoints such as /sync legitimately time-out\n // (which is also reported as a ConnectionError) and will re-attempt,\n // but spinning up the reconnector in this case is ok,\n // as all code ran on session and sync start should be reentrant\n if (err.name === \"ConnectionError\") {\n this._reconnector.onRequestFailed(this);\n }\n });\n }\n\n return hsRequest;\n }\n\n private _unauthedRequest(method: RequestMethod, url: string, queryParams?: Record<string, any>, body?: Record<string, any>, options?: BaseRequestOptions): IHomeServerRequest {\n return this._baseRequest(method, url, queryParams, body, options);\n }\n\n private _authedRequest(method: RequestMethod, url: string, queryParams?: Record<string, any>, body?: Record<string, any>, options?: BaseRequestOptions): IHomeServerRequest {\n return this._baseRequest(method, url, queryParams, body, options, this._accessToken);\n }\n\n private _post(csPath: string, queryParams: Record<string, any>, body: Record<string, any>, options?: BaseRequestOptions): IHomeServerRequest {\n return this._authedRequest(\"POST\", this._url(csPath, options?.prefix || CS_R0_PREFIX), queryParams, body, options);\n }\n\n private _put(csPath: string, queryParams: Record<string, any>, body?: Record<string, any>, options?: BaseRequestOptions): IHomeServerRequest {\n return this._authedRequest(\"PUT\", this._url(csPath, options?.prefix || CS_R0_PREFIX), queryParams, body, options);\n }\n\n private _get(csPath: string, queryParams?: Record<string, any>, body?: Record<string, any>, options?: BaseRequestOptions): IHomeServerRequest {\n return this._authedRequest(\"GET\", this._url(csPath, options?.prefix || CS_R0_PREFIX), queryParams, body, options);\n }\n\n sync(since: string, filter: string, timeout: number, options?: BaseRequestOptions): IHomeServerRequest {\n return this._get(\"/sync\", {since, timeout, filter}, undefined, options);\n }\n\n context(roomId: string, eventId: string, limit: number, filter: string): IHomeServerRequest {\n return this._get(`/rooms/${encodeURIComponent(roomId)}/context/${encodeURIComponent(eventId)}`, {filter, limit});\n }\n\n // params is from, dir and optionally to, limit, filter.\n messages(roomId: string, params: Record<string, any>, options?: BaseRequestOptions): IHomeServerRequest {\n return this._get(`/rooms/${encodeURIComponent(roomId)}/messages`, params, undefined, options);\n }\n\n // params is at, membership and not_membership\n members(roomId: string, params: Record<string, any>, options?: BaseRequestOptions): IHomeServerRequest {\n return this._get(`/rooms/${encodeURIComponent(roomId)}/members`, params, undefined, options);\n }\n\n send(roomId: string, eventType: string, txnId: string, content: Record<string, any>, options?: BaseRequestOptions): IHomeServerRequest {\n return this._put(`/rooms/${encodeURIComponent(roomId)}/send/${encodeURIComponent(eventType)}/${encodeURIComponent(txnId)}`, {}, content, options);\n }\n\n redact(roomId: string, eventId: string, txnId: string, content: Record<string, any>, options?: BaseRequestOptions): IHomeServerRequest {\n return this._put(`/rooms/${encodeURIComponent(roomId)}/redact/${encodeURIComponent(eventId)}/${encodeURIComponent(txnId)}`, {}, content, options);\n }\n\n receipt(roomId: string, receiptType: string, eventId: string, options?: BaseRequestOptions): IHomeServerRequest {\n return this._post(`/rooms/${encodeURIComponent(roomId)}/receipt/${encodeURIComponent(receiptType)}/${encodeURIComponent(eventId)}`,\n {}, {}, options);\n }\n\n state(roomId: string, eventType: string, stateKey: string, options?: BaseRequestOptions): IHomeServerRequest {\n return this._get(`/rooms/${encodeURIComponent(roomId)}/state/${encodeURIComponent(eventType)}/${encodeURIComponent(stateKey)}`, {}, undefined, options);\n }\n\n getLoginFlows(): IHomeServerRequest {\n return this._unauthedRequest(\"GET\", this._url(\"/login\"));\n }\n\n register(username: string | null, password: string, initialDeviceDisplayName: string, auth?: Record<string, any>, inhibitLogin: boolean = true , options: BaseRequestOptions = {}): IHomeServerRequest {\n options.allowedStatusCodes = [401];\n const body: any = {\n auth,\n password,\n initial_device_displayname: initialDeviceDisplayName,\n inhibit_login: inhibitLogin,\n };\n if (username) {\n // username is optional for registration\n body.username = username;\n }\n return this._unauthedRequest( \"POST\", this._url(\"/register\", CS_V3_PREFIX), undefined, body, options);\n }\n\n passwordLogin(username: string, password: string, initialDeviceDisplayName: string, options?: BaseRequestOptions): IHomeServerRequest {\n return this._unauthedRequest(\"POST\", this._url(\"/login\"), undefined, {\n \"type\": \"m.login.password\",\n \"identifier\": {\n \"type\": \"m.id.user\",\n \"user\": username\n },\n \"password\": password,\n \"initial_device_display_name\": initialDeviceDisplayName\n }, options);\n }\n\n tokenLogin(loginToken: string, txnId: string, initialDeviceDisplayName: string, options?: BaseRequestOptions): IHomeServerRequest {\n return this._unauthedRequest(\"POST\", this._url(\"/login\"), undefined, {\n \"type\": \"m.login.token\",\n \"identifier\": {\n \"type\": \"m.id.user\",\n },\n \"token\": loginToken,\n \"txn_id\": txnId,\n \"initial_device_display_name\": initialDeviceDisplayName\n }, options);\n }\n\n createFilter(userId: string, filter: Record<string, any>, options?: BaseRequestOptions): IHomeServerRequest {\n return this._post(`/user/${encodeURIComponent(userId)}/filter`, {}, filter, options);\n }\n\n versions(options?: BaseRequestOptions): IHomeServerRequest {\n return this._unauthedRequest(\"GET\", `${this._homeserver}/_matrix/client/versions`, undefined, undefined, options);\n }\n\n uploadKeys(dehydratedDeviceId: string, payload: Record<string, any>, options?: BaseRequestOptions): IHomeServerRequest {\n let path = \"/keys/upload\";\n if (dehydratedDeviceId) {\n path = path + `/${encodeURIComponent(dehydratedDeviceId)}`;\n }\n return this._post(path, {}, payload, options);\n }\n\n queryKeys(queryRequest: Record<string, any>, options?: BaseRequestOptions): IHomeServerRequest {\n return this._post(\"/keys/query\", {}, queryRequest, options);\n }\n\n claimKeys(payload: Record<string, any>, options?: BaseRequestOptions): IHomeServerRequest {\n return this._post(\"/keys/claim\", {}, payload, options);\n }\n\n sendToDevice(type: string, payload: Record<string, any>, txnId: string, options?: BaseRequestOptions): IHomeServerRequest {\n return this._put(`/sendToDevice/${encodeURIComponent(type)}/${encodeURIComponent(txnId)}`, {}, payload, options);\n }\n \n roomKeysVersion(version?: string, options?: BaseRequestOptions): IHomeServerRequest {\n let versionPart = \"\";\n if (version) {\n versionPart = `/${encodeURIComponent(version)}`;\n }\n return this._get(`/room_keys/version${versionPart}`, undefined, undefined, options);\n }\n\n roomKeyForRoomAndSession(version: string, roomId: string, sessionId: string, options?: BaseRequestOptions): IHomeServerRequest {\n return this._get(`/room_keys/keys/${encodeURIComponent(roomId)}/${encodeURIComponent(sessionId)}`, {version}, undefined, options);\n }\n\n uploadRoomKeysToBackup(version: string, payload: Record<string, any>, options?: BaseRequestOptions): IHomeServerRequest {\n return this._put(`/room_keys/keys`, {version}, payload, options);\n }\n\n uploadAttachment(blob: Blob, filename: string, options?: BaseRequestOptions): IHomeServerRequest {\n return this._authedRequest(\"POST\", `${this._homeserver}/_matrix/media/r0/upload`, {filename}, blob, options);\n }\n\n setPusher(pusher: Record<string, any>, options?: BaseRequestOptions): IHomeServerRequest {\n return this._post(\"/pushers/set\", {}, pusher, options);\n }\n\n getPushers(options?: BaseRequestOptions): IHomeServerRequest {\n return this._get(\"/pushers\", undefined, undefined, options);\n }\n\n join(roomId: string, options?: BaseRequestOptions): IHomeServerRequest {\n return this._post(`/rooms/${encodeURIComponent(roomId)}/join`, {}, {}, options);\n }\n\n joinIdOrAlias(roomIdOrAlias: string, options?: BaseRequestOptions): IHomeServerRequest {\n return this._post(`/join/${encodeURIComponent(roomIdOrAlias)}`, {}, {}, options);\n }\n\n leave(roomId: string, options?: BaseRequestOptions): IHomeServerRequest {\n return this._post(`/rooms/${encodeURIComponent(roomId)}/leave`, {}, {}, options);\n }\n\n forget(roomId: string, options?: BaseRequestOptions): IHomeServerRequest {\n return this._post(`/rooms/${encodeURIComponent(roomId)}/forget`, {}, {}, options);\n }\n\n logout(options?: BaseRequestOptions): IHomeServerRequest {\n return this._post(`/logout`, {}, {}, options);\n }\n\n getDehydratedDevice(options: BaseRequestOptions = {}): IHomeServerRequest {\n options.prefix = DEHYDRATION_PREFIX;\n return this._get(`/dehydrated_device`, undefined, undefined, options);\n }\n\n createDehydratedDevice(payload: Record<string, any>, options: BaseRequestOptions = {}): IHomeServerRequest {\n options.prefix = DEHYDRATION_PREFIX;\n return this._put(`/dehydrated_device`, {}, payload, options);\n }\n\n claimDehydratedDevice(deviceId: string, options: BaseRequestOptions = {}): IHomeServerRequest {\n options.prefix = DEHYDRATION_PREFIX;\n return this._post(`/dehydrated_device/claim`, {}, {device_id: deviceId}, options);\n }\n\n profile(userId: string, options?: BaseRequestOptions): IHomeServerRequest {\n return this._get(`/profile/${encodeURIComponent(userId)}`);\n }\n\n createRoom(payload: Record<string, any>, options?: BaseRequestOptions): IHomeServerRequest {\n return this._post(`/createRoom`, {}, payload, options);\n }\n \n setAccountData(ownUserId: string, type: string, content: Record<string, any>, options?: BaseRequestOptions): IHomeServerRequest {\n return this._put(`/user/${encodeURIComponent(ownUserId)}/account_data/${encodeURIComponent(type)}`, {}, content, options);\n }\n}\n\nimport {Request as MockRequest} from \"../../mocks/Request.js\";\n\nexport function tests() {\n return {\n \"superficial happy path for GET\": async assert => {\n // @ts-ignore\n const hsApi = new HomeServerApi({\n request: () => new MockRequest().respond(200, 42),\n homeserver: \"https://hs.tld\",\n });\n // @ts-ignore\n const result = await hsApi._get(\"foo\").response();\n assert.strictEqual(result, 42);\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {AbortError} from \"../../utils/error\";\nimport type {Timeout} from \"../../platform/web/dom/Clock.js\";\n\ntype TimeoutCreator = (ms: number) => Timeout;\n\nconst enum Default { start = 2000 }\n\nexport class ExponentialRetryDelay {\n private readonly _start: number = Default.start;\n private _current: number = Default.start;\n private readonly _createTimeout: TimeoutCreator;\n private readonly _max: number;\n private _timeout?: Timeout;\n\n constructor(createTimeout: TimeoutCreator) {\n const start = 2000;\n this._start = start;\n this._current = start;\n this._createTimeout = createTimeout;\n this._max = 60 * 5 * 1000; //5 min\n }\n\n async waitForRetry(): Promise<void> {\n this._timeout = this._createTimeout(this._current);\n try {\n await this._timeout.elapsed();\n // only increase delay if we didn't get interrupted\n const next = 2 * this._current;\n this._current = Math.min(this._max, next);\n } catch(err) {\n // swallow AbortError, means abort was called\n if (!(err instanceof AbortError)) {\n throw err;\n }\n } finally {\n this._timeout = undefined;\n }\n }\n\n abort(): void {\n if (this._timeout) {\n this._timeout.abort();\n }\n }\n\n reset(): void {\n this._current = this._start;\n this.abort();\n }\n\n get nextValue(): number {\n return this._current;\n }\n}\n\n\nimport {Clock as MockClock} from \"../../mocks/Clock.js\";\n\nexport function tests() {\n return {\n \"test sequence\": async assert => {\n const clock = new MockClock();\n const retryDelay = new ExponentialRetryDelay(clock.createTimeout);\n let promise;\n\n assert.strictEqual(retryDelay.nextValue, 2000);\n promise = retryDelay.waitForRetry();\n clock.elapse(2000);\n await promise;\n\n assert.strictEqual(retryDelay.nextValue, 4000);\n promise = retryDelay.waitForRetry();\n clock.elapse(4000);\n await promise;\n\n assert.strictEqual(retryDelay.nextValue, 8000);\n promise = retryDelay.waitForRetry();\n clock.elapse(8000);\n await promise;\n\n assert.strictEqual(retryDelay.nextValue, 16000);\n promise = retryDelay.waitForRetry();\n clock.elapse(16000);\n await promise;\n\n assert.strictEqual(retryDelay.nextValue, 32000);\n promise = retryDelay.waitForRetry();\n clock.elapse(32000);\n await promise;\n\n assert.strictEqual(retryDelay.nextValue, 64000);\n promise = retryDelay.waitForRetry();\n clock.elapse(64000);\n await promise;\n\n assert.strictEqual(retryDelay.nextValue, 128000);\n promise = retryDelay.waitForRetry();\n clock.elapse(128000);\n await promise;\n\n assert.strictEqual(retryDelay.nextValue, 256000);\n promise = retryDelay.waitForRetry();\n clock.elapse(256000);\n await promise;\n\n assert.strictEqual(retryDelay.nextValue, 300000);\n promise = retryDelay.waitForRetry();\n clock.elapse(300000);\n await promise;\n\n assert.strictEqual(retryDelay.nextValue, 300000);\n promise = retryDelay.waitForRetry();\n clock.elapse(300000);\n await promise;\n },\n }\n \n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ObservableValue} from \"../../observable/ObservableValue\";\nimport type {ExponentialRetryDelay} from \"./ExponentialRetryDelay\";\nimport type {TimeMeasure} from \"../../platform/web/dom/Clock.js\";\nimport type {OnlineStatus} from \"../../platform/web/dom/OnlineStatus.js\";\nimport type {VersionResponse} from \"./types/response\";\nimport type {HomeServerApi} from \"./HomeServerApi\";\n\nexport enum ConnectionStatus {\n \"Waiting\",\n \"Reconnecting\",\n \"Online\"\n};\n\ntype Ctor = {\n retryDelay: ExponentialRetryDelay;\n createMeasure: () => TimeMeasure;\n onlineStatus: OnlineStatus\n};\n\nexport class Reconnector {\n private readonly _retryDelay: ExponentialRetryDelay;\n private readonly _createTimeMeasure: () => TimeMeasure;\n private readonly _onlineStatus: OnlineStatus;\n private readonly _state: ObservableValue<ConnectionStatus>;\n private _isReconnecting: boolean;\n private _versionsResponse?: VersionResponse;\n private _stateSince: TimeMeasure;\n\n constructor({retryDelay, createMeasure, onlineStatus}: Ctor) {\n this._onlineStatus = onlineStatus;\n this._retryDelay = retryDelay;\n this._createTimeMeasure = createMeasure;\n // assume online, and do our thing when something fails\n this._state = new ObservableValue(ConnectionStatus.Online);\n this._isReconnecting = false;\n }\n\n get lastVersionsResponse(): VersionResponse | undefined {\n return this._versionsResponse;\n }\n\n get connectionStatus(): ObservableValue<ConnectionStatus> {\n return this._state;\n }\n\n get retryIn(): number {\n if (this._state.get() === ConnectionStatus.Waiting) {\n return this._retryDelay.nextValue - this._stateSince.measure();\n }\n return 0;\n }\n\n async onRequestFailed(hsApi: HomeServerApi): Promise<void> {\n if (!this._isReconnecting) { \n this._isReconnecting = true;\n \n const onlineStatusSubscription = this._onlineStatus && this._onlineStatus.subscribe(online => {\n if (online) {\n this.tryNow();\n }\n });\n\n try {\n await this._reconnectLoop(hsApi);\n } catch (err) {\n // nothing is catching the error above us,\n // so just log here\n console.error(err);\n } finally {\n if (onlineStatusSubscription) {\n // unsubscribe from this._onlineStatus\n onlineStatusSubscription();\n }\n this._isReconnecting = false;\n }\n }\n }\n\n tryNow(): void {\n if (this._retryDelay) {\n // this will interrupt this._retryDelay.waitForRetry() in _reconnectLoop\n this._retryDelay.abort();\n }\n }\n\n private _setState(state: ConnectionStatus): void {\n if (state !== this._state.get()) {\n if (state === ConnectionStatus.Waiting) {\n this._stateSince = this._createTimeMeasure();\n } else {\n this._stateSince = null;\n }\n this._state.set(state);\n }\n }\n \n private async _reconnectLoop(hsApi: HomeServerApi): Promise<void> {\n this._versionsResponse = undefined;\n this._retryDelay.reset();\n\n while (!this._versionsResponse) {\n try {\n this._setState(ConnectionStatus.Reconnecting);\n // use 30s timeout, as a tradeoff between not giving up\n // too quickly on a slow server, and not waiting for\n // a stale connection when we just came online again\n const versionsRequest = hsApi.versions({timeout: 30000});\n this._versionsResponse = await versionsRequest.response();\n this._setState(ConnectionStatus.Online);\n } catch (err) {\n if (err.name === \"ConnectionError\") {\n this._setState(ConnectionStatus.Waiting);\n await this._retryDelay.waitForRetry();\n } else {\n throw err;\n }\n }\n }\n }\n}\n\n\nimport {Clock as MockClock} from \"../../mocks/Clock.js\";\nimport {ExponentialRetryDelay as _ExponentialRetryDelay} from \"./ExponentialRetryDelay\";\nimport {ConnectionError} from \"../error.js\"\n\nexport function tests() {\n function createHsApiMock(remainingFailures) {\n return {\n versions() {\n return {\n response() {\n if (remainingFailures) {\n remainingFailures -= 1;\n return Promise.reject(new ConnectionError());\n } else {\n return Promise.resolve(42);\n }\n }\n };\n }\n }\n }\n\n return {\n \"test reconnecting with 1 failure\": async assert => {\n const clock = new MockClock();\n const {createMeasure} = clock;\n const onlineStatus = new ObservableValue(false);\n const retryDelay = new _ExponentialRetryDelay(clock.createTimeout);\n const reconnector = new Reconnector({retryDelay, onlineStatus, createMeasure});\n const {connectionStatus} = reconnector;\n const statuses: ConnectionStatus[] = [];\n const subscription = reconnector.connectionStatus.subscribe(s => {\n statuses.push(s);\n });\n // @ts-ignore\n reconnector.onRequestFailed(createHsApiMock(1));\n await connectionStatus.waitFor(s => s === ConnectionStatus.Waiting).promise;\n clock.elapse(2000);\n await connectionStatus.waitFor(s => s === ConnectionStatus.Online).promise;\n assert.deepEqual(statuses, [\n ConnectionStatus.Reconnecting,\n ConnectionStatus.Waiting,\n ConnectionStatus.Reconnecting,\n ConnectionStatus.Online\n ]);\n assert.strictEqual(reconnector.lastVersionsResponse, 42);\n subscription();\n },\n \"test reconnecting with onlineStatus\": async assert => {\n const clock = new MockClock();\n const {createMeasure} = clock;\n const onlineStatus = new ObservableValue(false);\n const retryDelay = new _ExponentialRetryDelay(clock.createTimeout);\n const reconnector = new Reconnector({retryDelay, onlineStatus, createMeasure});\n const {connectionStatus} = reconnector;\n // @ts-ignore\n reconnector.onRequestFailed(createHsApiMock(1));\n await connectionStatus.waitFor(s => s === ConnectionStatus.Waiting).promise;\n onlineStatus.set(true); //skip waiting\n await connectionStatus.waitFor(s => s === ConnectionStatus.Online).promise;\n assert.equal(connectionStatus.get(), ConnectionStatus.Online);\n assert.strictEqual(reconnector.lastVersionsResponse, 42);\n },\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * Decrypt an attachment.\n * @param {ArrayBuffer} ciphertextBuffer The encrypted attachment data buffer.\n * @param {Object} info The information needed to decrypt the attachment.\n * @param {Object} info.key AES-CTR JWK key object.\n * @param {string} info.iv Base64 encoded 16 byte AES-CTR IV.\n * @param {string} info.hashes.sha256 Base64 encoded SHA-256 hash of the ciphertext.\n * @return {Promise} A promise that resolves with an ArrayBuffer when the attachment is decrypted.\n */\nexport async function decryptAttachment(platform, ciphertextBuffer, info) {\n if (info === undefined || info.key === undefined || info.iv === undefined\n || info.hashes === undefined || info.hashes.sha256 === undefined) {\n throw new Error(\"Invalid info. Missing info.key, info.iv or info.hashes.sha256 key\");\n }\n\n const {crypto} = platform;\n const {base64} = platform.encoding;\n var ivArray = base64.decode(info.iv);\n // re-encode to not deal with padded vs unpadded\n var expectedSha256base64 = base64.encode(base64.decode(info.hashes.sha256));\n // Check the sha256 hash\n const digestResult = await crypto.digest(\"SHA-256\", ciphertextBuffer);\n if (base64.encode(new Uint8Array(digestResult)) != expectedSha256base64) {\n throw new Error(\"Mismatched SHA-256 digest\");\n }\n var counterLength;\n if (info.v == \"v1\" || info.v == \"v2\") {\n // Version 1 and 2 use a 64 bit counter.\n counterLength = 64;\n } else {\n // Version 0 uses a 128 bit counter.\n counterLength = 128;\n }\n\n const decryptedBuffer = await crypto.aes.decryptCTR({\n jwkKey: info.key,\n iv: ivArray,\n data: ciphertextBuffer,\n counterLength\n });\n return decryptedBuffer;\n}\n\nexport async function encryptAttachment(platform, blob) {\n const {crypto} = platform;\n const {base64} = platform.encoding;\n const iv = await crypto.aes.generateIV();\n const key = await crypto.aes.generateKey(\"jwk\", 256);\n const buffer = await blob.readAsBuffer();\n const ciphertext = await crypto.aes.encryptCTR({jwkKey: key, iv, data: buffer});\n const digest = await crypto.digest(\"SHA-256\", ciphertext);\n return {\n blob: platform.createBlob(ciphertext, 'application/octet-stream'),\n info: {\n v: \"v2\",\n key,\n iv: base64.encodeUnpadded(iv),\n hashes: {\n sha256: base64.encodeUnpadded(digest)\n }\n }\n };\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {encodeQueryParams} from \"./common\";\nimport {decryptAttachment} from \"../e2ee/attachment.js\";\nimport {Platform} from \"../../platform/web/Platform.js\";\nimport {BlobHandle} from \"../../platform/web/dom/BlobHandle.js\";\nimport type {Attachment, EncryptedFile} from \"./types/response\";\n\nexport class MediaRepository {\n private readonly _homeserver: string;\n private readonly _platform: Platform;\n\n constructor({homeserver, platform}: {homeserver:string, platform: Platform}) {\n this._homeserver = homeserver;\n this._platform = platform;\n }\n\n mxcUrlThumbnail(url: string, width: number, height: number, method: \"crop\" | \"scale\"): string | null {\n const parts = this._parseMxcUrl(url);\n if (parts) {\n const [serverName, mediaId] = parts;\n const httpUrl = `${this._homeserver}/_matrix/media/r0/thumbnail/${encodeURIComponent(serverName)}/${encodeURIComponent(mediaId)}`;\n return httpUrl + \"?\" + encodeQueryParams({width: Math.round(width), height: Math.round(height), method});\n }\n return null;\n }\n\n mxcUrl(url: string): string | null {\n const parts = this._parseMxcUrl(url);\n if (parts) {\n const [serverName, mediaId] = parts;\n return `${this._homeserver}/_matrix/media/r0/download/${encodeURIComponent(serverName)}/${encodeURIComponent(mediaId)}`;\n } else {\n return null;\n }\n }\n\n private _parseMxcUrl(url: string): string[] | null {\n const prefix = \"mxc://\";\n if (url.startsWith(prefix)) {\n return url.substr(prefix.length).split(\"/\", 2);\n } else {\n return null;\n }\n }\n\n async downloadEncryptedFile(fileEntry: EncryptedFile, cache: boolean = false): Promise<BlobHandle> {\n const url = this.mxcUrl(fileEntry.url);\n const {body: encryptedBuffer} = await this._platform.request(url, {method: \"GET\", format: \"buffer\", cache}).response();\n const decryptedBuffer = await decryptAttachment(this._platform, encryptedBuffer, fileEntry);\n return this._platform.createBlob(decryptedBuffer, fileEntry.mimetype);\n }\n\n async downloadPlaintextFile(mxcUrl: string, mimetype: string, cache: boolean = false): Promise<BlobHandle> {\n const url = this.mxcUrl(mxcUrl);\n const {body: buffer} = await this._platform.request(url, {method: \"GET\", format: \"buffer\", cache}).response();\n return this._platform.createBlob(buffer, mimetype);\n }\n\n async downloadAttachment(content: Attachment, cache: boolean = false): Promise<BlobHandle> {\n if (content.file) {\n return this.downloadEncryptedFile(content.file, cache);\n } else {\n return this.downloadPlaintextFile(content.url!, content.info?.mimetype, cache);\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {AbortError} from \"../../utils/error\";\nimport {HomeServerError} from \"../error.js\";\nimport {HomeServerApi} from \"./HomeServerApi\";\nimport {ExponentialRetryDelay} from \"./ExponentialRetryDelay\";\nimport {Clock} from \"../../platform/web/dom/Clock.js\";\nimport type {IHomeServerRequest} from \"./HomeServerRequest.js\";\n\nclass Request implements IHomeServerRequest {\n public readonly methodName: string;\n public readonly args: any[];\n private responseResolve: (result: any) => void;\n public responseReject: (error: Error) => void;\n private responseCodeResolve?: (result: any) => void;\n private responseCodeReject?: (result: any) => void;\n private _requestResult?: IHomeServerRequest;\n private readonly _responsePromise: Promise<any>;\n private _responseCodePromise: Promise<any>;\n\n constructor(methodName: string, args: any[]) {\n this.methodName = methodName;\n this.args = args;\n this._responsePromise = new Promise((resolve, reject) => {\n this.responseResolve = resolve;\n this.responseReject = reject;\n });\n }\n\n abort(): void {\n if (this._requestResult) {\n this._requestResult.abort();\n } else {\n this.responseReject(new AbortError());\n this.responseCodeReject?.(new AbortError());\n }\n }\n\n response(): Promise<any> {\n return this._responsePromise;\n }\n\n responseCode(): Promise<number> {\n if (this.requestResult) {\n return this.requestResult.responseCode();\n }\n if (!this._responseCodePromise) {\n this._responseCodePromise = new Promise((resolve, reject) => {\n this.responseCodeResolve = resolve;\n this.responseCodeReject = reject;\n });\n }\n return this._responseCodePromise;\n }\n\n async setRequestResult(result) {\n this._requestResult = result;\n const response = await this._requestResult?.response();\n this.responseResolve(response);\n const responseCode = await this._requestResult?.responseCode();\n this.responseCodeResolve?.(responseCode);\n }\n\n get requestResult() {\n return this._requestResult;\n }\n}\n\nclass HomeServerApiWrapper {\n private readonly _scheduler: RequestScheduler;\n\n constructor(scheduler: RequestScheduler) {\n this._scheduler = scheduler;\n }\n}\n\n// add request-wrapping methods to prototype\nfor (const methodName of Object.getOwnPropertyNames(HomeServerApi.prototype)) {\n if (methodName !== \"constructor\" && !methodName.startsWith(\"_\")) {\n HomeServerApiWrapper.prototype[methodName] = function(...args) {\n return this._scheduler._hsApiRequest(methodName, args);\n };\n }\n}\n\nexport class RequestScheduler {\n private readonly _hsApi: HomeServerApi;\n private readonly _clock: Clock;\n private readonly _requests: Set<Request> = new Set();\n private _stopped = false;\n private _wrapper = new HomeServerApiWrapper(this);\n\n constructor({ hsApi, clock }: { hsApi: HomeServerApi; clock: Clock }) {\n this._hsApi = hsApi;\n this._clock = clock;\n }\n\n get hsApi(): HomeServerApi {\n return this._wrapper as unknown as HomeServerApi;\n }\n\n stop(): void {\n this._stopped = true;\n for (const request of this._requests) {\n request.abort();\n }\n this._requests.clear();\n }\n\n start(): void {\n this._stopped = false;\n }\n\n private _hsApiRequest(name: string, args: any[]): Request {\n const request = new Request(name, args);\n this._doSend(request);\n return request;\n }\n\n private async _doSend(request: Request): Promise<void> {\n this._requests.add(request);\n try {\n let retryDelay: ExponentialRetryDelay | undefined;\n while (!this._stopped) {\n try {\n const requestResult = this._hsApi[\n request.methodName\n ].apply(this._hsApi, request.args);\n // so the request can be aborted\n await request.setRequestResult(requestResult);\n return;\n } catch (err) {\n if (\n err instanceof HomeServerError &&\n err.errcode === \"M_LIMIT_EXCEEDED\"\n ) {\n if (Number.isSafeInteger(err.retry_after_ms)) {\n await this._clock\n .createTimeout(err.retry_after_ms)\n .elapsed();\n } else {\n if (!retryDelay) {\n retryDelay = new ExponentialRetryDelay(\n this._clock.createTimeout\n );\n }\n await retryDelay.waitForRetry();\n }\n } else {\n request.responseReject(err);\n return;\n }\n }\n }\n if (this._stopped) {\n request.abort();\n }\n } finally {\n this._requests.delete(request);\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020, 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ObservableValue} from \"../observable/ObservableValue\";\nimport {createEnum} from \"../utils/enum\";\n\nconst INCREMENTAL_TIMEOUT = 30000;\n\nexport const SyncStatus = createEnum(\n \"InitialSync\",\n \"CatchupSync\",\n \"Syncing\",\n \"Stopped\"\n);\n\nfunction timelineIsEmpty(roomResponse) {\n try {\n const events = roomResponse?.timeline?.events;\n return Array.isArray(events) && events.length === 0;\n } catch (err) {\n return true;\n }\n}\n\n/**\n * Sync steps in js-pseudocode:\n * ```js\n * // can only read some stores\n * const preparation = await room.prepareSync(roomResponse, membership, newRoomKeys, prepareTxn);\n * // can do async work that is not related to storage (such as decryption)\n * await room.afterPrepareSync(preparation);\n * // writes and calculates changes\n * const changes = await room.writeSync(roomResponse, isInitialSync, preparation, syncTxn);\n * // applies and emits changes once syncTxn is committed\n * room.afterSync(changes);\n * if (room.needsAfterSyncCompleted(changes)) {\n * // can do network requests\n * await room.afterSyncCompleted(changes);\n * }\n * ```\n */\nexport class Sync {\n constructor({hsApi, session, storage, logger}) {\n this._hsApi = hsApi;\n this._logger = logger;\n this._session = session;\n this._storage = storage;\n this._currentRequest = null;\n this._status = new ObservableValue(SyncStatus.Stopped);\n this._error = null;\n }\n\n get status() {\n return this._status;\n }\n\n /** the error that made the sync stop */\n get error() {\n return this._error;\n }\n\n start() {\n // not already syncing?\n if (this._status.get() !== SyncStatus.Stopped) {\n return;\n }\n this._error = null;\n let syncToken = this._session.syncToken;\n if (syncToken) {\n this._status.set(SyncStatus.CatchupSync);\n } else {\n this._status.set(SyncStatus.InitialSync);\n }\n this._syncLoop(syncToken);\n }\n\n async _syncLoop(syncToken) {\n // if syncToken is falsy, it will first do an initial sync ... \n while(this._status.get() !== SyncStatus.Stopped) {\n let roomStates;\n let sessionChanges;\n let wasCatchupOrInitial = this._status.get() === SyncStatus.CatchupSync || this._status.get() === SyncStatus.InitialSync;\n await this._logger.run(\"sync\", async log => {\n log.set(\"token\", syncToken);\n log.set(\"status\", this._status.get());\n try {\n // unless we are happily syncing already, we want the server to return\n // as quickly as possible, even if there are no events queued. This\n // serves two purposes:\n //\n // * When the connection dies, we want to know asap when it comes back,\n // so that we can hide the error from the user. (We don't want to\n // have to wait for an event or a timeout).\n //\n // * We want to know if the server has any to_device messages queued up\n // for us. We do that by calling it with a zero timeout until it\n // doesn't give us any more to_device messages.\n const timeout = this._status.get() === SyncStatus.Syncing ? INCREMENTAL_TIMEOUT : 0; \n const syncResult = await this._syncRequest(syncToken, timeout, log);\n syncToken = syncResult.syncToken;\n roomStates = syncResult.roomStates;\n sessionChanges = syncResult.sessionChanges;\n // initial sync or catchup sync\n if (this._status.get() !== SyncStatus.Syncing && syncResult.hadToDeviceMessages) {\n this._status.set(SyncStatus.CatchupSync);\n } else {\n this._status.set(SyncStatus.Syncing);\n }\n } catch (err) {\n // retry same request on timeout\n if (err.name === \"ConnectionError\" && err.isTimeout) {\n // don't run afterSyncCompleted\n return;\n }\n this._error = err;\n if (err.name !== \"AbortError\") {\n // sync wasn't asked to stop, but is stopping\n // because of the error.\n log.error = err;\n log.logLevel = log.level.Fatal;\n }\n log.set(\"stopping\", true);\n this._status.set(SyncStatus.Stopped);\n }\n if (this._status.get() !== SyncStatus.Stopped) {\n // TODO: if we're not going to run this phase in parallel with the next\n // sync request (because this causes OTKs to be uploaded twice)\n // should we move this inside _syncRequest?\n // Alternatively, we can try to fix the OTK upload issue while still\n // running in parallel.\n await log.wrap(\"afterSyncCompleted\", log => this._runAfterSyncCompleted(sessionChanges, roomStates, log));\n }\n },\n this._logger.level.Info,\n (filter, log) => {\n if (log.durationWithoutType(\"network\") >= 2000 || log.error || wasCatchupOrInitial) {\n return filter.minLevel(log.level.Detail);\n } else {\n return filter.minLevel(log.level.Info);\n }\n });\n }\n }\n\n async _runAfterSyncCompleted(sessionChanges, roomStates, log) {\n const isCatchupSync = this._status.get() === SyncStatus.CatchupSync;\n const sessionPromise = (async () => {\n try {\n await log.wrap(\"session\", log => this._session.afterSyncCompleted(sessionChanges, isCatchupSync, log), log.level.Detail);\n } catch (err) {} // error is logged, but don't fail sessionPromise\n })();\n\n const roomsNeedingAfterSyncCompleted = roomStates.filter(rs => {\n return rs.room.needsAfterSyncCompleted(rs.changes);\n });\n const roomsPromises = roomsNeedingAfterSyncCompleted.map(async rs => {\n try {\n await log.wrap(\"room\", log => rs.room.afterSyncCompleted(rs.changes, log), log.level.Detail);\n } catch (err) {} // error is logged, but don't fail roomsPromises\n });\n // run everything in parallel,\n // we don't want to delay the next sync too much\n // Also, since all promises won't reject (as they have a try/catch)\n // it's fine to use Promise.all\n await Promise.all(roomsPromises.concat(sessionPromise));\n }\n\n async _syncRequest(syncToken, timeout, log) {\n let {syncFilterId} = this._session;\n if (typeof syncFilterId !== \"string\") {\n this._currentRequest = this._hsApi.createFilter(this._session.user.id, {room: {state: {lazy_load_members: true}}}, {log});\n syncFilterId = (await this._currentRequest.response()).filter_id;\n }\n const totalRequestTimeout = timeout + (80 * 1000); // same as riot-web, don't get stuck on wedged long requests\n this._currentRequest = this._hsApi.sync(syncToken, syncFilterId, timeout, {timeout: totalRequestTimeout, log});\n const response = await this._currentRequest.response();\n\n const isInitialSync = !syncToken;\n const sessionState = new SessionSyncProcessState();\n const inviteStates = this._parseInvites(response.rooms);\n const {roomStates, archivedRoomStates} = await this._parseRoomsResponse(\n response.rooms, inviteStates, isInitialSync, log);\n\n try {\n // take a lock on olm sessions used in this sync so sending a message doesn't change them while syncing\n sessionState.lock = await log.wrap(\"obtainSyncLock\", () => this._session.obtainSyncLock(response));\n await log.wrap(\"prepare\", log => this._prepareSync(sessionState, roomStates, response, log));\n await log.wrap(\"afterPrepareSync\", log => Promise.all(roomStates.map(rs => {\n return rs.room.afterPrepareSync(rs.preparation, log);\n })));\n await log.wrap(\"write\", async log => this._writeSync(\n sessionState, inviteStates, roomStates, archivedRoomStates,\n response, syncFilterId, isInitialSync, log));\n } finally {\n sessionState.dispose();\n }\n // sync txn comitted, emit updates and apply changes to in-memory state\n log.wrap(\"after\", log => this._afterSync(\n sessionState, inviteStates, roomStates, archivedRoomStates, log));\n\n const toDeviceEvents = response.to_device?.events;\n return {\n syncToken: response.next_batch,\n roomStates,\n sessionChanges: sessionState.changes,\n hadToDeviceMessages: Array.isArray(toDeviceEvents) && toDeviceEvents.length > 0,\n };\n }\n\n _openPrepareSyncTxn() {\n const storeNames = this._storage.storeNames;\n return this._storage.readTxn([\n storeNames.olmSessions,\n storeNames.inboundGroupSessions,\n // to read fragments when loading sync writer when rejoining archived room\n storeNames.timelineFragments,\n // to read fragments when loading sync writer when rejoining archived room\n // to read events that can now be decrypted\n storeNames.timelineEvents,\n ]);\n }\n\n async _prepareSync(sessionState, roomStates, response, log) {\n const prepareTxn = await this._openPrepareSyncTxn();\n sessionState.preparation = await log.wrap(\"session\", log => this._session.prepareSync(\n response, sessionState.lock, prepareTxn, log));\n\n const newKeysByRoom = sessionState.preparation?.newKeysByRoom;\n\n // add any rooms with new keys but no sync response to the list of rooms to be synced\n if (newKeysByRoom) {\n const {hasOwnProperty} = Object.prototype;\n for (const roomId of newKeysByRoom.keys()) {\n const isRoomInResponse = response.rooms?.join && hasOwnProperty.call(response.rooms.join, roomId);\n if (!isRoomInResponse) {\n let room = this._session.rooms.get(roomId);\n if (room) {\n roomStates.push(new RoomSyncProcessState(room, false, {}, room.membership));\n }\n }\n }\n }\n \n await Promise.all(roomStates.map(async rs => {\n const newKeys = newKeysByRoom?.get(rs.room.id);\n rs.preparation = await log.wrap(\"room\", async log => {\n // if previously joined and we still have the timeline for it,\n // this loads the syncWriter at the correct position to continue writing the timeline\n if (rs.isNewRoom) {\n await rs.room.load(null, prepareTxn, log);\n }\n return rs.room.prepareSync(\n rs.roomResponse, rs.membership, newKeys, prepareTxn, log)\n }, log.level.Detail);\n }));\n\n // This is needed for safari to not throw TransactionInactiveErrors on the syncTxn. See docs/INDEXEDDB.md\n await prepareTxn.complete();\n }\n\n async _writeSync(sessionState, inviteStates, roomStates, archivedRoomStates, response, syncFilterId, isInitialSync, log) {\n const syncTxn = await this._openSyncTxn();\n try {\n sessionState.changes = await log.wrap(\"session\", log => this._session.writeSync(\n response, syncFilterId, sessionState.preparation, syncTxn, log));\n await Promise.all(inviteStates.map(async is => {\n is.changes = await log.wrap(\"invite\", log => is.invite.writeSync(\n is.membership, is.roomResponse, syncTxn, log));\n }));\n await Promise.all(roomStates.map(async rs => {\n rs.changes = await log.wrap(\"room\", log => rs.room.writeSync(\n rs.roomResponse, isInitialSync, rs.preparation, syncTxn, log));\n }));\n // important to do this after roomStates,\n // as we're referring to the roomState to get the summaryChanges\n await Promise.all(archivedRoomStates.map(async ars => {\n const summaryChanges = ars.roomState?.summaryChanges;\n ars.changes = await log.wrap(\"archivedRoom\", log => ars.archivedRoom.writeSync(\n summaryChanges, ars.roomResponse, ars.membership, syncTxn, log));\n }));\n } catch(err) {\n // avoid corrupting state by only\n // storing the sync up till the point\n // the exception occurred\n syncTxn.abort(log);\n throw syncTxn.getCause(err);\n }\n await syncTxn.complete(log);\n }\n\n _afterSync(sessionState, inviteStates, roomStates, archivedRoomStates, log) {\n log.wrap(\"session\", log => this._session.afterSync(sessionState.changes, log), log.level.Detail);\n for(let ars of archivedRoomStates) {\n log.wrap(\"archivedRoom\", log => {\n ars.archivedRoom.afterSync(ars.changes, log);\n ars.archivedRoom.release();\n }, log.level.Detail);\n }\n for(let rs of roomStates) {\n log.wrap(\"room\", log => rs.room.afterSync(rs.changes, log), log.level.Detail);\n }\n for(let is of inviteStates) {\n log.wrap(\"invite\", log => is.invite.afterSync(is.changes, log), log.level.Detail);\n }\n this._session.applyRoomCollectionChangesAfterSync(inviteStates, roomStates, archivedRoomStates, log);\n }\n\n _openSyncTxn() {\n const storeNames = this._storage.storeNames;\n return this._storage.readWriteTxn([\n storeNames.session,\n storeNames.roomSummary,\n storeNames.archivedRoomSummary,\n storeNames.invites,\n storeNames.roomState,\n storeNames.roomMembers,\n storeNames.timelineEvents,\n storeNames.timelineRelations,\n storeNames.timelineFragments,\n storeNames.pendingEvents,\n storeNames.userIdentities,\n storeNames.groupSessionDecryptions,\n storeNames.deviceIdentities,\n // to discard outbound session when somebody leaves a room\n // and to create room key messages when somebody joins\n storeNames.outboundGroupSessions,\n storeNames.operations,\n storeNames.accountData,\n // to decrypt and store new room keys\n storeNames.olmSessions,\n storeNames.inboundGroupSessions,\n ]);\n }\n \n async _parseRoomsResponse(roomsSection, inviteStates, isInitialSync, log) {\n const roomStates = [];\n const archivedRoomStates = [];\n if (roomsSection) {\n const allMemberships = [\"join\", \"leave\"];\n for(const membership of allMemberships) {\n const membershipSection = roomsSection[membership];\n if (membershipSection) {\n for (const [roomId, roomResponse] of Object.entries(membershipSection)) {\n // ignore rooms with empty timelines during initial sync,\n // see https://github.com/vector-im/hydrogen-web/issues/15\n if (isInitialSync && timelineIsEmpty(roomResponse)) {\n continue;\n }\n const invite = this._session.invites.get(roomId);\n // if there is an existing invite, add a process state for it\n // so its writeSync and afterSync will run and remove the invite\n if (invite) {\n inviteStates.push(new InviteSyncProcessState(invite, false, null, membership));\n }\n const roomState = this._createRoomSyncState(roomId, roomResponse, membership, isInitialSync);\n if (roomState) {\n roomStates.push(roomState);\n }\n const ars = await this._createArchivedRoomSyncState(roomId, roomState, roomResponse, membership, isInitialSync, log);\n if (ars) {\n archivedRoomStates.push(ars);\n }\n }\n }\n }\n }\n return {roomStates, archivedRoomStates};\n }\n\n _createRoomSyncState(roomId, roomResponse, membership, isInitialSync) {\n let isNewRoom = false;\n let room = this._session.rooms.get(roomId);\n // create room only either on new join,\n // or for an archived room during initial sync,\n // where we create the summaryChanges with a joined\n // room to then adopt by the archived room.\n // This way the limited timeline, members, ...\n // we receive also gets written.\n // In any case, don't create a room for a rejected invite\n if (!room && (membership === \"join\" || (isInitialSync && membership === \"leave\"))) {\n room = this._session.createJoinedRoom(roomId);\n isNewRoom = true;\n }\n if (room) {\n return new RoomSyncProcessState(\n room, isNewRoom, roomResponse, membership);\n }\n }\n\n async _createArchivedRoomSyncState(roomId, roomState, roomResponse, membership, isInitialSync, log) {\n let archivedRoom;\n if (roomState?.shouldAdd && !isInitialSync) {\n // when adding a joined room during incremental sync,\n // always create the archived room to write the removal\n // of the archived summary\n archivedRoom = this._session.createOrGetArchivedRoomForSync(roomId);\n } else if (membership === \"leave\") {\n if (roomState) {\n // we still have a roomState, so we just left it\n // in this case, create a new archivedRoom\n archivedRoom = this._session.createOrGetArchivedRoomForSync(roomId);\n } else {\n // this is an update of an already left room, restore\n // it from storage first, so we can increment it.\n // this happens for example when our membership changes\n // after leaving (e.g. being (un)banned, possibly after being kicked), etc\n archivedRoom = await this._session.loadArchivedRoom(roomId, log);\n }\n }\n if (archivedRoom) {\n return new ArchivedRoomSyncProcessState(\n archivedRoom, roomState, roomResponse, membership);\n }\n }\n\n _parseInvites(roomsSection) {\n const inviteStates = [];\n if (roomsSection?.invite) {\n for (const [roomId, roomResponse] of Object.entries(roomsSection.invite)) {\n let invite = this._session.invites.get(roomId);\n let isNewInvite = false;\n if (!invite) {\n invite = this._session.createInvite(roomId);\n isNewInvite = true;\n }\n inviteStates.push(new InviteSyncProcessState(invite, isNewInvite, roomResponse, \"invite\"));\n }\n }\n return inviteStates;\n }\n\n stop() {\n if (this._status.get() === SyncStatus.Stopped) {\n return;\n }\n this._status.set(SyncStatus.Stopped);\n if (this._currentRequest) {\n this._currentRequest.abort();\n this._currentRequest = null;\n }\n }\n}\n\nclass SessionSyncProcessState {\n constructor() {\n this.lock = null;\n this.preparation = null;\n this.changes = null;\n }\n\n dispose() {\n this.lock?.release();\n }\n}\n\nclass RoomSyncProcessState {\n constructor(room, isNewRoom, roomResponse, membership) {\n this.room = room;\n this.isNewRoom = isNewRoom;\n this.roomResponse = roomResponse;\n this.membership = membership;\n this.preparation = null;\n this.changes = null;\n }\n\n get id() {\n return this.room.id;\n }\n\n get shouldAdd() {\n return this.isNewRoom && this.membership === \"join\";\n }\n\n get shouldRemove() {\n return !this.isNewRoom && this.membership !== \"join\";\n }\n\n get summaryChanges() {\n return this.changes?.summaryChanges;\n }\n}\n\n\nclass ArchivedRoomSyncProcessState {\n constructor(archivedRoom, roomState, roomResponse, membership, isInitialSync) {\n this.archivedRoom = archivedRoom;\n this.roomState = roomState;\n this.roomResponse = roomResponse;\n this.membership = membership;\n this.isInitialSync = isInitialSync;\n this.changes = null;\n }\n\n get id() {\n return this.archivedRoom.id;\n }\n\n get shouldAdd() {\n return (this.roomState || this.isInitialSync) && this.membership === \"leave\";\n }\n\n get shouldRemove() {\n return this.membership === \"join\";\n }\n}\n\nclass InviteSyncProcessState {\n constructor(invite, isNewInvite, roomResponse, membership) {\n this.invite = invite;\n this.isNewInvite = isNewInvite;\n this.membership = membership;\n this.roomResponse = roomResponse;\n this.changes = null;\n }\n\n get id() {\n return this.invite.id;\n }\n\n get shouldAdd() {\n return this.isNewInvite;\n }\n\n get shouldRemove() {\n return this.membership !== \"invite\";\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2021 Daniel Fedorin <danila.fedorin@gmail.com>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\ntype Handler<T> = (value?: T) => void;\n\nexport class EventEmitter<T> {\n private _handlersByName: { [event in keyof T]?: Set<Handler<T[event]>> }\n\n constructor() {\n this._handlersByName = {};\n }\n\n emit<K extends keyof T>(name: K, value?: T[K]): void {\n const handlers = this._handlersByName[name];\n if (handlers) {\n handlers.forEach(h => h(value));\n }\n }\n\n disposableOn<K extends keyof T>(name: K, callback: Handler<T[K]>): () => void {\n this.on(name, callback);\n return () => {\n this.off(name, callback);\n }\n }\n\n on<K extends keyof T>(name: K, callback: Handler<T[K]>): void {\n let handlers = this._handlersByName[name];\n if (!handlers) {\n this.onFirstSubscriptionAdded(name);\n this._handlersByName[name] = handlers = new Set();\n }\n handlers.add(callback);\n }\n\n off<K extends keyof T>(name: K, callback: Handler<T[K]>): void {\n const handlers = this._handlersByName[name];\n if (handlers) {\n handlers.delete(callback);\n if (handlers.size === 0) {\n delete this._handlersByName[name];\n this.onLastSubscriptionRemoved(name);\n }\n }\n }\n\n onFirstSubscriptionAdded<K extends keyof T>(name: K): void {}\n\n onLastSubscriptionRemoved<K extends keyof T>(name: K): void {}\n}\n\nexport function tests() {\n return {\n test_on_off(assert) {\n let counter = 0;\n const e = new EventEmitter<{ change: never }>();\n const callback = () => counter += 1;\n e.on(\"change\", callback);\n e.emit(\"change\");\n e.off(\"change\", callback);\n e.emit(\"change\");\n assert.equal(counter, 1);\n },\n\n test_emit_value(assert) {\n let value = 0;\n const e = new EventEmitter<{ change: number }>();\n const callback = (v) => value = v;\n e.on(\"change\", callback);\n e.emit(\"change\", 5);\n e.off(\"change\", callback);\n assert.equal(value, 5);\n },\n\n test_double_on(assert) {\n let counter = 0;\n const e = new EventEmitter<{ change: never }>();\n const callback = () => counter += 1;\n e.on(\"change\", callback);\n e.on(\"change\", callback);\n e.emit(\"change\");\n e.off(\"change\", callback);\n assert.equal(counter, 1);\n }\n };\n}\n","/* Copyright 2015 Mark Haines\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nvar escaped = /[\\\\\\\"\\x00-\\x1F]/g;\nvar escapes = {};\nfor (var i = 0; i < 0x20; ++i) {\n escapes[String.fromCharCode(i)] = (\n '\\\\U' + ('0000' + i.toString(16)).slice(-4).toUpperCase()\n );\n}\nescapes['\\b'] = '\\\\b';\nescapes['\\t'] = '\\\\t';\nescapes['\\n'] = '\\\\n';\nescapes['\\f'] = '\\\\f';\nescapes['\\r'] = '\\\\r';\nescapes['\\\"'] = '\\\\\\\"';\nescapes['\\\\'] = '\\\\\\\\';\n\nfunction escapeString(value) {\n escaped.lastIndex = 0;\n return value.replace(escaped, function(c) { return escapes[c]; });\n}\n\nfunction stringify(value) {\n switch (typeof value) {\n case 'string':\n return '\"' + escapeString(value) + '\"';\n case 'number':\n return isFinite(value) ? value : 'null';\n case 'boolean':\n return value;\n case 'object':\n if (value === null) {\n return 'null';\n }\n if (Array.isArray(value)) {\n return stringifyArray(value);\n }\n return stringifyObject(value);\n default:\n throw new Error('Cannot stringify: ' + typeof value);\n }\n}\n\nfunction stringifyArray(array) {\n var sep = '[';\n var result = '';\n for (var i = 0; i < array.length; ++i) {\n result += sep;\n sep = ',';\n result += stringify(array[i]);\n }\n if (sep != ',') {\n return '[]';\n } else {\n return result + ']';\n }\n}\n\nfunction stringifyObject(object) {\n var sep = '{';\n var result = '';\n var keys = Object.keys(object);\n keys.sort();\n for (var i = 0; i < keys.length; ++i) {\n var key = keys[i];\n result += sep + '\"' + escapeString(key) + '\":';\n sep = ',';\n result += stringify(object[key]);\n }\n if (sep != ',') {\n return '{}';\n } else {\n return result + '}';\n }\n}\n\n/** */\nmodule.exports = {stringify: stringify};\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport anotherjson from \"another-json\";\nimport {createEnum} from \"../../utils/enum\";\n\nexport const DecryptionSource = createEnum(\"Sync\", \"Timeline\", \"Retry\");\n\n// use common prefix so it's easy to clear properties that are not e2ee related during session clear\nexport const SESSION_E2EE_KEY_PREFIX = \"e2ee:\";\nexport const OLM_ALGORITHM = \"m.olm.v1.curve25519-aes-sha2\";\nexport const MEGOLM_ALGORITHM = \"m.megolm.v1.aes-sha2\";\n\nexport class DecryptionError extends Error {\n constructor(code, event, detailsObj = null) {\n super(`Decryption error ${code}${detailsObj ? \": \"+JSON.stringify(detailsObj) : \"\"}`);\n this.code = code;\n this.event = event;\n this.details = detailsObj;\n }\n}\n\nexport const SIGNATURE_ALGORITHM = \"ed25519\";\n\nexport function verifyEd25519Signature(olmUtil, userId, deviceOrKeyId, ed25519Key, value, log = undefined) {\n const clone = Object.assign({}, value);\n delete clone.unsigned;\n delete clone.signatures;\n const canonicalJson = anotherjson.stringify(clone);\n const signature = value?.signatures?.[userId]?.[`${SIGNATURE_ALGORITHM}:${deviceOrKeyId}`];\n try {\n if (!signature) {\n throw new Error(\"no signature\");\n }\n // throws when signature is invalid\n olmUtil.ed25519_verify(ed25519Key, canonicalJson, signature);\n return true;\n } catch (err) {\n if (log) {\n const logItem = log.log({l: \"Invalid signature, ignoring.\", ed25519Key, canonicalJson, signature});\n logItem.error = err;\n logItem.logLevel = log.level.Warn;\n }\n return false;\n }\n}\n\nexport function createRoomEncryptionEvent() {\n return {\n \"type\": \"m.room.encryption\",\n \"state_key\": \"\",\n \"content\": {\n \"algorithm\": MEGOLM_ALGORITHM,\n \"rotation_period_ms\": 604800000,\n \"rotation_period_msgs\": 100\n }\n }\n}\n\n\n// Use enum when converting to TS\nexport const HistoryVisibility = Object.freeze({\n Joined: \"joined\",\n Invited: \"invited\",\n WorldReadable: \"world_readable\",\n Shared: \"shared\",\n});\n\nexport function shouldShareKey(membership, historyVisibility) {\n switch (historyVisibility) {\n case HistoryVisibility.WorldReadable:\n return true;\n case HistoryVisibility.Shared:\n // was part of room at some time\n return membership !== undefined;\n case HistoryVisibility.Joined:\n return membership === \"join\";\n case HistoryVisibility.Invited:\n return membership === \"invite\" || membership === \"join\";\n default:\n return false;\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {MEGOLM_ALGORITHM} from \"../e2ee/common.js\";\n\n\nfunction applyTimelineEntries(data, timelineEntries, isInitialSync, canMarkUnread, ownUserId) {\n if (timelineEntries.length) {\n data = timelineEntries.reduce((data, entry) => {\n return processTimelineEvent(data, entry,\n isInitialSync, canMarkUnread, ownUserId);\n }, data);\n }\n return data;\n}\n\nexport function reduceStateEvents(roomResponse, callback, value) {\n const stateEvents = roomResponse?.state?.events;\n // state comes before timeline\n if (Array.isArray(stateEvents)) {\n value = stateEvents.reduce(callback, value);\n }\n const timelineEvents = roomResponse?.timeline?.events;\n // and after that state events in the timeline\n if (Array.isArray(timelineEvents)) {\n value = timelineEvents.reduce((data, event) => {\n if (typeof event.state_key === \"string\") {\n value = callback(value, event);\n }\n return value;\n }, value);\n }\n return value;\n}\n\nfunction applySyncResponse(data, roomResponse, membership, ownUserId) {\n if (roomResponse.summary) {\n data = updateSummary(data, roomResponse.summary);\n }\n if (membership !== data.membership) {\n data = data.cloneIfNeeded();\n data.membership = membership;\n }\n if (roomResponse.account_data) {\n data = roomResponse.account_data.events.reduce(processRoomAccountData, data);\n }\n // process state events in state and in timeline.\n // non-state events are handled by applyTimelineEntries\n // so decryption is handled properly\n data = reduceStateEvents(roomResponse, (data, event) => processStateEvent(data, event, ownUserId), data);\n const unreadNotifications = roomResponse.unread_notifications;\n if (unreadNotifications) {\n data = processNotificationCounts(data, unreadNotifications);\n }\n\n return data;\n}\n\nfunction processNotificationCounts(data, unreadNotifications) {\n const highlightCount = unreadNotifications.highlight_count || 0;\n if (highlightCount !== data.highlightCount) {\n data = data.cloneIfNeeded();\n data.highlightCount = highlightCount;\n }\n const notificationCount = unreadNotifications.notification_count;\n if (notificationCount !== data.notificationCount) {\n data = data.cloneIfNeeded();\n data.notificationCount = notificationCount;\n }\n return data;\n} \n\nfunction processRoomAccountData(data, event) {\n if (event?.type === \"m.tag\") {\n let tags = event?.content?.tags;\n if (!tags || Array.isArray(tags) || typeof tags !== \"object\") {\n tags = null;\n }\n data = data.cloneIfNeeded();\n data.tags = tags;\n }\n return data;\n}\n\nexport function processStateEvent(data, event, ownUserId) {\n if (event.type === \"m.room.create\") {\n data = data.cloneIfNeeded();\n data.lastMessageTimestamp = event.origin_server_ts;\n } else if (event.type === \"m.room.encryption\") {\n const algorithm = event.content?.algorithm;\n if (!data.encryption && algorithm === MEGOLM_ALGORITHM) {\n data = data.cloneIfNeeded();\n data.encryption = event.content;\n }\n } else if (event.type === \"m.room.name\") {\n const newName = event.content?.name;\n if (newName !== data.name) {\n data = data.cloneIfNeeded();\n data.name = newName;\n }\n } else if (event.type === \"m.room.avatar\") {\n const newUrl = event.content?.url;\n if (newUrl !== data.avatarUrl) {\n data = data.cloneIfNeeded();\n data.avatarUrl = newUrl;\n }\n } else if (event.type === \"m.room.canonical_alias\") {\n const content = event.content;\n data = data.cloneIfNeeded();\n data.canonicalAlias = content.alias;\n } else if (event.type === \"m.room.member\") {\n const content = event.content;\n if (content.is_direct === true && content.membership === \"invite\" && !data.isDirectMessage) {\n let other;\n if (event.sender === ownUserId) {\n other = event.state_key;\n } else if (event.state_key === ownUserId) {\n other = event.sender;\n }\n if (other) {\n data = data.cloneIfNeeded();\n data.isDirectMessage = true;\n data.dmUserId = other;\n }\n } else if (content.membership === \"leave\" && data.isDirectMessage && data.dmUserId === event.state_key) {\n data = data.cloneIfNeeded();\n data.isDirectMessage = false;\n data.dmUserId = null;\n }\n }\n return data;\n}\n\nfunction processTimelineEvent(data, eventEntry, isInitialSync, canMarkUnread, ownUserId) {\n if (eventEntry.eventType === \"m.room.message\") {\n if (!data.lastMessageTimestamp || eventEntry.timestamp > data.lastMessageTimestamp) {\n data = data.cloneIfNeeded();\n data.lastMessageTimestamp = eventEntry.timestamp;\n }\n if (!isInitialSync && eventEntry.sender !== ownUserId && canMarkUnread) {\n data = data.cloneIfNeeded();\n data.isUnread = true;\n }\n }\n return data;\n}\n\nfunction updateSummary(data, summary) {\n const heroes = summary[\"m.heroes\"];\n const joinCount = summary[\"m.joined_member_count\"];\n const inviteCount = summary[\"m.invited_member_count\"];\n // TODO: we could easily calculate if all members are available here and set hasFetchedMembers?\n // so we can avoid calling /members...\n // we'd need to do a count query in the roomMembers store though ...\n if (heroes && Array.isArray(heroes)) {\n data = data.cloneIfNeeded();\n data.heroes = heroes;\n }\n if (Number.isInteger(inviteCount)) {\n data = data.cloneIfNeeded();\n data.inviteCount = inviteCount;\n }\n if (Number.isInteger(joinCount)) {\n data = data.cloneIfNeeded();\n data.joinCount = joinCount;\n }\n return data;\n}\n\nexport class SummaryData {\n constructor(copy, roomId) {\n this.roomId = copy ? copy.roomId : roomId;\n this.name = copy ? copy.name : null;\n this.lastMessageTimestamp = copy ? copy.lastMessageTimestamp : null;\n this.isUnread = copy ? copy.isUnread : false;\n this.encryption = copy ? copy.encryption : null;\n this.membership = copy ? copy.membership : null;\n this.inviteCount = copy ? copy.inviteCount : 0;\n this.joinCount = copy ? copy.joinCount : 0;\n this.heroes = copy ? copy.heroes : null;\n this.canonicalAlias = copy ? copy.canonicalAlias : null;\n this.hasFetchedMembers = copy ? copy.hasFetchedMembers : false;\n this.isTrackingMembers = copy ? copy.isTrackingMembers : false;\n this.avatarUrl = copy ? copy.avatarUrl : null;\n this.notificationCount = copy ? copy.notificationCount : 0;\n this.highlightCount = copy ? copy.highlightCount : 0;\n this.tags = copy ? copy.tags : null;\n this.isDirectMessage = copy ? copy.isDirectMessage : false;\n this.dmUserId = copy ? copy.dmUserId : null;\n this.cloned = copy ? true : false;\n }\n\n changedKeys(other) {\n const props = Object.getOwnPropertyNames(this);\n return props.filter(prop => {\n return prop !== \"cloned\" && this[prop] !== other[prop]\n });\n }\n\n cloneIfNeeded() {\n if (this.cloned) {\n return this;\n } else {\n return new SummaryData(this);\n }\n }\n\n serialize() {\n return Object.entries(this).reduce((obj, [key, value]) => {\n if (key !== \"cloned\" && value !== null) {\n obj[key] = value;\n }\n return obj;\n }, {});\n }\n\n applyTimelineEntries(timelineEntries, isInitialSync, canMarkUnread, ownUserId) {\n return applyTimelineEntries(this, timelineEntries, isInitialSync, canMarkUnread, ownUserId);\n }\n\n applySyncResponse(roomResponse, membership, ownUserId) {\n return applySyncResponse(this, roomResponse, membership, ownUserId);\n }\n\n get needsHeroes() {\n return !this.name && !this.canonicalAlias && this.heroes && this.heroes.length > 0;\n }\n\n isNewJoin(oldData) {\n return this.membership === \"join\" && oldData.membership !== \"join\";\n }\n}\n\nexport class RoomSummary {\n\tconstructor(roomId) {\n this._data = null;\n this.applyChanges(new SummaryData(null, roomId));\n\t}\n\n get data() {\n return this._data;\n }\n\n writeClearUnread(txn) {\n const data = new SummaryData(this._data);\n data.isUnread = false;\n data.notificationCount = 0;\n data.highlightCount = 0;\n txn.roomSummary.set(data.serialize());\n return data;\n }\n\n writeHasFetchedMembers(value, txn) {\n const data = new SummaryData(this._data);\n data.hasFetchedMembers = value;\n txn.roomSummary.set(data.serialize());\n return data;\n }\n\n writeIsTrackingMembers(value, txn) {\n const data = new SummaryData(this._data);\n data.isTrackingMembers = value;\n txn.roomSummary.set(data.serialize());\n return data;\n }\n\n\twriteData(data, txn) {\n\t\tif (data !== this._data) {\n txn.roomSummary.set(data.serialize());\n return data;\n\t\t}\n\t}\n\n /** move summary to archived store when leaving the room */\n writeArchivedData(data, txn) {\n if (data !== this._data) {\n txn.archivedRoomSummary.set(data.serialize());\n return data;\n }\n }\n\n async writeAndApplyData(data, storage) {\n if (data === this._data) {\n return false;\n }\n const txn = await storage.readWriteTxn([\n storage.storeNames.roomSummary,\n ]);\n try {\n txn.roomSummary.set(data.serialize());\n } catch (err) {\n txn.abort();\n throw err;\n }\n await txn.complete();\n this.applyChanges(data);\n return true;\n }\n\n applyChanges(data) {\n this._data = data;\n // clear cloned flag, so cloneIfNeeded makes a copy and\n // this._data is not modified if any field is changed.\n this._data.cloned = false;\n }\n\n\tasync load(summary) {\n this.applyChanges(new SummaryData(summary));\n\t}\n}\n\nexport function tests() {\n return {\n \"serialize doesn't include null fields or cloned\": assert => {\n const roomId = \"!123:hs.tld\";\n const data = new SummaryData(null, roomId);\n const clone = data.cloneIfNeeded();\n const serialized = clone.serialize();\n assert.strictEqual(serialized.cloned, undefined);\n assert.equal(serialized.roomId, roomId);\n const nullCount = Object.values(serialized).reduce((count, value) => count + value === null ? 1 : 0, 0);\n assert.strictEqual(nullCount, 0);\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport enum StoreNames {\n session = \"session\",\n roomState = \"roomState\",\n roomSummary = \"roomSummary\",\n archivedRoomSummary = \"archivedRoomSummary\",\n invites = \"invites\",\n roomMembers = \"roomMembers\",\n timelineEvents = \"timelineEvents\",\n timelineRelations = \"timelineRelations\",\n timelineFragments = \"timelineFragments\",\n pendingEvents = \"pendingEvents\",\n userIdentities = \"userIdentities\",\n deviceIdentities = \"deviceIdentities\",\n olmSessions = \"olmSessions\",\n inboundGroupSessions = \"inboundGroupSessions\",\n outboundGroupSessions = \"outboundGroupSessions\",\n groupSessionDecryptions = \"groupSessionDecryptions\",\n operations = \"operations\",\n accountData = \"accountData\",\n}\n\nexport const STORE_NAMES: Readonly<StoreNames[]> = Object.values(StoreNames);\n\nexport class StorageError extends Error {\n errcode?: string;\n cause: Error | null;\n\n constructor(message: string, cause: Error | null = null) {\n super(message);\n if (cause) {\n this.errcode = cause.name;\n }\n this.cause = cause;\n }\n\n get name(): string {\n return \"StorageError\";\n }\n}\n\nexport const KeyLimits = {\n get minStorageKey(): number {\n // for indexeddb, we use unsigned 32 bit integers as keys\n return 0;\n },\n \n get middleStorageKey(): number {\n // for indexeddb, we use unsigned 32 bit integers as keys\n return 0x7FFFFFFF;\n },\n\n get maxStorageKey(): number {\n // for indexeddb, we use unsigned 32 bit integers as keys\n return 0xFFFFFFFF;\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {KeyLimits} from \"../../storage/common\";\nimport {Direction} from \"./Direction\";\n\n// key for events in the timelineEvents store\nexport class EventKey {\n constructor(\n public fragmentId: number,\n public eventIndex: number\n ) {\n }\n\n nextFragmentKey(): EventKey {\n // could take MIN_EVENT_INDEX here if it can't be paged back\n return new EventKey(this.fragmentId + 1, KeyLimits.middleStorageKey);\n }\n\n nextKeyForDirection(direction: Direction): EventKey {\n if (direction.isForward) {\n return this.nextKey();\n } else {\n return this.previousKey();\n }\n }\n\n previousKey(): EventKey {\n return new EventKey(this.fragmentId, this.eventIndex - 1);\n }\n\n nextKey(): EventKey {\n return new EventKey(this.fragmentId, this.eventIndex + 1);\n }\n\n static get maxKey(): EventKey {\n return new EventKey(KeyLimits.maxStorageKey, KeyLimits.maxStorageKey);\n }\n\n static get minKey(): EventKey {\n return new EventKey(KeyLimits.minStorageKey, KeyLimits.minStorageKey);\n }\n\n static get defaultLiveKey(): EventKey {\n return EventKey.defaultFragmentKey(KeyLimits.minStorageKey);\n }\n\n static defaultFragmentKey(fragmentId: number): EventKey {\n return new EventKey(fragmentId, KeyLimits.middleStorageKey);\n }\n\n toString(): string {\n return `[${this.fragmentId}/${this.eventIndex}]`;\n }\n\n equals(other: EventKey): boolean {\n return this.fragmentId === other?.fragmentId && this.eventIndex === other?.eventIndex;\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n//entries can be sorted, first by fragment, then by entry index.\nimport {EventKey} from \"../EventKey\";\nexport const PENDING_FRAGMENT_ID = Number.MAX_SAFE_INTEGER;\n\ninterface FragmentIdComparer {\n compare: (a: number, b: number) => number\n}\n\nexport abstract class BaseEntry {\n constructor(\n protected readonly _fragmentIdComparer: FragmentIdComparer\n ) {\n }\n\n abstract get fragmentId(): number;\n abstract get entryIndex(): number;\n abstract updateFrom(other: BaseEntry): void;\n\n compare(otherEntry: BaseEntry): number {\n if (this.fragmentId === otherEntry.fragmentId) {\n return this.entryIndex - otherEntry.entryIndex;\n } else if (this.fragmentId === PENDING_FRAGMENT_ID) {\n return 1;\n } else if (otherEntry.fragmentId === PENDING_FRAGMENT_ID) {\n return -1;\n } else {\n // This might throw if the relation of two fragments is unknown.\n return this._fragmentIdComparer.compare(this.fragmentId, otherEntry.fragmentId);\n }\n }\n\n asEventKey(): EventKey {\n return new EventKey(this.fragmentId, this.entryIndex);\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport type {StateEvent} from \"../storage/types\";\n\nexport function getPrevContentFromStateEvent(event) {\n // where to look for prev_content is a bit of a mess,\n // see https://matrix.to/#/!NasysSDfxKxZBzJJoE:matrix.org/$DvrAbZJiILkOmOIuRsNoHmh2v7UO5CWp_rYhlGk34fQ?via=matrix.org&via=pixie.town&via=amorgan.xyz\n return event.unsigned?.prev_content || event.prev_content;\n}\n\nexport const REDACTION_TYPE = \"m.room.redaction\";\n\nexport function isRedacted(event) {\n return !!event?.unsigned?.redacted_because;\n}\n\nexport enum RoomStatus {\n None = 1 << 0,\n BeingCreated = 1 << 1,\n Invited = 1 << 2,\n Joined = 1 << 3,\n Replaced = 1 << 4,\n Archived = 1 << 5,\n}\n\nexport enum RoomType {\n DirectMessage,\n Private,\n Public\n}\n\ntype RoomResponse = {\n state?: {\n events?: Array<StateEvent>\n },\n timeline?: {\n events?: Array<StateEvent>\n }\n}\n\n/** iterates over any state events in a sync room response, in the order that they should be applied (from older to younger events) */\nexport function iterateResponseStateEvents(roomResponse: RoomResponse, callback: (StateEvent) => Promise<void> | void): Promise<void> | void {\n let promises: Promise<void>[] | undefined = undefined;\n const callCallback = stateEvent => {\n const result = callback(stateEvent);\n if (result instanceof Promise) {\n promises = promises ?? [];\n promises.push(result);\n }\n };\n // first iterate over state events, they precede the timeline\n const stateEvents = roomResponse.state?.events;\n if (stateEvents) {\n for (let i = 0; i < stateEvents.length; i++) {\n callCallback(stateEvents[i]);\n }\n }\n // now see if there are any state events within the timeline\n let timelineEvents = roomResponse.timeline?.events;\n if (timelineEvents) {\n for (let i = 0; i < timelineEvents.length; i++) {\n const event = timelineEvents[i];\n if (typeof event.state_key === \"string\") {\n callCallback(event);\n }\n }\n }\n if (promises) {\n return Promise.all(promises).then(() => undefined);\n }\n}\n\nexport function tests() {\n return {\n \"test iterateResponseStateEvents with both state and timeline sections\": assert => {\n const roomResponse = {\n state: {\n events: [\n {type: \"m.room.member\", state_key: \"1\"},\n {type: \"m.room.member\", state_key: \"2\", content: {a: 1}},\n ]\n },\n timeline: {\n events: [\n {type: \"m.room.message\"},\n {type: \"m.room.member\", state_key: \"3\"},\n {type: \"m.room.message\"},\n {type: \"m.room.member\", state_key: \"2\", content: {a: 2}},\n ]\n }\n } as unknown as RoomResponse;\n const expectedStateKeys = [\"1\", \"2\", \"3\", \"2\"];\n const expectedAForMember2 = [1, 2];\n iterateResponseStateEvents(roomResponse, event => {\n assert.strictEqual(event.type, \"m.room.member\");\n assert.strictEqual(expectedStateKeys.shift(), event.state_key);\n if (event.state_key === \"2\") {\n assert.strictEqual(expectedAForMember2.shift(), event.content.a);\n }\n });\n assert.strictEqual(expectedStateKeys.length, 0);\n assert.strictEqual(expectedAForMember2.length, 0);\n },\n \"test iterateResponseStateEvents with empty response\": assert => {\n iterateResponseStateEvents({}, () => {\n assert.fail(\"no events expected\");\n });\n }\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {REDACTION_TYPE} from \"../common\";\n\nexport const REACTION_TYPE = \"m.reaction\";\nexport const ANNOTATION_RELATION_TYPE = \"m.annotation\";\n\nexport function createAnnotation(targetId, key) {\n return {\n \"m.relates_to\": {\n \"event_id\": targetId,\n key,\n \"rel_type\": ANNOTATION_RELATION_TYPE\n }\n };\n}\n\nexport function getRelationTarget(relation) {\n return relation.event_id || relation[\"m.in_reply_to\"]?.event_id\n}\n\nexport function setRelationTarget(relation, target) {\n if (relation.event_id !== undefined) {\n relation.event_id = target;\n } else if (relation[\"m.in_reply_to\"]) {\n relation[\"m.in_reply_to\"].event_id = target;\n }\n}\n\nexport function getRelatedEventId(event) {\n\tif (event.type === REDACTION_TYPE) {\n return event.redacts;\n } else {\n const relation = getRelation(event);\n if (relation) {\n return getRelationTarget(relation);\n }\n }\n return null;\n}\n\nexport function getRelationFromContent(content) {\n return content?.[\"m.relates_to\"];\n}\n\nexport function getRelation(event) {\n\treturn getRelationFromContent(event.content);\n}\n\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport class PendingAnnotation {\n constructor() {\n // TODO: use simple member for reaction and redaction as we can't/shouldn't really have more than 2 entries\n // this contains both pending annotation entries, and pending redactions of remote annotation entries \n this._entries = [];\n }\n\n get firstTimestamp() {\n return this._entries.reduce((ts, e) => {\n if (e.isRedaction) {\n return ts;\n }\n return Math.min(e.timestamp, ts);\n }, Number.MAX_SAFE_INTEGER);\n }\n\n get annotationEntry() {\n return this._entries.find(e => !e.isRedaction);\n }\n\n get redactionEntry() {\n return this._entries.find(e => e.isRedaction);\n }\n\n get count() {\n return this._entries.reduce((count, e) => {\n return count + (e.isRedaction ? -1 : 1);\n }, 0);\n }\n\n add(entry) {\n this._entries.push(entry);\n }\n\n remove(entry) {\n const idx = this._entries.indexOf(entry);\n if (idx === -1) {\n return false;\n }\n this._entries.splice(idx, 1);\n return true;\n }\n\n get willAnnotate() {\n const lastEntry = this._entries.reduce((lastEntry, e) => {\n if (!lastEntry || e.pendingEvent.queueIndex > lastEntry.pendingEvent.queueIndex) {\n return e;\n }\n return lastEntry;\n }, null);\n if (lastEntry) {\n return !lastEntry.isRedaction;\n }\n return false;\n }\n\n get isEmpty() {\n return this._entries.length === 0;\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nfunction htmlEscape(string) {\n return string.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\");\n}\n\nfunction fallbackForNonTextualMessage(msgtype) {\n switch (msgtype) {\n case \"m.file\":\n return \"sent a file.\";\n case \"m.image\":\n return \"sent an image.\";\n case \"m.video\":\n return \"sent a video.\";\n case \"m.audio\":\n return \"sent an audio file.\";\n }\n return null;\n}\n\nfunction fallbackPrefix(msgtype) {\n return msgtype === \"m.emote\" ? \"* \" : \"\";\n}\n\nfunction _createReplyContent(targetId, msgtype, body, formattedBody) {\n return {\n msgtype,\n body,\n \"format\": \"org.matrix.custom.html\",\n \"formatted_body\": formattedBody,\n \"m.relates_to\": {\n \"m.in_reply_to\": {\n \"event_id\": targetId\n }\n }\n };\n}\n\nexport function createReplyContent(entry, msgtype, body) {\n // TODO check for absense of sender / body / msgtype / etc?\n const nonTextual = fallbackForNonTextualMessage(entry.content.msgtype);\n const prefix = fallbackPrefix(entry.content.msgtype);\n const sender = entry.sender;\n const name = entry.displayName || sender;\n\n const formattedBody = nonTextual || entry.content.formatted_body ||\n (entry.content.body && htmlEscape(entry.content.body)) || \"\";\n const formattedFallback = `<mx-reply><blockquote>In reply to ${prefix}` +\n `<a href=\"https://matrix.to/#/${sender}\">${name}</a><br />` +\n `${formattedBody}</blockquote></mx-reply>`;\n\n const plainBody = nonTextual || entry.content.body || \"\";\n const bodyLines = plainBody.split(\"\\n\");\n bodyLines[0] = `> ${prefix}<${sender}> ${bodyLines[0]}`\n const plainFallback = bodyLines.join(\"\\n> \");\n\n const newBody = plainFallback + '\\n\\n' + body;\n const newFormattedBody = formattedFallback + htmlEscape(body);\n return _createReplyContent(entry.id, msgtype, newBody, newFormattedBody);\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseEntry} from \"./BaseEntry\";\nimport {REDACTION_TYPE} from \"../../common\";\nimport {createAnnotation, ANNOTATION_RELATION_TYPE, getRelationFromContent} from \"../relations.js\";\nimport {PendingAnnotation} from \"../PendingAnnotation.js\";\nimport {createReplyContent} from \"./reply.js\"\n\n/** Deals mainly with local echo for relations and redactions,\n * so it is shared between PendingEventEntry and EventEntry */\nexport class BaseEventEntry extends BaseEntry {\n constructor(fragmentIdComparer) {\n super(fragmentIdComparer);\n this._pendingRedactions = null;\n this._pendingAnnotations = null;\n this._contextEntry = null;\n this._contextForEntries = null;\n }\n\n get isReply() {\n return !!this.relation?.[\"m.in_reply_to\"];\n }\n\n get isRedacting() {\n return !!this._pendingRedactions;\n }\n\n get isRedacted() {\n return this.isRedacting;\n }\n\n get isRedaction() {\n return this.eventType === REDACTION_TYPE;\n }\n\n get redactionReason() {\n if (this._pendingRedactions) {\n return this._pendingRedactions[0].content?.reason;\n }\n return null;\n }\n\n setContextEntry(entry) {\n this._contextEntry = entry;\n entry._setAsContextOf(this);\n }\n\n _setAsContextOf(entry) {\n if (!this._contextForEntries) {\n this._contextForEntries = [];\n }\n this._contextForEntries.push(entry);\n }\n\n get contextForEntries() {\n return this._contextForEntries;\n }\n\n get contextEntry() {\n return this._contextEntry;\n }\n\n /**\n Aggregates relation or redaction of remote relation. \n Used in two situations:\n - to aggregate local relation/redaction of remote relation\n - to mark this entry as being redacted in Timeline._updateEntriesFetchedFromHomeserver\n @return [string] returns the name of the field that has changed, if any\n */\n addLocalRelation(entry) {\n if (entry.eventType === REDACTION_TYPE && entry.isRelatedToId(this.id)) {\n if (!this._pendingRedactions) {\n this._pendingRedactions = [];\n }\n this._pendingRedactions.push(entry);\n if (this._pendingRedactions.length === 1) {\n return \"isRedacted\";\n }\n } else {\n const relationEntry = entry.redactingEntry || entry;\n if (relationEntry.isRelatedToId(this.id)) {\n if (relationEntry.relation.rel_type === ANNOTATION_RELATION_TYPE) {\n if (this._addPendingAnnotation(entry)) {\n return \"pendingAnnotations\";\n }\n }\n }\n }\n }\n \n /**\n deaggregates local relation or a local redaction of a remote relation.\n @return [string] returns the name of the field that has changed, if any\n */\n removeLocalRelation(entry) {\n if (entry.eventType === REDACTION_TYPE && entry.isRelatedToId(this.id) && this._pendingRedactions) {\n const countBefore = this._pendingRedactions.length;\n this._pendingRedactions = this._pendingRedactions.filter(e => e !== entry);\n if (this._pendingRedactions.length === 0) {\n this._pendingRedactions = null;\n if (countBefore !== 0) {\n return \"isRedacted\";\n }\n }\n } else {\n const relationEntry = entry.redactingEntry || entry;\n if (relationEntry.isRelatedToId(this.id)) {\n if (relationEntry.relation?.rel_type === ANNOTATION_RELATION_TYPE && this._pendingAnnotations) {\n if (this._removePendingAnnotation(entry)) {\n return \"pendingAnnotations\";\n }\n }\n }\n }\n }\n\n _addPendingAnnotation(entry) {\n if (!this._pendingAnnotations) {\n this._pendingAnnotations = new Map();\n }\n const {key} = (entry.redactingEntry || entry).relation;\n if (key) {\n let annotation = this._pendingAnnotations.get(key);\n if (!annotation) {\n annotation = new PendingAnnotation();\n this._pendingAnnotations.set(key, annotation);\n }\n annotation.add(entry);\n return true;\n }\n return false;\n }\n\n _removePendingAnnotation(entry) {\n const {key} = (entry.redactingEntry || entry).relation;\n if (key) {\n let annotation = this._pendingAnnotations.get(key);\n if (annotation.remove(entry) && annotation.isEmpty) {\n this._pendingAnnotations.delete(key);\n }\n if (this._pendingAnnotations.size === 0) {\n this._pendingAnnotations = null;\n }\n return true;\n }\n return false;\n }\n\n async abortPendingRedaction() {\n if (this._pendingRedactions) {\n for (const pee of this._pendingRedactions) {\n await pee.pendingEvent.abort();\n }\n // removing the pending events will call removeLocalRelation,\n // so don't clear _pendingRedactions here\n }\n }\n\n get pendingRedaction() {\n if (this._pendingRedactions) {\n return this._pendingRedactions[0];\n }\n return null;\n }\n\n annotate(key) {\n return createAnnotation(this.id, key);\n }\n\n reply(msgtype, body) {\n return createReplyContent(this, msgtype, body);\n }\n\n /** takes both remote event id and local txn id into account, see overriding in PendingEventEntry */\n isRelatedToId(id) {\n return id && this.relatedEventId === id;\n }\n\n haveAnnotation(key) {\n const haveRemoteReaction = this.annotations?.[key]?.me || false;\n const pendingAnnotation = this.pendingAnnotations?.get(key);\n const willAnnotate = pendingAnnotation?.willAnnotate || false;\n /*\n We have an annotation in these case:\n - remote annotation with me, no pending\n - remote annotation with me, pending redaction and then annotation\n - pending annotation without redaction after it\n */\n return (haveRemoteReaction && (!pendingAnnotation || willAnnotate)) ||\n (!haveRemoteReaction && willAnnotate);\n }\n\n get relation() {\n return getRelationFromContent(this.content);\n }\n\n get pendingAnnotations() {\n return this._pendingAnnotations;\n }\n\n get annotations() {\n return null; //overwritten in EventEntry\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {PENDING_FRAGMENT_ID} from \"./BaseEntry\";\nimport {BaseEventEntry} from \"./BaseEventEntry.js\";\n\nexport class PendingEventEntry extends BaseEventEntry {\n constructor({pendingEvent, member, clock, redactingEntry}) {\n super(null);\n this._pendingEvent = pendingEvent;\n /** @type {RoomMember} */\n this._member = member;\n // try to come up with a timestamp that is around construction time and\n // will be roughly sorted by queueIndex, so it can be used to as a secondary\n // sorting dimension for reactions\n this._timestamp = clock.now() - (100 - pendingEvent.queueIndex);\n this._redactingEntry = redactingEntry;\n }\n\n get fragmentId() {\n return PENDING_FRAGMENT_ID;\n }\n\n get entryIndex() {\n return this._pendingEvent.queueIndex;\n }\n\n get content() {\n return this._pendingEvent.content;\n }\n\n get event() {\n return null;\n }\n\n get eventType() {\n return this._pendingEvent.eventType;\n }\n\n get stateKey() {\n return null;\n }\n\n get sender() {\n return this._member?.userId;\n }\n\n get displayName() {\n return this._member?.name;\n }\n\n get avatarUrl() {\n return this._member?.avatarUrl;\n }\n\n get timestamp() {\n return this._timestamp;\n }\n\n get isPending() {\n return true;\n }\n\n get id() {\n return this._pendingEvent.txnId;\n }\n\n get pendingEvent() {\n return this._pendingEvent;\n }\n\n notifyUpdate() {\n \n }\n\n isRelatedToId(id) {\n if (id && id === this._pendingEvent.relatedTxnId) {\n return true;\n }\n return super.isRelatedToId(id);\n }\n\n get relatedEventId() {\n return this._pendingEvent.relatedEventId;\n }\n\n get redactingEntry() {\n return this._redactingEntry;\n }\n\n get contextEventId() {\n if (this.isReply) {\n return this._pendingEvent.relatedEventId ?? this._pendingEvent.relatedTxnId;\n }\n return null;\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\nimport {createEnum} from \"../../../utils/enum\";\nimport {AbortError} from \"../../../utils/error\";\nimport {REDACTION_TYPE} from \"../common\";\nimport {getRelationFromContent, getRelationTarget, setRelationTarget} from \"../timeline/relations.js\";\n\nexport const SendStatus = createEnum(\n \"Waiting\",\n \"EncryptingAttachments\",\n \"UploadingAttachments\",\n \"Encrypting\",\n \"Sending\",\n \"Sent\",\n \"Error\",\n);\n\nconst unencryptedContentFields = [ \"m.relates_to\" ];\n\nexport class PendingEvent {\n constructor({data, remove, emitUpdate, attachments}) {\n this._data = data;\n this._attachments = attachments;\n this._emitUpdate = emitUpdate;\n this._removeFromQueueCallback = remove;\n this._aborted = false;\n this._status = SendStatus.Waiting;\n this._sendRequest = null;\n this._attachmentsTotalBytes = 0;\n if (this._attachments) {\n this._attachmentsTotalBytes = Object.values(this._attachments).reduce((t, a) => t + a.size, 0);\n }\n }\n\n get roomId() { return this._data.roomId; }\n get queueIndex() { return this._data.queueIndex; }\n get eventType() { return this._data.eventType; }\n get txnId() { return this._data.txnId; }\n get remoteId() { return this._data.remoteId; }\n get content() { return this._data.content; }\n get relatedTxnId() { return this._data.relatedTxnId; }\n get relatedEventId() {\n const relation = getRelationFromContent(this.content);\n if (relation) {\n // may be null when target is not sent yet, is intended\n return getRelationTarget(relation);\n } else {\n return this._data.relatedEventId;\n }\n }\n\n setRelatedEventId(eventId) {\n const relation = getRelationFromContent(this.content);\n if (relation) {\n setRelationTarget(relation, eventId);\n } else {\n this._data.relatedEventId = eventId;\n }\n }\n\n get data() { return this._data; }\n\n getAttachment(key) {\n return this._attachments && this._attachments[key];\n }\n\n get needsSending() {\n return !this.remoteId && !this.aborted;\n }\n\n get needsEncryption() {\n return this._data.needsEncryption && !this.aborted;\n }\n\n get needsUpload() {\n return this._data.needsUpload && !this.aborted;\n }\n\n get isMissingAttachments() {\n return this.needsUpload && !this._attachments;\n }\n\n setEncrypting() {\n this._status = SendStatus.Encrypting;\n this._emitUpdate(\"status\");\n }\n\n get contentForEncryption() {\n const content = Object.assign({}, this._data.content);\n for (const field of unencryptedContentFields) {\n delete content[field];\n }\n return content;\n }\n\n _preserveContentFields(into) {\n const content = this._data.content;\n for (const field of unencryptedContentFields) {\n if (content[field] !== undefined) {\n into[field] = content[field];\n }\n }\n }\n\n setEncrypted(type, content) {\n this._preserveContentFields(content);\n this._data.encryptedEventType = type;\n this._data.encryptedContent = content;\n this._data.needsEncryption = false;\n }\n\n setError(error) {\n this._status = SendStatus.Error;\n this._error = error;\n this._emitUpdate(\"status\");\n }\n\n setWaiting() {\n this._status = SendStatus.Waiting;\n this._emitUpdate(\"status\");\n }\n\n get status() { return this._status; }\n get error() { return this._error; }\n\n get hasStartedSending() {\n return this._status === SendStatus.Sending || this._status === SendStatus.Sent;\n }\n\n get attachmentsTotalBytes() {\n return this._attachmentsTotalBytes;\n }\n\n get attachmentsSentBytes() {\n return this._attachments && Object.values(this._attachments).reduce((t, a) => t + a.sentBytes, 0);\n }\n\n async uploadAttachments(hsApi, log) {\n if (!this.needsUpload) {\n return;\n }\n if (!this._attachments) {\n throw new Error(\"attachments missing\");\n }\n if (this.needsEncryption) {\n this._status = SendStatus.EncryptingAttachments;\n this._emitUpdate(\"status\");\n for (const attachment of Object.values(this._attachments)) {\n await log.wrap(\"encrypt\", () => {\n log.set(\"size\", attachment.size);\n return attachment.encrypt();\n });\n if (this.aborted) {\n throw new AbortError();\n }\n }\n }\n this._status = SendStatus.UploadingAttachments;\n this._emitUpdate(\"status\");\n const entries = Object.entries(this._attachments);\n // upload smallest attachments first\n entries.sort(([, a1], [, a2]) => a1.size - a2.size);\n for (const [urlPath, attachment] of entries) {\n await log.wrap(\"upload\", log => {\n log.set(\"size\", attachment.size);\n return attachment.upload(hsApi, () => {\n this._emitUpdate(\"attachmentsSentBytes\");\n }, log);\n });\n attachment.applyToContent(urlPath, this.content);\n }\n this._data.needsUpload = false;\n }\n\n async abort() {\n if (!this._aborted) {\n this._aborted = true;\n if (this._attachments) {\n for (const attachment of Object.values(this._attachments)) {\n attachment.abort();\n }\n }\n this._sendRequest?.abort();\n await this._removeFromQueueCallback();\n }\n }\n\n get aborted() {\n return this._aborted;\n }\n\n async send(hsApi, log) {\n this._status = SendStatus.Sending;\n this._emitUpdate(\"status\");\n const eventType = this._data.encryptedEventType || this._data.eventType;\n const content = this._data.encryptedContent || this._data.content;\n if (eventType === REDACTION_TYPE) {\n this._sendRequest = hsApi.redact(\n this.roomId,\n this._data.relatedEventId,\n this.txnId,\n content,\n {log}\n );\n } else {\n this._sendRequest = hsApi.send(\n this.roomId,\n eventType,\n this.txnId,\n content,\n {log}\n );\n }\n const response = await this._sendRequest.response();\n this._sendRequest = null;\n // both /send and /redact have the same response format\n this._data.remoteId = response.event_id;\n log.set(\"id\", this._data.remoteId);\n this._status = SendStatus.Sent;\n this._emitUpdate(\"status\");\n }\n\n dispose() {\n if (this._attachments) {\n for (const attachment of Object.values(this._attachments)) {\n attachment.dispose();\n }\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseEventEntry} from \"./BaseEventEntry.js\";\nimport {getPrevContentFromStateEvent, isRedacted} from \"../../common\";\nimport {getRelationFromContent, getRelatedEventId} from \"../relations.js\";\n\nexport class EventEntry extends BaseEventEntry {\n constructor(eventEntry, fragmentIdComparer) {\n super(fragmentIdComparer);\n this._eventEntry = eventEntry;\n this._decryptionError = null;\n this._decryptionResult = null;\n }\n\n clone() {\n const clone = new EventEntry(this._eventEntry, this._fragmentIdComparer);\n clone.updateFrom(this);\n return clone;\n }\n\n updateFrom(other) {\n if (other._decryptionResult && !this._decryptionResult) {\n this._decryptionResult = other._decryptionResult;\n }\n if (other._decryptionError && !this._decryptionError) {\n this._decryptionError = other._decryptionError;\n }\n this._contextForEntries = other.contextForEntries;\n this._contextEntry = other.contextEntry;\n }\n\n get event() {\n return this._eventEntry.event;\n }\n\n get fragmentId() {\n return this._eventEntry.fragmentId;\n }\n\n get entryIndex() {\n return this._eventEntry.eventIndex;\n }\n\n get content() {\n return this._decryptionResult?.event?.content || this._eventEntry.event.content;\n }\n\n get prevContent() {\n // doesn't look at _decryptionResult because state events are not encrypted\n return getPrevContentFromStateEvent(this._eventEntry.event);\n }\n\n get eventType() {\n return this._decryptionResult?.event?.type || this._eventEntry.event.type;\n }\n\n get stateKey() {\n return this._eventEntry.event.state_key;\n }\n\n get sender() {\n return this._eventEntry.event.sender;\n }\n\n get displayName() {\n return this._eventEntry.displayName;\n }\n\n get avatarUrl() {\n return this._eventEntry.avatarUrl;\n }\n\n get timestamp() {\n return this._eventEntry.event.origin_server_ts;\n }\n\n get id() {\n return this._eventEntry.event.event_id;\n }\n\n setDecryptionResult(result) {\n this._decryptionResult = result;\n }\n\n get isEncrypted() {\n return this._eventEntry.event.type === \"m.room.encrypted\";\n }\n\n get isDecrypted() {\n return !!this._decryptionResult?.event;\n }\n\n get isVerified() {\n return this.isEncrypted && this._decryptionResult?.isVerified;\n }\n\n get isUnverified() {\n return this.isEncrypted && this._decryptionResult?.isUnverified;\n }\n\n setDecryptionError(err) {\n this._decryptionError = err;\n }\n\n get decryptionError() {\n return this._decryptionError;\n }\n\n get relatedEventId() {\n return getRelatedEventId(this.event);\n }\n\n get isRedacted() {\n return super.isRedacted || isRedacted(this._eventEntry.event);\n }\n\n get redactionReason() {\n const redactionEvent = this._eventEntry.event.unsigned?.redacted_because;\n if (redactionEvent) {\n return redactionEvent.content?.reason;\n }\n // fall back to local echo reason\n return super.redactionReason;\n }\n\n get annotations() {\n return this._eventEntry.annotations;\n }\n\n get relation() {\n const originalContent = this._eventEntry.event.content;\n const originalRelation = originalContent && getRelationFromContent(originalContent);\n return originalRelation || getRelationFromContent(this.content);\n }\n\n // similar to relatedEventID but only for replies\n get contextEventId() {\n if (this.isReply) {\n return this.relatedEventId;\n }\n return null;\n }\n\n}\n\nimport {withTextBody, withContent, createEvent} from \"../../../../mocks/event.js\";\nimport {Clock as MockClock} from \"../../../../mocks/Clock.js\";\nimport {PendingEventEntry} from \"./PendingEventEntry.js\";\nimport {PendingEvent} from \"../../sending/PendingEvent.js\";\nimport {createAnnotation} from \"../relations.js\";\n\nexport function tests() {\n let queueIndex = 0;\n const clock = new MockClock();\n\n function addPendingReaction(target, key) {\n queueIndex += 1;\n target.addLocalRelation(new PendingEventEntry({\n pendingEvent: new PendingEvent({data: {\n eventType: \"m.reaction\",\n content: createAnnotation(target.id, key),\n queueIndex,\n txnId: `t${queueIndex}`\n }}),\n clock\n }));\n return target;\n }\n\n function addPendingRedaction(target, key) {\n const pendingReaction = target.pendingAnnotations?.get(key)?.annotationEntry;\n let redactingEntry = pendingReaction;\n // make up a remote entry if we don't have a pending reaction and have an aggregated remote entry\n if (!pendingReaction && target.annotations[key].me) {\n redactingEntry = new EventEntry({\n event: withContent(createAnnotation(target.id, key), createEvent(\"m.reaction\", \"!def\"))\n });\n }\n queueIndex += 1;\n target.addLocalRelation(new PendingEventEntry({\n pendingEvent: new PendingEvent({data: {\n eventType: \"m.room.redaction\",\n relatedTxnId: pendingReaction ? pendingReaction.id : null,\n relatedEventId: pendingReaction ? null : redactingEntry.id,\n queueIndex,\n txnId: `t${queueIndex}`\n }}),\n redactingEntry,\n clock\n }));\n return target;\n }\n\n function remoteAnnotation(key, me, count, obj = {}) {\n obj[key] = {me, count};\n return obj;\n }\n\n return {\n // testing it here because parent class always assumes annotations is null\n \"haveAnnotation\": assert => {\n const msgEvent = withTextBody(\"hi!\", createEvent(\"m.room.message\", \"!abc\"));\n const e1 = new EventEntry({event: msgEvent});\n assert.equal(false, e1.haveAnnotation(\"🚀\"));\n const e2 = new EventEntry({event: msgEvent, annotations: remoteAnnotation(\"🚀\", false, 1)});\n assert.equal(false, e2.haveAnnotation(\"🚀\"));\n const e3 = new EventEntry({event: msgEvent, annotations: remoteAnnotation(\"🚀\", true, 1)});\n assert.equal(true, e3.haveAnnotation(\"🚀\"));\n const e4 = new EventEntry({event: msgEvent, annotations: remoteAnnotation(\"🚀\", true, 2)});\n assert.equal(true, e4.haveAnnotation(\"🚀\"));\n const e5 = addPendingReaction(new EventEntry({event: msgEvent}), \"🚀\");\n assert.equal(true, e5.haveAnnotation(\"🚀\"));\n const e6 = addPendingRedaction(new EventEntry({event: msgEvent, annotations: remoteAnnotation(\"🚀\", true, 1)}), \"🚀\");\n assert.equal(false, e6.haveAnnotation(\"🚀\"));\n const e7 = addPendingReaction(\n addPendingRedaction(\n new EventEntry({event: msgEvent, annotations: remoteAnnotation(\"🚀\", true, 1)}),\n \"🚀\"),\n \"🚀\");\n assert.equal(true, e7.haveAnnotation(\"🚀\"));\n const e8 = addPendingRedaction(\n addPendingReaction(\n new EventEntry({event: msgEvent}),\n \"🚀\"),\n \"🚀\");\n assert.equal(false, e8.haveAnnotation(\"🚀\"));\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport function createEventEntry(key, roomId, event) {\n return {\n fragmentId: key.fragmentId,\n eventIndex: key.eventIndex,\n roomId,\n event: event,\n };\n}\n\nexport function directionalAppend(array, value, direction) {\n if (direction.isForward) {\n array.push(value);\n } else {\n array.unshift(value);\n }\n}\n\nexport function directionalConcat(array, otherArray, direction) {\n if (direction.isForward) {\n return array.concat(otherArray);\n } else {\n return otherArray.concat(array);\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {getPrevContentFromStateEvent} from \"../common\";\n\nexport const EVENT_TYPE = \"m.room.member\";\n\nexport class RoomMember {\n constructor(data) {\n this._data = data;\n }\n\n static fromUserId(roomId, userId, membership) {\n return new RoomMember({roomId, userId, membership});\n }\n\n static fromMemberEvent(roomId, memberEvent) {\n const userId = memberEvent?.state_key;\n if (typeof userId !== \"string\") {\n return;\n }\n const content = memberEvent.content;\n const prevContent = getPrevContentFromStateEvent(memberEvent);\n const membership = content?.membership;\n // fall back to prev_content for these as synapse doesn't (always?)\n // put them on content for \"leave\" memberships\n const displayName = content?.displayname || prevContent?.displayname;\n const avatarUrl = content?.avatar_url || prevContent?.avatar_url;\n return this._validateAndCreateMember(roomId, userId, membership, displayName, avatarUrl);\n }\n /**\n * Creates a (historical) member from a member event that is the next member event\n * after the point in time where we need a member for. This will use `prev_content`.\n */\n static fromReplacingMemberEvent(roomId, memberEvent) {\n const userId = memberEvent && memberEvent.state_key;\n if (typeof userId !== \"string\") {\n return;\n }\n const content = getPrevContentFromStateEvent(memberEvent);\n return this._validateAndCreateMember(roomId, userId,\n content?.membership,\n content?.displayname,\n content?.avatar_url\n );\n }\n\n static _validateAndCreateMember(roomId, userId, membership, displayName, avatarUrl) {\n if (typeof membership !== \"string\") {\n return;\n }\n return new RoomMember({\n roomId,\n userId,\n membership,\n avatarUrl,\n displayName,\n });\n }\n\n get membership() {\n return this._data.membership;\n }\n\n /**\n * @return {String?} the display name, if any\n */\n get displayName() {\n return this._data.displayName;\n }\n\n /**\n * @return {String} the display name or userId\n */\n get name() {\n return this._data.displayName || this._data.userId;\n }\n\n /**\n * @return {String?} the avatar mxc url, if any\n */\n get avatarUrl() {\n return this._data.avatarUrl;\n }\n\n get roomId() {\n return this._data.roomId;\n }\n\n get userId() {\n return this._data.userId;\n }\n\n serialize() {\n return this._data;\n }\n\n equals(other) {\n const data = this._data;\n const otherData = other._data;\n return data.roomId === otherData.roomId &&\n data.userId === otherData.userId &&\n data.membership === otherData.membership &&\n data.displayName === otherData.displayName &&\n data.avatarUrl === otherData.avatarUrl;\n }\n}\n\nexport class MemberChange {\n constructor(member, previousMembership) {\n this.member = member;\n this.previousMembership = previousMembership;\n }\n\n get roomId() {\n return this.member.roomId;\n }\n\n get userId() {\n return this.member.userId;\n }\n\n get membership() {\n return this.member.membership;\n }\n\n get wasInvited() {\n return this.previousMembership === \"invite\" && this.membership !== \"invite\";\n }\n\n get hasLeft() {\n return this.previousMembership === \"join\" && this.membership !== \"join\";\n }\n\n /** The result can be a false negative when all of these apply:\n * - the complete set of room members hasn't been fetched yet.\n * - the member event for this change was received in the\n * state section and wasn't present in the timeline section.\n * - the room response was limited, e.g. there was a gap.\n * \n * This is because during sync, in this case it is not possible\n * to distinguish between a new member that joined the room\n * during a gap and a lazy-loading member.\n * */\n get hasJoined() {\n return this.previousMembership !== \"join\" && this.membership === \"join\";\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport function isValidFragmentId(id) {\n return typeof id === \"number\";\n}\n\n// copied over from matrix-js-sdk, copyright 2016 OpenMarket Ltd\n/* _REDACT_KEEP_KEY_MAP gives the keys we keep when an event is redacted\n *\n * This is specified here:\n * http://matrix.org/speculator/spec/HEAD/client_server/latest.html#redactions\n *\n * Also:\n * - We keep 'unsigned' since that is created by the local server\n * - We keep user_id for backwards-compat with v1\n */\nconst _REDACT_KEEP_KEY_MAP = [\n 'event_id', 'type', 'room_id', 'user_id', 'sender', 'state_key', 'prev_state',\n 'content', 'unsigned', 'origin_server_ts',\n].reduce(function(ret, val) {\n ret[val] = 1; return ret;\n}, {});\n\n// a map from event type to the .content keys we keep when an event is redacted\nconst _REDACT_KEEP_CONTENT_MAP = {\n 'm.room.member': {'membership': 1},\n 'm.room.create': {'creator': 1},\n 'm.room.join_rules': {'join_rule': 1},\n 'm.room.power_levels': {'ban': 1, 'events': 1, 'events_default': 1,\n 'kick': 1, 'redact': 1, 'state_default': 1,\n 'users': 1, 'users_default': 1,\n },\n 'm.room.aliases': {'aliases': 1},\n};\n// end of matrix-js-sdk code\n\nexport function redactEvent(redactionEvent, redactedEvent) {\n for (const key of Object.keys(redactedEvent)) {\n if (!_REDACT_KEEP_KEY_MAP[key]) {\n delete redactedEvent[key];\n }\n }\n const { content } = redactedEvent;\n const keepMap = _REDACT_KEEP_CONTENT_MAP[redactedEvent.type];\n for (const key of Object.keys(content)) {\n if (!keepMap?.[key]) {\n delete content[key];\n }\n }\n redactedEvent.unsigned = redactedEvent.unsigned || {};\n redactedEvent.unsigned.redacted_because = redactionEvent;\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/*\nlookups will be far more frequent than changing fragment order,\nso data structure should be optimized for fast lookup\n\nwe can have a Map: fragmentId to sortIndex\n\nchanging the order, we would need to rebuild the index\nlets do this the stupid way for now, changing any fragment rebuilds all islands\n\nto build this:\nfirst load all fragments\nput them in a map by id\nnow iterate through them\n\nuntil no more fragments\n get the first\n create an island array, and add to list with islands\n going backwards and forwards\n get and remove sibling and prepend/append it to island array\n stop when no more previous/next\n return list with islands\n\n*/\n\nimport {isValidFragmentId} from \"./common.js\";\n\nfunction findBackwardSiblingFragments(current, byId) {\n const sortedSiblings = [];\n while (isValidFragmentId(current.previousId)) {\n const previous = byId.get(current.previousId);\n if (!previous) {\n break;\n }\n if (previous.nextId !== current.id) {\n throw new Error(`Previous fragment ${previous.id} doesn't point back to ${current.id}`);\n }\n byId.delete(current.previousId);\n sortedSiblings.unshift(previous);\n current = previous;\n }\n return sortedSiblings;\n}\n\nfunction findForwardSiblingFragments(current, byId) {\n const sortedSiblings = [];\n while (isValidFragmentId(current.nextId)) {\n const next = byId.get(current.nextId);\n if (!next) {\n break;\n }\n if (next.previousId !== current.id) {\n throw new Error(`Next fragment ${next.id} doesn't point back to ${current.id}`);\n }\n byId.delete(current.nextId);\n sortedSiblings.push(next);\n current = next;\n }\n return sortedSiblings;\n}\n\n\nfunction createIslands(fragments) {\n const byId = new Map();\n for(let f of fragments) {\n byId.set(f.id, f);\n }\n\n const islands = [];\n while(byId.size) {\n const current = byId.values().next().value;\n byId.delete(current.id);\n // new island\n const previousSiblings = findBackwardSiblingFragments(current, byId);\n const nextSiblings = findForwardSiblingFragments(current, byId);\n const island = previousSiblings.concat(current, nextSiblings);\n islands.push(island);\n }\n return islands.map(a => new Island(a));\n}\n\nclass Fragment {\n constructor(id, previousId, nextId) {\n this.id = id;\n this.previousId = previousId;\n this.nextId = nextId;\n }\n}\n\nclass Island {\n constructor(sortedFragments) {\n this._idToSortIndex = new Map();\n sortedFragments.forEach((f, i) => {\n this._idToSortIndex.set(f.id, i);\n });\n }\n\n compare(idA, idB) {\n const sortIndexA = this._idToSortIndex.get(idA);\n if (sortIndexA === undefined) {\n throw new Error(`first id ${idA} isn't part of this island`);\n }\n const sortIndexB = this._idToSortIndex.get(idB);\n if (sortIndexB === undefined) {\n throw new Error(`second id ${idB} isn't part of this island`);\n }\n return sortIndexA - sortIndexB;\n }\n\n get fragmentIds() {\n return this._idToSortIndex.keys();\n }\n}\n\nexport class CompareError extends Error {\n get name() { return \"CompareError\"; }\n}\n\n/*\nindex for fast lookup of how two fragments can be sorted\n*/\nexport class FragmentIdComparer {\n constructor(fragments) {\n this._fragmentsById = fragments.reduce((map, f) => {map.set(f.id, f); return map;}, new Map());\n this.rebuild(fragments);\n }\n\n _getIsland(id) {\n const island = this._idToIsland.get(id);\n if (island === undefined) {\n throw new CompareError(`Unknown fragment id ${id}`);\n }\n return island;\n }\n\n compare(idA, idB) {\n if (idA === idB) {\n return 0;\n }\n const islandA = this._getIsland(idA);\n const islandB = this._getIsland(idB);\n if (islandA !== islandB) {\n throw new CompareError(`${idA} and ${idB} are on different islands, can't tell order`);\n }\n return islandA.compare(idA, idB);\n }\n\n rebuild(fragments) {\n const islands = createIslands(fragments);\n this._idToIsland = new Map();\n for(let island of islands) {\n for(let id of island.fragmentIds) {\n this._idToIsland.set(id, island);\n }\n }\n }\n\n /** use for fragments coming out of persistence, not newly created ones, or also fragments for a new island (like for a permalink) */\n add(fragment) {\n const copy = new Fragment(fragment.id, fragment.previousId, fragment.nextId);\n this._fragmentsById.set(fragment.id, copy);\n this.rebuild(this._fragmentsById.values());\n }\n\n /** use for appending newly created fragments */\n append(id, previousId) {\n const fragment = new Fragment(id, previousId, null);\n const prevFragment = this._fragmentsById.get(previousId);\n if (prevFragment) {\n prevFragment.nextId = id;\n }\n this._fragmentsById.set(id, fragment);\n this.rebuild(this._fragmentsById.values());\n }\n\n /** use for prepending newly created fragments */\n prepend(id, nextId) {\n const fragment = new Fragment(id, null, nextId);\n const nextFragment = this._fragmentsById.get(nextId);\n if (nextFragment) {\n nextFragment.previousId = id;\n }\n this._fragmentsById.set(id, fragment);\n this.rebuild(this._fragmentsById.values());\n }\n}\n\nexport function tests() {\n return {\n test_1_island_3_fragments(assert) {\n const index = new FragmentIdComparer([\n {id: 3, previousId: 2},\n {id: 1, nextId: 2},\n {id: 2, nextId: 3, previousId: 1},\n ]);\n assert(index.compare(1, 2) < 0);\n assert(index.compare(2, 1) > 0);\n\n assert(index.compare(1, 3) < 0);\n assert(index.compare(3, 1) > 0);\n \n assert(index.compare(2, 3) < 0);\n assert(index.compare(3, 2) > 0);\n \n assert.equal(index.compare(1, 1), 0);\n },\n test_falsy_id(assert) {\n const index = new FragmentIdComparer([\n {id: 0, nextId: 1},\n {id: 1, previousId: 0},\n ]);\n assert(index.compare(0, 1) < 0);\n assert(index.compare(1, 0) > 0);\n },\n test_falsy_id_reverse(assert) {\n const index = new FragmentIdComparer([\n {id: 1, previousId: 0},\n {id: 0, nextId: 1},\n ]);\n assert(index.compare(0, 1) < 0);\n assert(index.compare(1, 0) > 0);\n },\n test_allow_unknown_id(assert) {\n // as we tend to load fragments incrementally\n // as events come into view, we need to allow\n // unknown previousId/nextId in the fragments that we do load\n assert.doesNotThrow(() => {\n new FragmentIdComparer([\n {id: 1, previousId: 2},\n {id: 0, nextId: 3},\n ]);\n });\n },\n test_throw_on_link_mismatch(assert) {\n // as we tend to load fragments incrementally\n // as events come into view, we need to allow\n // unknown previousId/nextId in the fragments that we do load\n assert.throws(() => {\n new FragmentIdComparer([\n {id: 1, previousId: 0},\n {id: 0, nextId: 2},\n ]);\n });\n },\n test_2_island_dont_compare(assert) {\n const index = new FragmentIdComparer([\n {id: 1},\n {id: 2},\n ]);\n assert.throws(() => index.compare(1, 2));\n assert.throws(() => index.compare(2, 1));\n },\n test_2_island_compare_internally(assert) {\n const index = new FragmentIdComparer([\n {id: 1, nextId: 2},\n {id: 2, previousId: 1},\n {id: 11, nextId: 12},\n {id: 12, previousId: 11},\n \n ]);\n\n assert(index.compare(1, 2) < 0);\n assert(index.compare(11, 12) < 0);\n \n assert.throws(() => index.compare(1, 11));\n assert.throws(() => index.compare(12, 2));\n },\n test_unknown_id(assert) {\n const index = new FragmentIdComparer([{id: 1}]);\n assert.throws(() => index.compare(1, 2));\n assert.throws(() => index.compare(2, 1));\n },\n test_rebuild_flushes_old_state(assert) {\n const index = new FragmentIdComparer([\n {id: 1, nextId: 2},\n {id: 2, previousId: 1},\n ]);\n index.rebuild([\n {id: 11, nextId: 12},\n {id: 12, previousId: 11},\n ]);\n \n assert.throws(() => index.compare(1, 2));\n assert(index.compare(11, 12) < 0);\n },\n test_append(assert) {\n const index = new FragmentIdComparer([]);\n // add livefragment when opening timeline,\n // note that there is no nextId as the sync\n // hasn't come in yet\n index.add({id: 1});\n // now sync comes in and replaces the live fragment\n index.append(2, 1);\n assert(index.compare(1, 2) < 0);\n },\n test_prepend(assert) {\n const index = new FragmentIdComparer([]);\n index.add({id: 2});\n index.prepend(1, 2);\n assert(index.compare(1, 2) < 0);\n }\n }\n}\n","const scriptRel = (function detectScriptRel() {\n // @ts-ignore\n const relList = document.createElement('link').relList;\n // @ts-ignore\n return relList && relList.supports && relList.supports('modulepreload')\n ? 'modulepreload'\n : 'preload';\n})();const seen = {};const base = './';export const __vitePreload = function preload(baseModule, deps) {\n // @ts-ignore\n if (!__VITE_IS_MODERN__ || !deps || deps.length === 0) {\n return baseModule();\n }\n return Promise.all(deps.map((dep) => {\n // @ts-ignore\n dep = `${base}${dep}`;\n // @ts-ignore\n if (dep in seen)\n return;\n // @ts-ignore\n seen[dep] = true;\n const isCss = dep.endsWith('.css');\n const cssSelector = isCss ? '[rel=\"stylesheet\"]' : '';\n // @ts-ignore check if the file is already preloaded by SSR markup\n if (document.querySelector(`link[href=\"${dep}\"]${cssSelector}`)) {\n return;\n }\n // @ts-ignore\n const link = document.createElement('link');\n // @ts-ignore\n link.rel = isCss ? 'stylesheet' : scriptRel;\n if (!isCss) {\n link.as = 'script';\n link.crossOrigin = '';\n }\n link.href = dep;\n // @ts-ignore\n document.head.appendChild(link);\n if (isCss) {\n return new Promise((res, rej) => {\n link.addEventListener('load', res);\n link.addEventListener('error', () => rej(new Error(`Unable to preload CSS for ${dep}`)));\n });\n }\n })).then(() => baseModule());\n}","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { StorageError } from \"../common\";\n\nfunction _sourceName(source: IDBIndex | IDBObjectStore): string {\n return \"objectStore\" in source ?\n `${source.objectStore.name}.${source.name}` :\n source.name;\n}\n\nfunction _sourceDatabase(source: IDBIndex | IDBObjectStore): string {\n return \"objectStore\" in source ?\n source.objectStore?.transaction?.db?.name :\n source.transaction?.db?.name;\n}\n\nexport class IDBError extends StorageError {\n storeName: string;\n databaseName: string;\n\n constructor(message: string, sourceOrCursor: IDBIndex | IDBCursor | IDBObjectStore | null, cause: DOMException | null = null) {\n const source = (sourceOrCursor && \"source\" in sourceOrCursor) ? sourceOrCursor.source : sourceOrCursor;\n const storeName = source ? _sourceName(source) : \"\";\n const databaseName = source ? _sourceDatabase(source) : \"\";\n let fullMessage = `${message} on ${databaseName}.${storeName}`;\n if (cause) {\n fullMessage += \": \";\n if (typeof cause.name === \"string\") {\n fullMessage += `(name: ${cause.name}) `;\n }\n if (typeof cause.code === \"number\") {\n fullMessage += `(code: ${cause.code}) `;\n }\n }\n if (cause) {\n fullMessage += cause.message;\n }\n super(fullMessage, cause);\n this.storeName = storeName;\n this.databaseName = databaseName;\n }\n}\n\nexport class IDBRequestError extends IDBError {\n private errorEvent: Event;\n\n constructor(errorEvent: Event) {\n const request = errorEvent.target as IDBRequest;\n const source = request.source;\n const cause = request.error;\n super(\"IDBRequest failed\", source, cause);\n this.errorEvent = errorEvent;\n }\n\n preventTransactionAbort() {\n this.errorEvent.preventDefault();\n }\n}\n\nexport class IDBRequestAttemptError extends IDBError {\n constructor(method: string, source: IDBIndex | IDBObjectStore, cause: DOMException, params: any[]) {\n super(`${method}(${params.map(p => JSON.stringify(p)).join(\", \")}) failed`, source, cause);\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { IDBRequestError } from \"./error\";\nimport { StorageError } from \"../common\";\nimport { AbortError } from \"../../../utils/error\";\n\nlet needsSyncPromise = false;\n\nexport const DONE = { done: true }\nexport const NOT_DONE = { done: false }\n\n/* should be called on legacy platforms to see\n if transactions close before draining the microtask queue (IE11 on Windows 7).\n If this is the case, promises need to be resolved\n synchronously from the idb request handler to prevent the transaction from closing prematurely.\n*/\nexport async function checkNeedsSyncPromise(): Promise<boolean> {\n // important to have it turned off while doing the test,\n // otherwise reqAsPromise would not fail\n needsSyncPromise = false;\n const NAME = \"test-idb-needs-sync-promise\";\n const db = await openDatabase(NAME, db => {\n db.createObjectStore(\"test\", {keyPath: \"key\"});\n }, 1);\n const txn = db.transaction(\"test\", \"readonly\");\n try {\n await reqAsPromise(txn.objectStore(\"test\").get(1));\n await reqAsPromise(txn.objectStore(\"test\").get(2));\n } catch (err) {\n // err.name would be either TransactionInactiveError or InvalidStateError,\n // but let's not exclude any other failure modes\n needsSyncPromise = true;\n }\n // we could delete the store here, \n // but let's not create it on every page load on legacy platforms,\n // and just keep it around\n return needsSyncPromise;\n}\n\n// storage keys are defined to be unsigned 32bit numbers in KeyLimits, which is assumed by idb\nexport function encodeUint32(n: number): string {\n const hex = n.toString(16);\n return \"0\".repeat(8 - hex.length) + hex;\n}\n\n// used for logs where timestamp is part of key, which is larger than 32 bit\nexport function encodeUint64(n: number): string {\n const hex = n.toString(16);\n return \"0\".repeat(16 - hex.length) + hex;\n}\n\nexport function decodeUint32(str: string): number {\n return parseInt(str, 16);\n}\n\nexport type CreateObjectStore = (db : IDBDatabase, txn: IDBTransaction | null, oldVersion: number, version: number) => any\n\nexport function openDatabase(name: string, createObjectStore: CreateObjectStore, version: number, idbFactory: IDBFactory = window.indexedDB): Promise<IDBDatabase> {\n const req = idbFactory.open(name, version);\n req.onupgradeneeded = async (ev : IDBVersionChangeEvent) => {\n const req = ev.target as IDBRequest<IDBDatabase>;\n const db = req.result;\n const txn = req.transaction!;\n const oldVersion = ev.oldVersion;\n try {\n await createObjectStore(db, txn, oldVersion, version);\n } catch (err) {\n // try aborting on error, if that hasn't been done already\n try {\n txn.abort();\n } catch (err) {}\n }\n }; \n return reqAsPromise(req);\n}\n\nexport function reqAsPromise<T>(req: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n req.addEventListener(\"success\", event => {\n resolve((event.target as IDBRequest<T>).result);\n // @ts-ignore\n needsSyncPromise && Promise._flush && Promise._flush();\n });\n req.addEventListener(\"error\", event => {\n const error = new IDBRequestError(event);\n reject(error);\n // @ts-ignore\n needsSyncPromise && Promise._flush && Promise._flush();\n });\n });\n}\n\nexport function txnAsPromise(txn): Promise<void> {\n let error;\n return new Promise((resolve, reject) => {\n txn.addEventListener(\"complete\", () => {\n resolve();\n // @ts-ignore\n needsSyncPromise && Promise._flush && Promise._flush();\n });\n txn.addEventListener(\"abort\", event => {\n reject(new AbortError());\n // @ts-ignore\n needsSyncPromise && Promise._flush && Promise._flush();\n });\n });\n}\n\n/**\n * This type is rather complicated, but I hope that this is for a good reason. There\n * are currently two uses for `iterateCursor`: iterating a regular cursor, and iterating\n * a key-only cursor, which does not have values. These two uses are distinct, and iteration\n * never stops or starts having a value halfway through.\n *\n * Each of the argument functions currently either assumes the value will be there, or that it won't. We thus can't\n * just accept a function argument `(T | undefined) => { done: boolean }`, since this messes with\n * the type safety in both cases: the former case will have to check for `undefined`, and\n * the latter would have an argument that can be `T`, even though it never will.\n *\n * So the approach here is to let TypeScript infer and accept (via generics) the type of\n * the cursor, which is either `IDBCursorWithValue` or `IDBCursor`. Since the type is accepted\n * via generics, we can actually vary the types of the actual function arguments depending on it.\n * Thus, when a value is available (an `IDBCursorWithValue` is given), we require a function `(T) => ...`, and when it is not, we require\n * a function `(undefined) => ...`.\n */\ntype CursorIterator<T, I extends IDBCursor> = (value: I extends IDBCursorWithValue ? T : undefined, key: IDBValidKey, cursor: I) => { done: boolean, jumpTo?: IDBValidKey }\n\nexport function iterateCursor<T, I extends IDBCursor = IDBCursorWithValue>(cursorRequest: IDBRequest<I | null>, processValue: CursorIterator<T, I>): Promise<boolean> {\n // TODO: does cursor already have a value here??\n return new Promise<boolean>((resolve, reject) => {\n cursorRequest.onerror = event => {\n reject(new IDBRequestError(event));\n // @ts-ignore\n needsSyncPromise && Promise._flush && Promise._flush();\n };\n // collect results\n cursorRequest.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<I>).result;\n if (!cursor) {\n resolve(false);\n // @ts-ignore\n needsSyncPromise && Promise._flush && Promise._flush();\n return; // end of results\n }\n const result = processValue(cursor[\"value\"], cursor.key, cursor);\n // TODO: don't use object for result and assume it's jumpTo when not === true/false or undefined\n const done = result?.done;\n const jumpTo = result?.jumpTo;\n\n if (done) {\n resolve(true);\n // @ts-ignore\n needsSyncPromise && Promise._flush && Promise._flush();\n } else if(jumpTo) {\n cursor.continue(jumpTo);\n } else {\n cursor.continue();\n }\n };\n }).catch(err => {\n throw new StorageError(\"iterateCursor failed\", err);\n });\n}\n\ntype Pred<T> = (value: T) => boolean\n\nexport async function fetchResults<T>(cursor: IDBRequest, isDone: Pred<T[]>): Promise<T[]> {\n const results: T[] = [];\n await iterateCursor<T>(cursor, (value) => {\n results.push(value);\n return {done: isDone(results)};\n });\n return results;\n}\n\ntype ToCursor = (store: IDBObjectStore) => IDBRequest\n\nexport async function select<T>(db: IDBDatabase, storeName: string, toCursor: ToCursor, isDone: Pred<T[]>): Promise<T[]> {\n if (!isDone) {\n isDone = () => false;\n }\n if (!toCursor) {\n toCursor = store => store.openCursor();\n }\n const tx = db.transaction([storeName], \"readonly\");\n const store = tx.objectStore(storeName);\n const cursor = toCursor(store);\n return await fetchResults(cursor, isDone);\n}\n\nexport async function findStoreValue<T>(db: IDBDatabase, storeName: string, toCursor: ToCursor, matchesValue: Pred<T>): Promise<T> {\n if (!matchesValue) {\n matchesValue = () => true;\n }\n if (!toCursor) {\n toCursor = store => store.openCursor();\n }\n\n const tx = db.transaction([storeName], \"readwrite\");\n const store = tx.objectStore(storeName);\n const cursor = await reqAsPromise(toCursor(store));\n let match;\n const matched = await iterateCursor<T>(cursor, (value) => {\n if (matchesValue(value)) {\n match = value;\n return DONE;\n }\n return NOT_DONE;\n });\n if (!matched) {\n throw new StorageError(\"Value not found\");\n }\n return match;\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {iterateCursor, DONE, NOT_DONE, reqAsPromise} from \"./utils\";\nimport {StorageError} from \"../common\";\nimport {ILogItem} from \"../../../logging/types\";\nimport {IDBKey} from \"./Transaction\";\n\n// this is the part of the Transaction class API that is used here and in the Store subclass,\n// to make it easier to replace it with alternative implementations in schema.ts and unit tests\nexport interface ITransaction {\n idbFactory: IDBFactory;\n IDBKeyRange: typeof IDBKeyRange;\n databaseName: string;\n addWriteError(error: StorageError, refItem: ILogItem | undefined, operationName: string, keys: IDBKey[] | undefined);\n}\n\ntype Reducer<A,B> = (acc: B, val: A) => B\n\nexport type IDBQuery = IDBValidKey | IDBKeyRange | undefined | null\n\ninterface QueryTargetInterface<T> {\n openCursor(range?: IDBQuery, direction?: IDBCursorDirection | undefined): IDBRequest<IDBCursorWithValue | null>;\n openKeyCursor(range?: IDBQuery, direction?: IDBCursorDirection | undefined): IDBRequest<IDBCursor | null>;\n supports(method: string): boolean;\n keyPath: string | string[];\n count(keyRange?: IDBKeyRange): IDBRequest<number>;\n get(key: IDBValidKey | IDBKeyRange): IDBRequest<T | undefined>;\n getKey(key: IDBValidKey | IDBKeyRange): IDBRequest<IDBValidKey | undefined>;\n}\n\nexport class QueryTarget<T> {\n protected _target: QueryTargetInterface<T>;\n protected _transaction: ITransaction;\n\n constructor(target: QueryTargetInterface<T>, transaction: ITransaction) {\n this._target = target;\n this._transaction = transaction;\n }\n\n get idbFactory(): IDBFactory {\n return this._transaction.idbFactory;\n }\n\n get IDBKeyRange(): typeof IDBKeyRange {\n return this._transaction.IDBKeyRange;\n }\n\n get databaseName(): string {\n return this._transaction.databaseName;\n }\n\n _openCursor(range?: IDBQuery, direction?: IDBCursorDirection): IDBRequest<IDBCursorWithValue | null> {\n if (range && direction) {\n return this._target.openCursor(range, direction);\n } else if (range) {\n return this._target.openCursor(range);\n } else if (direction) {\n return this._target.openCursor(null, direction);\n } else {\n return this._target.openCursor();\n }\n }\n\n supports(methodName: string): boolean {\n return this._target.supports(methodName);\n }\n\n count(keyRange?: IDBKeyRange): Promise<number> {\n return reqAsPromise(this._target.count(keyRange));\n }\n\n get(key: IDBValidKey | IDBKeyRange): Promise<T | undefined> {\n return reqAsPromise(this._target.get(key));\n }\n\n getKey(key: IDBValidKey | IDBKeyRange): Promise<IDBValidKey | undefined> {\n if (this._target.supports(\"getKey\")) {\n return reqAsPromise(this._target.getKey(key));\n } else {\n return reqAsPromise(this._target.get(key)).then(value => {\n if (value) {\n let keyPath = this._target.keyPath;\n if (typeof keyPath === \"string\") {\n keyPath = [keyPath];\n }\n return keyPath.reduce((obj, key) => obj[key], value);\n }\n });\n }\n }\n\n reduce<B>(range: IDBQuery, reducer: Reducer<T,B>, initialValue: B): Promise<boolean> {\n return this._reduce(range, reducer, initialValue, \"next\");\n }\n\n reduceReverse<B>(range: IDBQuery, reducer: Reducer<T,B>, initialValue: B): Promise<boolean> {\n return this._reduce(range, reducer, initialValue, \"prev\");\n }\n \n selectLimit(range: IDBQuery, amount: number): Promise<T[]> {\n return this._selectLimit(range, amount, \"next\");\n }\n\n selectLimitReverse(range: IDBQuery, amount: number): Promise<T[]> {\n return this._selectLimit(range, amount, \"prev\");\n }\n\n selectWhile(range: IDBQuery, predicate: (v: T) => boolean): Promise<T[]> {\n return this._selectWhile(range, predicate, \"next\");\n }\n\n selectWhileReverse(range: IDBQuery, predicate: (v: T) => boolean): Promise<T[]> {\n return this._selectWhile(range, predicate, \"prev\");\n }\n\n async selectAll(range?: IDBQuery, direction?: IDBCursorDirection): Promise<T[]> {\n const cursor = this._openCursor(range, direction);\n const results: T[] = [];\n await iterateCursor<T>(cursor, (value) => {\n results.push(value);\n return NOT_DONE;\n });\n return results;\n }\n\n selectFirst(range: IDBQuery): Promise<T | undefined> {\n return this._find(range, () => true, \"next\");\n }\n\n selectLast(range: IDBQuery): Promise<T | undefined> {\n return this._find(range, () => true, \"prev\");\n }\n\n find(range: IDBQuery, predicate: (v: T) => boolean): Promise<T | undefined> {\n return this._find(range, predicate, \"next\");\n }\n\n findReverse(range: IDBQuery, predicate: (v : T) => boolean): Promise<T | undefined> {\n return this._find(range, predicate, \"prev\");\n }\n\n async findMaxKey(range: IDBQuery): Promise<IDBValidKey | undefined> {\n const cursor = this._target.openKeyCursor(range, \"prev\");\n let maxKey;\n await iterateCursor(cursor, (_, key) => {\n maxKey = key;\n return DONE;\n });\n return maxKey;\n }\n\n\n async iterateValues(range: IDBQuery, callback: (val: T, key: IDBValidKey, cur: IDBCursorWithValue) => boolean): Promise<void> {\n const cursor = this._target.openCursor(range, \"next\");\n await iterateCursor<T>(cursor, (value, key, cur) => {\n return {done: callback(value, key, cur)};\n });\n }\n\n async iterateKeys(range: IDBQuery, callback: (key: IDBValidKey, cur: IDBCursor) => boolean): Promise<void> {\n const cursor = this._target.openKeyCursor(range, \"next\");\n await iterateCursor(cursor, (_, key, cur) => {\n return {done: callback(key, cur)};\n });\n }\n\n /**\n * Checks if a given set of keys exist.\n * If the callback returns true, the search is halted and callback won't be called again.\n */\n async findExistingKeys(keys: IDBValidKey[], backwards: boolean, callback: (key: IDBValidKey, pk: IDBValidKey) => boolean): Promise<void> {\n const compareKeys = (a, b) => backwards ? -this.idbFactory.cmp(a, b) : this.idbFactory.cmp(a, b);\n const sortedKeys = keys.slice().sort(compareKeys);\n const firstKey = sortedKeys[0];\n const lastKey = sortedKeys[sortedKeys.length - 1];\n const direction = backwards ? \"prev\" : \"next\";\n const cursor = this._target.openKeyCursor(this.IDBKeyRange.bound(firstKey, lastKey), direction);\n let index = 0;\n await iterateCursor(cursor, (value, key, cursor) => {\n while (index < sortedKeys.length && compareKeys(sortedKeys[index], key) < 0) {\n index += 1;\n }\n let done = false;\n if (sortedKeys[index] === key) {\n const pk = cursor.primaryKey;\n done = callback(key, pk);\n index += 1;\n }\n if (done || index >= sortedKeys.length) {\n return DONE;\n } else {\n return {\n done: false,\n jumpTo: sortedKeys[index],\n }\n }\n });\n }\n\n _reduce<B>(range: IDBQuery, reducer: (reduced: B, value: T) => B, initialValue: B, direction: IDBCursorDirection): Promise<boolean> {\n let reducedValue = initialValue;\n const cursor = this._openCursor(range, direction);\n return iterateCursor<T>(cursor, (value) => {\n reducedValue = reducer(reducedValue, value);\n return NOT_DONE;\n });\n }\n\n _selectLimit(range: IDBQuery, amount: number, direction: IDBCursorDirection): Promise<T[]> {\n return this._selectUntil(range, (results) => {\n return results.length === amount;\n }, direction);\n }\n\n async _selectUntil(range: IDBQuery, predicate: (vs: T[], v: T) => boolean, direction: IDBCursorDirection): Promise<T[]> {\n const cursor = this._openCursor(range, direction);\n const results: T[] = [];\n await iterateCursor<T>(cursor, (value) => {\n results.push(value);\n return {done: predicate(results, value)};\n });\n return results;\n }\n\n // allows you to fetch one too much that won't get added when the predicate fails\n async _selectWhile(range: IDBQuery, predicate: (v: T) => boolean, direction: IDBCursorDirection): Promise<T[]> {\n const cursor = this._openCursor(range, direction);\n const results: T[] = [];\n await iterateCursor<T>(cursor, (value) => {\n const passesPredicate = predicate(value);\n if (passesPredicate) {\n results.push(value);\n }\n return {done: !passesPredicate};\n });\n return results;\n }\n\n async iterateWhile(range: IDBQuery, predicate: (v: T) => boolean): Promise<void> {\n const cursor = this._openCursor(range, \"next\");\n await iterateCursor<T>(cursor, (value) => {\n const passesPredicate = predicate(value);\n return {done: !passesPredicate};\n });\n }\n\n async _find(range: IDBQuery, predicate: (v: T) => boolean, direction: IDBCursorDirection): Promise<T | undefined> {\n const cursor = this._openCursor(range, direction);\n let result;\n const found = await iterateCursor<T>(cursor, (value) => {\n const found = predicate(value);\n if (found) {\n result = value;\n }\n return {done: found};\n });\n if (found) {\n return result;\n }\n }\n}\n\nimport {createMockDatabase, createMockIDBFactory, getMockIDBKeyRange} from \"../../../mocks/Storage\";\nimport {txnAsPromise} from \"./utils\";\nimport {QueryTargetWrapper, Store} from \"./Store\";\n\nexport async function tests() {\n\n class MockTransaction {\n constructor(public readonly idbFactory: IDBFactory, readonly idbKeyRangeType: typeof IDBKeyRange) {}\n\n get IDBKeyRange(): typeof IDBKeyRange {\n return this.idbKeyRangeType;\n }\n get databaseName(): string { return \"mockdb\"; }\n addWriteError(error: StorageError, refItem: ILogItem | undefined, operationName: string, keys: IDBKey[] | undefined) {}\n }\n\n interface TestEntry {\n key: string\n }\n\n async function createTestStore(): Promise<Store<TestEntry>> {\n const idbFactory = await createMockIDBFactory();\n const idbKeyRangeType = await getMockIDBKeyRange();\n const mockImpl = new MockTransaction(idbFactory, idbKeyRangeType);\n const db = await createMockDatabase(\"findExistingKeys\", (db: IDBDatabase) => {\n db.createObjectStore(\"test\", {keyPath: \"key\"});\n }, idbFactory);\n const txn = db.transaction([\"test\"], \"readwrite\");\n return new Store<TestEntry>(txn.objectStore(\"test\"), mockImpl);\n }\n\n return {\n \"findExistingKeys should not match on empty store\": async assert => {\n const store = await createTestStore();\n await store.findExistingKeys([\"2db1a709-d8f1-4c40-a835-f312badd277a\", \"fe7aa5c2-d4ed-4278-b3b0-f49d48d11df2\"], false, () => {\n assert.fail(\"no key should match\");\n return false;\n });\n },\n \"findExistingKeys should not match any existing keys (in between sorting order)\": async assert => {\n const store = await createTestStore();\n store.add({key: \"43cd16eb-a6b4-4b9d-ab36-ab87d1b038c3\"});\n store.add({key: \"b655e7c5-e02d-4823-a7af-4202b12de659\"});\n await store.findExistingKeys([\"2db1a709-d8f1-4c40-a835-f312badd277a\", \"fe7aa5c2-d4ed-4278-b3b0-f49d48d11df2\"], false, () => {\n assert.fail(\"no key should match\");\n return false;\n });\n },\n \"findExistingKeys should match only existing keys\": async assert => {\n const store = await createTestStore();\n store.add({key: \"2db1a709-d8f1-4c40-a835-f312badd277a\"});\n store.add({key: \"43cd16eb-a6b4-4b9d-ab36-ab87d1b038c3\"});\n store.add({key: \"fe7aa5c2-d4ed-4278-b3b0-f49d48d11df2\"});\n const found: string[] = [];\n await store.findExistingKeys([\n \"2db1a709-d8f1-4c40-a835-f312badd277a\",\n \"eac3ef5c-a48f-4e19-b41d-ebd1d84c53f2\",\n \"fe7aa5c2-d4ed-4278-b3b0-f49d48d11df2\"\n ], false, (key: IDBValidKey) => {\n found.push(key as string);\n return false;\n });\n assert.equal(found.length, 2);\n assert.equal(found[0], \"2db1a709-d8f1-4c40-a835-f312badd277a\");\n assert.equal(found[1], \"fe7aa5c2-d4ed-4278-b3b0-f49d48d11df2\");\n },\n \"findExistingKeys should match all if all exist\": async assert => {\n const store = await createTestStore();\n store.add({key: \"2db1a709-d8f1-4c40-a835-f312badd277a\"});\n store.add({key: \"fe7aa5c2-d4ed-4278-b3b0-f49d48d11df2\"});\n store.add({key: \"b655e7c5-e02d-4823-a7af-4202b12de659\"});\n const found: string[] = [];\n await store.findExistingKeys([\n \"2db1a709-d8f1-4c40-a835-f312badd277a\",\n \"b655e7c5-e02d-4823-a7af-4202b12de659\",\n \"fe7aa5c2-d4ed-4278-b3b0-f49d48d11df2\"\n ], false, (key: IDBValidKey) => {\n found.push(key as string);\n return false;\n });\n assert.equal(found.length, 3);\n assert.equal(found[0], \"2db1a709-d8f1-4c40-a835-f312badd277a\");\n assert.equal(found[1], \"b655e7c5-e02d-4823-a7af-4202b12de659\");\n assert.equal(found[2], \"fe7aa5c2-d4ed-4278-b3b0-f49d48d11df2\");\n },\n \"findExistingKeys should stop matching when callback returns true\": async assert => {\n const store = await createTestStore();\n store.add({key: \"2db1a709-d8f1-4c40-a835-f312badd277a\"});\n store.add({key: \"fe7aa5c2-d4ed-4278-b3b0-f49d48d11df2\"});\n store.add({key: \"b655e7c5-e02d-4823-a7af-4202b12de659\"});\n const found: string[] = [];\n await store.findExistingKeys([\n \"2db1a709-d8f1-4c40-a835-f312badd277a\",\n \"b655e7c5-e02d-4823-a7af-4202b12de659\",\n \"fe7aa5c2-d4ed-4278-b3b0-f49d48d11df2\"\n ], false, (key: IDBValidKey) => {\n found.push(key as string);\n return true;\n });\n assert.equal(found.length, 1);\n assert.equal(found[0], \"2db1a709-d8f1-4c40-a835-f312badd277a\");\n },\n \n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {QueryTarget, IDBQuery, ITransaction} from \"./QueryTarget\";\nimport {IDBRequestError, IDBRequestAttemptError} from \"./error\";\nimport {reqAsPromise} from \"./utils\";\nimport {Transaction, IDBKey} from \"./Transaction\";\nimport {ILogItem} from \"../../../logging/types\";\n\nconst LOG_REQUESTS = false;\n\nfunction logRequest(method: string, params: any[], source: any): void {\n const storeName = source?.name;\n const databaseName = source?.transaction?.db?.name;\n console.info(`${databaseName}.${storeName}.${method}(${params.map(p => JSON.stringify(p)).join(\", \")})`);\n}\n\nexport class QueryTargetWrapper<T> {\n private _qt: IDBIndex | IDBObjectStore;\n\n constructor(qt: IDBIndex | IDBObjectStore) {\n this._qt = qt;\n }\n\n get keyPath(): string | string[] {\n return this._qtStore.keyPath;\n }\n\n get _qtStore(): IDBObjectStore {\n if (\"objectStore\" in this._qt) {\n return this._qt.objectStore;\n }\n return this._qt;\n }\n\n supports(methodName: string): boolean {\n return !!this._qt[methodName];\n }\n \n openKeyCursor(range?: IDBQuery, direction?: IDBCursorDirection | undefined): IDBRequest<IDBCursor | null> {\n try {\n // not supported on Edge 15\n if (!this._qt.openKeyCursor) {\n LOG_REQUESTS && logRequest(\"openCursor\", [range, direction], this._qt);\n return this.openCursor(range, direction);\n }\n LOG_REQUESTS && logRequest(\"openKeyCursor\", [range, direction], this._qt);\n return this._qt.openKeyCursor(range, direction)\n } catch(err) {\n throw new IDBRequestAttemptError(\"openKeyCursor\", this._qt, err, [range, direction]);\n }\n }\n \n openCursor(range?: IDBQuery, direction?: IDBCursorDirection | undefined): IDBRequest<IDBCursorWithValue | null> {\n try {\n LOG_REQUESTS && logRequest(\"openCursor\", [], this._qt);\n return this._qt.openCursor(range, direction)\n } catch(err) {\n throw new IDBRequestAttemptError(\"openCursor\", this._qt, err, [range, direction]);\n }\n }\n\n put(item: T, key?: IDBValidKey | undefined): IDBRequest<IDBValidKey> {\n try {\n LOG_REQUESTS && logRequest(\"put\", [item, key], this._qt);\n return this._qtStore.put(item, key);\n } catch(err) {\n throw new IDBRequestAttemptError(\"put\", this._qt, err, [item, key]);\n }\n }\n\n add(item: T, key?: IDBValidKey | undefined): IDBRequest<IDBValidKey> {\n try {\n LOG_REQUESTS && logRequest(\"add\", [item, key], this._qt);\n return this._qtStore.add(item, key);\n } catch(err) {\n throw new IDBRequestAttemptError(\"add\", this._qt, err, [item, key]);\n }\n }\n\n get(key: IDBValidKey | IDBKeyRange): IDBRequest<T | undefined> {\n try {\n LOG_REQUESTS && logRequest(\"get\", [key], this._qt);\n return this._qt.get(key);\n } catch(err) {\n throw new IDBRequestAttemptError(\"get\", this._qt, err, [key]);\n }\n }\n \n getKey(key: IDBValidKey | IDBKeyRange): IDBRequest<IDBValidKey | undefined> {\n try {\n LOG_REQUESTS && logRequest(\"getKey\", [key], this._qt);\n return this._qt.getKey(key)\n } catch(err) {\n throw new IDBRequestAttemptError(\"getKey\", this._qt, err, [key]);\n }\n }\n\n delete(key: IDBValidKey | IDBKeyRange): IDBRequest<undefined> {\n try {\n LOG_REQUESTS && logRequest(\"delete\", [key], this._qt);\n return this._qtStore.delete(key);\n } catch(err) {\n throw new IDBRequestAttemptError(\"delete\", this._qt, err, [key]);\n }\n }\n\n count(keyRange?: IDBKeyRange): IDBRequest<number> {\n try {\n return this._qt.count(keyRange);\n } catch(err) {\n throw new IDBRequestAttemptError(\"count\", this._qt, err, [keyRange]);\n }\n }\n\n index(name: string): IDBIndex {\n try {\n return this._qtStore.index(name);\n } catch(err) {\n // TODO: map to different error? this is not a request\n throw new IDBRequestAttemptError(\"index\", this._qt, err, [name]);\n }\n }\n\n get indexNames(): string[] {\n return Array.from(this._qtStore.indexNames);\n }\n}\n\nexport class Store<T> extends QueryTarget<T> {\n constructor(idbStore: IDBObjectStore, transaction: ITransaction) {\n super(new QueryTargetWrapper<T>(idbStore), transaction);\n }\n\n get _idbStore(): QueryTargetWrapper<T> {\n return (this._target as QueryTargetWrapper<T>);\n }\n\n index(indexName: string): QueryTarget<T> {\n return new QueryTarget<T>(new QueryTargetWrapper<T>(this._idbStore.index(indexName)), this._transaction);\n }\n\n put(value: T, log?: ILogItem): void {\n // If this request fails, the error will bubble up to the transaction and abort it,\n // which is the behaviour we want. Therefore, it is ok to not create a promise for this\n // request and await it.\n // \n // Perhaps at some later point, we will want to handle an error (like ConstraintError) for\n // individual write requests. In that case, we should add a method that returns a promise (e.g. putAndObserve)\n // and call preventDefault on the event to prevent it from aborting the transaction\n // \n // Note that this can still throw synchronously, like it does for TransactionInactiveError,\n // see https://www.w3.org/TR/IndexedDB-2/#transaction-lifetime-concept\n const request = this._idbStore.put(value);\n this._prepareErrorLog(request, log, \"put\", undefined, value);\n }\n\n add(value: T, log?: ILogItem): void {\n // ok to not monitor result of request, see comment in `put`.\n const request = this._idbStore.add(value);\n this._prepareErrorLog(request, log, \"add\", undefined, value);\n }\n\n async tryAdd(value: T, log: ILogItem): Promise<boolean> {\n try {\n await reqAsPromise(this._idbStore.add(value));\n return true;\n } catch (err) {\n if (err instanceof IDBRequestError) {\n log.log({l: \"could not write\", id: this._getKeys(value), e: err}, log.level.Warn);\n err.preventTransactionAbort();\n return false;\n } else {\n throw err;\n }\n }\n }\n\n delete(keyOrKeyRange: IDBValidKey | IDBKeyRange, log?: ILogItem): void {\n // ok to not monitor result of request, see comment in `put`.\n const request = this._idbStore.delete(keyOrKeyRange);\n this._prepareErrorLog(request, log, \"delete\", keyOrKeyRange, undefined);\n }\n\n private _prepareErrorLog(request: IDBRequest, log: ILogItem | undefined, operationName: string, key: IDBKey | undefined, value: T | undefined) {\n if (log) {\n log.ensureRefId();\n }\n reqAsPromise(request).catch(err => {\n let keys : IDBKey[] | undefined = undefined;\n if (value) {\n keys = this._getKeys(value);\n } else if (key) {\n keys = [key];\n }\n this._transaction.addWriteError(err, log, operationName, keys);\n });\n }\n\n private _getKeys(value: T): IDBValidKey[] {\n const keys: IDBValidKey[] = [];\n const {keyPath} = this._idbStore;\n try {\n keys.push(this._readKeyPath(value, keyPath));\n } catch (err) {\n console.warn(\"could not read keyPath\", keyPath);\n }\n for (const indexName of this._idbStore.indexNames) {\n try {\n const index = this._idbStore.index(indexName);\n keys.push(this._readKeyPath(value, index.keyPath));\n } catch (err) {\n console.warn(\"could not read index\", indexName);\n }\n }\n return keys;\n }\n\n private _readKeyPath(value: T, keyPath: string[] | string): IDBValidKey {\n if (Array.isArray(keyPath)) {\n let field: any = value;\n for (const part of keyPath) {\n if (typeof field === \"object\") {\n field = field[part];\n } else {\n break;\n }\n }\n return field as IDBValidKey;\n } else {\n return value[keyPath] as IDBValidKey;\n }\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport function stringify(value: any): string {\n return JSON.stringify(encodeValue(value));\n}\n\nexport function parse(value: string): any {\n return decodeValue(JSON.parse(value));\n}\n\nfunction encodeValue(value: any): any {\n if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n // TypedArray\n if (value.byteLength) {\n return {_type: value.constructor.name, value: Array.from(value)};\n }\n let newObj = {};\n for (const prop in value) {\n if (value.hasOwnProperty(prop)) {\n newObj[prop] = encodeValue(value[prop]);\n }\n }\n return newObj;\n } else {\n return value;\n }\n}\n\nfunction decodeValue(value: any): any {\n if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n if (typeof value._type === \"string\") {\n switch (value._type) {\n case \"Int8Array\": return Int8Array.from(value.value);\n case \"Uint8Array\": return Uint8Array.from(value.value);\n case \"Uint8ClampedArray\": return Uint8ClampedArray.from(value.value);\n case \"Int16Array\": return Int16Array.from(value.value);\n case \"Uint16Array\": return Uint16Array.from(value.value);\n case \"Int32Array\": return Int32Array.from(value.value);\n case \"Uint32Array\": return Uint32Array.from(value.value);\n case \"Float32Array\": return Float32Array.from(value.value);\n case \"Float64Array\": return Float64Array.from(value.value);\n case \"BigInt64Array\": return BigInt64Array.from(value.value);\n case \"BigUint64Array\": return BigUint64Array.from(value.value);\n default:\n return value.value;\n }\n }\n let newObj = {};\n for (const prop in value) {\n if (value.hasOwnProperty(prop)) {\n newObj[prop] = decodeValue(value[prop]);\n }\n }\n return newObj;\n } else {\n return value;\n }\n}\n\nexport function tests() {\n return {\n \"Uint8Array and primitives\": assert => {\n const value = {\n foo: \"bar\",\n bar: 5,\n baz: false,\n fuzz: new Uint8Array([3, 1, 2])\n };\n const serialized = stringify(value);\n assert.strictEqual(typeof serialized, \"string\");\n const deserialized = parse(serialized);\n assert.strictEqual(deserialized.foo, \"bar\");\n assert.strictEqual(deserialized.bar, 5);\n assert.strictEqual(deserialized.baz, false);\n assert(deserialized.fuzz instanceof Uint8Array);\n assert.strictEqual(deserialized.fuzz.length, 3);\n assert.strictEqual(deserialized.fuzz[0], 3);\n assert.strictEqual(deserialized.fuzz[1], 1);\n assert.strictEqual(deserialized.fuzz[2], 2);\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\nimport {Store} from \"../Store\";\nimport {IDOMStorage} from \"../types\";\nimport {SESSION_E2EE_KEY_PREFIX} from \"../../../e2ee/common.js\";\nimport {parse, stringify} from \"../../../../utils/typedJSON\";\nimport type {ILogItem} from \"../../../../logging/types\";\n\nexport interface SessionEntry {\n key: string;\n value: any;\n}\n\nexport class SessionStore {\n private _sessionStore: Store<SessionEntry>\n private _localStorage: IDOMStorage;\n\n constructor(sessionStore: Store<SessionEntry>, localStorage: IDOMStorage) {\n this._sessionStore = sessionStore;\n this._localStorage = localStorage;\n }\n\n private get _localStorageKeyPrefix(): string {\n return `${this._sessionStore.databaseName}.session.`;\n }\n\n async get(key: string): Promise<any> {\n const entry = await this._sessionStore.get(key);\n if (entry) {\n return entry.value;\n }\n }\n\n _writeKeyToLocalStorage(key: string, value: any) {\n // we backup to localStorage so when idb gets cleared for some reason, we don't lose our e2ee identity\n try {\n const lsKey = this._localStorageKeyPrefix + key;\n const lsValue = stringify(value);\n this._localStorage.setItem(lsKey, lsValue);\n } catch (err) {\n console.error(\"could not write to localStorage\", err);\n }\n }\n\n writeE2EEIdentityToLocalStorage() {\n this._sessionStore.iterateValues(undefined, (entry: SessionEntry, key: string) => {\n if (key.startsWith(SESSION_E2EE_KEY_PREFIX)) {\n this._writeKeyToLocalStorage(key, entry.value);\n }\n return false;\n });\n }\n\n async tryRestoreE2EEIdentityFromLocalStorage(log: ILogItem): Promise<boolean> {\n let success = false;\n const lsPrefix = this._localStorageKeyPrefix;\n const prefix = lsPrefix + SESSION_E2EE_KEY_PREFIX;\n for(let i = 0; i < this._localStorage.length; i += 1) {\n const lsKey = this._localStorage.key(i)!;\n if (lsKey.startsWith(prefix)) {\n const value = parse(this._localStorage.getItem(lsKey)!);\n const key = lsKey.substr(lsPrefix.length);\n // we check if we don't have this key already, as we don't want to override anything\n const hasKey = (await this._sessionStore.getKey(key)) === key;\n log.set(key, !hasKey);\n if (!hasKey) {\n this._sessionStore.put({key, value});\n success = true;\n }\n }\n }\n return success;\n }\n\n set(key: string, value: any): void {\n if (key.startsWith(SESSION_E2EE_KEY_PREFIX)) {\n this._writeKeyToLocalStorage(key, value);\n }\n this._sessionStore.put({key, value});\n }\n\n add(key: string, value: any): void {\n if (key.startsWith(SESSION_E2EE_KEY_PREFIX)) {\n this._writeKeyToLocalStorage(key, value);\n }\n this._sessionStore.add({key, value});\n }\n\n remove(key: string): void {\n if (key.startsWith(SESSION_E2EE_KEY_PREFIX)) {\n this._localStorage.removeItem(this._localStorageKeyPrefix + key);\n }\n this._sessionStore.delete(key);\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\nstore contains:\n\troomId\n\tname\n\tlastMessage\n\tunreadCount\n\tmentionCount\n\tisEncrypted\n\tisDirectMessage\n\tmembership\n\tinviteCount\n\tjoinCount\n*/\nimport {Store} from \"../Store\";\nimport {SummaryData} from \"../../../room/RoomSummary\";\n\n/** Used for both roomSummary and archivedRoomSummary stores */\nexport class RoomSummaryStore {\n private _summaryStore: Store<SummaryData>;\n\n constructor(summaryStore: Store<SummaryData>) {\n this._summaryStore = summaryStore;\n }\n\n getAll(): Promise<SummaryData[]> {\n return this._summaryStore.selectAll();\n }\n\n set(summary: SummaryData): void {\n this._summaryStore.put(summary);\n }\n\n get(roomId: string): Promise<SummaryData | null> {\n return this._summaryStore.get(roomId);\n }\n\n async has(roomId: string): Promise<boolean> {\n const fetchedKey = await this._summaryStore.getKey(roomId);\n return roomId === fetchedKey;\n }\n\n remove(roomId: string): void {\n this._summaryStore.delete(roomId);\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\nimport {Store} from \"../Store\";\nimport {MemberData} from \"./RoomMemberStore\";\n\n// TODO: Move to Invite when that's TypeScript.\nexport interface InviteData {\n roomId: string;\n isEncrypted: boolean;\n isDirectMessage: boolean;\n name?: string;\n avatarUrl?: string;\n avatarColorId: number;\n canonicalAlias?: string;\n timestamp: number;\n joinRule: string;\n inviter?: MemberData;\n}\n\nexport class InviteStore {\n private _inviteStore: Store<InviteData>;\n\n constructor(inviteStore: Store<InviteData>) {\n this._inviteStore = inviteStore;\n }\n\n getAll(): Promise<InviteData[]> {\n return this._inviteStore.selectAll();\n }\n\n set(invite: InviteData): void {\n this._inviteStore.put(invite);\n }\n\n remove(roomId: string): void {\n this._inviteStore.delete(roomId);\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport type {ILogItem, ISerializedItem} from \"./types\";\n\nexport enum LogLevel {\n All = 1,\n Debug,\n Detail,\n Info,\n Warn,\n Error,\n Fatal,\n Off\n}\n\nexport class LogFilter {\n private _min?: LogLevel;\n private _parentFilter?: LogFilter;\n\n constructor(parentFilter?: LogFilter) {\n this._parentFilter = parentFilter;\n }\n\n filter(item: ILogItem, children: ISerializedItem[] | null): boolean {\n if (this._parentFilter) {\n if (!this._parentFilter.filter(item, children)) {\n return false;\n }\n }\n // neither our children or us have a loglevel high enough, filter out.\n if (this._min !== undefined && !Array.isArray(children) && item.logLevel < this._min) {\n return false;\n } else {\n return true;\n }\n }\n\n /* methods to build the filter */\n minLevel(logLevel: LogLevel): LogFilter {\n this._min = logLevel;\n return this;\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\nimport {LogLevel} from \"./LogFilter\";\nimport type {ILogger, ILogExport, ILogItem, LabelOrValues, LogCallback, LogItemValues} from \"./types\";\n\nfunction noop (): void {}\n\nexport class NullLogger implements ILogger {\n public readonly item: ILogItem = new NullLogItem(this);\n\n log(): void {}\n\n run<T>(_, callback: LogCallback<T>): T {\n return callback(this.item); \n }\n\n wrapOrRun<T>(item: ILogItem | undefined, _, callback: LogCallback<T>): T {\n if (item) {\n return item.wrap(_, callback);\n } else {\n return this.run(_, callback);\n }\n }\n\n runDetached(_, callback): ILogItem {\n new Promise(r => r(callback(this.item))).then(noop, noop);\n return this.item;\n }\n\n async export(): Promise<ILogExport | undefined> {\n return undefined;\n }\n\n get level(): typeof LogLevel {\n return LogLevel;\n }\n}\n\nexport class NullLogItem implements ILogItem {\n public readonly logger: NullLogger;\n public readonly logLevel: LogLevel;\n public children?: Array<ILogItem>;\n public values: LogItemValues;\n public error?: Error;\n\n constructor(logger: NullLogger) {\n this.logger = logger;\n }\n\n wrap<T>(_: LabelOrValues, callback: LogCallback<T>): T {\n return callback(this);\n }\n\n log(): ILogItem {\n return this;\n }\n set(): ILogItem { return this; }\n\n runDetached(_: LabelOrValues, callback: LogCallback<unknown>): ILogItem {\n new Promise(r => r(callback(this))).then(noop, noop);\n return this;\n }\n\n wrapDetached(_: LabelOrValues, _callback: LogCallback<unknown>): void {\n return this.refDetached();\n }\n\n refDetached(): void {}\n\n ensureRefId(): void {}\n\n get level(): typeof LogLevel {\n return LogLevel;\n }\n\n get duration(): 0 {\n return 0;\n }\n\n catch(err: Error): Error {\n return err;\n }\n\n child(): ILogItem {\n return this;\n }\n\n finish(): void {}\n\n serialize(): undefined {\n return undefined;\n }\n}\n\nexport const Instance = new NullLogger(); \n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {EventKey} from \"../../../room/timeline/EventKey\";\nimport { StorageError } from \"../../common\";\nimport { encodeUint32, decodeUint32 } from \"../utils\";\nimport {KeyLimits} from \"../../common\";\nimport {Store} from \"../Store\";\nimport {TimelineEvent, StateEvent} from \"../../types\";\nimport {ILogItem} from \"../../../../logging/types\";\n\ninterface Annotation {\n count: number;\n me: boolean;\n firstTimestamp: number;\n}\n\ninterface TimelineEventEntry {\n roomId: string;\n fragmentId: number;\n eventIndex: number;\n event: TimelineEvent | StateEvent;\n displayName?: string;\n avatarUrl?: string;\n annotations?: { [key : string]: Annotation };\n}\n\ntype TimelineEventStorageEntry = TimelineEventEntry & { key: string, eventIdKey: string };\n\nfunction encodeKey(roomId: string, fragmentId: number, eventIndex: number): string {\n return `${roomId}|${encodeUint32(fragmentId)}|${encodeUint32(eventIndex)}`;\n}\n\nfunction decodeKey(key: string): { roomId: string, eventKey: EventKey } {\n const [roomId, fragmentId, eventIndex] = key.split(\"|\");\n return {roomId, eventKey: new EventKey(decodeUint32(fragmentId), decodeUint32(eventIndex))};\n}\n\nfunction encodeEventIdKey(roomId: string, eventId: string): string {\n return `${roomId}|${eventId}`;\n}\n\nfunction decodeEventIdKey(eventIdKey: string): { roomId: string, eventId: string } {\n const [roomId, eventId] = eventIdKey.split(\"|\");\n return {roomId, eventId};\n}\n\nclass Range {\n private _IDBKeyRange: typeof IDBKeyRange;\n private _only?: EventKey;\n private _lower?: EventKey;\n private _upper?: EventKey;\n private _lowerOpen: boolean;\n private _upperOpen: boolean;\n\n constructor(_IDBKeyRange: any, only?: EventKey, lower?: EventKey, upper?: EventKey, lowerOpen: boolean = false, upperOpen: boolean = false) {\n this._IDBKeyRange = _IDBKeyRange;\n this._only = only;\n this._lower = lower;\n this._upper = upper;\n this._lowerOpen = lowerOpen;\n this._upperOpen = upperOpen;\n }\n\n asIDBKeyRange(roomId: string): IDBKeyRange | undefined {\n try {\n // only\n if (this._only) {\n return this._IDBKeyRange.only(encodeKey(roomId, this._only.fragmentId, this._only.eventIndex));\n }\n // lowerBound\n // also bound as we don't want to move into another roomId\n if (this._lower && !this._upper) {\n return this._IDBKeyRange.bound(\n encodeKey(roomId, this._lower.fragmentId, this._lower.eventIndex),\n encodeKey(roomId, this._lower.fragmentId, KeyLimits.maxStorageKey),\n this._lowerOpen,\n false\n );\n }\n // upperBound\n // also bound as we don't want to move into another roomId\n if (!this._lower && this._upper) {\n return this._IDBKeyRange.bound(\n encodeKey(roomId, this._upper.fragmentId, KeyLimits.minStorageKey),\n encodeKey(roomId, this._upper.fragmentId, this._upper.eventIndex),\n false,\n this._upperOpen\n );\n }\n // bound\n if (this._lower && this._upper) {\n return this._IDBKeyRange.bound(\n encodeKey(roomId, this._lower.fragmentId, this._lower.eventIndex),\n encodeKey(roomId, this._upper.fragmentId, this._upper.eventIndex),\n this._lowerOpen,\n this._upperOpen\n );\n }\n } catch(err) {\n throw new StorageError(`IDBKeyRange failed with data: ` + JSON.stringify(this), err);\n }\n }\n}\n/*\n * @typedef {Object} Gap\n * @property {?string} prev_batch the pagination token for this backwards facing gap\n * @property {?string} next_batch the pagination token for this forwards facing gap\n *\n * @typedef {Object} Event\n * @property {string} event_id the id of the event\n * @property {string} type the\n * @property {?string} state_key the state key of this state event\n *\n * @typedef {Object} Entry\n * @property {string} roomId\n * @property {EventKey} eventKey\n * @property {?Event} event if an event entry, the event\n * @property {?Gap} gap if a gap entry, the gap\n*/\nexport class TimelineEventStore {\n private _timelineStore: Store<TimelineEventStorageEntry>;\n\n constructor(timelineStore: Store<TimelineEventStorageEntry>) {\n this._timelineStore = timelineStore;\n }\n\n /** Creates a range that only includes the given key\n * @param eventKey the key\n * @return the created range\n */\n onlyRange(eventKey: EventKey): Range {\n return new Range(this._timelineStore.IDBKeyRange, eventKey);\n }\n\n /** Creates a range that includes all keys before eventKey, and optionally also the key itself.\n * @param eventKey the key\n * @param [open=false] whether the key is included (false) or excluded (true) from the range at the upper end.\n * @return the created range\n */\n upperBoundRange(eventKey: EventKey, open=false): Range {\n return new Range(this._timelineStore.IDBKeyRange, undefined, undefined, eventKey, undefined, open);\n }\n\n /** Creates a range that includes all keys after eventKey, and optionally also the key itself.\n * @param eventKey the key\n * @param [open=false] whether the key is included (false) or excluded (true) from the range at the lower end.\n * @return the created range\n */\n lowerBoundRange(eventKey: EventKey, open=false): Range {\n return new Range(this._timelineStore.IDBKeyRange, undefined, eventKey, undefined, open);\n }\n\n /** Creates a range that includes all keys between `lower` and `upper`, and optionally the given keys as well.\n * @param lower the lower key\n * @param upper the upper key\n * @param [lowerOpen=false] whether the lower key is included (false) or excluded (true) from the range.\n * @param [upperOpen=false] whether the upper key is included (false) or excluded (true) from the range.\n * @return the created range\n */\n boundRange(lower: EventKey, upper: EventKey, lowerOpen=false, upperOpen=false): Range {\n return new Range(this._timelineStore.IDBKeyRange, undefined, lower, upper, lowerOpen, upperOpen);\n }\n\n /** Looks up the last `amount` entries in the timeline for `roomId`.\n * @param roomId\n * @param fragmentId\n * @param amount\n * @return a promise resolving to an array with 0 or more entries, in ascending order.\n */\n async lastEvents(roomId: string, fragmentId: number, amount: number): Promise<TimelineEventEntry[]> {\n const eventKey = EventKey.maxKey;\n eventKey.fragmentId = fragmentId;\n return this.eventsBefore(roomId, eventKey, amount);\n }\n\n /** Looks up the first `amount` entries in the timeline for `roomId`.\n * @param roomId\n * @param fragmentId\n * @param amount\n * @return a promise resolving to an array with 0 or more entries, in ascending order.\n */\n async firstEvents(roomId: string, fragmentId: number, amount: number): Promise<TimelineEventEntry[]> {\n const eventKey = EventKey.minKey;\n eventKey.fragmentId = fragmentId;\n return this.eventsAfter(roomId, eventKey, amount);\n }\n\n /** Looks up `amount` entries after `eventKey` in the timeline for `roomId` within the same fragment.\n * The entry for `eventKey` is not included.\n * @param roomId\n * @param eventKey\n * @param amount\n * @return a promise resolving to an array with 0 or more entries, in ascending order.\n */\n eventsAfter(roomId: string, eventKey: EventKey, amount: number): Promise<TimelineEventEntry[]> {\n const idbRange = this.lowerBoundRange(eventKey, true).asIDBKeyRange(roomId);\n return this._timelineStore.selectLimit(idbRange, amount);\n }\n\n /** Looks up `amount` entries before `eventKey` in the timeline for `roomId` within the same fragment.\n * The entry for `eventKey` is not included.\n * @param roomId\n * @param eventKey\n * @param amount\n * @return a promise resolving to an array with 0 or more entries, in ascending order.\n */\n async eventsBefore(roomId: string, eventKey: EventKey, amount: number): Promise<TimelineEventEntry[]> {\n const range = this.upperBoundRange(eventKey, true).asIDBKeyRange(roomId);\n const events = await this._timelineStore.selectLimitReverse(range, amount);\n events.reverse(); // because we fetched them backwards\n return events;\n }\n\n async getEventKeysForIds(roomId: string, eventIds: string[]): Promise<Map<string, EventKey>> {\n const byEventId = this._timelineStore.index(\"byEventId\");\n const keys = eventIds.map(eventId => encodeEventIdKey(roomId, eventId));\n const results = new Map();\n await byEventId.findExistingKeys(keys, false, (indexKey, pk) => {\n const {eventId} = decodeEventIdKey(indexKey as string);\n const {eventKey} = decodeKey(pk as string);\n results.set(eventId, eventKey);\n return false;\n });\n return results;\n }\n\n /** Finds the first eventId that occurs in the store, if any.\n * For optimal performance, `eventIds` should be in chronological order.\n *\n * The order in which results are returned might be different than `eventIds`.\n * Call the return value to obtain the next {id, event} pair.\n * @param roomId\n * @param eventIds\n * @return\n */\n // performance comment from above refers to the fact that there *might*\n // be a correlation between event_id sorting order and chronology.\n // In that case we could avoid running over all eventIds, as the reported order by findExistingKeys\n // would match the order of eventIds. That's why findLast is also passed as backwards to keysExist.\n // also passing them in chronological order makes sense as that's how we'll receive them almost always.\n async findFirstOccurringEventId(roomId: string, eventIds: string[]): Promise<string | undefined> {\n const byEventId = this._timelineStore.index(\"byEventId\");\n const keys = eventIds.map(eventId => encodeEventIdKey(roomId, eventId));\n const results = new Array(keys.length);\n let firstFoundKey: string | undefined;\n\n // find first result that is found and has no undefined results before it\n function firstFoundAndPrecedingResolved(): string | undefined {\n for(let i = 0; i < results.length; ++i) {\n if (results[i] === undefined) {\n return;\n } else if(results[i] === true) {\n return keys[i];\n }\n }\n }\n\n await byEventId.findExistingKeys(keys, false, (key, found) => {\n // T[].search(T, number), but we want T[].search(R, number), so cast\n const index = (keys as IDBValidKey[]).indexOf(key);\n results[index] = found;\n firstFoundKey = firstFoundAndPrecedingResolved();\n return !!firstFoundKey;\n });\n return firstFoundKey && decodeEventIdKey(firstFoundKey).eventId;\n }\n\n /** Inserts a new entry into the store.\n * \n * If the event already exists in the store (either the eventKey or the event id\n * are already known for the given roomId), this operation has no effect.\n * \n * Returns if the event was not yet known and the entry was written.\n */\n tryInsert(entry: TimelineEventEntry, log: ILogItem): Promise<boolean> {\n (entry as TimelineEventStorageEntry).key = encodeKey(entry.roomId, entry.fragmentId, entry.eventIndex);\n (entry as TimelineEventStorageEntry).eventIdKey = encodeEventIdKey(entry.roomId, entry.event.event_id);\n return this._timelineStore.tryAdd(entry as TimelineEventStorageEntry, log);\n }\n\n /** Updates the entry into the store with the given [roomId, eventKey] combination.\n * If not yet present, will insert. Might be slower than add.\n * @param entry the entry to update.\n * @return nothing. To wait for the operation to finish, await the transaction it's part of.\n */\n update(entry: TimelineEventEntry): void {\n this._timelineStore.put(entry as TimelineEventStorageEntry);\n }\n\n get(roomId: string, eventKey: EventKey): Promise<TimelineEventEntry | undefined> {\n return this._timelineStore.get(encodeKey(roomId, eventKey.fragmentId, eventKey.eventIndex));\n }\n\n getByEventId(roomId: string, eventId: string): Promise<TimelineEventEntry | undefined> {\n return this._timelineStore.index(\"byEventId\").get(encodeEventIdKey(roomId, eventId));\n }\n\n removeAllForRoom(roomId: string): void {\n const minKey = encodeKey(roomId, KeyLimits.minStorageKey, KeyLimits.minStorageKey);\n const maxKey = encodeKey(roomId, KeyLimits.maxStorageKey, KeyLimits.maxStorageKey);\n const range = this._timelineStore.IDBKeyRange.bound(minKey, maxKey);\n this._timelineStore.delete(range);\n }\n}\n\nimport {createMockStorage} from \"../../../../mocks/Storage\";\nimport {createEvent, withTextBody} from \"../../../../mocks/event.js\";\nimport {createEventEntry} from \"../../../room/timeline/persistence/common.js\";\nimport {Instance as nullLogger} from \"../../../../logging/NullLogger\";\n\nexport function tests() {\n\n const sortedIds = [\n \"$2wZy1W-QdcwaAwz68nfz1oc-3SsZKVDy8d86ERP1Pm0\",\n \"$4RWaZ5142grUgTnQyr_5qiPTOwzAOimt5MsXg6m1diM\",\n \"$4izqHE2Wf5US_-e_za942pZ10CDNJjDncUMmhqBUVQw\",\n \"$Oil2Afq2cBLqMAeJTAHjA3Is9T5Wmaa2ogVRlFJ_gzE\",\n \"$Wyl-7u-YqnPJElkPufIRXRFTYP-eFxQ4iD-SmLQo2Rw\",\n \"$b-eWaZtp22vL9mp0h7odbpphOZQ-rnp54qjyTQPARgo\",\n \"$sS9rTv8u2m9o4RaMI2jGOnpMtb9t8_0euiQLhNFW380\",\n \"$uZLkB9rzTKvJAK2QrQNX-prwQ2Niajdi0fvvRnyCtz8\",\n \"$vGecIBZFex9_vlQf1E1LjtQXE3q5GwERIHMiy4mOWv0\",\n \"$vdLgAnwjHj0cicU3MA4ynLHUBGOIFhvvksY3loqzjF\",\n ];\n\n const insertedIds = [\n sortedIds[5],\n sortedIds[3],\n sortedIds[9],\n sortedIds[7],\n sortedIds[1],\n ];\n\n const checkedIds = [\n sortedIds[2],\n sortedIds[4],\n sortedIds[3],\n sortedIds[0],\n sortedIds[8],\n sortedIds[9],\n sortedIds[6],\n ];\n\n const roomId = \"!fjsdf423423jksdfdsf:hs.tld\";\n\n function createEventWithId(id) {\n return withTextBody(\"hello\", createEvent(\"m.room.message\", id, \"@alice:hs.tld\"));\n }\n\n return {\n \"getEventKeysForIds\": async assert => {\n const storage = await createMockStorage();\n const txn = await storage.readWriteTxn([storage.storeNames.timelineEvents]);\n let eventKey = EventKey.defaultFragmentKey(109);\n for (const insertedId of insertedIds) {\n const entry = createEventEntry(eventKey.nextKey(), roomId, createEventWithId(insertedId));\n assert(await txn.timelineEvents.tryInsert(entry, nullLogger.item));\n eventKey = eventKey.nextKey();\n }\n const eventKeyMap = await txn.timelineEvents.getEventKeysForIds(roomId, checkedIds);\n assert.equal(eventKeyMap.size, 2);\n const eventKey1 = eventKeyMap.get(\"$Oil2Afq2cBLqMAeJTAHjA3Is9T5Wmaa2ogVRlFJ_gzE\")!;\n assert.equal(eventKey1.fragmentId, 109);\n assert.equal(eventKey1.eventIndex, 0x80000001);\n const eventKey2 = eventKeyMap.get(\"$vdLgAnwjHj0cicU3MA4ynLHUBGOIFhvvksY3loqzjF\")!;\n assert.equal(eventKey2.fragmentId, 109);\n assert.equal(eventKey2.eventIndex, 0x80000002);\n }\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport const MIN_UNICODE = \"\\u{0}\";\nexport const MAX_UNICODE = \"\\u{10FFFF}\";\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\nimport {MIN_UNICODE, MAX_UNICODE} from \"./common\";\nimport {Store} from \"../Store\";\n\nfunction encodeKey(roomId: string, targetEventId: string, relType: string, sourceEventId: string): string {\n return `${roomId}|${targetEventId}|${relType}|${sourceEventId}`;\n}\n\ninterface RelationEntry {\n roomId: string;\n targetEventId: string;\n sourceEventId: string;\n relType: string;\n}\n\nfunction decodeKey(key: string): RelationEntry {\n const [roomId, targetEventId, relType, sourceEventId] = key.split(\"|\");\n return {roomId, targetEventId, relType, sourceEventId};\n}\n\nexport class TimelineRelationStore {\n private _store: Store<{ key: string }>;\n\n constructor(store: Store<{ key: string }>) {\n this._store = store;\n }\n\n add(roomId: string, targetEventId: string, relType: string, sourceEventId: string): void {\n this._store.add({key: encodeKey(roomId, targetEventId, relType, sourceEventId)});\n }\n\n remove(roomId: string, targetEventId: string, relType: string, sourceEventId: string): void {\n this._store.delete(encodeKey(roomId, targetEventId, relType, sourceEventId));\n }\n\n removeAllForTarget(roomId: string, targetId: string): void {\n const range = this._store.IDBKeyRange.bound(\n encodeKey(roomId, targetId, MIN_UNICODE, MIN_UNICODE),\n encodeKey(roomId, targetId, MAX_UNICODE, MAX_UNICODE),\n true,\n true\n );\n this._store.delete(range);\n }\n\n removeAllForRoom(roomId: string) {\n const range = this._store.IDBKeyRange.bound(\n encodeKey(roomId, MIN_UNICODE, MIN_UNICODE, MIN_UNICODE),\n encodeKey(roomId, MAX_UNICODE, MAX_UNICODE, MAX_UNICODE),\n true,\n true\n );\n this._store.delete(range);\n }\n\n async getForTargetAndType(roomId: string, targetId: string, relType: string): Promise<RelationEntry[]> {\n // exclude both keys as they are theoretical min and max,\n // but we should't have a match for just the room id, or room id with max\n const range = this._store.IDBKeyRange.bound(\n encodeKey(roomId, targetId, relType, MIN_UNICODE),\n encodeKey(roomId, targetId, relType, MAX_UNICODE),\n true,\n true\n );\n const items = await this._store.selectAll(range);\n return items.map(i => decodeKey(i.key));\n }\n\n async getAllForTarget(roomId: string, targetId: string): Promise<RelationEntry[]> {\n // exclude both keys as they are theoretical min and max,\n // but we should't have a match for just the room id, or room id with max\n const range = this._store.IDBKeyRange.bound(\n encodeKey(roomId, targetId, MIN_UNICODE, MIN_UNICODE),\n encodeKey(roomId, targetId, MAX_UNICODE, MAX_UNICODE),\n true,\n true\n );\n const items = await this._store.selectAll(range);\n return items.map(i => decodeKey(i.key));\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {MAX_UNICODE} from \"./common\";\nimport {Store} from \"../Store\";\nimport {StateEvent} from \"../../types\";\n\nfunction encodeKey(roomId: string, eventType: string, stateKey: string) {\n return `${roomId}|${eventType}|${stateKey}`;\n}\n\nexport interface RoomStateEntry {\n roomId: string;\n event: StateEvent;\n key: string;\n}\n\nexport class RoomStateStore {\n private _roomStateStore: Store<RoomStateEntry>;\n\n constructor(idbStore: Store<RoomStateEntry>) {\n this._roomStateStore = idbStore;\n }\n\n get(roomId: string, type: string, stateKey: string): Promise<RoomStateEntry | undefined> {\n const key = encodeKey(roomId, type, stateKey);\n return this._roomStateStore.get(key);\n }\n\n set(roomId: string, event: StateEvent): void {\n const key = encodeKey(roomId, event.type, event.state_key);\n const entry = {roomId, event, key};\n this._roomStateStore.put(entry);\n }\n\n removeAllForRoom(roomId: string): void {\n // exclude both keys as they are theoretical min and max,\n // but we should't have a match for just the room id, or room id with max\n const range = this._roomStateStore.IDBKeyRange.bound(roomId, `${roomId}|${MAX_UNICODE}`, true, true);\n this._roomStateStore.delete(range);\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020, 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {MAX_UNICODE} from \"./common\";\nimport {Store} from \"../Store\";\n\nfunction encodeKey(roomId: string, userId: string) {\n return `${roomId}|${userId}`;\n}\n\nfunction decodeKey(key: string): { roomId: string, userId: string } {\n const [roomId, userId] = key.split(\"|\");\n return {roomId, userId};\n}\n\n// TODO: Move to RoomMember when that's TypeScript.\nexport interface MemberData {\n roomId: string;\n userId: string;\n avatarUrl: string;\n displayName: string;\n membership: \"join\" | \"leave\" | \"invite\" | \"ban\";\n}\n\ntype MemberStorageEntry = MemberData & { key: string }\n\n// no historical members\nexport class RoomMemberStore {\n private _roomMembersStore: Store<MemberStorageEntry>;\n\n constructor(roomMembersStore: Store<MemberStorageEntry>) {\n this._roomMembersStore = roomMembersStore;\n }\n\n get(roomId: string, userId: string): Promise<MemberStorageEntry | undefined> {\n return this._roomMembersStore.get(encodeKey(roomId, userId));\n }\n\n set(member: MemberData): void {\n // Object.assign would be more typesafe, but small objects \n (member as MemberStorageEntry).key = encodeKey(member.roomId, member.userId);\n this._roomMembersStore.put(member as MemberStorageEntry);\n }\n\n getAll(roomId: string): Promise<MemberData[]> {\n const range = this._roomMembersStore.IDBKeyRange.lowerBound(encodeKey(roomId, \"\"));\n return this._roomMembersStore.selectWhile(range, member => {\n return member.roomId === roomId;\n });\n }\n\n async getAllUserIds(roomId: string): Promise<string[]> {\n const userIds: string[] = [];\n const range = this._roomMembersStore.IDBKeyRange.lowerBound(encodeKey(roomId, \"\"));\n await this._roomMembersStore.iterateKeys(range, key => {\n const decodedKey = decodeKey(key as string);\n // prevent running into the next room\n if (decodedKey.roomId === roomId) {\n userIds.push(decodedKey.userId);\n return false; // fetch more\n }\n return true; // done\n });\n return userIds;\n }\n\n removeAllForRoom(roomId: string): void {\n // exclude both keys as they are theoretical min and max,\n // but we should't have a match for just the room id, or room id with max\n const range = this._roomMembersStore.IDBKeyRange.bound(roomId, `${roomId}|${MAX_UNICODE}`, true, true);\n this._roomMembersStore.delete(range);\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { StorageError } from \"../../common\";\nimport {KeyLimits} from \"../../common\";\nimport { encodeUint32 } from \"../utils\";\nimport {Store} from \"../Store\";\n\ninterface Fragment {\n roomId: string;\n id: number;\n previousId: number | null;\n nextId: number | null;\n previousToken: string | null;\n nextToken: string | null;\n}\n\ntype FragmentEntry = Fragment & { key: string }\n\nfunction encodeKey(roomId: string, fragmentId: number): string {\n return `${roomId}|${encodeUint32(fragmentId)}`;\n}\n\nexport class TimelineFragmentStore {\n private _store: Store<FragmentEntry>;\n\n constructor(store: Store<FragmentEntry>) {\n this._store = store;\n }\n\n _allRange(roomId: string): IDBKeyRange {\n try {\n return this._store.IDBKeyRange.bound(\n encodeKey(roomId, KeyLimits.minStorageKey),\n encodeKey(roomId, KeyLimits.maxStorageKey)\n );\n } catch (err) {\n throw new StorageError(`error from IDBKeyRange with roomId ${roomId}`, err);\n }\n }\n\n all(roomId: string): Promise<FragmentEntry[]> {\n return this._store.selectAll(this._allRange(roomId));\n }\n\n /** Returns the fragment without a nextToken and without nextId,\n if any, with the largest id if there are multiple (which should not happen) */\n liveFragment(roomId: string): Promise<FragmentEntry | undefined> {\n // why do we need this?\n // Ok, take the case where you've got a /context fragment and a /sync fragment\n // They are not connected. So, upon loading the persister, which one do we take? We can't sort them ...\n // we assume that the one without a nextToken and without a nextId is a live one\n // there should really be only one like this\n\n // reverse because assuming live fragment has bigger id than non-live ones\n return this._store.findReverse(this._allRange(roomId), fragment => {\n return typeof fragment.nextId !== \"number\" && typeof fragment.nextToken !== \"string\";\n });\n }\n\n // should generate an id an return it?\n // depends if we want to do anything smart with fragment ids,\n // like give them meaning depending on range. not for now probably ...\n add(fragment: Fragment): void {\n (fragment as FragmentEntry).key = encodeKey(fragment.roomId, fragment.id);\n this._store.add(fragment as FragmentEntry);\n }\n\n update(fragment: FragmentEntry): void {\n this._store.put(fragment);\n }\n\n get(roomId: string, fragmentId: number): Promise<FragmentEntry | undefined> {\n return this._store.get(encodeKey(roomId, fragmentId));\n }\n\n removeAllForRoom(roomId: string): void {\n this._store.delete(this._allRange(roomId));\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { encodeUint32, decodeUint32 } from \"../utils\";\nimport {KeyLimits} from \"../../common\";\nimport {Store} from \"../Store\";\nimport {Content} from \"../../types\";\n\ninterface PendingEntry {\n roomId: string;\n queueIndex: number;\n eventType: string;\n content: Content;\n relatexTxnId: string | null;\n relatedEventId: string | null;\n txnId?: string;\n needsEncryption: boolean;\n needsUpload: boolean;\n key: string;\n}\n\nfunction encodeKey(roomId: string, queueIndex: number): string {\n return `${roomId}|${encodeUint32(queueIndex)}`;\n}\n\nfunction decodeKey(key: string): { roomId: string, queueIndex: number } {\n const [roomId, encodedQueueIndex] = key.split(\"|\");\n const queueIndex = decodeUint32(encodedQueueIndex);\n return {roomId, queueIndex};\n}\n\nexport class PendingEventStore {\n private _eventStore: Store<PendingEntry>;\n\n constructor(eventStore: Store<PendingEntry>) {\n this._eventStore = eventStore;\n }\n\n async getMaxQueueIndex(roomId: string): Promise<number | undefined> {\n const range = this._eventStore.IDBKeyRange.bound(\n encodeKey(roomId, KeyLimits.minStorageKey),\n encodeKey(roomId, KeyLimits.maxStorageKey),\n false,\n false,\n );\n const maxKey = await this._eventStore.findMaxKey(range);\n if (maxKey) {\n return decodeKey(maxKey as string).queueIndex;\n }\n }\n\n remove(roomId: string, queueIndex: number) {\n const keyRange = this._eventStore.IDBKeyRange.only(encodeKey(roomId, queueIndex));\n this._eventStore.delete(keyRange);\n }\n\n async exists(roomId: string, queueIndex: number): Promise<boolean> {\n const keyRange = this._eventStore.IDBKeyRange.only(encodeKey(roomId, queueIndex));\n const key = await this._eventStore.getKey(keyRange);\n return !!key;\n }\n \n add(pendingEvent: PendingEntry): void {\n pendingEvent.key = encodeKey(pendingEvent.roomId, pendingEvent.queueIndex);\n this._eventStore.add(pendingEvent);\n }\n\n update(pendingEvent: PendingEntry): void {\n this._eventStore.put(pendingEvent);\n }\n\n getAll(): Promise<PendingEntry[]> {\n return this._eventStore.selectAll();\n }\n\n removeAllForRoom(roomId: string): void {\n const minKey = encodeKey(roomId, KeyLimits.minStorageKey);\n const maxKey = encodeKey(roomId, KeyLimits.maxStorageKey);\n const range = this._eventStore.IDBKeyRange.bound(minKey, maxKey);\n this._eventStore.delete(range);\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\nimport {Store} from \"../Store\";\n\ninterface UserIdentity {\n userId: string;\n roomIds: string[];\n deviceTrackingStatus: number;\n}\n\nexport class UserIdentityStore {\n private _store: Store<UserIdentity>;\n\n constructor(store: Store<UserIdentity>) {\n this._store = store;\n }\n\n get(userId: string): Promise<UserIdentity | undefined> {\n return this._store.get(userId);\n }\n\n set(userIdentity: UserIdentity): void {\n this._store.put(userIdentity);\n }\n\n remove(userId: string): void {\n this._store.delete(userId);\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {MAX_UNICODE, MIN_UNICODE} from \"./common\";\nimport {Store} from \"../Store\";\n\nexport interface DeviceIdentity {\n userId: string;\n deviceId: string;\n ed25519Key: string;\n curve25519Key: string;\n algorithms: string[];\n displayName: string;\n key: string;\n}\n\nfunction encodeKey(userId: string, deviceId: string): string {\n return `${userId}|${deviceId}`;\n}\n\nfunction decodeKey(key: string): { userId: string, deviceId: string } {\n const [userId, deviceId] = key.split(\"|\");\n return {userId, deviceId};\n}\n\nexport class DeviceIdentityStore {\n private _store: Store<DeviceIdentity>;\n \n constructor(store: Store<DeviceIdentity>) {\n this._store = store;\n }\n\n getAllForUserId(userId: string): Promise<DeviceIdentity[]> {\n const range = this._store.IDBKeyRange.lowerBound(encodeKey(userId, \"\"));\n return this._store.selectWhile(range, device => {\n return device.userId === userId;\n });\n }\n\n async getAllDeviceIds(userId: string): Promise<string[]> {\n const deviceIds: string[] = [];\n const range = this._store.IDBKeyRange.lowerBound(encodeKey(userId, \"\"));\n await this._store.iterateKeys(range, key => {\n const decodedKey = decodeKey(key as string);\n // prevent running into the next room\n if (decodedKey.userId === userId) {\n deviceIds.push(decodedKey.deviceId);\n return false; // fetch more\n }\n return true; // done\n });\n return deviceIds;\n }\n\n get(userId: string, deviceId: string): Promise<DeviceIdentity | undefined> {\n return this._store.get(encodeKey(userId, deviceId));\n }\n\n set(deviceIdentity: DeviceIdentity): void {\n deviceIdentity.key = encodeKey(deviceIdentity.userId, deviceIdentity.deviceId);\n this._store.put(deviceIdentity);\n }\n\n getByCurve25519Key(curve25519Key: string): Promise<DeviceIdentity | undefined> {\n return this._store.index(\"byCurve25519Key\").get(curve25519Key);\n }\n\n remove(userId: string, deviceId: string): void {\n this._store.delete(encodeKey(userId, deviceId));\n }\n\n removeAllForUser(userId: string): void {\n // exclude both keys as they are theoretical min and max,\n // but we should't have a match for just the room id, or room id with max\n const range = this._store.IDBKeyRange.bound(encodeKey(userId, MIN_UNICODE), encodeKey(userId, MAX_UNICODE), true, true);\n this._store.delete(range);\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\nimport {Store} from \"../Store\";\n\nfunction encodeKey(senderKey: string, sessionId: string): string {\n return `${senderKey}|${sessionId}`;\n}\n\nfunction decodeKey(key: string): { senderKey: string, sessionId: string } {\n const [senderKey, sessionId] = key.split(\"|\");\n return {senderKey, sessionId};\n}\n\nexport type OlmSessionEntry = {\n session: string;\n sessionId: string;\n senderKey: string;\n lastUsed: number;\n}\n\ntype OlmSessionStoredEntry = OlmSessionEntry & { key: string };\n\nexport class OlmSessionStore {\n private _store: Store<OlmSessionStoredEntry>;\n\n constructor(store: Store<OlmSessionStoredEntry>) {\n this._store = store;\n }\n\n async getSessionIds(senderKey: string): Promise<string[]> {\n const sessionIds: string[] = [];\n const range = this._store.IDBKeyRange.lowerBound(encodeKey(senderKey, \"\"));\n await this._store.iterateKeys(range, key => {\n const decodedKey = decodeKey(key as string);\n // prevent running into the next room\n if (decodedKey.senderKey === senderKey) {\n sessionIds.push(decodedKey.sessionId);\n return false; // fetch more\n }\n return true; // done\n });\n return sessionIds;\n }\n\n getAll(senderKey: string): Promise<OlmSessionEntry[]> {\n const range = this._store.IDBKeyRange.lowerBound(encodeKey(senderKey, \"\"));\n return this._store.selectWhile(range, session => {\n return session.senderKey === senderKey;\n });\n }\n\n get(senderKey: string, sessionId: string): Promise<OlmSessionEntry | undefined> {\n return this._store.get(encodeKey(senderKey, sessionId));\n }\n\n set(session: OlmSessionEntry): void {\n (session as OlmSessionStoredEntry).key = encodeKey(session.senderKey, session.sessionId);\n this._store.put(session as OlmSessionStoredEntry);\n }\n\n remove(senderKey: string, sessionId: string): void {\n this._store.delete(encodeKey(senderKey, sessionId));\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {MIN_UNICODE, MAX_UNICODE} from \"./common\";\nimport {Store} from \"../Store\";\n\nexport enum BackupStatus {\n NotBackedUp = 0,\n BackedUp = 1\n}\n\nexport enum KeySource {\n DeviceMessage = 1,\n Backup,\n Outbound\n}\n\nexport interface InboundGroupSessionEntry {\n roomId: string;\n senderKey: string;\n sessionId: string;\n session?: string;\n claimedKeys?: { [algorithm : string] : string };\n eventIds?: string[];\n backup: BackupStatus,\n source: KeySource\n}\n\ntype InboundGroupSessionStorageEntry = InboundGroupSessionEntry & { key: string };\n\n\nfunction encodeKey(roomId: string, senderKey: string, sessionId: string): string {\n return `${roomId}|${senderKey}|${sessionId}`;\n}\n\nexport class InboundGroupSessionStore {\n private _store: Store<InboundGroupSessionStorageEntry>;\n\n constructor(store: Store<InboundGroupSessionStorageEntry>) {\n this._store = store;\n }\n\n async has(roomId: string, senderKey: string, sessionId: string): Promise<boolean> {\n const key = encodeKey(roomId, senderKey, sessionId);\n const fetchedKey = await this._store.getKey(key);\n return key === fetchedKey;\n }\n\n get(roomId: string, senderKey: string, sessionId: string): Promise<InboundGroupSessionEntry | undefined> {\n return this._store.get(encodeKey(roomId, senderKey, sessionId));\n }\n\n set(session: InboundGroupSessionEntry): void {\n const storageEntry = session as InboundGroupSessionStorageEntry;\n storageEntry.key = encodeKey(session.roomId, session.senderKey, session.sessionId);\n this._store.put(storageEntry);\n }\n\n removeAllForRoom(roomId: string) {\n const range = this._store.IDBKeyRange.bound(\n encodeKey(roomId, MIN_UNICODE, MIN_UNICODE),\n encodeKey(roomId, MAX_UNICODE, MAX_UNICODE)\n );\n this._store.delete(range);\n }\n countNonBackedUpSessions(): Promise<number> {\n return this._store.index(\"byBackup\").count(this._store.IDBKeyRange.only(BackupStatus.NotBackedUp));\n }\n\n getFirstNonBackedUpSessions(amount: number): Promise<InboundGroupSessionEntry[]> {\n return this._store.index(\"byBackup\").selectLimit(this._store.IDBKeyRange.only(BackupStatus.NotBackedUp), amount);\n }\n\n async markAsBackedUp(roomId: string, senderKey: string, sessionId: string): Promise<void> {\n const entry = await this._store.get(encodeKey(roomId, senderKey, sessionId));\n if (entry) {\n entry.backup = BackupStatus.BackedUp;\n this._store.put(entry);\n }\n }\n\n async markAllAsNotBackedUp(): Promise<number> {\n const backedUpKey = this._store.IDBKeyRange.only(BackupStatus.BackedUp);\n let count = 0;\n await this._store.index(\"byBackup\").iterateValues(backedUpKey, (val: InboundGroupSessionEntry, key: IDBValidKey, cur: IDBCursorWithValue) => {\n val.backup = BackupStatus.NotBackedUp;\n cur.update(val);\n count += 1;\n return false;\n });\n return count;\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\nimport {Store} from \"../Store\";\n\ninterface OutboundSession {\n roomId: string;\n session: string;\n createdAt: number;\n}\n\nexport class OutboundGroupSessionStore {\n private _store: Store<OutboundSession>;\n\n constructor(store: Store<OutboundSession>) {\n this._store = store;\n }\n\n remove(roomId: string): void {\n this._store.delete(roomId);\n }\n\n get(roomId: string): Promise<OutboundSession | undefined> {\n return this._store.get(roomId);\n }\n\n set(session: OutboundSession): void {\n this._store.put(session);\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {MIN_UNICODE, MAX_UNICODE} from \"./common\";\nimport {Store} from \"../Store\";\n\nfunction encodeKey(roomId: string, sessionId: string, messageIndex: number | string): string {\n return `${roomId}|${sessionId}|${messageIndex}`;\n}\n\ninterface GroupSessionDecryption {\n eventId: string;\n timestamp: number;\n}\n\ntype GroupSessionEntry = GroupSessionDecryption & { key: string }\n\nexport class GroupSessionDecryptionStore {\n private _store: Store<GroupSessionEntry>;\n\n constructor(store: Store<GroupSessionEntry>) {\n this._store = store;\n }\n\n get(roomId: string, sessionId: string, messageIndex: number): Promise<GroupSessionDecryption | undefined> {\n return this._store.get(encodeKey(roomId, sessionId, messageIndex));\n }\n\n set(roomId: string, sessionId: string, messageIndex: number, decryption: GroupSessionDecryption): void {\n (decryption as GroupSessionEntry).key = encodeKey(roomId, sessionId, messageIndex);\n this._store.put(decryption as GroupSessionEntry);\n }\n \n removeAllForRoom(roomId: string): void {\n const range = this._store.IDBKeyRange.bound(\n encodeKey(roomId, MIN_UNICODE, MIN_UNICODE),\n encodeKey(roomId, MAX_UNICODE, MAX_UNICODE)\n );\n this._store.delete(range);\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\nimport {MIN_UNICODE, MAX_UNICODE} from \"./common\";\nimport {Store} from \"../Store\";\n\nexport function encodeScopeTypeKey(scope: string, type: string): string {\n return `${scope}|${type}`;\n}\n\ninterface BaseOperation {\n id: string;\n scope: string;\n userIds: string[];\n}\n\ntype OperationType = { type: \"share_room_key\"; roomKeyMessage: RoomKeyMessage; }\n\ntype Operation = BaseOperation & OperationType\n\ntype OperationEntry = Operation & { scopeTypeKey: string; }\n\ninterface RoomKeyMessage {\n room_id: string;\n session_id: string;\n session_key: string;\n algorithm: string;\n chain_index: number;\n}\n\nexport class OperationStore {\n private _store: Store<OperationEntry>;\n\n constructor(store: Store<OperationEntry>) {\n this._store = store;\n }\n\n getAll(): Promise<Operation[]> {\n return this._store.selectAll();\n }\n\n async getAllByTypeAndScope(type: string, scope: string): Promise<Operation[]> {\n const key = encodeScopeTypeKey(scope, type);\n const results: Operation[] = [];\n await this._store.index(\"byScopeAndType\").iterateWhile(key, value => {\n if (value.scopeTypeKey !== key) {\n return false;\n }\n results.push(value);\n return true;\n });\n return results;\n }\n\n add(operation: Operation): void {\n (operation as OperationEntry).scopeTypeKey = encodeScopeTypeKey(operation.scope, operation.type);\n this._store.add(operation as OperationEntry);\n }\n\n update(operation: Operation): void {\n this._store.put(operation as OperationEntry);\n }\n\n remove(id: string): void {\n this._store.delete(id);\n }\n\n async removeAllForScope(scope: string): Promise<undefined> {\n const range = this._store.IDBKeyRange.bound(\n encodeScopeTypeKey(scope, MIN_UNICODE),\n encodeScopeTypeKey(scope, MAX_UNICODE)\n );\n const index = this._store.index(\"byScopeAndType\");\n await index.iterateValues(range, (_, __, cur) => {\n cur.delete();\n return true;\n });\n return;\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\nimport {Store} from \"../Store\";\nimport {Content} from \"../../types\";\n\ninterface AccountDataEntry {\n type: string;\n content: Content;\n}\n\nexport class AccountDataStore {\n private _store: Store<AccountDataEntry>;\n\n constructor(store: Store<AccountDataEntry>) {\n this._store = store;\n }\n\n async get(type: string): Promise<AccountDataEntry | undefined> {\n return await this._store.get(type);\n }\n\n set(event: AccountDataEntry): void {\n this._store.put(event);\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {StoreNames} from \"../common\";\nimport {txnAsPromise} from \"./utils\";\nimport {StorageError} from \"../common\";\nimport {Store} from \"./Store\";\nimport {Storage} from \"./Storage\";\nimport {SessionStore} from \"./stores/SessionStore\";\nimport {RoomSummaryStore} from \"./stores/RoomSummaryStore\";\nimport {InviteStore} from \"./stores/InviteStore\";\nimport {TimelineEventStore} from \"./stores/TimelineEventStore\";\nimport {TimelineRelationStore} from \"./stores/TimelineRelationStore\";\nimport {RoomStateStore} from \"./stores/RoomStateStore\";\nimport {RoomMemberStore} from \"./stores/RoomMemberStore\";\nimport {TimelineFragmentStore} from \"./stores/TimelineFragmentStore\";\nimport {PendingEventStore} from \"./stores/PendingEventStore\";\nimport {UserIdentityStore} from \"./stores/UserIdentityStore\";\nimport {DeviceIdentityStore} from \"./stores/DeviceIdentityStore\";\nimport {OlmSessionStore} from \"./stores/OlmSessionStore\";\nimport {InboundGroupSessionStore} from \"./stores/InboundGroupSessionStore\";\nimport {OutboundGroupSessionStore} from \"./stores/OutboundGroupSessionStore\";\nimport {GroupSessionDecryptionStore} from \"./stores/GroupSessionDecryptionStore\";\nimport {OperationStore} from \"./stores/OperationStore\";\nimport {AccountDataStore} from \"./stores/AccountDataStore\";\nimport type {ILogger, ILogItem} from \"../../../logging/types\";\n\nexport type IDBKey = IDBValidKey | IDBKeyRange;\n\nclass WriteErrorInfo {\n constructor(\n public readonly error: StorageError,\n public readonly refItem: ILogItem | undefined,\n public readonly operationName: string,\n public readonly keys: IDBKey[] | undefined,\n ) {}\n}\n\nexport class Transaction {\n private _txn: IDBTransaction;\n private _allowedStoreNames: StoreNames[];\n private _stores: { [storeName in StoreNames]?: any };\n private _storage: Storage;\n private _writeErrors: WriteErrorInfo[];\n\n constructor(txn: IDBTransaction, allowedStoreNames: StoreNames[], storage: Storage) {\n this._txn = txn;\n this._allowedStoreNames = allowedStoreNames;\n this._stores = {};\n this._storage = storage;\n this._writeErrors = [];\n }\n\n get idbFactory(): IDBFactory {\n return this._storage.idbFactory;\n }\n\n get IDBKeyRange(): typeof IDBKeyRange {\n return this._storage.IDBKeyRange;\n }\n\n get databaseName(): string {\n return this._storage.databaseName;\n }\n\n get logger(): ILogger {\n return this._storage.logger;\n }\n\n _idbStore(name: StoreNames): Store<any> {\n if (!this._allowedStoreNames.includes(name)) {\n // more specific error? this is a bug, so maybe not ...\n throw new StorageError(`Invalid store for transaction: ${name}, only ${this._allowedStoreNames.join(\", \")} are allowed.`);\n }\n return new Store(this._txn.objectStore(name), this);\n }\n\n _store<T>(name: StoreNames, mapStore: (idbStore: Store<any>) => T): T {\n if (!this._stores[name]) {\n const idbStore = this._idbStore(name);\n this._stores[name] = mapStore(idbStore);\n }\n return this._stores[name];\n }\n\n get session(): SessionStore {\n return this._store(StoreNames.session, idbStore => new SessionStore(idbStore, this._storage.localStorage));\n }\n\n get roomSummary(): RoomSummaryStore {\n return this._store(StoreNames.roomSummary, idbStore => new RoomSummaryStore(idbStore));\n }\n \n get archivedRoomSummary(): RoomSummaryStore {\n return this._store(StoreNames.archivedRoomSummary, idbStore => new RoomSummaryStore(idbStore));\n }\n\n get invites(): InviteStore {\n return this._store(StoreNames.invites, idbStore => new InviteStore(idbStore));\n }\n\n get timelineFragments(): TimelineFragmentStore {\n return this._store(StoreNames.timelineFragments, idbStore => new TimelineFragmentStore(idbStore));\n }\n\n get timelineEvents(): TimelineEventStore {\n return this._store(StoreNames.timelineEvents, idbStore => new TimelineEventStore(idbStore));\n }\n\n get timelineRelations(): TimelineRelationStore {\n return this._store(StoreNames.timelineRelations, idbStore => new TimelineRelationStore(idbStore));\n }\n\n get roomState(): RoomStateStore {\n return this._store(StoreNames.roomState, idbStore => new RoomStateStore(idbStore));\n }\n\n get roomMembers(): RoomMemberStore {\n return this._store(StoreNames.roomMembers, idbStore => new RoomMemberStore(idbStore));\n }\n\n get pendingEvents(): PendingEventStore {\n return this._store(StoreNames.pendingEvents, idbStore => new PendingEventStore(idbStore));\n }\n\n get userIdentities(): UserIdentityStore {\n return this._store(StoreNames.userIdentities, idbStore => new UserIdentityStore(idbStore));\n }\n\n get deviceIdentities(): DeviceIdentityStore {\n return this._store(StoreNames.deviceIdentities, idbStore => new DeviceIdentityStore(idbStore));\n }\n \n get olmSessions(): OlmSessionStore {\n return this._store(StoreNames.olmSessions, idbStore => new OlmSessionStore(idbStore));\n }\n \n get inboundGroupSessions(): InboundGroupSessionStore {\n return this._store(StoreNames.inboundGroupSessions, idbStore => new InboundGroupSessionStore(idbStore));\n }\n \n get outboundGroupSessions(): OutboundGroupSessionStore {\n return this._store(StoreNames.outboundGroupSessions, idbStore => new OutboundGroupSessionStore(idbStore));\n }\n\n get groupSessionDecryptions(): GroupSessionDecryptionStore {\n return this._store(StoreNames.groupSessionDecryptions, idbStore => new GroupSessionDecryptionStore(idbStore));\n }\n\n get operations(): OperationStore {\n return this._store(StoreNames.operations, idbStore => new OperationStore(idbStore));\n }\n\n get accountData(): AccountDataStore {\n return this._store(StoreNames.accountData, idbStore => new AccountDataStore(idbStore));\n }\n\n async complete(log?: ILogItem): Promise<void> {\n try {\n await txnAsPromise(this._txn);\n } catch (err) {\n if (this._writeErrors.length) {\n this._logWriteErrors(log);\n throw this._writeErrors[0].error;\n }\n throw err;\n }\n }\n\n getCause(error: Error) {\n if (error instanceof StorageError) {\n if (error.errcode === \"AbortError\" && this._writeErrors.length) {\n return this._writeErrors[0].error;\n }\n }\n return error;\n }\n\n abort(log?: ILogItem): void {\n // TODO: should we wrap the exception in a StorageError?\n try {\n this._txn.abort();\n } catch (abortErr) {\n log?.set(\"couldNotAbortTxn\", true);\n }\n if (this._writeErrors.length) {\n this._logWriteErrors(log);\n }\n }\n\n addWriteError(error: StorageError, refItem: ILogItem | undefined, operationName: string, keys: IDBKey[] | undefined) {\n // don't log subsequent `AbortError`s\n if (error.errcode !== \"AbortError\" || this._writeErrors.length === 0) {\n this._writeErrors.push(new WriteErrorInfo(error, refItem, operationName, keys));\n }\n }\n\n private _logWriteErrors(parentItem: ILogItem | undefined) {\n const callback = errorGroupItem => {\n // we don't have context when there is no parentItem, so at least log stores\n if (!parentItem) {\n errorGroupItem.set(\"allowedStoreNames\", this._allowedStoreNames);\n }\n for (const info of this._writeErrors) {\n errorGroupItem.wrap({l: info.operationName, id: info.keys}, item => {\n if (info.refItem) {\n item.refDetached(info.refItem);\n }\n item.catch(info.error);\n });\n }\n };\n const label = `${this._writeErrors.length} storage write operation(s) failed`;\n if (parentItem) {\n parentItem.wrap(label, callback);\n } else {\n this.logger.run(label, callback);\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {IDOMStorage} from \"./types\";\nimport {Transaction} from \"./Transaction\";\nimport { STORE_NAMES, StoreNames, StorageError } from \"../common\";\nimport { reqAsPromise } from \"./utils\";\nimport { ILogger } from \"../../../logging/types\";\n\nconst WEBKITEARLYCLOSETXNBUG_BOGUS_KEY = \"782rh281re38-boguskey\";\n\nexport class Storage {\n private _db: IDBDatabase;\n private _hasWebkitEarlyCloseTxnBug: boolean;\n\n readonly logger: ILogger;\n readonly idbFactory: IDBFactory\n readonly IDBKeyRange: typeof IDBKeyRange;\n readonly storeNames: typeof StoreNames;\n readonly localStorage: IDOMStorage;\n\n constructor(idbDatabase: IDBDatabase, idbFactory: IDBFactory, _IDBKeyRange: typeof IDBKeyRange, hasWebkitEarlyCloseTxnBug: boolean, localStorage: IDOMStorage, logger: ILogger) {\n this._db = idbDatabase;\n this.idbFactory = idbFactory;\n this.IDBKeyRange = _IDBKeyRange;\n this._hasWebkitEarlyCloseTxnBug = hasWebkitEarlyCloseTxnBug;\n this.storeNames = StoreNames;\n this.localStorage = localStorage;\n this.logger = logger;\n }\n\n _validateStoreNames(storeNames: StoreNames[]): void {\n const idx = storeNames.findIndex(name => !STORE_NAMES.includes(name));\n if (idx !== -1) {\n throw new StorageError(`Tried top, a transaction unknown store ${storeNames[idx]}`);\n }\n }\n\n async readTxn(storeNames: StoreNames[]): Promise<Transaction> {\n this._validateStoreNames(storeNames);\n try {\n const txn = this._db.transaction(storeNames, \"readonly\");\n // https://bugs.webkit.org/show_bug.cgi?id=222746 workaround,\n // await a bogus idb request on the new txn so it doesn't close early if we await a microtask first\n if (this._hasWebkitEarlyCloseTxnBug) {\n await reqAsPromise(txn.objectStore(storeNames[0]).get(WEBKITEARLYCLOSETXNBUG_BOGUS_KEY));\n }\n return new Transaction(txn, storeNames, this);\n } catch(err) {\n throw new StorageError(\"readTxn failed\", err);\n }\n }\n\n async readWriteTxn(storeNames: StoreNames[]): Promise<Transaction> {\n this._validateStoreNames(storeNames);\n try {\n const txn = this._db.transaction(storeNames, \"readwrite\");\n // https://bugs.webkit.org/show_bug.cgi?id=222746 workaround,\n // await a bogus idb request on the new txn so it doesn't close early if we await a microtask first\n if (this._hasWebkitEarlyCloseTxnBug) {\n await reqAsPromise(txn.objectStore(storeNames[0]).get(WEBKITEARLYCLOSETXNBUG_BOGUS_KEY));\n }\n return new Transaction(txn, storeNames, this);\n } catch(err) {\n throw new StorageError(\"readWriteTxn failed\", err);\n }\n }\n\n close(): void {\n this._db.close();\n }\n\n get databaseName(): string {\n return this._db.name;\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { iterateCursor, NOT_DONE, txnAsPromise } from \"./utils\";\nimport { STORE_NAMES, StoreNames } from \"../common\";\n\nexport type Export = { [storeName in StoreNames] : any[] }\n\nexport async function exportSession(db: IDBDatabase): Promise<Export> {\n const txn = db.transaction(STORE_NAMES, \"readonly\");\n const data = {};\n await Promise.all(STORE_NAMES.map(async name => {\n const results: any[] = data[name] = []; // initialize in deterministic order\n const store = txn.objectStore(name);\n await iterateCursor<any>(store.openCursor(), (value) => {\n results.push(value);\n return NOT_DONE;\n });\n }));\n return data as Export;\n}\n\nexport async function importSession(db: IDBDatabase, data: Export): Promise<void> {\n const txn = db.transaction(STORE_NAMES, \"readwrite\");\n for (const name of STORE_NAMES) {\n const store = txn.objectStore(name);\n for (const value of data[name]) {\n store.add(value);\n }\n }\n await txnAsPromise(txn);\n}\n","import {IDOMStorage} from \"./types\";\nimport {ITransaction} from \"./QueryTarget\";\nimport {iterateCursor, NOT_DONE, reqAsPromise} from \"./utils\";\nimport {RoomMember, EVENT_TYPE as MEMBER_EVENT_TYPE} from \"../../room/members/RoomMember.js\";\nimport {SESSION_E2EE_KEY_PREFIX} from \"../../e2ee/common.js\";\nimport {SummaryData} from \"../../room/RoomSummary\";\nimport {RoomMemberStore, MemberData} from \"./stores/RoomMemberStore\";\nimport {InboundGroupSessionStore, InboundGroupSessionEntry, BackupStatus, KeySource} from \"./stores/InboundGroupSessionStore\";\nimport {RoomStateEntry} from \"./stores/RoomStateStore\";\nimport {SessionStore} from \"./stores/SessionStore\";\nimport {Store} from \"./Store\";\nimport {encodeScopeTypeKey} from \"./stores/OperationStore\";\nimport {MAX_UNICODE} from \"./stores/common\";\nimport {ILogItem} from \"../../../logging/types\";\n\n\nexport type MigrationFunc = (db: IDBDatabase, txn: IDBTransaction, localStorage: IDOMStorage, log: ILogItem) => Promise<void> | void;\n// FUNCTIONS SHOULD ONLY BE APPENDED!!\n// the index in the array is the database version\nexport const schema: MigrationFunc[] = [\n createInitialStores,\n createMemberStore,\n migrateSession,\n createE2EEStores,\n migrateEncryptionFlag,\n createAccountDataStore,\n createInviteStore,\n createArchivedRoomSummaryStore,\n migrateOperationScopeIndex,\n createTimelineRelationsStore,\n fixMissingRoomsInUserIdentities,\n changeSSSSKeyPrefix,\n backupAndRestoreE2EEAccountToLocalStorage,\n clearAllStores,\n addInboundSessionBackupIndex,\n migrateBackupStatus\n];\n// TODO: how to deal with git merge conflicts of this array?\n\n// TypeScript note: for now, do not bother introducing interfaces / alias\n// for old schemas. Just take them as `any`. \n\nfunction createDatabaseNameHelper(db: IDBDatabase): ITransaction {\n // the Store object gets passed in several things through the Transaction class (a wrapper around IDBTransaction),\n // the only thing we should need here is the databaseName though, so we mock it out.\n // ideally we should have an easier way to go from the idb primitive layer to the specific store classes where\n // we implement logic, but for now we need this.\n const databaseNameHelper: ITransaction = {\n databaseName: db.name,\n get idbFactory(): IDBFactory { throw new Error(\"unused\");},\n get IDBKeyRange(): typeof IDBKeyRange { throw new Error(\"unused\");},\n addWriteError() {},\n };\n return databaseNameHelper;\n}\n\n\n// how do we deal with schema updates vs existing data migration in a way that \n//v1\nfunction createInitialStores(db: IDBDatabase): void {\n db.createObjectStore(\"session\", {keyPath: \"key\"});\n // any way to make keys unique here? (just use put?)\n db.createObjectStore(\"roomSummary\", {keyPath: \"roomId\"});\n\n // need index to find live fragment? prooobably ok without for now\n //key = room_id | fragment_id\n db.createObjectStore(\"timelineFragments\", {keyPath: \"key\"});\n //key = room_id | fragment_id | event_index\n const timelineEvents = db.createObjectStore(\"timelineEvents\", {keyPath: \"key\"});\n //eventIdKey = room_id | event_id\n timelineEvents.createIndex(\"byEventId\", \"eventIdKey\", {unique: true});\n //key = room_id | event.type | event.state_key,\n db.createObjectStore(\"roomState\", {keyPath: \"key\"});\n db.createObjectStore(\"pendingEvents\", {keyPath: \"key\"});\n}\n//v2\nasync function createMemberStore(db: IDBDatabase, txn: IDBTransaction): Promise<void> {\n // Cast ok here because only \"set\" is used\n const roomMembers = new RoomMemberStore(db.createObjectStore(\"roomMembers\", {keyPath: \"key\"}) as any);\n // migrate existing member state events over\n const roomState = txn.objectStore(\"roomState\");\n await iterateCursor<RoomStateEntry>(roomState.openCursor(), entry => {\n if (entry.event.type === MEMBER_EVENT_TYPE) {\n roomState.delete(entry.key);\n const member = RoomMember.fromMemberEvent(entry.roomId, entry.event);\n if (member) {\n roomMembers.set(member.serialize());\n }\n }\n return NOT_DONE;\n });\n}\n//v3\nasync function migrateSession(db: IDBDatabase, txn: IDBTransaction, localStorage: IDOMStorage): Promise<void> {\n const session = txn.objectStore(\"session\");\n try {\n const PRE_MIGRATION_KEY = 1;\n const entry = await reqAsPromise(session.get(PRE_MIGRATION_KEY));\n if (entry) {\n session.delete(PRE_MIGRATION_KEY);\n const {syncToken, syncFilterId, serverVersions} = entry.value;\n // Cast ok here because only \"set\" is used and we don't look into return\n const store = new SessionStore(session as any, localStorage);\n store.set(\"sync\", {token: syncToken, filterId: syncFilterId});\n store.set(\"serverVersions\", serverVersions);\n }\n } catch (err) {\n txn.abort();\n console.error(\"could not migrate session\", err.stack);\n }\n}\n//v4\nfunction createE2EEStores(db: IDBDatabase): void {\n db.createObjectStore(\"userIdentities\", {keyPath: \"userId\"});\n const deviceIdentities = db.createObjectStore(\"deviceIdentities\", {keyPath: \"key\"});\n deviceIdentities.createIndex(\"byCurve25519Key\", \"curve25519Key\", {unique: true});\n db.createObjectStore(\"olmSessions\", {keyPath: \"key\"});\n db.createObjectStore(\"inboundGroupSessions\", {keyPath: \"key\"});\n db.createObjectStore(\"outboundGroupSessions\", {keyPath: \"roomId\"});\n db.createObjectStore(\"groupSessionDecryptions\", {keyPath: \"key\"});\n const operations = db.createObjectStore(\"operations\", {keyPath: \"id\"});\n operations.createIndex(\"byTypeAndScope\", \"typeScopeKey\", {unique: false});\n}\n\n// v5\nasync function migrateEncryptionFlag(db: IDBDatabase, txn: IDBTransaction): Promise<void> {\n // migrate room summary isEncrypted -> encryption prop\n const roomSummary = txn.objectStore(\"roomSummary\");\n const roomState = txn.objectStore(\"roomState\");\n const summaries: any[] = [];\n await iterateCursor<any>(roomSummary.openCursor(), summary => {\n summaries.push(summary);\n return NOT_DONE;\n });\n for (const summary of summaries) {\n const encryptionEntry = await reqAsPromise(roomState.get(`${summary.roomId}|m.room.encryption|`));\n if (encryptionEntry) {\n summary.encryption = encryptionEntry?.event?.content;\n delete summary.isEncrypted;\n roomSummary.put(summary);\n }\n }\n}\n\n// v6\nfunction createAccountDataStore(db: IDBDatabase): void {\n db.createObjectStore(\"accountData\", {keyPath: \"type\"});\n}\n\n// v7\nfunction createInviteStore(db: IDBDatabase): void {\n db.createObjectStore(\"invites\", {keyPath: \"roomId\"});\n}\n\n// v8\nfunction createArchivedRoomSummaryStore(db: IDBDatabase): void {\n db.createObjectStore(\"archivedRoomSummary\", {keyPath: \"summary.roomId\"});\n}\n\n// v9\nasync function migrateOperationScopeIndex(db: IDBDatabase, txn: IDBTransaction): Promise<void> {\n try {\n const operations = txn.objectStore(\"operations\");\n operations.deleteIndex(\"byTypeAndScope\");\n await iterateCursor<any>(operations.openCursor(), (op, key, cur) => {\n const {typeScopeKey} = op;\n delete op.typeScopeKey;\n const [type, scope] = typeScopeKey.split(\"|\");\n op.scopeTypeKey = encodeScopeTypeKey(scope, type);\n cur.update(op);\n return NOT_DONE;\n });\n operations.createIndex(\"byScopeAndType\", \"scopeTypeKey\", {unique: false});\n } catch (err) {\n txn.abort();\n console.error(\"could not migrate operations\", err.stack);\n }\n}\n\n//v10\nfunction createTimelineRelationsStore(db: IDBDatabase) : void {\n db.createObjectStore(\"timelineRelations\", {keyPath: \"key\"});\n}\n\n//v11 doesn't change the schema,\n// but ensured all userIdentities have all the roomIds they should (see #470)\n\n// 2022-07-20: The fix dated from August 2021, and have removed it now because of a\n// refactoring needed in the device tracker, which made it inconvenient to expose addRoomToIdentity\nfunction fixMissingRoomsInUserIdentities() {}\n\n// v12 move ssssKey to e2ee:ssssKey so it will get backed up in the next step\nasync function changeSSSSKeyPrefix(db: IDBDatabase, txn: IDBTransaction) {\n const session = txn.objectStore(\"session\");\n const ssssKey = await reqAsPromise(session.get(\"ssssKey\"));\n if (ssssKey) {\n session.put({key: `${SESSION_E2EE_KEY_PREFIX}ssssKey`, value: ssssKey.value});\n }\n}\n// v13\nasync function backupAndRestoreE2EEAccountToLocalStorage(db: IDBDatabase, txn: IDBTransaction, localStorage: IDOMStorage, log: ILogItem) {\n const session = txn.objectStore(\"session\");\n const sessionStore = new SessionStore(new Store(session, createDatabaseNameHelper(db)), localStorage);\n // if we already have an e2ee identity, write a backup to local storage.\n // further updates to e2ee keys in the session store will also write to local storage from 0.2.15 on,\n // but here we make sure a backup is immediately created after installing the update and we don't wait until\n // the olm account needs to change\n sessionStore.writeE2EEIdentityToLocalStorage();\n // and if we already have a backup, restore it now for any missing key in idb.\n // this will restore the backup every time the idb database is dropped as it will\n // run through all the migration steps when recreating it.\n const restored = await sessionStore.tryRestoreE2EEIdentityFromLocalStorage(log);\n log.set(\"restored\", restored);\n}\n// v14 clear all stores apart from e2ee keys because of possible timeline corruption in #515, will trigger initial sync\nasync function clearAllStores(db: IDBDatabase, txn: IDBTransaction) {\n for (const storeName of db.objectStoreNames) {\n const store = txn.objectStore(storeName);\n switch (storeName) {\n case \"inboundGroupSessions\":\n case \"outboundGroupSessions\":\n case \"olmSessions\":\n case \"operations\":\n continue;\n case \"session\": {\n await iterateCursor(store.openCursor(), (value, key, cursor) => {\n if (!(key as string).startsWith(SESSION_E2EE_KEY_PREFIX)) {\n cursor.delete();\n }\n return NOT_DONE;\n })\n break;\n }\n default: {\n store.clear();\n break;\n }\n }\n }\n}\n\n// v15 add backup index to inboundGroupSessions\nasync function addInboundSessionBackupIndex(db: IDBDatabase, txn: IDBTransaction, localStorage: IDOMStorage, log: ILogItem): Promise<void> {\n const inboundGroupSessions = txn.objectStore(\"inboundGroupSessions\");\n inboundGroupSessions.createIndex(\"byBackup\", \"backup\", {unique: false});\n}\n\n\n// v16 migrates the backup and source fields of inbound group sessions\nasync function migrateBackupStatus(db: IDBDatabase, txn: IDBTransaction, localStorage: IDOMStorage, log: ILogItem): Promise<void> {\n const inboundGroupSessions = txn.objectStore(\"inboundGroupSessions\");\n let countWithSession = 0;\n let countWithoutSession = 0;\n await iterateCursor<InboundGroupSessionEntry>(inboundGroupSessions.openCursor(), (value, key, cursor) => {\n if (value.session) {\n value.backup = BackupStatus.NotBackedUp;\n // we'll also have backup keys in here, we can't tell,\n // but the worst thing that can happen is that we try\n // to backup keys that were already in backup, which\n // the server will ignore\n value.source = KeySource.DeviceMessage;\n cursor.update(value);\n countWithSession += 1;\n } else {\n countWithoutSession += 1;\n }\n return NOT_DONE;\n });\n log.set(\"countWithoutSession\", countWithoutSession);\n log.set(\"countWithSession\", countWithSession);\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n\nimport {openDatabase, txnAsPromise, reqAsPromise} from \"./utils\";\n\n// filed as https://bugs.webkit.org/show_bug.cgi?id=222746\nexport async function detectWebkitEarlyCloseTxnBug(idbFactory: IDBFactory): Promise<boolean> {\n const dbName = \"hydrogen_webkit_test_inactive_txn_bug\";\n try {\n const db = await openDatabase(dbName, db => {\n db.createObjectStore(\"test\", {keyPath: \"key\"});\n }, 1, idbFactory);\n const readTxn = db.transaction([\"test\"], \"readonly\");\n await reqAsPromise(readTxn.objectStore(\"test\").get(\"somekey\"));\n // schedule a macro task in between the two txns\n await new Promise(r => setTimeout(r, 0));\n const writeTxn = db.transaction([\"test\"], \"readwrite\");\n await Promise.resolve();\n writeTxn.objectStore(\"test\").add({key: \"somekey\", value: \"foo\"});\n await txnAsPromise(writeTxn);\n db.close();\n } catch (err) {\n if (err.name === \"TransactionInactiveError\") {\n return true;\n }\n }\n return false;\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {IDOMStorage} from \"./types\";\nimport {Storage} from \"./Storage\";\nimport { openDatabase, reqAsPromise } from \"./utils\";\nimport { exportSession, importSession, Export } from \"./export\";\nimport { schema } from \"./schema\";\nimport { detectWebkitEarlyCloseTxnBug } from \"./quirks\";\nimport { ILogItem } from \"../../../logging/types\";\n\nconst sessionName = (sessionId: string) => `hydrogen_session_${sessionId}`;\nconst openDatabaseWithSessionId = function(sessionId: string, idbFactory: IDBFactory, localStorage: IDOMStorage, log: ILogItem) {\n const create = (db, txn, oldVersion, version) => createStores(db, txn, oldVersion, version, localStorage, log);\n return openDatabase(sessionName(sessionId), create, schema.length, idbFactory);\n}\n\ninterface ServiceWorkerHandler {\n preventConcurrentSessionAccess: (sessionId: string) => Promise<void>;\n}\n\nasync function requestPersistedStorage(): Promise<boolean> {\n // don't assume browser so we can run in node with fake-idb\n const glob = this;\n if (glob?.navigator?.storage?.persist) {\n return await glob.navigator.storage.persist();\n } else if (glob?.document.requestStorageAccess) {\n try {\n await glob.document.requestStorageAccess();\n return true;\n } catch (err) {\n console.warn(\"requestStorageAccess threw an error:\", err);\n return false;\n }\n } else {\n return false;\n }\n}\n\nexport class StorageFactory {\n private _serviceWorkerHandler: ServiceWorkerHandler;\n private _idbFactory: IDBFactory;\n private _IDBKeyRange: typeof IDBKeyRange;\n private _localStorage: IDOMStorage;\n\n constructor(serviceWorkerHandler: ServiceWorkerHandler, idbFactory: IDBFactory = window.indexedDB, _IDBKeyRange = window.IDBKeyRange, localStorage: IDOMStorage = window.localStorage) {\n this._serviceWorkerHandler = serviceWorkerHandler;\n this._idbFactory = idbFactory;\n this._IDBKeyRange = _IDBKeyRange;\n this._localStorage = localStorage;\n }\n\n async create(sessionId: string, log: ILogItem): Promise<Storage> {\n await this._serviceWorkerHandler?.preventConcurrentSessionAccess(sessionId);\n requestPersistedStorage().then(persisted => {\n // Firefox lies here though, and returns true even if the user denied the request\n if (!persisted) {\n console.warn(\"no persisted storage, database can be evicted by browser\");\n }\n });\n\n const hasWebkitEarlyCloseTxnBug = await detectWebkitEarlyCloseTxnBug(this._idbFactory);\n const db = await openDatabaseWithSessionId(sessionId, this._idbFactory, this._localStorage, log);\n return new Storage(db, this._idbFactory, this._IDBKeyRange, hasWebkitEarlyCloseTxnBug, this._localStorage, log.logger);\n }\n\n delete(sessionId: string): Promise<IDBDatabase> {\n const databaseName = sessionName(sessionId);\n const req = this._idbFactory.deleteDatabase(databaseName);\n return reqAsPromise(req);\n }\n\n async export(sessionId: string, log: ILogItem): Promise<Export> {\n const db = await openDatabaseWithSessionId(sessionId, this._idbFactory, this._localStorage, log);\n return await exportSession(db);\n }\n\n async import(sessionId: string, data: Export, log: ILogItem): Promise<void> {\n const db = await openDatabaseWithSessionId(sessionId, this._idbFactory, this._localStorage, log);\n return await importSession(db, data);\n }\n}\n\nasync function createStores(db: IDBDatabase, txn: IDBTransaction, oldVersion: number | null, version: number, localStorage: IDOMStorage, log: ILogItem): Promise<void> {\n const startIdx = oldVersion || 0;\n return log.wrap(\n { l: \"storage migration\", oldVersion, version },\n async (log) => {\n for (let i = startIdx; i < version; ++i) {\n const migrationFunc = schema[i];\n await log.wrap(`v${i + 1}`, (log) => migrationFunc(db, txn, localStorage, log));\n }\n });\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {EventEntry} from \"../entries/EventEntry.js\";\nimport {REDACTION_TYPE, isRedacted} from \"../../common\";\nimport {ANNOTATION_RELATION_TYPE, getRelation} from \"../relations.js\";\nimport {redactEvent} from \"../common.js\";\n\nexport class RelationWriter {\n constructor({roomId, ownUserId, fragmentIdComparer}) {\n this._roomId = roomId;\n this._ownUserId = ownUserId;\n this._fragmentIdComparer = fragmentIdComparer;\n }\n\n // this needs to happen again after decryption too for edits\n async writeRelation(sourceEntry, txn, log) {\n const {relatedEventId} = sourceEntry;\n if (relatedEventId) {\n const relation = getRelation(sourceEntry.event);\n if (relation && relation.rel_type) {\n // we don't consider replies (which aren't relations in the MSC2674 sense)\n txn.timelineRelations.add(this._roomId, relation.event_id, relation.rel_type, sourceEntry.id);\n }\n const target = await txn.timelineEvents.getByEventId(this._roomId, relatedEventId);\n if (target) {\n const updatedStorageEntries = await this._applyRelation(sourceEntry, target, txn, log);\n if (updatedStorageEntries) {\n return updatedStorageEntries.map(e => {\n txn.timelineEvents.update(e);\n return new EventEntry(e, this._fragmentIdComparer);\n });\n }\n }\n }\n return null;\n }\n\n /**\n * @param {Object} storageEntry the event object, as it will be stored in storage.\n * Will be modified (but not written to storage) in case this event is\n * a relation target for which we've previously received relations.\n * @param {Direction} direction of the gap fill\n * */\n async writeGapRelation(storageEntry, direction, txn, log) {\n const sourceEntry = new EventEntry(storageEntry, this._fragmentIdComparer);\n const result = await this.writeRelation(sourceEntry, txn, log);\n // when back-paginating, it can also happen that we've received relations\n // for this event before, which now upon receiving the target need to be aggregated.\n if (direction.isBackward && !isRedacted(storageEntry.event)) {\n const relations = await txn.timelineRelations.getAllForTarget(this._roomId, sourceEntry.id);\n if (relations.length) {\n for (const r of relations) {\n const relationStorageEntry = await txn.timelineEvents.getByEventId(this._roomId, r.sourceEventId);\n if (relationStorageEntry) {\n const relationEntry = new EventEntry(relationStorageEntry, this._fragmentIdComparer);\n await this._applyRelation(relationEntry, storageEntry, txn, log);\n }\n }\n }\n }\n\n return result;\n }\n\n /**\n * @param {EventEntry} sourceEntry\n * @param {Object} targetStorageEntry event entry as stored in the timelineEvents store\n * @return {[Object]} array of event storage entries that have been updated\n * */\n async _applyRelation(sourceEntry, targetStorageEntry, txn, log) {\n if (sourceEntry.eventType === REDACTION_TYPE) {\n return log.wrap(\"redact\", async log => {\n const redactedEvent = targetStorageEntry.event;\n const relation = getRelation(redactedEvent); // get this before redacting\n const redacted = this._applyRedaction(sourceEntry.event, targetStorageEntry, txn, log);\n if (redacted) {\n const updated = [targetStorageEntry];\n if (relation) {\n const relationTargetStorageEntry = await this._reaggregateRelation(redactedEvent, relation, txn, log);\n if (relationTargetStorageEntry) {\n updated.push(relationTargetStorageEntry);\n }\n }\n return updated;\n }\n return null;\n });\n } else {\n const relation = getRelation(sourceEntry.event);\n if (relation && !isRedacted(targetStorageEntry.event)) {\n const relType = relation.rel_type;\n if (relType === ANNOTATION_RELATION_TYPE) {\n const aggregated = log.wrap(\"react\", log => {\n return this._aggregateAnnotation(sourceEntry.event, targetStorageEntry, log);\n });\n if (aggregated) {\n return [targetStorageEntry];\n }\n }\n }\n }\n return null;\n }\n\n _applyRedaction(redactionEvent, redactedStorageEntry, txn, log) {\n const redactedEvent = redactedStorageEntry.event;\n log.set(\"redactionId\", redactionEvent.event_id);\n log.set(\"id\", redactedEvent.event_id);\n\n const relation = getRelation(redactedEvent);\n if (relation && relation.rel_type) {\n txn.timelineRelations.remove(this._roomId, relation.event_id, relation.rel_type, redactedEvent.event_id);\n }\n // check if we're the target of a relation and remove all relations then as well\n txn.timelineRelations.removeAllForTarget(this._roomId, redactedEvent.event_id);\n\n redactEvent(redactionEvent, redactedEvent);\n delete redactedStorageEntry.annotations;\n\n return true;\n }\n\n _aggregateAnnotation(annotationEvent, targetStorageEntry/*, log*/) {\n // TODO: do we want to verify it is a m.reaction event somehow?\n const relation = getRelation(annotationEvent);\n if (!relation) {\n return false;\n }\n\n let {annotations} = targetStorageEntry;\n if (!annotations) {\n targetStorageEntry.annotations = annotations = {};\n }\n let annotation = annotations[relation.key];\n if (!annotation) {\n annotations[relation.key] = annotation = {\n count: 0,\n me: false,\n firstTimestamp: Number.MAX_SAFE_INTEGER\n };\n }\n const sentByMe = annotationEvent.sender === this._ownUserId;\n\n annotation.me = annotation.me || sentByMe;\n annotation.count += 1;\n annotation.firstTimestamp = Math.min(\n annotation.firstTimestamp,\n annotationEvent.origin_server_ts\n );\n\n return true;\n }\n\n async _reaggregateRelation(redactedRelationEvent, redactedRelation, txn, log) {\n if (redactedRelation.rel_type === ANNOTATION_RELATION_TYPE) {\n return log.wrap(\"reaggregate annotations\", log => this._reaggregateAnnotation(\n redactedRelation.event_id,\n redactedRelation.key,\n txn, log\n ));\n }\n return null;\n }\n\n async _reaggregateAnnotation(targetId, key, txn, log) {\n const target = await txn.timelineEvents.getByEventId(this._roomId, targetId);\n if (!target || !target.annotations) { // unknown or redacted event\n return null;\n }\n log.set(\"id\", targetId);\n const relations = await txn.timelineRelations.getForTargetAndType(this._roomId, targetId, ANNOTATION_RELATION_TYPE);\n log.set(\"relations\", relations.length);\n delete target.annotations[key];\n if (isObjectEmpty(target.annotations)) {\n delete target.annotations;\n }\n await Promise.all(relations.map(async relation => {\n const annotation = await txn.timelineEvents.getByEventId(this._roomId, relation.sourceEventId);\n if (!annotation) {\n log.log({l: \"missing annotation\", id: relation.sourceEventId});\n }\n if (getRelation(annotation.event).key === key) {\n this._aggregateAnnotation(annotation.event, target, log);\n }\n }));\n return target;\n }\n}\n\nfunction isObjectEmpty(obj) {\n for (const key in obj) {\n if (obj.hasOwnProperty(key)) {\n return false;\n }\n }\n return true;\n}\n\n\nimport {createMockStorage} from \"../../../../mocks/Storage\";\nimport {createEvent, withTextBody, withRedacts, withContent} from \"../../../../mocks/event.js\";\nimport {createAnnotation} from \"../relations.js\";\nimport {FragmentIdComparer} from \"../FragmentIdComparer.js\";\nimport {NullLogItem} from \"../../../../logging/NullLogger\";\n\nexport function tests() {\n const fragmentIdComparer = new FragmentIdComparer([]);\n const roomId = \"$abc\";\n const alice = \"@alice:hs.tld\";\n const bob = \"@bob:hs.tld\";\n\n return {\n \"apply redaction\": async assert => {\n const event = withTextBody(\"Dogs > Cats\", createEvent(\"m.room.message\", \"!abc\", bob));\n const reason = \"nonsense, cats are the best!\";\n const redaction = withRedacts(event.event_id, reason, createEvent(\"m.room.redaction\", \"!def\", alice));\n const redactionEntry = new EventEntry({fragmentId: 1, eventIndex: 3, event: redaction, roomId}, fragmentIdComparer);\n const relationWriter = new RelationWriter({roomId, ownUserId: bob, fragmentIdComparer});\n\n const storage = await createMockStorage();\n const txn = await storage.readWriteTxn([storage.storeNames.timelineEvents, storage.storeNames.timelineRelations]);\n txn.timelineEvents.tryInsert({fragmentId: 1, eventIndex: 2, event, roomId}, new NullLogItem());\n const updatedEntries = await relationWriter.writeRelation(redactionEntry, txn, new NullLogItem());\n await txn.complete();\n\n assert.equal(updatedEntries.length, 1);\n const redactedMessage = updatedEntries[0];\n assert.equal(redactedMessage.id, \"!abc\");\n assert.equal(redactedMessage.content.body, undefined);\n assert.equal(redactedMessage.redactionReason, reason);\n \n const readTxn = await storage.readTxn([storage.storeNames.timelineEvents]);\n const storedMessage = await readTxn.timelineEvents.getByEventId(roomId, \"!abc\");\n await readTxn.complete();\n assert.equal(storedMessage.event.content.body, undefined);\n assert.equal(storedMessage.event.unsigned.redacted_because.content.reason, reason);\n },\n \"aggregate reaction\": async assert => {\n const event = withTextBody(\"Dogs > Cats\", createEvent(\"m.room.message\", \"!abc\", bob));\n const reaction = withContent(createAnnotation(event.event_id, \"🐶\"), createEvent(\"m.reaction\", \"!def\", alice));\n reaction.origin_server_ts = 5;\n const reactionEntry = new EventEntry({event: reaction, roomId}, fragmentIdComparer);\n const relationWriter = new RelationWriter({roomId, ownUserId: alice, fragmentIdComparer});\n\n const storage = await createMockStorage();\n const txn = await storage.readWriteTxn([storage.storeNames.timelineEvents, storage.storeNames.timelineRelations]);\n txn.timelineEvents.tryInsert({fragmentId: 1, eventIndex: 2, event, roomId}, new NullLogItem());\n const updatedEntries = await relationWriter.writeRelation(reactionEntry, txn, new NullLogItem());\n await txn.complete();\n\n assert.equal(updatedEntries.length, 1);\n const reactedMessage = updatedEntries[0];\n assert.equal(reactedMessage.id, \"!abc\");\n const annotation = reactedMessage.annotations[\"🐶\"];\n assert.equal(annotation.me, true);\n assert.equal(annotation.count, 1);\n assert.equal(annotation.firstTimestamp, 5);\n \n const readTxn = await storage.readTxn([storage.storeNames.timelineEvents]);\n const storedMessage = await readTxn.timelineEvents.getByEventId(roomId, \"!abc\");\n await readTxn.complete();\n assert(storedMessage.annotations[\"🐶\"]);\n },\n \"aggregate second reaction\": async assert => {\n const event = withTextBody(\"Dogs > Cats\", createEvent(\"m.room.message\", \"!abc\", bob));\n const reaction1 = withContent(createAnnotation(event.event_id, \"🐶\"), createEvent(\"m.reaction\", \"!def\", alice));\n reaction1.origin_server_ts = 5;\n const reaction1Entry = new EventEntry({event: reaction1, roomId}, fragmentIdComparer);\n const reaction2 = withContent(createAnnotation(event.event_id, \"🐶\"), createEvent(\"m.reaction\", \"!hij\", bob));\n reaction2.origin_server_ts = 10;\n const reaction2Entry = new EventEntry({event: reaction2, roomId}, fragmentIdComparer);\n const relationWriter = new RelationWriter({roomId, ownUserId: alice, fragmentIdComparer});\n\n const storage = await createMockStorage();\n const txn = await storage.readWriteTxn([storage.storeNames.timelineEvents, storage.storeNames.timelineRelations]);\n txn.timelineEvents.tryInsert({fragmentId: 1, eventIndex: 2, event, roomId}, new NullLogItem());\n await relationWriter.writeRelation(reaction1Entry, txn, new NullLogItem());\n const updatedEntries = await relationWriter.writeRelation(reaction2Entry, txn, new NullLogItem());\n await txn.complete();\n\n assert.equal(updatedEntries.length, 1);\n\n const reactedMessage = updatedEntries[0];\n assert.equal(reactedMessage.id, \"!abc\");\n const annotation = reactedMessage.annotations[\"🐶\"];\n assert.equal(annotation.me, true);\n assert.equal(annotation.count, 2);\n assert.equal(annotation.firstTimestamp, 5);\n },\n \"redact second reaction\": async assert => {\n const event = withTextBody(\"Dogs > Cats\", createEvent(\"m.room.message\", \"!abc\", bob));\n const myReaction = withContent(createAnnotation(event.event_id, \"🐶\"), createEvent(\"m.reaction\", \"!def\", alice));\n myReaction.origin_server_ts = 5;\n const bobReaction = withContent(createAnnotation(event.event_id, \"🐶\"), createEvent(\"m.reaction\", \"!hij\", bob));\n bobReaction.origin_server_ts = 10;\n const myReactionRedaction = withRedacts(myReaction.event_id, \"\", createEvent(\"m.room.redaction\", \"!pol\", alice));\n\n const myReactionEntry = new EventEntry({event: myReaction, roomId}, fragmentIdComparer);\n const bobReactionEntry = new EventEntry({event: bobReaction, roomId}, fragmentIdComparer);\n const myReactionRedactionEntry = new EventEntry({event: myReactionRedaction, roomId}, fragmentIdComparer);\n const relationWriter = new RelationWriter({roomId, ownUserId: alice, fragmentIdComparer});\n\n const storage = await createMockStorage();\n const txn = await storage.readWriteTxn([storage.storeNames.timelineEvents, storage.storeNames.timelineRelations]);\n txn.timelineEvents.tryInsert({fragmentId: 1, eventIndex: 2, event, roomId}, new NullLogItem());\n txn.timelineEvents.tryInsert({fragmentId: 1, eventIndex: 3, event: myReaction, roomId}, new NullLogItem());\n await relationWriter.writeRelation(myReactionEntry, txn, new NullLogItem());\n txn.timelineEvents.tryInsert({fragmentId: 1, eventIndex: 4, event: bobReaction, roomId}, new NullLogItem());\n await relationWriter.writeRelation(bobReactionEntry, txn, new NullLogItem());\n const updatedEntries = await relationWriter.writeRelation(myReactionRedactionEntry, txn, new NullLogItem());\n await txn.complete();\n\n assert.equal(updatedEntries.length, 2);\n\n const redactedReaction = updatedEntries[0];\n assert.equal(redactedReaction.id, \"!def\");\n const reaggregatedMessage = updatedEntries[1];\n assert.equal(reaggregatedMessage.id, \"!abc\");\n const annotation = reaggregatedMessage.annotations[\"🐶\"];\n assert.equal(annotation.me, false);\n assert.equal(annotation.count, 1);\n assert.equal(annotation.firstTimestamp, 10);\n\n const readTxn = await storage.readTxn([storage.storeNames.timelineEvents]);\n const storedMessage = await readTxn.timelineEvents.getByEventId(roomId, \"!abc\");\n await readTxn.complete();\n assert.equal(storedMessage.annotations[\"🐶\"].count, 1);\n },\n \n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport class Direction {\n constructor(public readonly isForward: boolean) {\n }\n\n get isBackward(): boolean {\n return !this.isForward;\n }\n\n asApiString(): string {\n return this.isForward ? \"f\" : \"b\";\n }\n\n reverse(): Direction {\n return this.isForward ? Direction.Backward : Direction.Forward\n }\n\n static get Forward(): Direction {\n return _forward;\n }\n\n static get Backward(): Direction {\n return _backward;\n }\n}\n\nconst _forward = new Direction(true);\nconst _backward = new Direction(false);\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseEntry} from \"./BaseEntry\";\nimport {Direction} from \"../Direction\";\nimport {isValidFragmentId} from \"../common.js\";\nimport {KeyLimits} from \"../../../storage/common\";\n\nexport class FragmentBoundaryEntry extends BaseEntry {\n constructor(fragment, isFragmentStart, fragmentIdComparer) {\n super(fragmentIdComparer);\n this._fragment = fragment;\n // TODO: should isFragmentStart be Direction instead of bool?\n this._isFragmentStart = isFragmentStart;\n }\n\n static start(fragment, fragmentIdComparer) {\n return new FragmentBoundaryEntry(fragment, true, fragmentIdComparer);\n }\n\n static end(fragment, fragmentIdComparer) {\n return new FragmentBoundaryEntry(fragment, false, fragmentIdComparer);\n }\n \n get started() {\n return this._isFragmentStart;\n }\n\n get hasEnded() {\n return !this.started;\n }\n\n get fragment() {\n return this._fragment;\n }\n\n get fragmentId() {\n return this._fragment.id;\n }\n\n get entryIndex() {\n if (this.started) {\n return KeyLimits.minStorageKey;\n } else {\n return KeyLimits.maxStorageKey;\n }\n }\n\n get isGap() {\n return !!this.token && !this.edgeReached;\n }\n\n get token() {\n if (this.started) {\n return this.fragment.previousToken;\n } else {\n return this.fragment.nextToken;\n }\n }\n\n set token(token) {\n if (this.started) {\n this.fragment.previousToken = token;\n } else {\n this.fragment.nextToken = token;\n }\n }\n\n get edgeReached() {\n if (this.started) {\n return this.fragment.startReached;\n } else {\n return this.fragment.endReached;\n }\n }\n\n set edgeReached(reached) {\n \n if (this.started) {\n this.fragment.startReached = reached;\n } else {\n this.fragment.endReached = reached;\n }\n }\n\n \n\n get linkedFragmentId() {\n if (this.started) {\n return this.fragment.previousId;\n } else {\n return this.fragment.nextId;\n }\n }\n\n set linkedFragmentId(id) {\n if (this.started) {\n this.fragment.previousId = id;\n } else {\n this.fragment.nextId = id;\n }\n }\n\n get hasLinkedFragment() {\n return isValidFragmentId(this.linkedFragmentId);\n }\n\n get direction() {\n if (this.started) {\n return Direction.Backward;\n } else {\n return Direction.Forward;\n }\n }\n\n withUpdatedFragment(fragment) {\n return new FragmentBoundaryEntry(fragment, this._isFragmentStart, this._fragmentIdComparer);\n }\n\n createNeighbourEntry(neighbour) {\n return new FragmentBoundaryEntry(neighbour, !this._isFragmentStart, this._fragmentIdComparer);\n }\n\n addLocalRelation() {}\n removeLocalRelation() {}\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {EventKey} from \"../EventKey\";\nimport {EventEntry} from \"../entries/EventEntry.js\";\nimport {FragmentBoundaryEntry} from \"../entries/FragmentBoundaryEntry.js\";\nimport {createEventEntry} from \"./common.js\";\nimport {EVENT_TYPE as MEMBER_EVENT_TYPE} from \"../../members/RoomMember.js\";\n\n// Synapse bug? where the m.room.create event appears twice in sync response\n// when first syncing the room\nfunction deduplicateEvents(events) {\n const eventIds = new Set();\n return events.filter(e => {\n if (eventIds.has(e.event_id)) {\n return false;\n } else {\n eventIds.add(e.event_id);\n return true;\n }\n });\n}\n\nexport class SyncWriter {\n constructor({roomId, fragmentIdComparer, memberWriter, relationWriter}) {\n this._roomId = roomId;\n this._memberWriter = memberWriter;\n this._relationWriter = relationWriter;\n this._fragmentIdComparer = fragmentIdComparer;\n this._lastLiveKey = null;\n }\n\n async load(txn, log) {\n const liveFragment = await txn.timelineFragments.liveFragment(this._roomId);\n if (liveFragment) {\n const [lastEvent] = await txn.timelineEvents.lastEvents(this._roomId, liveFragment.id, 1);\n // fall back to the default event index in case the fragment was somehow written but no events\n // we should only create fragments when really writing timeline events now\n // (see https://github.com/vector-im/hydrogen-web/issues/112) but can't hurt to be extra robust.\n const eventIndex = lastEvent ? lastEvent.eventIndex : EventKey.defaultLiveKey.eventIndex;\n this._lastLiveKey = new EventKey(liveFragment.id, eventIndex);\n }\n // if there is no live fragment, we don't create it here because load gets a readonly txn.\n // this is on purpose, load shouldn't modify the store\n if (this._lastLiveKey) {\n log.set(\"live key\", this._lastLiveKey.toString());\n }\n }\n\n async _createLiveFragment(txn, previousToken) {\n const liveFragment = await txn.timelineFragments.liveFragment(this._roomId);\n if (!liveFragment) {\n if (!previousToken) {\n previousToken = null;\n }\n const fragment = {\n roomId: this._roomId,\n id: EventKey.defaultLiveKey.fragmentId,\n previousId: null,\n nextId: null,\n previousToken: previousToken,\n nextToken: null\n };\n txn.timelineFragments.add(fragment);\n this._fragmentIdComparer.add(fragment);\n return fragment;\n } else {\n return liveFragment;\n }\n }\n\n async _replaceLiveFragment(oldFragmentId, newFragmentId, previousToken, txn) {\n const oldFragment = await txn.timelineFragments.get(this._roomId, oldFragmentId);\n if (!oldFragment) {\n throw new Error(`old live fragment doesn't exist: ${oldFragmentId}`);\n }\n oldFragment.nextId = newFragmentId;\n txn.timelineFragments.update(oldFragment);\n const newFragment = {\n roomId: this._roomId,\n id: newFragmentId,\n previousId: oldFragmentId,\n nextId: null,\n previousToken: previousToken,\n nextToken: null\n };\n txn.timelineFragments.add(newFragment);\n this._fragmentIdComparer.append(newFragmentId, oldFragmentId);\n return {oldFragment, newFragment};\n }\n\n /**\n * creates a new live fragment if the timeline is limited, or if no live fragment is created yet\n * @param {EventKey} currentKey current key so far, might be none if room hasn't synced yet\n * @param {Array<BaseEntrie>} entries array to add fragment boundary entries when creating a new fragment\n * @param {Object} timeline timeline part of the room sync response\n * @param {Transaction} txn used to read and write from the fragment store\n * @return {EventKey} the new event key to start writing events at\n */\n async _ensureLiveFragment(currentKey, entries, timeline, txn, log) {\n if (!currentKey) {\n // means we haven't synced this room yet (just joined or did initial sync)\n \n // as this is probably a limited sync, prev_batch should be there\n // (but don't fail if it isn't, we won't be able to back-paginate though)\n let liveFragment = await this._createLiveFragment(txn, timeline.prev_batch);\n currentKey = new EventKey(liveFragment.id, EventKey.defaultLiveKey.eventIndex);\n entries.push(FragmentBoundaryEntry.start(liveFragment, this._fragmentIdComparer));\n log.log({l: \"live fragment\", first: true, id: currentKey.fragmentId});\n } else if (timeline.limited) {\n // replace live fragment for limited sync, *only* if we had a live fragment already\n const oldFragmentId = currentKey.fragmentId;\n currentKey = currentKey.nextFragmentKey();\n const {oldFragment, newFragment} = await this._replaceLiveFragment(oldFragmentId, currentKey.fragmentId, timeline.prev_batch, txn);\n entries.push(FragmentBoundaryEntry.end(oldFragment, this._fragmentIdComparer));\n entries.push(FragmentBoundaryEntry.start(newFragment, this._fragmentIdComparer));\n log.log({l: \"live fragment\", limited: true, id: currentKey.fragmentId});\n }\n return currentKey;\n }\n\n async _writeStateEvents(stateEvents, txn, log) {\n let nonMemberStateEvents = 0;\n for (const event of stateEvents) {\n // member events are written prior by MemberWriter\n if (event.type !== MEMBER_EVENT_TYPE) {\n txn.roomState.set(this._roomId, event);\n nonMemberStateEvents += 1;\n }\n }\n log.set(\"stateEvents\", nonMemberStateEvents);\n }\n\n async _writeTimeline(timelineEvents, timeline, memberSync, currentKey, txn, log) {\n const entries = [];\n const updatedEntries = [];\n if (timelineEvents?.length) {\n // only create a fragment when we will really write an event\n currentKey = await this._ensureLiveFragment(currentKey, entries, timeline, txn, log);\n log.set(\"timelineEvents\", timelineEvents.length);\n let timelineStateEventCount = 0;\n for(const event of timelineEvents) {\n // store event in timeline\n currentKey = currentKey.nextKey();\n const storageEntry = createEventEntry(currentKey, this._roomId, event);\n let member = await memberSync.lookupMemberAtEvent(event.sender, event, txn);\n if (member) {\n storageEntry.displayName = member.displayName;\n storageEntry.avatarUrl = member.avatarUrl;\n }\n const couldInsert = await txn.timelineEvents.tryInsert(storageEntry, log);\n if (!couldInsert) {\n continue;\n }\n const entry = new EventEntry(storageEntry, this._fragmentIdComparer);\n entries.push(entry);\n const updatedRelationTargetEntries = await this._relationWriter.writeRelation(entry, txn, log);\n if (updatedRelationTargetEntries) {\n updatedEntries.push(...updatedRelationTargetEntries);\n }\n // update state events after writing event, so for a member event,\n // we only update the member info after having written the member event\n // to the timeline, as we want that event to have the old profile info.\n // member events are written prior by MemberWriter.\n if (typeof event.state_key === \"string\" && event.type !== MEMBER_EVENT_TYPE) {\n timelineStateEventCount += 1;\n txn.roomState.set(this._roomId, event);\n }\n }\n log.set(\"timelineStateEventCount\", timelineStateEventCount);\n }\n return {currentKey, entries, updatedEntries};\n }\n\n async _handleRejoinOverlap(timeline, txn, log) {\n if (this._lastLiveKey) {\n const {fragmentId} = this._lastLiveKey;\n const [lastEvent] = await txn.timelineEvents.lastEvents(this._roomId, fragmentId, 1);\n if (lastEvent) {\n const lastEventId = lastEvent.event.event_id;\n const {events} = timeline;\n const index = events.findIndex(event => event.event_id === lastEventId);\n if (index !== -1) {\n log.set(\"overlap_event_id\", lastEventId);\n return Object.assign({}, timeline, {\n limited: false,\n events: events.slice(index + 1),\n });\n }\n }\n }\n if (!timeline.limited) {\n log.set(\"force_limited_without_overlap\", true);\n return Object.assign({}, timeline, {limited: true});\n }\n return timeline;\n }\n\n /**\n * @type {SyncWriterResult}\n * @property {Array<BaseEntry>} entries new timeline entries written\n * @property {EventKey} newLiveKey the advanced key to write events at\n * \n * @param {Object} roomResponse [description]\n * @param {boolean} isRejoin whether the room was rejoined in the sync being processed\n * @param {Transaction} txn \n * @return {SyncWriterResult}\n */\n async writeSync(roomResponse, isRejoin, hasFetchedMembers, txn, log) {\n let {timeline} = roomResponse;\n // we have rejoined the room after having synced it before,\n // check for overlap with the last synced event\n log.set(\"isRejoin\", isRejoin);\n if (isRejoin) {\n timeline = await this._handleRejoinOverlap(timeline, txn, log);\n }\n let timelineEvents;\n if (Array.isArray(timeline?.events)) {\n timelineEvents = deduplicateEvents(timeline.events);\n }\n const {state} = roomResponse;\n let stateEvents;\n if (Array.isArray(state?.events)) {\n stateEvents = state.events;\n }\n const memberSync = this._memberWriter.prepareMemberSync(stateEvents, timelineEvents, hasFetchedMembers);\n if (stateEvents) {\n await this._writeStateEvents(stateEvents, txn, log);\n }\n const {currentKey, entries, updatedEntries} =\n await this._writeTimeline(timelineEvents, timeline, memberSync, this._lastLiveKey, txn, log);\n const memberChanges = await memberSync.write(txn);\n return {entries, updatedEntries, newLiveKey: currentKey, memberChanges};\n }\n\n afterSync(newLiveKey) {\n this._lastLiveKey = newLiveKey;\n }\n\n get lastMessageKey() {\n return this._lastLiveKey;\n }\n}\n\nimport {createMockStorage} from \"../../../../mocks/Storage\";\nimport {createEvent, withTextBody} from \"../../../../mocks/event.js\";\nimport {Instance as nullLogger} from \"../../../../logging/NullLogger\";\nexport function tests() {\n const roomId = \"!abc:hs.tld\";\n return {\n \"calling timelineEvents.tryInsert with the same event id a second time fails\": async assert => {\n const storage = await createMockStorage();\n const txn = await storage.readWriteTxn([storage.storeNames.timelineEvents]);\n const event = withTextBody(\"hello!\", createEvent(\"m.room.message\", \"$abc\", \"@alice:hs.tld\"));\n const entry1 = createEventEntry(EventKey.defaultLiveKey, roomId, event);\n assert.equal(await txn.timelineEvents.tryInsert(entry1, nullLogger.item), true);\n const entry2 = createEventEntry(EventKey.defaultLiveKey.nextKey(), roomId, event);\n assert.equal(await txn.timelineEvents.tryInsert(entry2, nullLogger.item), false);\n // fake-indexeddb still aborts the transaction when preventDefault is called by tryInsert, so don't await as it will abort\n // await txn.complete();\n },\n \"calling timelineEvents.tryInsert with the same event key a second time fails\": async assert => {\n const storage = await createMockStorage();\n const txn = await storage.readWriteTxn([storage.storeNames.timelineEvents]);\n const event1 = withTextBody(\"hello!\", createEvent(\"m.room.message\", \"$abc\", \"@alice:hs.tld\"));\n const entry1 = createEventEntry(EventKey.defaultLiveKey, roomId, event1);\n assert.equal(await txn.timelineEvents.tryInsert(entry1, nullLogger.item), true);\n const event2 = withTextBody(\"hello!\", createEvent(\"m.room.message\", \"$def\", \"@alice:hs.tld\"));\n const entry2 = createEventEntry(EventKey.defaultLiveKey, roomId, event2);\n assert.equal(await txn.timelineEvents.tryInsert(entry2, nullLogger.item), false);\n // fake-indexeddb still aborts the transaction when preventDefault is called by tryInsert, so don't await as it will abort\n // await txn.complete();\n },\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n\ntype FindCallback<T> = (value: T) => boolean;\n/**\n * Very simple least-recently-used cache implementation\n * that should be fast enough for very small cache sizes\n */\nexport class BaseLRUCache<T> {\n\n public readonly limit: number;\n protected _entries: T[];\n\n constructor(limit: number) {\n this.limit = limit;\n this._entries = [];\n }\n\n get size() { return this._entries.length; }\n\n protected _get(findEntryFn: FindCallback<T>) {\n return this._getByIndexAndMoveUp(this._entries.findIndex(findEntryFn));\n }\n\n protected _getByIndexAndMoveUp(idx: number) {\n if (idx !== -1) {\n const entry = this._entries[idx];\n // move to top\n if (idx > 0) {\n this._entries.splice(idx, 1);\n this._entries.unshift(entry);\n }\n return entry;\n }\n }\n\n protected _set(value: T, findEntryFn?: FindCallback<T>) {\n let indexToRemove = findEntryFn ? this._entries.findIndex(findEntryFn) : -1;\n this._entries.unshift(value);\n if (indexToRemove === -1) {\n if (this._entries.length > this.limit) {\n indexToRemove = this._entries.length - 1;\n }\n } else {\n // we added the entry at the start since we looked for the index\n indexToRemove += 1;\n }\n if (indexToRemove !== -1) {\n this.onEvictEntry(this._entries[indexToRemove]);\n this._entries.splice(indexToRemove, 1);\n }\n }\n\n protected onEvictEntry(entry: T) {}\n}\n\nexport class LRUCache<T, K> extends BaseLRUCache<T> {\n private _keyFn: (T) => K;\n\n constructor(limit, keyFn: (T) => K) {\n super(limit);\n this._keyFn = keyFn;\n }\n\n get(key: K): T | undefined {\n return this._get(e => this._keyFn(e) === key);\n }\n\n set(value: T) {\n const key = this._keyFn(value);\n this._set(value, e => this._keyFn(e) === key);\n }\n}\n\nexport function tests() {\n interface NameTuple {\n id: number;\n name: string;\n }\n\n return {\n \"can retrieve added entries\": assert => {\n const cache = new LRUCache<NameTuple, number>(2, e => e.id);\n cache.set({id: 1, name: \"Alice\"});\n cache.set({id: 2, name: \"Bob\"});\n assert.equal(cache.get(1)!.name, \"Alice\");\n assert.equal(cache.get(2)!.name, \"Bob\");\n },\n \"first entry is evicted first\": assert => {\n const cache = new LRUCache<NameTuple, number>(2, e => e.id);\n cache.set({id: 1, name: \"Alice\"});\n cache.set({id: 2, name: \"Bob\"});\n cache.set({id: 3, name: \"Charly\"});\n assert.equal(cache.get(1), undefined);\n assert.equal(cache.get(2)!.name, \"Bob\");\n assert.equal(cache.get(3)!.name, \"Charly\");\n assert.equal(cache.size, 2);\n },\n \"second entry is evicted if first is requested\": assert => {\n const cache = new LRUCache<NameTuple, number>(2, e => e.id);\n cache.set({id: 1, name: \"Alice\"});\n cache.set({id: 2, name: \"Bob\"});\n cache.get(1);\n cache.set({id: 3, name: \"Charly\"});\n assert.equal(cache.get(1)!.name, \"Alice\");\n assert.equal(cache.get(2), undefined);\n assert.equal(cache.get(3)!.name, \"Charly\");\n assert.equal(cache.size, 2);\n },\n \"setting an entry twice removes the first\": assert => {\n const cache = new LRUCache<NameTuple, number>(2, e => e.id);\n cache.set({id: 1, name: \"Alice\"});\n cache.set({id: 2, name: \"Bob\"});\n cache.set({id: 1, name: \"Al Ice\"});\n cache.set({id: 3, name: \"Charly\"});\n assert.equal(cache.get(1)!.name, \"Al Ice\");\n assert.equal(cache.get(2), undefined);\n assert.equal(cache.get(3)!.name, \"Charly\");\n assert.equal(cache.size, 2);\n },\n \"evict callback is called\": assert => {\n let evictions = 0;\n class CustomCache extends LRUCache<NameTuple, number> {\n onEvictEntry(entry) {\n assert.equal(entry.name, \"Alice\");\n evictions += 1;\n }\n }\n const cache = new CustomCache(2, e => e.id);\n cache.set({id: 1, name: \"Alice\"});\n cache.set({id: 2, name: \"Bob\"});\n cache.set({id: 3, name: \"Charly\"});\n assert.equal(evictions, 1);\n },\n \"evict callback is called when replacing entry with same identity\": assert => {\n let evictions = 0;\n class CustomCache extends LRUCache<NameTuple, number> {\n onEvictEntry(entry) {\n assert.equal(entry.name, \"Alice\");\n evictions += 1;\n }\n }\n const cache = new CustomCache(2, e => e.id);\n cache.set({id: 1, name: \"Alice\"});\n cache.set({id: 1, name: \"Bob\"});\n assert.equal(evictions, 1);\n },\n \n };\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {MemberChange, RoomMember, EVENT_TYPE as MEMBER_EVENT_TYPE} from \"../../members/RoomMember.js\";\nimport {LRUCache} from \"../../../../utils/LRUCache\";\n\nexport class MemberWriter {\n constructor(roomId) {\n this._roomId = roomId;\n this._cache = new LRUCache(5, member => member.userId);\n }\n\n prepareMemberSync(stateEvents, timelineEvents, hasFetchedMembers) {\n return new MemberSync(this, stateEvents, timelineEvents, hasFetchedMembers);\n }\n\n async _writeMember(member, txn) {\n let existingMember = this._cache.get(member.userId);\n if (!existingMember) {\n const memberData = await txn.roomMembers.get(this._roomId, member.userId);\n if (memberData) {\n existingMember = new RoomMember(memberData);\n }\n }\n // either never heard of the member, or something changed\n if (!existingMember || !existingMember.equals(member)) {\n txn.roomMembers.set(member.serialize());\n this._cache.set(member);\n return new MemberChange(member, existingMember?.membership);\n }\n }\n\n async lookupMember(userId, txn) {\n let member = this._cache.get(userId);\n if (!member) {\n const memberData = await txn.roomMembers.get(this._roomId, userId);\n if (memberData) {\n member = new RoomMember(memberData);\n this._cache.set(member);\n }\n }\n return member;\n }\n}\n\nclass MemberSync {\n constructor(memberWriter, stateEvents, timelineEvents, hasFetchedMembers) {\n this._memberWriter = memberWriter;\n this._timelineEvents = timelineEvents;\n this._hasFetchedMembers = hasFetchedMembers;\n this._newStateMembers = null;\n if (stateEvents) {\n this._newStateMembers = this._stateEventsToMembers(stateEvents);\n }\n }\n\n get _roomId() {\n return this._memberWriter._roomId;\n }\n\n _stateEventsToMembers(stateEvents) {\n let members;\n for (const event of stateEvents) {\n if (event.type === MEMBER_EVENT_TYPE) {\n const member = RoomMember.fromMemberEvent(this._roomId, event);\n if (member) {\n if (!members) {\n members = new Map();\n }\n members.set(member.userId, member);\n }\n }\n }\n return members;\n }\n\n _timelineEventsToMembers(timelineEvents) {\n let members;\n // iterate backwards to only add the last member in the timeline\n for (let i = timelineEvents.length - 1; i >= 0; i--) {\n const e = timelineEvents[i];\n const userId = e.state_key;\n if (e.type === MEMBER_EVENT_TYPE && !members?.has(userId)) {\n const member = RoomMember.fromMemberEvent(this._roomId, e);\n if (member) {\n if (!members) {\n members = new Map();\n }\n members.set(member.userId, member);\n }\n }\n }\n return members;\n }\n\n async lookupMemberAtEvent(userId, event, txn) {\n let member;\n if (this._timelineEvents) {\n member = this._findPrecedingMemberEventInTimeline(userId, event);\n if (member) {\n return member;\n }\n }\n member = this._newStateMembers?.get(userId);\n if (member) {\n return member;\n }\n return await this._memberWriter.lookupMember(userId, txn);\n }\n\n async write(txn) {\n const memberChanges = new Map();\n let newTimelineMembers;\n if (this._timelineEvents) {\n newTimelineMembers = this._timelineEventsToMembers(this._timelineEvents);\n }\n if (this._newStateMembers) {\n for (const member of this._newStateMembers.values()) {\n if (!newTimelineMembers?.has(member.userId)) {\n const memberChange = await this._memberWriter._writeMember(member, txn);\n if (memberChange) {\n // if the member event appeared only in the state section,\n // AND we haven't heard about it AND we haven't fetched all members yet (to avoid #470),\n // this may be a lazy loading member (if it's not in a gap, we are certain\n // it is a ll member, in a gap, we can't tell), so we pass in our own membership as\n // as the previous one so we won't consider it a join to not have false positives (to avoid #192).\n // see also MemberChange.hasJoined\n const maybeLazyLoadingMember = !this._hasFetchedMembers && !memberChange.previousMembership;\n if (maybeLazyLoadingMember) {\n memberChange.previousMembership = member.membership;\n }\n memberChanges.set(memberChange.userId, memberChange);\n }\n }\n }\n }\n if (newTimelineMembers) {\n for (const member of newTimelineMembers.values()) {\n const memberChange = await this._memberWriter._writeMember(member, txn);\n if (memberChange) {\n memberChanges.set(memberChange.userId, memberChange);\n }\n }\n }\n return memberChanges;\n }\n\n // try to find the first member event before the given event,\n // so we respect historical display names within the chunk of timeline\n _findPrecedingMemberEventInTimeline(userId, event) {\n let eventIndex = -1;\n for (let i = this._timelineEvents.length - 1; i >= 0; i--) {\n const e = this._timelineEvents[i];\n if (e.event_id === event.event_id) {\n eventIndex = i;\n break;\n }\n }\n for (let i = eventIndex - 1; i >= 0; i--) {\n const e = this._timelineEvents[i];\n if (e.type === MEMBER_EVENT_TYPE && e.state_key === userId) {\n const member = RoomMember.fromMemberEvent(this._roomId, e);\n if (member) {\n return member;\n }\n }\n }\n }\n}\n\nexport function tests() {\n\n let idCounter = 0;\n\n function createMemberEvent(membership, userId, displayName, avatarUrl) {\n idCounter += 1;\n return {\n content: {\n membership,\n \"displayname\": displayName,\n \"avatar_url\": avatarUrl\n },\n event_id: `$${idCounter}`,\n sender: userId,\n \"state_key\": userId,\n type: \"m.room.member\"\n };\n }\n\n function createStorage(initialMembers = []) {\n const members = new Map();\n for (const m of initialMembers) {\n members.set(m.userId, m);\n }\n return {\n members,\n roomMembers: {\n async get(_, userId) {\n return members.get(userId);\n },\n set(member) {\n members.set(member.userId, member);\n }\n }\n }\n }\n\n function member(...args) {\n return RoomMember.fromMemberEvent(roomId, createMemberEvent.apply(null, args));\n }\n\n const roomId = \"abc\";\n const alice = \"@alice:hs.tld\";\n const avatar = \"mxc://hs.tld/def\";\n\n return {\n \"new join\": async assert => {\n const writer = new MemberWriter(roomId);\n const txn = createStorage();\n const memberSync = writer.prepareMemberSync([], [createMemberEvent(\"join\", alice)], false);\n const changes = await memberSync.write(txn);\n assert.equal(changes.size, 1);\n const change = changes.get(alice);\n assert(change.hasJoined);\n assert.equal(txn.members.get(alice).membership, \"join\");\n },\n \"accept invite\": async assert => {\n const writer = new MemberWriter(roomId);\n const txn = createStorage([member(\"invite\", alice)]);\n const memberSync = writer.prepareMemberSync([], [createMemberEvent(\"join\", alice)], false);\n const changes = await memberSync.write(txn);\n assert.equal(changes.size, 1);\n const change = changes.get(alice);\n assert.equal(change.previousMembership, \"invite\");\n assert(change.hasJoined);\n assert.equal(txn.members.get(alice).membership, \"join\");\n },\n \"change display name\": async assert => {\n const writer = new MemberWriter(roomId);\n const txn = createStorage([member(\"join\", alice, \"Alice\")]);\n const memberSync = writer.prepareMemberSync([], [createMemberEvent(\"join\", alice, \"Alies\")], false);\n const changes = await memberSync.write(txn);\n assert.equal(changes.size, 1);\n const change = changes.get(alice);\n assert(!change.hasJoined);\n assert.equal(change.member.displayName, \"Alies\");\n assert.equal(txn.members.get(alice).displayName, \"Alies\");\n },\n \"set avatar\": async assert => {\n const writer = new MemberWriter(roomId);\n const txn = createStorage([member(\"join\", alice, \"Alice\")]);\n const memberSync = writer.prepareMemberSync([], [createMemberEvent(\"join\", alice, \"Alice\", avatar)], false);\n const changes = await memberSync.write(txn);\n assert.equal(changes.size, 1);\n const change = changes.get(alice);\n assert(!change.hasJoined);\n assert.equal(change.member.avatarUrl, avatar);\n assert.equal(txn.members.get(alice).avatarUrl, avatar);\n },\n \"ignore redundant member event in timeline\": async assert => {\n const writer = new MemberWriter(roomId);\n const txn = createStorage([member(\"join\", alice, \"Alice\", avatar)]);\n const memberSync = writer.prepareMemberSync([], [createMemberEvent(\"join\", alice, \"Alice\", avatar)], false);\n const changes = await memberSync.write(txn);\n assert.equal(changes.size, 0);\n },\n \"ignore redundant member event in state\": async assert => {\n const writer = new MemberWriter(roomId);\n const txn = createStorage([member(\"join\", alice, \"Alice\", avatar)]);\n const memberSync = writer.prepareMemberSync([createMemberEvent(\"join\", alice, \"Alice\", avatar)], [], false);\n const changes = await memberSync.write(txn);\n assert.equal(changes.size, 0);\n },\n \"leave\": async assert => {\n const writer = new MemberWriter(roomId);\n const txn = createStorage([member(\"join\", alice, \"Alice\")]);\n const memberSync = writer.prepareMemberSync([], [createMemberEvent(\"leave\", alice, \"Alice\")], false);\n const changes = await memberSync.write(txn);\n assert.equal(changes.size, 1);\n const change = changes.get(alice);\n assert(change.hasLeft);\n assert(!change.hasJoined);\n },\n \"ban\": async assert => {\n const writer = new MemberWriter(roomId);\n const txn = createStorage([member(\"join\", alice, \"Alice\")]);\n const memberSync = writer.prepareMemberSync([], [createMemberEvent(\"ban\", alice, \"Alice\")], false);\n const changes = await memberSync.write(txn);\n assert.equal(changes.size, 1);\n const change = changes.get(alice);\n assert(change.hasLeft);\n assert(!change.hasJoined);\n },\n \"reject invite\": async assert => {\n const writer = new MemberWriter(roomId);\n const txn = createStorage([member(\"invite\", alice, \"Alice\")]);\n const memberSync = writer.prepareMemberSync([], [createMemberEvent(\"leave\", alice, \"Alice\")], false);\n const changes = await memberSync.write(txn);\n assert.equal(changes.size, 1);\n const change = changes.get(alice);\n assert(!change.hasLeft);\n assert(!change.hasJoined);\n },\n \"lazy loaded member we already know about doens't return change\": async assert => {\n const writer = new MemberWriter(roomId);\n const txn = createStorage([member(\"join\", alice, \"Alice\")]);\n const memberSync = writer.prepareMemberSync([createMemberEvent(\"join\", alice, \"Alice\")], [], false);\n const changes = await memberSync.write(txn);\n assert.equal(changes.size, 0);\n },\n \"lazy loaded member we already know about changes display name\": async assert => {\n const writer = new MemberWriter(roomId);\n const txn = createStorage([member(\"join\", alice, \"Alice\")]);\n const memberSync = writer.prepareMemberSync([createMemberEvent(\"join\", alice, \"Alies\")], [], false);\n const changes = await memberSync.write(txn);\n assert.equal(changes.size, 1);\n const change = changes.get(alice);\n assert(!change.hasJoined);\n assert.equal(change.member.displayName, \"Alies\");\n },\n \"unknown lazy loaded member returns change, but not considered a join\": async assert => {\n const writer = new MemberWriter(roomId);\n const txn = createStorage();\n const memberSync = writer.prepareMemberSync([createMemberEvent(\"join\", alice, \"Alice\")], [], false);\n const changes = await memberSync.write(txn);\n assert.equal(changes.size, 1);\n const change = changes.get(alice);\n assert(!change.hasJoined);\n assert(!change.hasLeft);\n assert.equal(change.member.membership, \"join\");\n assert.equal(txn.members.get(alice).displayName, \"Alice\");\n },\n \"new join through both timeline and state\": async assert => {\n const writer = new MemberWriter(roomId);\n const txn = createStorage();\n const aliceJoin = createMemberEvent(\"join\", alice, \"Alice\");\n const memberSync = writer.prepareMemberSync([aliceJoin], [aliceJoin], false);\n const changes = await memberSync.write(txn);\n assert.equal(changes.size, 1);\n const change = changes.get(alice);\n assert(change.hasJoined);\n assert(!change.hasLeft);\n },\n \"change display name in timeline with lazy loaded member in state\": async assert => {\n const writer = new MemberWriter(roomId);\n const txn = createStorage();\n const memberSync = writer.prepareMemberSync(\n [createMemberEvent(\"join\", alice, \"Alice\")],\n [createMemberEvent(\"join\", alice, \"Alies\")],\n false);\n const changes = await memberSync.write(txn);\n assert.equal(changes.size, 1);\n const change = changes.get(alice);\n assert(change.hasJoined);\n assert(!change.hasLeft);\n assert.equal(change.member.displayName, \"Alies\");\n },\n \"lookupMemberAtEvent returns closest member in the past\": async assert => {\n const event1 = createMemberEvent(\"join\", alice, \"Alice\");\n const event2 = createMemberEvent(\"join\", alice, \"Alies\");\n const event3 = createMemberEvent(\"join\", alice, \"Alys\");\n const events = [event1, event2, event3];\n // we write first because the MemberWriter assumes it is called before\n // the SyncWriter does any lookups\n const writer = new MemberWriter(roomId);\n const txn = createStorage();\n const memberSync = await writer.prepareMemberSync([], events, false);\n let member = await memberSync.lookupMemberAtEvent(event1.sender, event1, txn);\n assert.equal(member, undefined);\n member = await memberSync.lookupMemberAtEvent(event2.sender, event2, txn);\n assert.equal(member.displayName, \"Alice\");\n member = await memberSync.lookupMemberAtEvent(event3.sender, event3, txn);\n assert.equal(member.displayName, \"Alies\");\n\n assert.equal(txn.members.size, 0);\n const changes = await memberSync.write(txn);\n assert.equal(txn.members.size, 1);\n assert.equal(changes.size, 1);\n const change = changes.get(alice);\n assert(change.hasJoined);\n },\n \"lookupMemberAtEvent falls back on state event\": async assert => {\n const event1 = createMemberEvent(\"join\", alice, \"Alice\");\n const event2 = createMemberEvent(\"join\", alice, \"Alies\");\n // we write first because the MemberWriter assumes it is called before\n // the SyncWriter does any lookups\n const writer = new MemberWriter(roomId);\n const txn = createStorage();\n const memberSync = await writer.prepareMemberSync([event1], [event2], false);\n const member = await memberSync.lookupMemberAtEvent(event2.sender, event2, txn);\n assert.equal(member.displayName, \"Alice\");\n\n assert.equal(txn.members.size, 0);\n const changes = await memberSync.write(txn);\n assert.equal(txn.members.size, 1);\n assert.equal(changes.size, 1);\n const change = changes.get(alice);\n assert(change.hasJoined);\n },\n \"write works without event arrays\": async assert => {\n const writer = new MemberWriter(roomId);\n const txn = createStorage();\n const memberSync = await writer.prepareMemberSync(undefined, undefined, false);\n const changes = await memberSync.write(txn);\n assert.equal(changes.size, 0);\n },\n };\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {EventKey} from \"../EventKey\";\nimport {EventEntry} from \"../entries/EventEntry.js\";\nimport {createEventEntry, directionalAppend} from \"./common.js\";\nimport {RoomMember, EVENT_TYPE as MEMBER_EVENT_TYPE} from \"../../members/RoomMember.js\";\n\nexport class GapWriter {\n constructor({roomId, storage, fragmentIdComparer, relationWriter}) {\n this._roomId = roomId;\n this._storage = storage;\n this._fragmentIdComparer = fragmentIdComparer;\n this._relationWriter = relationWriter;\n }\n\n async _findOverlappingEvents(fragmentEntry, events, txn, log) {\n const eventIds = events.map(e => e.event_id);\n const existingEventKeyMap = await txn.timelineEvents.getEventKeysForIds(this._roomId, eventIds);\n log.set(\"existingEvents\", existingEventKeyMap.size);\n const nonOverlappingEvents = events.filter(e => !existingEventKeyMap.has(e.event_id));\n log.set(\"nonOverlappingEvents\", nonOverlappingEvents.length);\n let neighbourFragmentEntry;\n if (fragmentEntry.hasLinkedFragment) {\n log.set(\"linkedFragmentId\", fragmentEntry.linkedFragmentId);\n for (const eventKey of existingEventKeyMap.values()) {\n if (eventKey.fragmentId === fragmentEntry.linkedFragmentId) {\n log.set(\"foundLinkedFragment\", true);\n const neighbourFragment = await txn.timelineFragments.get(this._roomId, fragmentEntry.linkedFragmentId);\n neighbourFragmentEntry = fragmentEntry.createNeighbourEntry(neighbourFragment);\n break;\n }\n }\n }\n return {nonOverlappingEvents, neighbourFragmentEntry};\n }\n\n async _findFragmentEdgeEventKey(fragmentEntry, txn) {\n const {fragmentId, direction} = fragmentEntry;\n const event = await this._findFragmentEdgeEvent(fragmentId, direction, txn);\n if (event) {\n return new EventKey(event.fragmentId, event.eventIndex);\n } else {\n // no events yet in the fragment ... odd, but let's not fail and take the default key\n return EventKey.defaultFragmentKey(fragmentEntry.fragmentId);\n }\n }\n\n async _findFragmentEdgeEvent(fragmentId, direction, txn) {\n if (direction.isBackward) {\n const [firstEvent] = await txn.timelineEvents.firstEvents(this._roomId, fragmentId, 1);\n return firstEvent;\n } else {\n const [lastEvent] = await txn.timelineEvents.lastEvents(this._roomId, fragmentId, 1);\n return lastEvent;\n }\n }\n\n async _storeEvents(events, startKey, direction, state, txn, log) {\n const entries = [];\n const updatedEntries = [];\n // events is in reverse chronological order for backwards pagination,\n // e.g. order is moving away from the `from` point.\n let key = startKey;\n for (let i = 0; i < events.length; ++i) {\n const event = events[i];\n key = key.nextKeyForDirection(direction);\n const eventStorageEntry = createEventEntry(key, this._roomId, event);\n const member = this._findMember(event.sender, state, events, i, direction);\n if (member) {\n eventStorageEntry.displayName = member.displayName;\n eventStorageEntry.avatarUrl = member.avatarUrl;\n }\n // this will modify eventStorageEntry if it is a relation target\n const updatedRelationTargetEntries = await this._relationWriter.writeGapRelation(eventStorageEntry, direction, txn, log);\n if (updatedRelationTargetEntries) {\n updatedEntries.push(...updatedRelationTargetEntries);\n }\n if (await txn.timelineEvents.tryInsert(eventStorageEntry, log)) {\n const eventEntry = new EventEntry(eventStorageEntry, this._fragmentIdComparer);\n directionalAppend(entries, eventEntry, direction);\n }\n }\n return {entries, updatedEntries};\n }\n\n _findMember(userId, state, events, index, direction) {\n function isOurUser(event) {\n return event.type === MEMBER_EVENT_TYPE && event.state_key === userId;\n }\n // older messages are at a higher index in the array when going backwards\n const inc = direction.isBackward ? 1 : -1;\n for (let i = index + inc; i >= 0 && i < events.length; i += inc) {\n const event = events[i];\n if (isOurUser(event)) {\n return RoomMember.fromMemberEvent(this._roomId, event);\n }\n }\n // look into newer events, but using prev_content if found.\n // We do this before looking into `state` because it is not well specified\n // in the spec whether the events in there represent state before or after `chunk`.\n // So we look both directions first in chunk to make sure it doesn't matter.\n for (let i = index; i >= 0 && i < events.length; i -= inc) {\n const event = events[i];\n if (isOurUser(event)) {\n return RoomMember.fromReplacingMemberEvent(this._roomId, event);\n }\n }\n // assuming the member hasn't changed within the chunk, just take it from state if it's there.\n // Don't assume state is set though, as it can be empty at the top of the timeline in some circumstances \n const stateMemberEvent = state?.find(isOurUser);\n if (stateMemberEvent) {\n return RoomMember.fromMemberEvent(this._roomId, stateMemberEvent);\n }\n }\n\n async _updateFragments(fragmentEntry, neighbourFragmentEntry, end, entries, txn, log) {\n const {direction} = fragmentEntry;\n const changedFragments = [];\n directionalAppend(entries, fragmentEntry, direction);\n // set `end` as token, and if we found an event in the step before, link up the fragments in the fragment entry\n if (neighbourFragmentEntry) {\n // if neighbourFragmentEntry was found, it means the events were overlapping,\n // so no pagination should happen anymore.\n log.set(\"closedGapWith\", neighbourFragmentEntry.fragmentId);\n neighbourFragmentEntry.token = null;\n fragmentEntry.token = null;\n\n txn.timelineFragments.update(neighbourFragmentEntry.fragment);\n directionalAppend(entries, neighbourFragmentEntry, direction);\n\n // fragments that need to be changed in the fragmentIdComparer here\n // after txn succeeds\n changedFragments.push(fragmentEntry.fragment);\n changedFragments.push(neighbourFragmentEntry.fragment);\n } else {\n fragmentEntry.token = end;\n }\n txn.timelineFragments.update(fragmentEntry.fragment);\n\n return changedFragments;\n }\n\n async writeFragmentFill(fragmentEntry, response, txn, log) {\n const {fragmentId, direction} = fragmentEntry;\n // chunk is in reverse-chronological order when backwards\n const {chunk, start, state} = response;\n let {end} = response;\n\n if (!Array.isArray(chunk)) {\n throw new Error(\"Invalid chunk in response\");\n }\n if (typeof end !== \"string\" && typeof end !== \"undefined\") {\n throw new Error(\"Invalid end token in response\");\n }\n\n // make sure we have the latest fragment from the store\n const fragment = await txn.timelineFragments.get(this._roomId, fragmentId);\n if (!fragment) {\n throw new Error(`Unknown fragment: ${fragmentId}`);\n }\n fragmentEntry = fragmentEntry.withUpdatedFragment(fragment);\n // check that the request was done with the token we are aware of (extra care to avoid timeline corruption)\n if (fragmentEntry.token !== start) {\n throw new Error(\"start is not equal to prev_batch or next_batch\");\n }\n\n // begin (or end) of timeline reached\n if (chunk.length === 0) {\n fragmentEntry.edgeReached = true;\n await txn.timelineFragments.update(fragmentEntry.fragment);\n return {entries: [fragmentEntry], updatedEntries: [], fragments: []};\n }\n\n // find last event in fragment so we get the eventIndex to begin creating keys at\n let lastKey = await this._findFragmentEdgeEventKey(fragmentEntry, txn);\n log.set(\"lastKey\", lastKey.toString());\n // find out if any event in chunk is already present using findFirstOrLastOccurringEventId\n const {\n nonOverlappingEvents,\n neighbourFragmentEntry\n } = await this._findOverlappingEvents(fragmentEntry, chunk, txn, log);\n // create entries for all events in chunk, add them to entries\n const {entries, updatedEntries} = await this._storeEvents(nonOverlappingEvents, lastKey, direction, state, txn, log);\n const fragments = await this._updateFragments(fragmentEntry, neighbourFragmentEntry, end, entries, txn, log);\n \n return {entries, updatedEntries, fragments};\n }\n}\n\nimport {FragmentIdComparer} from \"../FragmentIdComparer.js\";\nimport {RelationWriter} from \"./RelationWriter.js\";\nimport {createMockStorage} from \"../../../../mocks/Storage\";\nimport {FragmentBoundaryEntry} from \"../entries/FragmentBoundaryEntry.js\";\nimport {NullLogItem} from \"../../../../logging/NullLogger\";\nimport {TimelineMock, eventIds, eventId} from \"../../../../mocks/TimelineMock.ts\";\nimport {SyncWriter} from \"./SyncWriter.js\";\nimport {MemberWriter} from \"./MemberWriter.js\";\nimport {KeyLimits} from \"../../../storage/common\";\n\nexport function tests() {\n const roomId = \"!room:hs.tdl\";\n const alice = \"alice@hs.tdl\";\n const logger = new NullLogItem();\n\n async function createGapFillTxn(storage) {\n return storage.readWriteTxn([\n storage.storeNames.roomMembers,\n storage.storeNames.pendingEvents,\n storage.storeNames.timelineEvents,\n storage.storeNames.timelineRelations,\n storage.storeNames.timelineFragments,\n ]);\n }\n\n async function setup() {\n const storage = await createMockStorage();\n const txn = await createGapFillTxn(storage);\n const fragmentIdComparer = new FragmentIdComparer([]);\n const relationWriter = new RelationWriter({\n roomId, fragmentIdComparer, ownUserId: alice,\n });\n const gapWriter = new GapWriter({\n roomId, storage, fragmentIdComparer, relationWriter\n });\n const memberWriter = new MemberWriter(roomId);\n const syncWriter = new SyncWriter({\n roomId,\n fragmentIdComparer,\n memberWriter,\n relationWriter\n });\n return { storage, txn, fragmentIdComparer, gapWriter, syncWriter, timelineMock: new TimelineMock() };\n }\n\n async function syncAndWrite(mocks, { previous, limit } = {}) {\n const {txn, timelineMock, syncWriter, fragmentIdComparer} = mocks;\n const syncResponse = timelineMock.sync(previous?.next_batch, limit);\n const {newLiveKey} = await syncWriter.writeSync(syncResponse, false, false, txn, logger);\n syncWriter.afterSync(newLiveKey);\n return {\n syncResponse,\n fragmentEntry: newLiveKey ? FragmentBoundaryEntry.start(\n await txn.timelineFragments.get(roomId, newLiveKey.fragmentId),\n fragmentIdComparer,\n ) : null,\n };\n }\n\n async function backfillAndWrite(mocks, fragmentEntry, limit) {\n const {txn, timelineMock, gapWriter} = mocks;\n const messageResponse = timelineMock.messages(fragmentEntry.token, undefined, fragmentEntry.direction.asApiString(), limit);\n await gapWriter.writeFragmentFill(fragmentEntry, messageResponse, txn, logger);\n }\n\n async function allFragmentEvents(mocks, fragmentId) {\n const {txn} = mocks;\n const entries = await txn.timelineEvents.eventsAfter(roomId, new EventKey(fragmentId, KeyLimits.minStorageKey));\n return entries.map(e => e.event);\n }\n\n async function fetchFragment(mocks, fragmentId) {\n const {txn} = mocks;\n return txn.timelineFragments.get(roomId, fragmentId);\n }\n\n function assertFilledLink(assert, fragment1, fragment2) {\n assert.equal(fragment1.nextId, fragment2.id);\n assert.equal(fragment2.previousId, fragment1.id);\n assert.equal(fragment1.nextToken, null);\n assert.equal(fragment2.previousToken, null);\n }\n\n function assertGapLink(assert, fragment1, fragment2) {\n assert.equal(fragment1.nextId, fragment2.id);\n assert.equal(fragment2.previousId, fragment1.id);\n assert.notEqual(fragment2.previousToken, null);\n }\n\n return {\n \"Backfilling after one sync\": async assert => {\n const mocks = await setup();\n const { timelineMock } = mocks;\n timelineMock.append(30);\n const {fragmentEntry} = await syncAndWrite(mocks);\n await backfillAndWrite(mocks, fragmentEntry, 10);\n const events = await allFragmentEvents(mocks, fragmentEntry.fragmentId);\n assert.deepEqual(events.map(e => e.event_id), eventIds(10, 30));\n await mocks.txn.complete();\n },\n \"Backfilling a fragment that is expected to close a gap, and does\": async assert => {\n const mocks = await setup();\n const { timelineMock } = mocks;\n timelineMock.append(10);\n const {syncResponse, fragmentEntry: firstFragmentEntry} = await syncAndWrite(mocks, { limit: 10 });\n timelineMock.append(15);\n const {fragmentEntry: secondFragmentEntry} = await syncAndWrite(mocks, { previous: syncResponse, limit: 10 });\n await backfillAndWrite(mocks, secondFragmentEntry, 10);\n\n const firstFragment = await fetchFragment(mocks, firstFragmentEntry.fragmentId);\n const secondFragment = await fetchFragment(mocks, secondFragmentEntry.fragmentId);\n assertFilledLink(assert, firstFragment, secondFragment)\n const firstEvents = await allFragmentEvents(mocks, firstFragmentEntry.fragmentId);\n assert.deepEqual(firstEvents.map(e => e.event_id), eventIds(0, 10));\n const secondEvents = await allFragmentEvents(mocks, secondFragmentEntry.fragmentId);\n assert.deepEqual(secondEvents.map(e => e.event_id), eventIds(10, 25));\n await mocks.txn.complete();\n },\n \"Backfilling a fragment that is expected to close a gap, but doesn't yet\": async assert => {\n const mocks = await setup();\n const { timelineMock } = mocks;\n timelineMock.append(10);\n const {syncResponse, fragmentEntry: firstFragmentEntry} = await syncAndWrite(mocks, { limit: 10 });\n timelineMock.append(20);\n const {fragmentEntry: secondFragmentEntry} = await syncAndWrite(mocks, { previous: syncResponse, limit: 10 });\n await backfillAndWrite(mocks, secondFragmentEntry, 10);\n\n const firstFragment = await fetchFragment(mocks, firstFragmentEntry.fragmentId);\n const secondFragment = await fetchFragment(mocks, secondFragmentEntry.fragmentId);\n assertGapLink(assert, firstFragment, secondFragment)\n const firstEvents = await allFragmentEvents(mocks, firstFragmentEntry.fragmentId);\n assert.deepEqual(firstEvents.map(e => e.event_id), eventIds(0, 10));\n const secondEvents = await allFragmentEvents(mocks, secondFragmentEntry.fragmentId);\n assert.deepEqual(secondEvents.map(e => e.event_id), eventIds(10, 30));\n await mocks.txn.complete();\n },\n \"Receiving a sync with the same events as the current fragment does not create infinite link\": async assert => {\n const mocks = await setup();\n const { txn, timelineMock } = mocks;\n timelineMock.append(10);\n const {syncResponse, fragmentEntry: fragmentEntry} = await syncAndWrite(mocks, { limit: 10 });\n // Mess with the saved token to receive old events in backfill\n fragmentEntry.token = syncResponse.next_batch;\n txn.timelineFragments.update(fragmentEntry.fragment);\n await backfillAndWrite(mocks, fragmentEntry, 10);\n\n const fragment = await fetchFragment(mocks, fragmentEntry.fragmentId);\n assert.notEqual(fragment.nextId, fragment.id);\n assert.notEqual(fragment.previousId, fragment.id);\n await mocks.txn.complete();\n },\n \"An event received by sync does not interrupt backfilling\": async assert => {\n const mocks = await setup();\n const { timelineMock } = mocks;\n timelineMock.append(10);\n const {syncResponse, fragmentEntry: firstFragmentEntry} = await syncAndWrite(mocks, { limit: 10 });\n timelineMock.append(11);\n const {fragmentEntry: secondFragmentEntry} = await syncAndWrite(mocks, { previous: syncResponse, limit: 10 });\n timelineMock.insertAfter(eventId(9), 5);\n await backfillAndWrite(mocks, secondFragmentEntry, 10);\n\n const firstEvents = await allFragmentEvents(mocks, firstFragmentEntry.fragmentId);\n assert.deepEqual(firstEvents.map(e => e.event_id), eventIds(0, 10));\n const secondEvents = await allFragmentEvents(mocks, secondFragmentEntry.fragmentId);\n assert.deepEqual(secondEvents.map(e => e.event_id), [...eventIds(21,26), ...eventIds(10, 21)]);\n const firstFragment = await fetchFragment(mocks, firstFragmentEntry.fragmentId);\n const secondFragment = await fetchFragment(mocks, secondFragmentEntry.fragmentId);\n assertFilledLink(assert, firstFragment, secondFragment)\n await mocks.txn.complete();\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseObservable} from \"../BaseObservable\";\n\nexport interface IListObserver<T> {\n onReset(list: BaseObservableList<T>): void;\n onAdd(index: number, value:T, list: BaseObservableList<T>): void;\n onUpdate(index: number, value: T, params: any, list: BaseObservableList<T>): void;\n onRemove(index: number, value: T, list: BaseObservableList<T>): void\n onMove(from: number, to: number, value: T, list: BaseObservableList<T>): void\n}\n\nexport function defaultObserverWith<T>(overrides: { [key in keyof IListObserver<T>]?: IListObserver<T>[key] }): IListObserver<T> {\n const defaults = {\n onReset(){},\n onAdd(){},\n onUpdate(){},\n onRemove(){},\n onMove(){},\n }\n return Object.assign(defaults, overrides);\n}\n\nexport abstract class BaseObservableList<T> extends BaseObservable<IListObserver<T>> implements Iterable<T> {\n emitReset() {\n for(let h of this._handlers) {\n h.onReset(this);\n }\n }\n // we need batch events, mostly on index based collection though?\n // maybe we should get started without?\n emitAdd(index: number, value: T): void {\n for(let h of this._handlers) {\n h.onAdd(index, value, this);\n }\n }\n\n emitUpdate(index: number, value: T, params?: any): void {\n for(let h of this._handlers) {\n h.onUpdate(index, value, params, this);\n }\n }\n\n emitRemove(index: number, value: T): void {\n for(let h of this._handlers) {\n h.onRemove(index, value, this);\n }\n }\n\n // toIdx assumes the item has already\n // been removed from its fromIdx\n emitMove(fromIdx: number, toIdx: number, value: T): void {\n for(let h of this._handlers) {\n h.onMove(fromIdx, toIdx, value, this);\n }\n }\n\n abstract [Symbol.iterator](): Iterator<T>;\n abstract get length(): number;\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * @license\n * Based off baseSortedIndex function in Lodash <https://lodash.com/>\n * Copyright JS Foundation and other contributors <https://js.foundation/>\n * Released under MIT license <https://lodash.com/license>\n * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>\n * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n */\nexport function sortedIndex<T>(array: T[], value: T, comparator: (x:T, y:T) => number): number {\n let low = 0;\n let high = array.length;\n\n while (low < high) {\n let mid = (low + high) >>> 1;\n let cmpResult = comparator(value, array[mid]);\n\n if (cmpResult > 0) {\n low = mid + 1;\n } else if (cmpResult < 0) {\n high = mid;\n } else {\n low = high = mid;\n }\n }\n return high;\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseObservable} from \"../BaseObservable\";\n\nexport interface IMapObserver<K, V> {\n onReset(): void;\n onAdd(key: K, value:V): void;\n onUpdate(key: K, value: V, params: any): void;\n onRemove(key: K, value: V): void\n}\n\nexport abstract class BaseObservableMap<K, V> extends BaseObservable<IMapObserver<K, V>> {\n emitReset() {\n for(let h of this._handlers) {\n h.onReset();\n }\n }\n // we need batch events, mostly on index based collection though?\n // maybe we should get started without?\n emitAdd(key: K, value: V) {\n for(let h of this._handlers) {\n h.onAdd(key, value);\n }\n }\n\n emitUpdate(key, value, params) {\n for(let h of this._handlers) {\n h.onUpdate(key, value, params);\n }\n }\n\n emitRemove(key, value) {\n for(let h of this._handlers) {\n h.onRemove(key, value);\n }\n }\n\n abstract [Symbol.iterator](): Iterator<[K, V]>;\n abstract get size(): number;\n abstract get(key: K): V | undefined;\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseObservableMap} from \"./BaseObservableMap\";\n\nexport class ObservableMap<K, V> extends BaseObservableMap<K, V> {\n private readonly _values: Map<K, V>;\n\n constructor(initialValues?: (readonly [K, V])[]) {\n super();\n this._values = new Map(initialValues);\n }\n\n update(key: K, params?: any): boolean {\n const value = this._values.get(key);\n if (value !== undefined) {\n // could be the same value, so it's already updated\n // but we don't assume this here\n this._values.set(key, value);\n this.emitUpdate(key, value, params);\n return true;\n }\n return false; // or return existing value?\n }\n\n add(key: K, value: V): boolean {\n if (!this._values.has(key)) {\n this._values.set(key, value);\n this.emitAdd(key, value);\n return true;\n }\n return false; // or return existing value?\n }\n\n remove(key: K): boolean {\n const value = this._values.get(key);\n if (value !== undefined) {\n this._values.delete(key);\n this.emitRemove(key, value);\n return true;\n } else {\n return false;\n }\n }\n\n set(key: K, value: V): boolean {\n if (this._values.has(key)) {\n // We set the value here because update only supports inline updates\n this._values.set(key, value);\n return this.update(key, undefined);\n } \n else {\n return this.add(key, value);\n }\n }\n\n reset(): void {\n this._values.clear();\n this.emitReset();\n }\n\n get(key: K): V | undefined {\n return this._values.get(key);\n }\n\n get size(): number {\n return this._values.size;\n }\n\n [Symbol.iterator](): Iterator<[K, V]> {\n return this._values.entries();\n }\n\n values(): Iterator<V> {\n return this._values.values();\n }\n\n keys(): Iterator<K> {\n return this._values.keys();\n }\n}\n\nexport function tests() {\n return {\n test_initial_values(assert) {\n const map = new ObservableMap([\n [\"a\", 5],\n [\"b\", 10]\n ]);\n assert.equal(map.size, 2);\n assert.equal(map.get(\"a\"), 5);\n assert.equal(map.get(\"b\"), 10);\n },\n\n test_add(assert) {\n let fired = 0;\n const map = new ObservableMap<number, {value: number}>();\n map.subscribe({\n onAdd(key, value) {\n fired += 1;\n assert.equal(key, 1);\n assert.deepEqual(value, {value: 5}); \n },\n onUpdate() {},\n onRemove() {},\n onReset() {}\n });\n map.add(1, {value: 5});\n assert.equal(map.size, 1);\n assert.equal(fired, 1);\n },\n\n test_update(assert) {\n let fired = 0;\n const map = new ObservableMap<number, {number: number}>();\n const value = {number: 5};\n map.add(1, value);\n map.subscribe({\n onUpdate(key, value, params) {\n fired += 1;\n assert.equal(key, 1);\n assert.deepEqual(value, {number: 6}); \n assert.equal(params, \"test\");\n },\n onAdd() {},\n onRemove() {},\n onReset() {}\n });\n value.number = 6;\n map.update(1, \"test\");\n assert.equal(fired, 1);\n },\n\n test_update_unknown(assert) {\n let fired = 0;\n const map = new ObservableMap<number, {number: number}>();\n map.subscribe({\n onUpdate() { fired += 1; },\n onAdd() {},\n onRemove() {},\n onReset() {}\n });\n const result = map.update(1);\n assert.equal(fired, 0);\n assert.equal(result, false);\n },\n\n test_set(assert) {\n let add_fired = 0, update_fired = 0;\n const map = new ObservableMap<number, {value: number}>();\n map.subscribe({\n onAdd(key, value) {\n add_fired += 1;\n assert.equal(key, 1);\n assert.deepEqual(value, {value: 5}); \n },\n onUpdate(key, value/*, params*/) {\n update_fired += 1;\n assert.equal(key, 1);\n assert.deepEqual(value, {value: 7}); \n },\n onRemove() {},\n onReset() {}\n });\n // Add\n map.set(1, {value: 5});\n assert.equal(map.size, 1);\n assert.equal(add_fired, 1);\n // Update\n map.set(1, {value: 7});\n assert.equal(map.size, 1);\n assert.equal(update_fired, 1);\n },\n\n test_remove(assert) {\n let fired = 0;\n const map = new ObservableMap<number, {value: number}>();\n const value = {value: 5};\n map.add(1, value);\n map.subscribe({\n onRemove(key, value) {\n fired += 1;\n assert.equal(key, 1);\n assert.deepEqual(value, {value: 5}); \n },\n onAdd() {},\n onUpdate() {},\n onReset() {}\n });\n map.remove(1);\n assert.equal(map.size, 0);\n assert.equal(fired, 1);\n },\n\n test_iterate(assert) {\n const results: any[] = [];\n const map = new ObservableMap<number, {number: number}>();\n map.add(1, {number: 5});\n map.add(2, {number: 6});\n map.add(3, {number: 7});\n for (let e of map) {\n results.push(e);\n }\n assert.equal(results.length, 3);\n assert.equal(results.find(([key]) => key === 1)[1].number, 5);\n assert.equal(results.find(([key]) => key === 2)[1].number, 6);\n assert.equal(results.find(([key]) => key === 3)[1].number, 7);\n },\n test_size(assert) {\n const map = new ObservableMap<number, {number: number}>();\n map.add(1, {number: 5});\n map.add(2, {number: 6});\n assert.equal(map.size, 2);\n },\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseObservableList} from \"./BaseObservableList\";\nimport {sortedIndex} from \"../../utils/sortedIndex\";\n\n/*\n\nwhen a value changes, it sorting order can change. It would still be at the old index prior to firing an onUpdate event.\nSo how do you know where it was before it changed, if not by going over all values?\n\nhow to make this fast?\n\nseems hard to solve with an array, because you need to map the key to it's previous location somehow, to efficiently find it,\nand move it.\n\nI wonder if we could do better with a binary search tree (BST).\nThe tree has a value with {key, value}. There is a plain Map mapping keys to this tuple,\nfor easy lookup. Now how do we find the index of this tuple in the BST?\n\neither we store in every node the amount of nodes on the left and right, or we decend into the part\nof the tree preceding the node we want to know about. Updating the counts upwards would probably be fine as this is log2 of\nthe size of the container.\n\nto be able to go from a key to an index, the value would have the have a link with the tree node though\n\nso key -> Map<key,value> -> value -> node -> *parentNode -> rootNode\nwith a node containing {value, leftCount, rightCount, leftNode, rightNode, parentNode}\n*/\n\n// does not assume whether or not the values are reference\n// types modified outside of the collection (and affecting sort order) or not\n\n// no duplicates allowed for now\nexport class SortedMapList extends BaseObservableList {\n constructor(sourceMap, comparator) {\n super();\n this._sourceMap = sourceMap;\n this._comparator = (a, b) => comparator(a.value, b.value);\n this._sortedPairs = null;\n this._mapSubscription = null;\n }\n \n onAdd(key, value) {\n const pair = {key, value};\n const idx = sortedIndex(this._sortedPairs, pair, this._comparator);\n this._sortedPairs.splice(idx, 0, pair);\n this.emitAdd(idx, value);\n }\n\n onRemove(key, value) {\n const pair = {key, value};\n const idx = sortedIndex(this._sortedPairs, pair, this._comparator);\n // assert key === this._sortedPairs[idx].key;\n this._sortedPairs.splice(idx, 1);\n this.emitRemove(idx, value);\n }\n\n onUpdate(key, value, params) {\n // if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it\n if (!this._sortedPairs) {\n return;\n }\n // TODO: suboptimal for performance, see above for idea with BST to speed this up if we need to\n const oldIdx = this._sortedPairs.findIndex(p => p.key === key);\n // neccesary to remove pair from array before\n // doing sortedIndex as it relies on being sorted\n this._sortedPairs.splice(oldIdx, 1);\n const pair = {key, value};\n const newIdx = sortedIndex(this._sortedPairs, pair, this._comparator);\n this._sortedPairs.splice(newIdx, 0, pair);\n if (oldIdx !== newIdx) {\n this.emitMove(oldIdx, newIdx, value);\n }\n this.emitUpdate(newIdx, value, params);\n }\n\n onReset() {\n this._sortedPairs = [];\n this.emitReset();\n }\n\n onSubscribeFirst() {\n this._mapSubscription = this._sourceMap.subscribe(this);\n this._sortedPairs = new Array(this._sourceMap.size);\n let i = 0;\n for (let [key, value] of this._sourceMap) {\n this._sortedPairs[i] = {key, value};\n ++i;\n }\n this._sortedPairs.sort(this._comparator);\n super.onSubscribeFirst();\n }\n\n onUnsubscribeLast() {\n super.onUnsubscribeLast();\n this._sortedPairs = null;\n this._mapSubscription = this._mapSubscription();\n }\n\n get(index) {\n return this._sortedPairs[index].value;\n }\n\n get length() {\n return this._sourceMap.size;\n }\n\n [Symbol.iterator]() {\n const it = this._sortedPairs.values();\n return {\n next() {\n const v = it.next();\n if (v.value) {\n v.value = v.value.value;\n }\n return v;\n }\n }\n }\n}\n\nimport {ObservableMap} from \"../map/ObservableMap\";\n\nexport function tests() {\n return {\n test_sortIndex(assert) {\n const a = [1, 5, 6, 8];\n const cmp = (a, b) => a - b;\n let idx = sortedIndex(a, 0, cmp);\n assert.equal(idx, 0);\n idx = sortedIndex(a, 3, cmp);\n assert.equal(idx, 1);\n idx = sortedIndex(a, 5, cmp);\n assert.equal(idx, 1);\n idx = sortedIndex(a, 8, cmp);\n assert.equal(idx, 3);\n },\n\n test_sortIndex_reverse(assert) {\n let idx = sortedIndex([8, 6, 5, 1], 6, (a, b) => b - a);\n assert.equal(idx, 1);\n },\n\n test_sortIndex_comparator_Array_compatible(assert) {\n const a = [5, 1, 8, 2];\n const cmp = (a, b) => a - b;\n a.sort(cmp);\n assert.deepEqual(a, [1, 2, 5, 8]);\n let idx = sortedIndex(a, 2, cmp);\n assert.equal(idx, 1);\n },\n\n test_initial_values(assert) {\n const map = new ObservableMap([\n [\"a\", 50],\n [\"b\", 6],\n [\"c\", -5],\n ]);\n const list = new SortedMapList(map, (a, b) => a - b);\n list.subscribe({}); //needed to populate iterator\n assert.deepEqual(Array.from(list), [-5, 6, 50]);\n assert.equal(list.length, 3);\n },\n\n test_add(assert) {\n const map = new ObservableMap([[\"a\", 50], [\"b\", 6], [\"c\", -5]]);\n const list = new SortedMapList(map, (a, b) => a - b);\n let fired = 0;\n list.subscribe({\n onAdd(idx, value) {\n fired++;\n assert.equal(idx, 2);\n assert.equal(value, 20);\n }\n });\n map.add(\"d\", 20);\n assert.equal(fired, 1);\n assert.equal(list.length, 4);\n },\n\n test_remove(assert) {\n const map = new ObservableMap([[\"a\", 50], [\"b\", 6], [\"c\", -5]]);\n const list = new SortedMapList(map, (a, b) => a - b);\n let fired = 0;\n list.subscribe({\n onRemove(idx, value) {\n fired++;\n assert.equal(idx, 2);\n assert.equal(value, 50);\n }\n });\n map.remove(\"a\");\n assert.equal(fired, 1);\n assert.equal(list.length, 2);\n },\n\n test_move_reference(assert) {\n const a = {number: 3};\n const map = new ObservableMap([\n [\"a\", a],\n [\"b\", {number: 11}],\n [\"c\", {number: 1}],\n ]);\n const list = new SortedMapList(map, (a, b) => a.number - b.number);\n let updateFired = 0, moveFired = 0;\n list.subscribe({\n onUpdate(idx, value, param) {\n updateFired++;\n assert.equal(idx, 2);\n assert.equal(value, a);\n assert.equal(param, \"number\");\n },\n\n onMove(oldIdx, newIdx, value) {\n moveFired++;\n assert.equal(oldIdx, 1);\n assert.equal(newIdx, 2);\n assert.equal(value, a);\n }\n });\n a.number = 111;\n map.update(\"a\", \"number\");\n assert.equal(moveFired, 1);\n assert.equal(updateFired, 1);\n },\n\n test_update_without_move(assert) {\n const a = {number: 3};\n const map = new ObservableMap([\n [\"a\", a],\n [\"b\", {number: 11}],\n [\"c\", {number: 1}],\n ]);\n const list = new SortedMapList(map, (a, b) => a.number - b.number);\n let updateFired = 0, moveFired = 0;\n list.subscribe({\n onUpdate(idx, value, param) {\n updateFired++;\n assert.equal(idx, 1);\n assert.equal(value, a);\n assert.equal(param, \"number\");\n },\n\n onMove() {\n moveFired++;\n }\n });\n assert.deepEqual(Array.from(list).map(v => v.number), [1, 3, 11]);\n // asume some part of a that doesn't affect\n // sorting order has changed here\n map.update(\"a\", \"number\");\n assert.equal(moveFired, 0);\n assert.equal(updateFired, 1);\n assert.deepEqual(Array.from(list).map(v => v.number), [1, 3, 11]);\n },\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseObservableMap} from \"./BaseObservableMap\";\n\nexport class FilteredMap extends BaseObservableMap {\n constructor(source, filter) {\n super();\n this._source = source;\n this._filter = filter;\n /** @type {Map<string, bool>} */\n this._included = null;\n this._subscription = null;\n }\n\n setFilter(filter) {\n this._filter = filter;\n if (this._subscription) {\n this._reapplyFilter();\n }\n }\n\n /**\n * reapply the filter\n */\n _reapplyFilter(silent = false) {\n if (this._filter) {\n const oldIncluded = this._included;\n this._included = this._included || new Map();\n for (const [key, value] of this._source) {\n const isIncluded = this._filter(value, key);\n this._included.set(key, isIncluded);\n if (!silent) {\n const wasIncluded = oldIncluded ? oldIncluded.get(key) : true;\n this._emitForUpdate(wasIncluded, isIncluded, key, value);\n }\n }\n } else { // no filter\n // did we have a filter before?\n if (this._included && !silent) {\n // add any non-included items again\n for (const [key, value] of this._source) {\n if (!this._included.get(key)) {\n this.emitAdd(key, value);\n }\n }\n }\n this._included = null;\n }\n }\n\n onAdd(key, value) {\n if (this._filter) {\n const included = this._filter(value, key);\n this._included.set(key, included);\n if (!included) {\n return;\n }\n }\n this.emitAdd(key, value);\n }\n\n onRemove(key, value) {\n const wasIncluded = !this._filter || this._included.get(key);\n this._included.delete(key);\n if (wasIncluded) {\n this.emitRemove(key, value);\n }\n }\n\n onUpdate(key, value, params) {\n // if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it\n if (!this._included) {\n return;\n }\n if (this._filter) {\n const wasIncluded = this._included.get(key);\n const isIncluded = this._filter(value, key);\n this._included.set(key, isIncluded);\n this._emitForUpdate(wasIncluded, isIncluded, key, value, params);\n } else {\n this.emitUpdate(key, value, params);\n }\n }\n\n _emitForUpdate(wasIncluded, isIncluded, key, value, params = null) {\n if (wasIncluded && !isIncluded) {\n this.emitRemove(key, value);\n } else if (!wasIncluded && isIncluded) {\n this.emitAdd(key, value);\n } else if (wasIncluded && isIncluded) {\n this.emitUpdate(key, value, params);\n }\n }\n\n onSubscribeFirst() {\n this._subscription = this._source.subscribe(this);\n this._reapplyFilter(true);\n super.onSubscribeFirst();\n }\n\n onUnsubscribeLast() {\n super.onUnsubscribeLast();\n this._included = null;\n this._subscription = this._subscription();\n }\n\n onReset() {\n this._reapplyFilter();\n this.emitReset();\n }\n\n [Symbol.iterator]() {\n return new FilterIterator(this._source, this._included);\n }\n\n get size() {\n let count = 0;\n this._included.forEach(included => {\n if (included) {\n count += 1;\n }\n });\n return count;\n }\n\n get(key) {\n const value = this._source.get(key);\n if (value && this._filter(value, key)) {\n return value;\n }\n }\n}\n\nclass FilterIterator {\n constructor(map, _included) {\n this._included = _included;\n this._sourceIterator = map[Symbol.iterator]();\n }\n\n next() {\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const sourceResult = this._sourceIterator.next();\n if (sourceResult.done) {\n return sourceResult;\n }\n const key = sourceResult.value[0];\n if (this._included.get(key)) {\n return sourceResult;\n }\n }\n }\n}\n\nimport {ObservableMap} from \"./ObservableMap\";\nexport function tests() {\n return {\n \"filter preloaded list\": assert => {\n const source = new ObservableMap();\n source.add(\"one\", 1);\n source.add(\"two\", 2);\n source.add(\"three\", 3);\n const oddNumbers = new FilteredMap(source, x => x % 2 !== 0);\n // can only iterate after subscribing\n oddNumbers.subscribe({});\n assert.equal(oddNumbers.size, 2);\n const it = oddNumbers[Symbol.iterator]();\n assert.deepEqual(it.next().value, [\"one\", 1]);\n assert.deepEqual(it.next().value, [\"three\", 3]);\n assert.equal(it.next().done, true);\n },\n // \"filter added values\": assert => {\n\n // },\n // \"filter removed values\": assert => {\n\n // },\n // \"filter changed values\": assert => {\n\n // },\n\n \"emits must trigger once\": assert => {\n const source = new ObservableMap();\n let count_add = 0, count_update = 0, count_remove = 0;\n source.add(\"num1\", 1);\n source.add(\"num2\", 2);\n source.add(\"num3\", 3);\n const oddMap = new FilteredMap(source, x => x % 2 !== 0);\n oddMap.subscribe({\n onAdd() {\n count_add += 1;\n },\n onRemove() {\n count_remove += 1;\n },\n onUpdate() {\n count_update += 1;\n }\n });\n source.set(\"num3\", 4);\n source.set(\"num3\", 5);\n source.set(\"num3\", 7);\n assert.strictEqual(count_add, 1);\n assert.strictEqual(count_update, 1);\n assert.strictEqual(count_remove, 1);\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseObservableMap} from \"./BaseObservableMap\";\n/*\nso a mapped value can emit updates on it's own with this._emitSpontaneousUpdate that is passed in the mapping function\nhow should the mapped value be notified of an update though? and can it then decide to not propagate the update?\n*/\nexport class MappedMap extends BaseObservableMap {\n constructor(source, mapper, updater) {\n super();\n this._source = source;\n this._mapper = mapper;\n this._updater = updater;\n this._mappedValues = new Map();\n }\n\n _emitSpontaneousUpdate(key, params) {\n const value = this._mappedValues.get(key);\n if (value) {\n this.emitUpdate(key, value, params);\n }\n }\n\n onAdd(key, value) {\n const emitSpontaneousUpdate = this._emitSpontaneousUpdate.bind(this, key);\n const mappedValue = this._mapper(value, emitSpontaneousUpdate);\n this._mappedValues.set(key, mappedValue);\n this.emitAdd(key, mappedValue);\n }\n\n onRemove(key/*, _value*/) {\n const mappedValue = this._mappedValues.get(key);\n if (this._mappedValues.delete(key)) {\n this.emitRemove(key, mappedValue);\n }\n }\n\n onUpdate(key, value, params) {\n // if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it\n if (!this._mappedValues) {\n return;\n }\n const mappedValue = this._mappedValues.get(key);\n if (mappedValue !== undefined) {\n this._updater?.(mappedValue, params, value);\n // TODO: map params somehow if needed?\n this.emitUpdate(key, mappedValue, params);\n }\n }\n\n onSubscribeFirst() {\n this._subscription = this._source.subscribe(this);\n for (let [key, value] of this._source) {\n const emitSpontaneousUpdate = this._emitSpontaneousUpdate.bind(this, key);\n const mappedValue = this._mapper(value, emitSpontaneousUpdate);\n this._mappedValues.set(key, mappedValue);\n }\n super.onSubscribeFirst();\n }\n\n onUnsubscribeLast() {\n super.onUnsubscribeLast();\n this._subscription = this._subscription();\n this._mappedValues.clear();\n }\n\n onReset() {\n this._mappedValues.clear();\n this.emitReset();\n }\n\n [Symbol.iterator]() {\n return this._mappedValues.entries();\n }\n\n get size() {\n return this._mappedValues.size;\n }\n\n get(key) {\n return this._mappedValues.get(key);\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseObservableMap} from \"./BaseObservableMap\";\n\nexport class JoinedMap extends BaseObservableMap {\n constructor(sources) {\n super();\n this._sources = sources;\n this._subscriptions = null;\n }\n\n onAdd(source, key, value) {\n if (!this._isKeyAtSourceOccluded(source, key)) {\n const occludingValue = this._getValueFromOccludedSources(source, key);\n if (occludingValue !== undefined) {\n // adding a value that will occlude another one should\n // first emit a remove\n this.emitRemove(key, occludingValue);\n }\n this.emitAdd(key, value);\n }\n }\n\n onRemove(source, key, value) {\n if (!this._isKeyAtSourceOccluded(source, key)) {\n this.emitRemove(key, value);\n const occludedValue = this._getValueFromOccludedSources(source, key);\n if (occludedValue !== undefined) {\n // removing a value that so far occluded another one should\n // emit an add for the occluded value after the removal\n this.emitAdd(key, occludedValue);\n }\n }\n }\n\n onUpdate(source, key, value, params) {\n // if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it\n if (!this._subscriptions) {\n return;\n }\n if (!this._isKeyAtSourceOccluded(source, key)) {\n this.emitUpdate(key, value, params);\n }\n }\n\n onReset() {\n this.emitReset();\n }\n\n onSubscribeFirst() {\n this._subscriptions = this._sources.map(source => new SourceSubscriptionHandler(source, this).subscribe());\n super.onSubscribeFirst();\n }\n\n _isKeyAtSourceOccluded(source, key) {\n // sources that come first in the sources array can\n // hide the keys in later sources, to prevent events\n // being emitted for the same key and different values,\n // so check the key is not present in earlier sources\n const index = this._sources.indexOf(source);\n for (let i = 0; i < index; i += 1) {\n if (this._sources[i].get(key) !== undefined) {\n return true;\n }\n }\n return false;\n }\n\n // get the value that the given source and key occlude, if any\n _getValueFromOccludedSources(source, key) {\n // sources that come first in the sources array can\n // hide the keys in later sources, to prevent events\n // being emitted for the same key and different values,\n // so check the key is not present in earlier sources\n const index = this._sources.indexOf(source);\n for (let i = index + 1; i < this._sources.length; i += 1) {\n const source = this._sources[i];\n const occludedValue = source.get(key);\n if (occludedValue !== undefined) {\n return occludedValue;\n }\n }\n return undefined;\n }\n\n onUnsubscribeLast() {\n super.onUnsubscribeLast();\n for (const s of this._subscriptions) {\n s.dispose();\n }\n }\n\n [Symbol.iterator]() {\n return new JoinedIterator(this._sources);\n }\n\n get size() {\n return this._sources.reduce((sum, s) => sum + s.size, 0);\n }\n\n get(key) {\n for (const s of this._sources) {\n const value = s.get(key);\n if (value) {\n return value;\n }\n }\n return null;\n }\n}\n\nclass JoinedIterator {\n constructor(sources) {\n this._sources = sources;\n this._sourceIndex = -1;\n this._currentIterator = null;\n this._encounteredKeys = new Set();\n }\n\n next() {\n let result;\n while (!result) {\n if (!this._currentIterator) {\n this._sourceIndex += 1;\n if (this._sources.length <= this._sourceIndex) {\n return {done: true};\n }\n this._currentIterator = this._sources[this._sourceIndex][Symbol.iterator]();\n }\n const sourceResult = this._currentIterator.next();\n if (sourceResult.done) {\n this._currentIterator = null;\n continue;\n } else {\n const key = sourceResult.value[0];\n if (!this._encounteredKeys.has(key)) {\n this._encounteredKeys.add(key);\n result = sourceResult;\n }\n }\n }\n return result;\n }\n}\n\nclass SourceSubscriptionHandler {\n constructor(source, joinedMap) {\n this._source = source;\n this._joinedMap = joinedMap;\n this._subscription = null;\n }\n\n subscribe() {\n this._subscription = this._source.subscribe(this);\n return this;\n }\n\n dispose() {\n this._subscription = this._subscription();\n }\n\n onAdd(key, value) {\n this._joinedMap.onAdd(this._source, key, value);\n }\n\n onRemove(key, value) {\n this._joinedMap.onRemove(this._source, key, value);\n }\n\n onUpdate(key, value, params) {\n this._joinedMap.onUpdate(this._source, key, value, params);\n }\n\n onReset() {\n this._joinedMap.onReset(this._source);\n }\n}\n\n\nimport { ObservableMap } from \"./ObservableMap\";\n\nexport function tests() {\n\n function observeMap(map) {\n const events = [];\n map.subscribe({\n onAdd(key, value) { events.push({type: \"add\", key, value}); },\n onRemove(key, value) { events.push({type: \"remove\", key, value}); },\n onUpdate(key, value, params) { events.push({type: \"update\", key, value, params}); }\n });\n return events;\n }\n\n return {\n \"joined iterator\": assert => {\n const firstKV = [\"a\", 1];\n const secondKV = [\"b\", 2];\n const thirdKV = [\"c\", 3];\n const it = new JoinedIterator([[firstKV, secondKV], [thirdKV]]);\n assert.equal(it.next().value, firstKV);\n assert.equal(it.next().value, secondKV);\n assert.equal(it.next().value, thirdKV);\n assert.equal(it.next().done, true);\n },\n \"prevent key collision during iteration\": assert => {\n const first = new ObservableMap();\n const second = new ObservableMap();\n const join = new JoinedMap([first, second]);\n second.add(\"a\", 2);\n second.add(\"b\", 3);\n first.add(\"a\", 1);\n const it = join[Symbol.iterator]();\n assert.deepEqual(it.next().value, [\"a\", 1]);\n assert.deepEqual(it.next().value, [\"b\", 3]);\n assert.equal(it.next().done, true);\n },\n \"adding occluded key doesn't emit add\": assert => {\n const first = new ObservableMap();\n const second = new ObservableMap();\n const join = new JoinedMap([first, second]);\n const events = observeMap(join);\n first.add(\"a\", 1);\n second.add(\"a\", 2);\n assert.equal(events.length, 1);\n assert.equal(events[0].type, \"add\");\n assert.equal(events[0].key, \"a\");\n assert.equal(events[0].value, 1);\n },\n \"updating occluded key doesn't emit update\": assert => {\n const first = new ObservableMap();\n const second = new ObservableMap();\n const join = new JoinedMap([first, second]);\n first.add(\"a\", 1);\n second.add(\"a\", 2);\n const events = observeMap(join);\n second.update(\"a\", 3);\n assert.equal(events.length, 0);\n },\n \"removal of occluding key emits add after remove\": assert => {\n const first = new ObservableMap();\n const second = new ObservableMap();\n const join = new JoinedMap([first, second]);\n first.add(\"a\", 1);\n second.add(\"a\", 2);\n const events = observeMap(join);\n first.remove(\"a\");\n assert.equal(events.length, 2);\n assert.equal(events[0].type, \"remove\");\n assert.equal(events[0].key, \"a\");\n assert.equal(events[0].value, 1);\n assert.equal(events[1].type, \"add\");\n assert.equal(events[1].key, \"a\");\n assert.equal(events[1].value, 2);\n },\n \"adding occluding key emits remove first\": assert => {\n const first = new ObservableMap();\n const second = new ObservableMap();\n const join = new JoinedMap([first, second]);\n second.add(\"a\", 2);\n const events = observeMap(join);\n first.add(\"a\", 1);\n assert.equal(events.length, 2);\n assert.equal(events[0].type, \"remove\");\n assert.equal(events[0].key, \"a\");\n assert.equal(events[0].value, 2);\n assert.equal(events[1].type, \"add\");\n assert.equal(events[1].key, \"a\");\n assert.equal(events[1].value, 1);\n }\n };\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseObservableList} from \"./BaseObservableList\";\n\nexport class ObservableArray<T> extends BaseObservableList<T> {\n private _items: T[];\n\n constructor(initialValues: T[] = []) {\n super();\n this._items = initialValues;\n }\n\n append(item: T): void {\n this._items.push(item);\n this.emitAdd(this._items.length - 1, item);\n }\n\n remove(idx: number): void {\n const [item] = this._items.splice(idx, 1);\n this.emitRemove(idx, item);\n }\n\n insertMany(idx: number, items: T[]): void {\n for(let item of items) {\n this.insert(idx, item);\n idx += 1;\n }\n }\n\n insert(idx: number, item: T): void {\n this._items.splice(idx, 0, item);\n this.emitAdd(idx, item);\n }\n\n move(fromIdx: number, toIdx: number): void {\n if (fromIdx < this._items.length && toIdx < this._items.length) {\n const [item] = this._items.splice(fromIdx, 1);\n this._items.splice(toIdx, 0, item);\n this.emitMove(fromIdx, toIdx, item);\n }\n }\n\n update(idx: number, item: T, params: any = null): void {\n if (idx < this._items.length) {\n this._items[idx] = item;\n this.emitUpdate(idx, item, params);\n }\n }\n\n get array(): Readonly<T[]> {\n return this._items;\n }\n\n at(idx: number): T | undefined {\n if (this._items && idx >= 0 && idx < this._items.length) {\n return this._items[idx];\n }\n }\n\n get length(): number {\n return this._items.length;\n }\n\n [Symbol.iterator]() {\n return this._items.values();\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\nimport {BaseObservableList} from \"./BaseObservableList\";\n\n/* inline update of item in collection backed by array, without replacing the preexising item */\nexport function findAndUpdateInArray<T>(predicate: (value: T) => boolean, array: T[], observable: BaseObservableList<T>, updater: (value: T) => any | false) {\n const index = array.findIndex(predicate);\n if (index !== -1) {\n const value = array[index];\n // allow bailing out of sending an emit if updater determined its not needed\n const params = updater(value);\n if (params !== false) {\n observable.emitUpdate(index, value, params);\n }\n // found\n return true;\n }\n return false;\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseObservableList} from \"./BaseObservableList\";\nimport {sortedIndex} from \"../../utils/sortedIndex\";\nimport {findAndUpdateInArray} from \"./common\";\n\nexport class SortedArray<T> extends BaseObservableList<T> {\n private _comparator: (left: T, right: T) => number;\n private _items: T[] = [];\n\n constructor(comparator: (left: T, right: T) => number) {\n super();\n this._comparator = comparator;\n }\n\n setManyUnsorted(items: T[]): void {\n this.setManySorted(items);\n }\n\n setManySorted(items: T[]): void {\n // TODO: we can make this way faster by only looking up the first and last key,\n // and merging whatever is inbetween with items\n // if items is not sorted, 💩🌀 will follow!\n // should we check?\n // Also, once bulk events are supported in collections,\n // we can do a bulk add event here probably if there are no updates\n // BAD CODE!\n for(let item of items) {\n this.set(item);\n }\n }\n\n findAndUpdate(predicate: (value: T) => boolean, updater: (value: T) => any | false): boolean {\n return findAndUpdateInArray(predicate, this._items, this, updater);\n }\n\n getAndUpdate(item: T, updater: (existing: T, item: T) => any, updateParams: any = null): void {\n const idx = this.indexOf(item);\n if (idx !== -1) {\n const existingItem = this._items[idx];\n const newItem = updater(existingItem, item);\n this._items[idx] = newItem;\n this.emitUpdate(idx, newItem, updateParams);\n }\n }\n\n update(item: T, updateParams: any = null): void {\n const idx = this.indexOf(item);\n if (idx !== -1) {\n this._items[idx] = item;\n this.emitUpdate(idx, item, updateParams);\n }\n }\n\n indexOf(item: T): number {\n const idx = sortedIndex(this._items, item, this._comparator);\n if (idx < this._items.length && this._comparator(this._items[idx], item) === 0) {\n return idx;\n } else {\n return -1;\n }\n }\n\n _getNext(item: T): T | undefined {\n let idx = sortedIndex(this._items, item, this._comparator);\n while(idx < this._items.length && this._comparator(this._items[idx], item) <= 0) {\n idx += 1;\n }\n return this.get(idx);\n }\n\n set(item: T, updateParams: any = null): void {\n const idx = sortedIndex(this._items, item, this._comparator);\n if (idx >= this._items.length || this._comparator(this._items[idx], item) !== 0) {\n this._items.splice(idx, 0, item);\n this.emitAdd(idx, item)\n } else {\n this._items[idx] = item;\n this.emitUpdate(idx, item, updateParams);\n }\n }\n\n get(idx: number): T | undefined {\n return this._items[idx];\n }\n\n remove(idx: number): void {\n const item = this._items[idx];\n this._items.splice(idx, 1);\n this.emitRemove(idx, item);\n }\n\n get array(): T[] {\n return this._items;\n }\n\n get length(): number {\n return this._items.length;\n }\n\n [Symbol.iterator]() {\n return new Iterator(this);\n }\n}\n\n// iterator that works even if the current value is removed while iterating\nclass Iterator<T> {\n private _sortedArray: SortedArray<T> | null\n private _current: T | null | undefined\n\n constructor(sortedArray: SortedArray<T>) {\n this._sortedArray = sortedArray;\n this._current = null;\n }\n\n next() {\n if (this._sortedArray) {\n if (this._current) {\n this._current = this._sortedArray._getNext(this._current);\n } else {\n this._current = this._sortedArray.get(0);\n }\n if (this._current) {\n return {value: this._current};\n } else {\n // cause done below\n this._sortedArray = null;\n }\n }\n if (!this._sortedArray) {\n return {done: true};\n }\n }\n}\n\nexport function tests() {\n return {\n \"setManyUnsorted\": assert => {\n const sa = new SortedArray<string>((a, b) => a.localeCompare(b));\n sa.setManyUnsorted([\"b\", \"a\", \"c\"]);\n assert.equal(sa.length, 3);\n assert.equal(sa.get(0), \"a\");\n assert.equal(sa.get(1), \"b\");\n assert.equal(sa.get(2), \"c\");\n }, \n \"_getNext\": assert => {\n const sa = new SortedArray<string>((a, b) => a.localeCompare(b));\n sa.setManyUnsorted([\"b\", \"a\", \"f\"]);\n assert.equal(sa._getNext(\"a\"), \"b\");\n assert.equal(sa._getNext(\"b\"), \"f\");\n // also finds the next if the value is not in the collection\n assert.equal(sa._getNext(\"c\"), \"f\");\n assert.equal(sa._getNext(\"f\"), undefined);\n },\n \"iterator with removals\": assert => {\n const queue = new SortedArray<{idx: number}>((a, b) => a.idx - b.idx);\n queue.setManyUnsorted([{idx: 5}, {idx: 3}, {idx: 1}, {idx: 4}, {idx: 2}]);\n const it = queue[Symbol.iterator]();\n assert.equal(it.next().value.idx, 1);\n assert.equal(it.next().value.idx, 2);\n queue.remove(1);\n assert.equal(it.next().value.idx, 3);\n queue.remove(1);\n assert.equal(it.next().value.idx, 4);\n queue.remove(1);\n assert.equal(it.next().value.idx, 5);\n queue.remove(1);\n assert.equal(it.next().done, true);\n // check done persists\n assert.equal(it.next().done, true);\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseObservableList} from \"./BaseObservableList\";\nimport {findAndUpdateInArray} from \"./common\";\n\nexport type Mapper<F,T> = (value: F) => T\nexport type Updater<F,T> = (mappedValue: T, params: any, value: F) => void;\n\nexport class BaseMappedList<F,T,R = T> extends BaseObservableList<T> {\n protected _sourceList: BaseObservableList<F>;\n protected _sourceUnsubscribe: (() => void) | null = null;\n _mapper: Mapper<F,R>;\n _updater?: Updater<F,T>;\n _removeCallback?: (value: T) => void;\n _mappedValues: T[] | null = null;\n\n constructor(sourceList: BaseObservableList<F>, mapper: Mapper<F,R>, updater?: Updater<F,T>, removeCallback?: (value: T) => void) {\n super();\n this._sourceList = sourceList;\n this._mapper = mapper;\n this._updater = updater;\n this._removeCallback = removeCallback;\n }\n\n findAndUpdate(predicate: (value: T) => boolean, updater: (value: T) => any | false) {\n return findAndUpdateInArray(predicate, this._mappedValues!, this, updater);\n }\n\n get length() {\n return this._mappedValues!.length;\n }\n\n [Symbol.iterator]() {\n return this._mappedValues!.values();\n }\n}\n\nexport function runAdd<F,T,R>(list: BaseMappedList<F,T,R>, index: number, mappedValue: T): void {\n list._mappedValues!.splice(index, 0, mappedValue);\n list.emitAdd(index, mappedValue);\n}\n\nexport function runUpdate<F,T,R>(list: BaseMappedList<F,T,R>, index: number, value: F, params: any): void {\n const mappedValue = list._mappedValues![index];\n if (list._updater) {\n list._updater(mappedValue, params, value);\n }\n list.emitUpdate(index, mappedValue, params);\n}\n\nexport function runRemove<F,T,R>(list: BaseMappedList<F,T,R>, index: number): void {\n const mappedValue = list._mappedValues![index];\n list._mappedValues!.splice(index, 1);\n if (list._removeCallback) {\n list._removeCallback(mappedValue);\n }\n list.emitRemove(index, mappedValue);\n}\n\nexport function runMove<F,T,R>(list: BaseMappedList<F,T,R>, fromIdx: number, toIdx: number): void {\n const mappedValue = list._mappedValues![fromIdx];\n list._mappedValues!.splice(fromIdx, 1);\n list._mappedValues!.splice(toIdx, 0, mappedValue);\n list.emitMove(fromIdx, toIdx, mappedValue);\n}\n\nexport function runReset<F,T,R>(list: BaseMappedList<F,T,R>): void {\n list._mappedValues = [];\n list.emitReset();\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {IListObserver} from \"./BaseObservableList\";\nimport {BaseMappedList, Mapper, Updater, runAdd, runUpdate, runRemove, runMove, runReset} from \"./BaseMappedList\";\n\nexport class AsyncMappedList<F,T> extends BaseMappedList<F,T,Promise<T>> implements IListObserver<F> {\n private _eventQueue: AsyncEvent<F>[] | null = null;\n private _flushing: boolean = false;\n\n onSubscribeFirst(): void {\n this._sourceUnsubscribe = this._sourceList.subscribe(this);\n this._eventQueue = [];\n this._mappedValues = [];\n let idx = 0;\n for (const item of this._sourceList) {\n this._eventQueue.push(new AddEvent(idx, item));\n idx += 1;\n }\n this._flush();\n }\n\n async _flush(): Promise<void> {\n if (this._flushing) {\n return;\n }\n this._flushing = true;\n try {\n while (this._eventQueue!.length) {\n const event = this._eventQueue!.shift();\n await event!.run(this);\n }\n } finally {\n this._flushing = false;\n }\n }\n\n onReset(): void {\n if (this._eventQueue) {\n this._eventQueue.push(new ResetEvent());\n this._flush();\n }\n }\n\n onAdd(index: number, value: F): void {\n if (this._eventQueue) {\n this._eventQueue.push(new AddEvent(index, value));\n this._flush();\n }\n }\n\n onUpdate(index: number, value: F, params: any): void {\n if (this._eventQueue) {\n this._eventQueue.push(new UpdateEvent(index, value, params));\n this._flush();\n }\n }\n\n onRemove(index: number): void {\n if (this._eventQueue) {\n this._eventQueue.push(new RemoveEvent(index));\n this._flush();\n }\n }\n\n onMove(fromIdx: number, toIdx: number): void {\n if (this._eventQueue) {\n this._eventQueue.push(new MoveEvent(fromIdx, toIdx));\n this._flush();\n }\n }\n\n onUnsubscribeLast(): void {\n this._sourceUnsubscribe!();\n this._eventQueue = null;\n this._mappedValues = null;\n }\n}\n\ntype AsyncEvent<F> = AddEvent<F> | UpdateEvent<F> | RemoveEvent<F> | MoveEvent<F> | ResetEvent<F>\n\nclass AddEvent<F> {\n constructor(public index: number, public value: F) {}\n\n async run<T>(list: AsyncMappedList<F,T>): Promise<void> {\n const mappedValue = await list._mapper(this.value);\n runAdd(list, this.index, mappedValue);\n }\n}\n\nclass UpdateEvent<F> {\n constructor(public index: number, public value: F, public params: any) {}\n\n async run<T>(list: AsyncMappedList<F,T>): Promise<void> {\n runUpdate(list, this.index, this.value, this.params);\n }\n}\n\nclass RemoveEvent<F> {\n constructor(public index: number) {}\n\n async run<T>(list: AsyncMappedList<F,T>): Promise<void> {\n runRemove(list, this.index);\n }\n}\n\nclass MoveEvent<F> {\n constructor(public fromIdx: number, public toIdx: number) {}\n\n async run<T>(list: AsyncMappedList<F,T>): Promise<void> {\n runMove(list, this.fromIdx, this.toIdx);\n }\n}\n\nclass ResetEvent<F> {\n async run<T>(list: AsyncMappedList<F,T>): Promise<void> {\n runReset(list);\n }\n}\n\nimport {ObservableArray} from \"./ObservableArray\";\nimport {ListObserver} from \"../../mocks/ListObserver.js\";\n\nexport function tests() {\n return {\n \"events are emitted in order\": async assert => {\n const double = n => n * n;\n const source = new ObservableArray<number>();\n const mapper = new AsyncMappedList(source, async n => {\n await new Promise(r => setTimeout(r, n));\n return {n: double(n)};\n }, (o, params, n) => {\n o.n = double(n);\n });\n const observer = new ListObserver();\n mapper.subscribe(observer);\n source.append(2); // will sleep this amount, so second append would take less time\n source.append(1);\n source.update(0, 7, \"lucky seven\")\n source.remove(0);\n {\n const {type, index, value} = await observer.next();\n assert.equal(mapper.length, 1);\n assert.equal(type, \"add\");\n assert.equal(index, 0);\n assert.equal(value.n, 4);\n }\n {\n const {type, index, value} = await observer.next();\n assert.equal(mapper.length, 2);\n assert.equal(type, \"add\");\n assert.equal(index, 1);\n assert.equal(value.n, 1);\n }\n {\n const {type, index, value, params} = await observer.next();\n assert.equal(mapper.length, 2);\n assert.equal(type, \"update\");\n assert.equal(index, 0);\n assert.equal(value.n, 49);\n assert.equal(params, \"lucky seven\");\n }\n {\n const {type, index, value} = await observer.next();\n assert.equal(mapper.length, 1);\n assert.equal(type, \"remove\");\n assert.equal(index, 0);\n assert.equal(value.n, 49);\n }\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseObservableList, IListObserver} from \"./BaseObservableList\";\n\nexport class ConcatList<T> extends BaseObservableList<T> implements IListObserver<T> {\n protected _sourceLists: BaseObservableList<T>[];\n protected _sourceUnsubscribes: (() => void)[] | null = null;\n\n constructor(...sourceLists: BaseObservableList<T>[]) {\n super();\n this._sourceLists = sourceLists;\n }\n\n _offsetForSource(sourceList: BaseObservableList<T>): number {\n const listIdx = this._sourceLists.indexOf(sourceList);\n let offset = 0;\n for (let i = 0; i < listIdx; ++i) {\n offset += this._sourceLists[i].length;\n }\n return offset;\n }\n\n onSubscribeFirst(): void {\n this._sourceUnsubscribes = this._sourceLists.map(sourceList => sourceList.subscribe(this));\n }\n\n onUnsubscribeLast(): void {\n for (const sourceUnsubscribe of this._sourceUnsubscribes!) {\n sourceUnsubscribe();\n }\n }\n\n onReset(): void {\n // TODO: not ideal if other source lists are large\n // but working impl for now\n // reset, and \n this.emitReset();\n let idx = 0;\n for(const item of this) {\n this.emitAdd(idx, item);\n idx += 1;\n }\n }\n\n onAdd(index: number, value: T, sourceList: BaseObservableList<T>): void {\n this.emitAdd(this._offsetForSource(sourceList) + index, value);\n }\n\n onUpdate(index: number, value: T, params: any, sourceList: BaseObservableList<T>): void {\n // if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it\n // as we are not supposed to call `length` on any uninitialized list\n if (!this._sourceUnsubscribes) {\n return;\n }\n this.emitUpdate(this._offsetForSource(sourceList) + index, value, params);\n }\n\n onRemove(index: number, value: T, sourceList: BaseObservableList<T>): void {\n this.emitRemove(this._offsetForSource(sourceList) + index, value);\n }\n\n onMove(fromIdx: number, toIdx: number, value: T, sourceList: BaseObservableList<T>): void {\n const offset = this._offsetForSource(sourceList);\n this.emitMove(offset + fromIdx, offset + toIdx, value);\n }\n\n get length(): number {\n let len = 0;\n for (let i = 0; i < this._sourceLists.length; ++i) {\n len += this._sourceLists[i].length;\n }\n return len;\n }\n\n [Symbol.iterator]() {\n let sourceListIdx = 0;\n let it = this._sourceLists[0][Symbol.iterator]();\n return {\n next: () => {\n let result = it.next();\n while (result.done) {\n sourceListIdx += 1;\n if (sourceListIdx >= this._sourceLists.length) {\n return result; //done\n }\n it = this._sourceLists[sourceListIdx][Symbol.iterator]();\n result = it.next();\n }\n return result;\n }\n }\n }\n}\n\nimport {ObservableArray} from \"./ObservableArray\";\nimport {defaultObserverWith} from \"./BaseObservableList\";\nexport async function tests() {\n return {\n test_length(assert) {\n const all = new ConcatList(\n new ObservableArray([1, 2, 3]),\n new ObservableArray([11, 12, 13])\n );\n assert.equal(all.length, 6);\n },\n test_iterator(assert) {\n const all = new ConcatList(\n new ObservableArray([1, 2, 3]),\n new ObservableArray([11, 12, 13])\n );\n const it = all[Symbol.iterator]();\n assert.equal(it.next().value, 1);\n assert.equal(it.next().value, 2);\n assert.equal(it.next().value, 3);\n assert.equal(it.next().value, 11);\n assert.equal(it.next().value, 12);\n assert.equal(it.next().value, 13);\n assert(it.next().done);\n },\n test_add(assert) {\n const list1 = new ObservableArray([1, 2, 3]);\n const list2 = new ObservableArray([11, 12, 13]);\n const all = new ConcatList(list1, list2);\n let fired = false;\n all.subscribe(defaultObserverWith({\n onAdd(index, value) {\n fired = true;\n assert.equal(index, 4);\n assert.equal(value, 11.5);\n }\n }));\n list2.insert(1, 11.5);\n assert(fired);\n },\n test_update(assert) {\n const list1 = new ObservableArray([1, 2, 3]);\n const list2 = new ObservableArray([11, 12, 13]);\n const all = new ConcatList(list1, list2);\n let fired = false;\n all.subscribe(defaultObserverWith({\n onUpdate(index, value) {\n fired = true;\n assert.equal(index, 4);\n assert.equal(value, 10);\n }\n }));\n list2.emitUpdate(1, 10);\n assert(fired);\n },\n };\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {SortedMapList} from \"./list/SortedMapList.js\";\nimport {FilteredMap} from \"./map/FilteredMap.js\";\nimport {MappedMap} from \"./map/MappedMap.js\";\nimport {JoinedMap} from \"./map/JoinedMap.js\";\nimport {BaseObservableMap} from \"./map/BaseObservableMap\";\n// re-export \"root\" (of chain) collections\nexport { ObservableArray } from \"./list/ObservableArray\";\nexport { SortedArray } from \"./list/SortedArray\";\nexport { MappedList } from \"./list/MappedList\";\nexport { AsyncMappedList } from \"./list/AsyncMappedList\";\nexport { ConcatList } from \"./list/ConcatList\";\nexport { ObservableMap } from \"./map/ObservableMap\";\n\n// avoid circular dependency between these classes\n// and BaseObservableMap (as they extend it)\nObject.assign(BaseObservableMap.prototype, {\n sortValues(comparator) {\n return new SortedMapList(this, comparator);\n },\n\n mapValues(mapper, updater) {\n return new MappedMap(this, mapper, updater);\n },\n\n filterValues(filter) {\n return new FilteredMap(this, filter);\n },\n\n join(...otherMaps) {\n return new JoinedMap([this].concat(otherMaps));\n }\n});\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2022 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport interface IDisposable {\n dispose(): void;\n}\n\nexport type Disposable = IDisposable | (() => void);\n\nfunction disposeValue(value: Disposable): void {\n if (typeof value === \"function\") {\n value();\n } else {\n value.dispose();\n }\n}\n\nfunction isDisposable(value: Disposable): boolean {\n return value && (typeof value === \"function\" || typeof value.dispose === \"function\");\n}\n\nexport class Disposables {\n private _disposables?: Disposable[] = [];\n\n track<D extends Disposable>(disposable: D): D {\n if (!isDisposable(disposable)) {\n throw new Error(\"Not a disposable\");\n }\n if (this.isDisposed) {\n console.warn(\"Disposables already disposed, disposing new value\");\n disposeValue(disposable);\n return disposable;\n }\n this._disposables!.push(disposable);\n return disposable;\n }\n\n untrack(disposable: Disposable): undefined {\n if (this.isDisposed) {\n console.warn(\"Disposables already disposed, cannot untrack\");\n return undefined;\n }\n const idx = this._disposables!.indexOf(disposable);\n if (idx >= 0) {\n this._disposables!.splice(idx, 1);\n }\n return undefined;\n }\n\n dispose(): void {\n if (this._disposables) {\n for (const d of this._disposables) {\n disposeValue(d);\n }\n this._disposables = undefined;\n }\n }\n\n get isDisposed(): boolean {\n return this._disposables === undefined;\n }\n\n disposeTracked(value: Disposable | undefined): undefined {\n if (value === undefined || value === null || this.isDisposed) {\n return undefined;\n }\n const idx = this._disposables!.indexOf(value);\n if (idx !== -1) {\n const [foundValue] = this._disposables!.splice(idx, 1);\n disposeValue(foundValue);\n } else {\n console.warn(\"disposable not found, did it leak?\", value);\n }\n return undefined;\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {directionalConcat, directionalAppend} from \"./common.js\";\nimport {Direction} from \"../Direction\";\nimport {EventEntry} from \"../entries/EventEntry.js\";\nimport {FragmentBoundaryEntry} from \"../entries/FragmentBoundaryEntry.js\";\n\nclass ReaderRequest {\n constructor(fn, log) {\n this.decryptRequest = null;\n this._promise = fn(this, log);\n }\n\n complete() {\n return this._promise;\n }\n\n dispose() {\n if (this.decryptRequest) {\n this.decryptRequest.dispose();\n this.decryptRequest = null;\n }\n }\n}\n\n/**\n * Raw because it doesn't do decryption and in the future it should not read relations either.\n * It is just about reading entries and following fragment links\n */\nasync function readRawTimelineEntriesWithTxn(roomId, eventKey, direction, amount, fragmentIdComparer, txn) {\n let entries = [];\n const timelineStore = txn.timelineEvents;\n const fragmentStore = txn.timelineFragments;\n\n while (entries.length < amount && eventKey) {\n let eventsWithinFragment;\n if (direction.isForward) {\n // TODO: should we pass amount - entries.length here?\n eventsWithinFragment = await timelineStore.eventsAfter(roomId, eventKey, amount);\n } else {\n eventsWithinFragment = await timelineStore.eventsBefore(roomId, eventKey, amount);\n }\n let eventEntries = eventsWithinFragment.map(e => new EventEntry(e, fragmentIdComparer));\n entries = directionalConcat(entries, eventEntries, direction);\n // prepend or append eventsWithinFragment to entries, and wrap them in EventEntry\n\n if (entries.length < amount) {\n const fragment = await fragmentStore.get(roomId, eventKey.fragmentId);\n // TODO: why does the first fragment not need to be added? (the next *is* added below)\n // it looks like this would be fine when loading in the sync island\n // (as the live fragment should be added already) but not for permalinks when we support them\n // \n // fragmentIdComparer.addFragment(fragment);\n let fragmentEntry = new FragmentBoundaryEntry(fragment, direction.isBackward, fragmentIdComparer);\n // append or prepend fragmentEntry, reuse func from GapWriter?\n directionalAppend(entries, fragmentEntry, direction);\n // only continue loading if the fragment boundary can't be backfilled\n if (!fragmentEntry.token && fragmentEntry.hasLinkedFragment) {\n const nextFragment = await fragmentStore.get(roomId, fragmentEntry.linkedFragmentId);\n fragmentIdComparer.add(nextFragment);\n const nextFragmentEntry = new FragmentBoundaryEntry(nextFragment, direction.isForward, fragmentIdComparer);\n directionalAppend(entries, nextFragmentEntry, direction);\n eventKey = nextFragmentEntry.asEventKey();\n } else {\n eventKey = null;\n }\n }\n }\n return entries;\n}\n\nexport class TimelineReader {\n constructor({roomId, storage, fragmentIdComparer}) {\n this._roomId = roomId;\n this._storage = storage;\n this._fragmentIdComparer = fragmentIdComparer;\n this._decryptEntries = null;\n }\n\n enableEncryption(decryptEntries) {\n this._decryptEntries = decryptEntries;\n }\n\n get readTxnStores() {\n const stores = [\n this._storage.storeNames.timelineEvents,\n this._storage.storeNames.timelineFragments,\n ];\n if (this._decryptEntries) {\n stores.push(this._storage.storeNames.inboundGroupSessions);\n }\n return stores;\n }\n\n readFrom(eventKey, direction, amount, log) {\n return new ReaderRequest(async (r, log) => {\n const txn = await this._storage.readTxn(this.readTxnStores);\n return await this._readFrom(eventKey, direction, amount, r, txn, log);\n }, log);\n }\n\n readFromEnd(amount, existingTxn = null, log) {\n return new ReaderRequest(async (r, log) => {\n const txn = existingTxn || await this._storage.readTxn(this.readTxnStores);\n const liveFragment = await txn.timelineFragments.liveFragment(this._roomId);\n let entries;\n // room hasn't been synced yet\n if (!liveFragment) {\n entries = [];\n } else {\n this._fragmentIdComparer.add(liveFragment);\n const liveFragmentEntry = FragmentBoundaryEntry.end(liveFragment, this._fragmentIdComparer);\n const eventKey = liveFragmentEntry.asEventKey();\n entries = await this._readFrom(eventKey, Direction.Backward, amount, r, txn, log);\n entries.unshift(liveFragmentEntry);\n }\n return entries;\n }, log);\n }\n\n async readById(id, log) {\n let stores = [this._storage.storeNames.timelineEvents];\n if (this._decryptEntries) {\n stores.push(this._storage.storeNames.inboundGroupSessions);\n }\n const txn = await this._storage.readTxn(stores); // todo: can we just use this.readTxnStores here? probably\n const storageEntry = await txn.timelineEvents.getByEventId(this._roomId, id);\n if (storageEntry) {\n const entry = new EventEntry(storageEntry, this._fragmentIdComparer);\n if (this._decryptEntries) {\n const request = this._decryptEntries([entry], txn, log);\n await request.complete();\n }\n return entry;\n }\n }\n\n async _readFrom(eventKey, direction, amount, r, txn, log) {\n const entries = await readRawTimelineEntriesWithTxn(this._roomId, eventKey, direction, amount, this._fragmentIdComparer, txn);\n if (this._decryptEntries) {\n r.decryptRequest = this._decryptEntries(entries, txn, log);\n try {\n await r.decryptRequest.complete();\n } finally {\n r.decryptRequest = null;\n }\n }\n return entries;\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {EventEntry} from \"./EventEntry.js\";\n\n// EventEntry but without the two properties that are populated via SyncWriter\n// Useful if you want to create an EventEntry that is ephemeral\n\nexport class NonPersistedEventEntry extends EventEntry {\n get fragmentId() {\n throw new Error(\"Cannot access fragmentId for non-persisted EventEntry\");\n }\n\n get entryIndex() {\n throw new Error(\"Cannot access entryIndex for non-persisted EventEntry\");\n }\n\n get isNonPersisted() {\n return true;\n }\n\n // overridden here because we reuse addLocalRelation() for updating this entry\n // we don't want the RedactedTile created using this entry to ever show \"is being redacted\"\n get isRedacting() {\n return false;\n }\n\n get isRedacted() {\n return super.isRedacting;\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport class User {\n constructor(userId) {\n this._userId = userId;\n }\n\n get id() {\n return this._userId;\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {SortedArray, AsyncMappedList, ConcatList, ObservableArray} from \"../../../observable/index.js\";\nimport {Disposables} from \"../../../utils/Disposables\";\nimport {Direction} from \"./Direction\";\nimport {TimelineReader} from \"./persistence/TimelineReader.js\";\nimport {PendingEventEntry} from \"./entries/PendingEventEntry.js\";\nimport {RoomMember} from \"../members/RoomMember.js\";\nimport {getRelation, ANNOTATION_RELATION_TYPE} from \"./relations.js\";\nimport {REDACTION_TYPE} from \"../common\";\nimport {NonPersistedEventEntry} from \"./entries/NonPersistedEventEntry.js\";\nimport {EVENT_TYPE as MEMBER_EVENT_TYPE} from \"../members/RoomMember.js\";\n\nexport class Timeline {\n constructor({roomId, storage, closeCallback, fragmentIdComparer, pendingEvents, clock, powerLevelsObservable, hsApi}) {\n this._roomId = roomId;\n this._storage = storage;\n this._closeCallback = closeCallback;\n this._fragmentIdComparer = fragmentIdComparer;\n this._disposables = new Disposables();\n this._pendingEvents = pendingEvents;\n this._clock = clock;\n // constructing this early avoid some problem while sync and openTimeline race\n this._remoteEntries = new SortedArray((a, b) => a.compare(b));\n this._ownMember = null;\n this._timelineReader = new TimelineReader({\n roomId: this._roomId,\n storage: this._storage,\n fragmentIdComparer: this._fragmentIdComparer\n });\n this._readerRequest = null;\n this._allEntries = null;\n /** Stores event entries that we had to fetch from hs/storage for reply previews (because they were not in timeline) */ \n this._contextEntriesNotInTimeline = new Map();\n /** Only used to decrypt non-persisted context entries fetched from the homeserver */\n this._decryptEntries = null;\n this._hsApi = hsApi;\n this.initializePowerLevels(powerLevelsObservable);\n }\n\n initializePowerLevels(observable) {\n if (observable) {\n this._powerLevels = observable.get();\n this._disposables.track(observable.subscribe(powerLevels => this._powerLevels = powerLevels));\n }\n }\n\n /** @package */\n async load(user, membership, log) {\n const txn = await this._storage.readTxn(this._timelineReader.readTxnStores.concat(\n this._storage.storeNames.roomMembers,\n this._storage.storeNames.roomState\n ));\n const memberData = await txn.roomMembers.get(this._roomId, user.id);\n if (memberData) {\n this._ownMember = new RoomMember(memberData);\n } else {\n // this should never happen, as our own join into the room would have\n // made us receive our own member event, but just to be on the safe side and not crash,\n // fall back to bare user id\n this._ownMember = RoomMember.fromUserId(this._roomId, user.id, membership);\n }\n // it should be fine to not update the local entries,\n // as they should only populate once the view subscribes to it\n // if they are populated already, the sender profile would be empty\n\n // choose good amount here between showing messages initially and\n // not spending too much time decrypting messages before showing the timeline.\n // more messages should be loaded automatically until the viewport is full by the view if needed.\n const readerRequest = this._disposables.track(this._timelineReader.readFromEnd(20, txn, log));\n try {\n const entries = await readerRequest.complete();\n this._loadContextEntriesWhereNeeded(entries);\n this._setupEntries(entries);\n } finally {\n this._disposables.disposeTracked(readerRequest);\n }\n // txn should be assumed to have finished here, as decryption will close it.\n }\n\n _setupEntries(timelineEntries) {\n this._remoteEntries.setManySorted(timelineEntries);\n if (this._pendingEvents) {\n this._localEntries = new AsyncMappedList(this._pendingEvents,\n pe => this._mapPendingEventToEntry(pe),\n (pee, params) => {\n // is sending but redacted, who do we detect that here to remove the relation?\n pee.notifyUpdate(params);\n },\n pee => this._applyAndEmitLocalRelationChange(pee, target => target.removeLocalRelation(pee))\n );\n } else {\n this._localEntries = new ObservableArray();\n }\n this._allEntries = new ConcatList(this._remoteEntries, this._localEntries);\n }\n\n async _mapPendingEventToEntry(pe) {\n // we load the redaction target for pending events,\n // so if we are redacting a relation, we can pass the redaction\n // to the relation target and the removal of the relation can\n // be taken into account for local echo.\n let redactingEntry;\n if (pe.eventType === REDACTION_TYPE) {\n redactingEntry = await this._getOrLoadEntry(pe.relatedTxnId, pe.relatedEventId);\n }\n const pee = new PendingEventEntry({\n pendingEvent: pe, member: this._ownMember,\n clock: this._clock, redactingEntry\n });\n this._loadContextEntriesWhereNeeded([pee]);\n this._applyAndEmitLocalRelationChange(pee, target => target.addLocalRelation(pee));\n return pee;\n }\n\n _applyAndEmitLocalRelationChange(pee, updater) {\n // this is the contract of findAndUpdate, used in _findAndUpdateRelatedEntry\n const updateOrFalse = e => {\n const params = updater(e);\n return params ? params : false;\n };\n this._findAndUpdateEntryById(pee.pendingEvent.relatedTxnId, pee.relatedEventId, updateOrFalse);\n // also look for a relation target to update with this redaction\n if (pee.redactingEntry) {\n // redactingEntry might be a PendingEventEntry or an EventEntry, so don't assume pendingEvent\n const relatedTxnId = pee.redactingEntry.pendingEvent?.relatedTxnId;\n this._findAndUpdateEntryById(relatedTxnId, pee.redactingEntry.relatedEventId, updateOrFalse);\n pee.redactingEntry.contextForEntries?.forEach(e => this._emitUpdateForEntry(e, \"contextEntry\"));\n }\n }\n\n _findAndUpdateEntryById(txnId, eventId, updateOrFalse) {\n let found = false;\n // first, look in local entries based on txn id\n if (txnId) {\n found = this._localEntries.findAndUpdate(\n e => e.id === txnId,\n updateOrFalse,\n );\n }\n // if not found here, look in remote entries based on event id\n if (!found && eventId) {\n this._remoteEntries.findAndUpdate(\n e => e.id === eventId,\n updateOrFalse\n );\n }\n }\n\n async getOwnAnnotationEntry(targetId, key) {\n const txn = await this._storage.readWriteTxn([\n this._storage.storeNames.timelineEvents,\n this._storage.storeNames.timelineRelations,\n ]);\n const relations = await txn.timelineRelations.getForTargetAndType(this._roomId, targetId, ANNOTATION_RELATION_TYPE);\n for (const relation of relations) {\n const annotation = await txn.timelineEvents.getByEventId(this._roomId, relation.sourceEventId);\n if (annotation && annotation.event.sender === this._ownMember.userId && getRelation(annotation.event).key === key) {\n const eventEntry = new EventEntry(annotation, this._fragmentIdComparer);\n this._addLocalRelationsToNewRemoteEntries([eventEntry]);\n return eventEntry;\n }\n }\n return null;\n }\n\n /** @package */\n updateOwnMember(member) {\n this._ownMember = member;\n }\n\n _addLocalRelationsToNewRemoteEntries(entries) {\n // because it is not safe to iterate a derived observable collection\n // before it has any subscriptions, we bail out if this isn't\n // the case yet. This can happen when sync adds or replaces entries\n // before load has finished and the view has subscribed to the timeline.\n // \n // Once the subscription is setup, MappedList will set up the local\n // relations as needed with _applyAndEmitLocalRelationChange,\n // so we're not missing anything by bailing out.\n //\n // _localEntries can also not yet exist\n if (!this._localEntries?.hasSubscriptions) {\n return;\n }\n // find any local relations to this new remote event\n for (const pee of this._localEntries) {\n // this will work because we set relatedEventId when removing remote echos\n if (pee.relatedEventId) {\n const relationTarget = entries.find(e => e.id === pee.relatedEventId);\n // no need to emit here as this entry is about to be added\n relationTarget?.addLocalRelation(pee);\n }\n if (pee.redactingEntry) {\n const eventId = pee.redactingEntry.relatedEventId;\n const relationTarget = entries.find(e => e.id === eventId);\n relationTarget?.addLocalRelation(pee);\n }\n }\n }\n\n // used in replaceEntries\n static _entryUpdater(existingEntry, entry) {\n // ensure other entries for which this existingEntry is a context point to the new entry instead of existingEntry\n existingEntry.contextForEntries?.forEach(event => event.setContextEntry(entry));\n entry.updateFrom(existingEntry);\n return entry;\n }\n\n /** @package */\n replaceEntries(entries) {\n this._addLocalRelationsToNewRemoteEntries(entries);\n for (const entry of entries) {\n try {\n this._remoteEntries.getAndUpdate(entry, Timeline._entryUpdater);\n const oldEntry = this._contextEntriesNotInTimeline.get(entry.id)\n if (oldEntry) {\n Timeline._entryUpdater(oldEntry, entry);\n this._contextEntriesNotInTimeline.set(entry.id, entry);\n }\n // Since this entry changed, all dependent entries should be updated\n entry.contextForEntries?.forEach(e => this._emitUpdateForEntry(e, \"contextEntry\"));\n } catch (err) {\n if (err.name === \"CompareError\") {\n // see FragmentIdComparer, if the replacing entry is on a fragment\n // that is currently not loaded into the FragmentIdComparer, it will\n // throw a CompareError, and it means that the event is not loaded \n // in the timeline (like when receiving a relation for an event\n // that is not loaded in memory) so we can just drop this error as\n // replacing an event that is not already loaded is a no-op.\n continue;\n } else {\n // don't swallow other errors\n throw err;\n }\n }\n }\n }\n\n /** @package */\n addEntries(newEntries) {\n this._addLocalRelationsToNewRemoteEntries(newEntries);\n this._updateEntriesFetchedFromHomeserver(newEntries);\n this._moveEntryToRemoteEntries(newEntries);\n this._loadContextEntriesWhereNeeded(newEntries);\n this._remoteEntries.setManySorted(newEntries);\n }\n\n /**\n * Update entries based on newly received events.\n * This is specific to events that are not in the timeline but had to be fetched from the homeserver\n * because they are context-events for other events in the timeline (i.e fetched from hs so that we\n * can render things like reply previews)\n */\n _updateEntriesFetchedFromHomeserver(entries) {\n /**\n * Updates for entries in timeline is handled by remoteEntries observable collection\n * Updates for entries not in timeline but fetched from storage is handled in this.replaceEntries()\n * This code is specific to entries fetched from HomeServer i.e NonPersistedEventEntry\n */\n for (const entry of entries) {\n const relatedEntry = this._contextEntriesNotInTimeline.get(entry.relatedEventId);\n if (relatedEntry?.isNonPersisted && relatedEntry?.addLocalRelation(entry)) {\n // update other entries for which this entry is a context entry\n relatedEntry.contextForEntries?.forEach(e => this._emitUpdateForEntry(e, \"contextEntry\"));\n }\n }\n }\n\n /**\n * If an event we had to fetch from hs/storage is now in the timeline (for eg, due to gap fill),\n * remove the event from _contextEntriesNotInTimeline since it is now in remoteEntries\n */\n _moveEntryToRemoteEntries(entries) {\n for (const entry of entries) {\n const fetchedEntry = this._contextEntriesNotInTimeline.get(entry.id);\n if (fetchedEntry) {\n fetchedEntry.contextForEntries.forEach(e => {\n e.setContextEntry(entry);\n this._emitUpdateForEntry(e, \"contextEntry\");\n });\n this._contextEntriesNotInTimeline.delete(entry.id);\n }\n }\n }\n\n _emitUpdateForEntry(entry, param) {\n const txnId = entry.isPending ? entry.id : null;\n const eventId = entry.isPending ? null : entry.id;\n this._findAndUpdateEntryById(txnId, eventId, () => param);\n }\n\n /**\n * For each entry in entries, this method associates a context-entry (if needed) to it.\n * The context-entry is fetched using the following strategies (in the same order as given):\n * - timeline\n * - storage\n * - homeserver\n * @param {EventEntry[]} entries \n */\n async _loadContextEntriesWhereNeeded(entries) {\n for (const entry of entries) {\n if (!entry.contextEventId) {\n continue;\n }\n const id = entry.contextEventId;\n // before looking into remoteEntries, check the entries\n // that about to be added first\n let contextEvent = entries.find(e => e.id === id);\n if (!contextEvent) {\n contextEvent = this._findLoadedEventById(id);\n }\n if (contextEvent) {\n entry.setContextEntry(contextEvent);\n // we don't emit an update here, as the add or update\n // that the callee will emit hasn't been emitted yet.\n } else {\n // we don't await here, which is not ideal,\n // but one of our callers, addEntries, is not async\n // so there is not much point.\n // Also, we want to run the entry fetching in parallel.\n this._loadContextEntryNotInTimeline(entry);\n }\n }\n }\n\n async _loadContextEntryNotInTimeline(entry) {\n const id = entry.contextEventId;\n let contextEvent = await this._getEventFromStorage(id);\n if (!contextEvent) {\n contextEvent = await this._getEventFromHomeserver(id);\n }\n if (contextEvent) {\n // this entry was created from storage/hs, so it's not tracked by remoteEntries\n // we track them here so that we can update reply previews later\n this._contextEntriesNotInTimeline.set(id, contextEvent);\n entry.setContextEntry(contextEvent);\n // here, we awaited something, so from now on we do have to emit\n // an update if we set the context entry.\n this._emitUpdateForEntry(entry, \"contextEntry\");\n }\n }\n\n /**\n * Fetches an entry with the given event-id from localEntries, remoteEntries or contextEntriesNotInTimeline.\n * @param {string} eventId event-id of the entry\n * @returns entry if found, undefined otherwise\n */\n _findLoadedEventById(eventId) {\n return this.getByEventId(eventId) ?? this._contextEntriesNotInTimeline.get(eventId);\n }\n\n async _getEventFromStorage(eventId) {\n const entry = await this._timelineReader.readById(eventId);\n return entry;\n }\n\n async _getEventFromHomeserver(eventId) {\n const response = await this._hsApi.context(this._roomId, eventId, 0).response();\n const sender = response.event.sender;\n const member = response.state.find(e => e.type === MEMBER_EVENT_TYPE && e.user_id === sender);\n const entry = {\n event: response.event,\n displayName: member.content.displayname,\n avatarUrl: member.content.avatar_url\n };\n const eventEntry = new NonPersistedEventEntry(entry, this._fragmentIdComparer);\n if (this._decryptEntries) {\n const request = this._decryptEntries([eventEntry]);\n await request.complete();\n }\n return eventEntry;\n }\n\n // tries to prepend `amount` entries to the `entries` list.\n /**\n * [loadAtTop description]\n * @param {[type]} amount [description]\n * @return {boolean} true if the top of the timeline has been reached\n * \n */\n async loadAtTop(amount) {\n if (this._disposables.isDisposed) {\n return true;\n }\n const firstEventEntry = this._remoteEntries.array.find(e => !!e.eventType);\n if (!firstEventEntry) {\n return true;\n }\n const readerRequest = this._disposables.track(this._timelineReader.readFrom(\n firstEventEntry.asEventKey(),\n Direction.Backward,\n amount\n ));\n try {\n const entries = await readerRequest.complete();\n this.addEntries(entries);\n return entries.length < amount;\n } finally {\n this._disposables.disposeTracked(readerRequest);\n }\n }\n\n async _getOrLoadEntry(txnId, eventId) {\n if (txnId) {\n // also look for redacting relation in pending events, in case the target is already being sent\n for (const p of this._localEntries) {\n if (p.id === txnId) {\n return p;\n }\n }\n }\n if (eventId) {\n return this.getByEventId(eventId) ?? await this._getEventFromStorage(eventId);\n }\n return null;\n }\n\n getByEventId(eventId) {\n for (let i = 0; i < this._remoteEntries.length; i += 1) {\n const entry = this._remoteEntries.get(i);\n if (entry.id === eventId) {\n return entry;\n }\n }\n return null;\n }\n\n /** @public */\n get entries() {\n return this._allEntries;\n }\n\n /**\n * @internal\n * @return {Array<EventEntry>} remote event entries, should not be modified\n */\n get remoteEntries() {\n return this._remoteEntries.array;\n }\n\n /** @public */\n dispose() {\n if (this._closeCallback) {\n this._disposables.dispose();\n this._closeCallback();\n this._closeCallback = null;\n }\n }\n\n /** @internal */\n enableEncryption(decryptEntries) {\n this._decryptEntries = decryptEntries;\n this._timelineReader.enableEncryption(decryptEntries);\n }\n\n get powerLevels() {\n return this._powerLevels;\n }\n\n get me() {\n return this._ownMember;\n }\n}\n\nimport {FragmentIdComparer} from \"./FragmentIdComparer.js\";\nimport {poll} from \"../../../mocks/poll.js\";\nimport {Clock as MockClock} from \"../../../mocks/Clock.js\";\nimport {createMockStorage} from \"../../../mocks/Storage\";\nimport {ListObserver} from \"../../../mocks/ListObserver.js\";\nimport {createEvent, withTextBody, withContent, withSender, withRedacts, withReply} from \"../../../mocks/event.js\";\nimport {NullLogItem} from \"../../../logging/NullLogger\";\nimport {EventEntry} from \"./entries/EventEntry.js\";\nimport {User} from \"../../User.js\";\nimport {PendingEvent} from \"../sending/PendingEvent.js\";\nimport {createAnnotation} from \"./relations.js\";\nimport {redactEvent} from \"./common.js\";\n\nexport function tests() {\n const fragmentIdComparer = new FragmentIdComparer([]);\n const roomId = \"$abc\";\n const alice = \"@alice:hs.tld\";\n const bob = \"@bob:hs.tld\";\n const hsApi = {\n context() {\n const result = {\n event: withTextBody(\"foo\", createEvent(\"m.room.message\", \"event_id_1\", alice)),\n state: [{\n type: MEMBER_EVENT_TYPE,\n user_id: alice,\n content: {\n displayName: \"\",\n avatarUrl: \"\"\n }\n }]\n };\n return { response: () => result };\n }\n };\n\n function getIndexFromIterable(it, n) {\n let i = 0;\n for (const item of it) {\n if (i === n) {\n return item;\n }\n i += 1;\n }\n throw new Error(\"not enough items in iterable\");\n }\n\n return {\n \"adding or replacing entries before subscribing to entries does not lose local relations\": async assert => {\n const pendingEvents = new ObservableArray();\n const timeline = new Timeline({roomId, storage: await createMockStorage(),\n closeCallback: () => {}, fragmentIdComparer, pendingEvents, clock: new MockClock()});\n // 1. load timeline\n await timeline.load(new User(alice), \"join\", new NullLogItem());\n // 2. test replaceEntries and addEntries don't fail\n const event1 = withTextBody(\"hi!\", withSender(bob, createEvent(\"m.room.message\", \"!abc\")));\n const entry1 = new EventEntry({event: event1, fragmentId: 1, eventIndex: 1}, fragmentIdComparer);\n timeline.replaceEntries([entry1]);\n const event2 = withTextBody(\"hi bob!\", withSender(alice, createEvent(\"m.room.message\", \"!def\")));\n const entry2 = new EventEntry({event: event2, fragmentId: 1, eventIndex: 2}, fragmentIdComparer);\n timeline.addEntries([entry2]);\n // 3. add local relation (redaction)\n pendingEvents.append(new PendingEvent({data: {\n roomId,\n queueIndex: 1,\n eventType: \"m.room.redaction\",\n txnId: \"t123\",\n content: {},\n relatedEventId: event2.event_id\n }}));\n // 4. subscribe (it's now safe to iterate timeline.entries) \n timeline.entries.subscribe(new ListObserver());\n // 5. check the local relation got correctly aggregated\n const locallyRedacted = await poll(() => Array.from(timeline.entries)[0].isRedacting);\n assert.equal(locallyRedacted, true);\n },\n \"add and remove local reaction, and cancel again\": async assert => {\n // 1. setup timeline with message\n const pendingEvents = new ObservableArray();\n const timeline = new Timeline({roomId, storage: await createMockStorage(),\n closeCallback: () => {}, fragmentIdComparer, pendingEvents, clock: new MockClock()});\n await timeline.load(new User(bob), \"join\", new NullLogItem());\n timeline.entries.subscribe(new ListObserver());\n const event = withTextBody(\"hi bob!\", withSender(alice, createEvent(\"m.room.message\", \"!abc\")));\n timeline.addEntries([new EventEntry({event, fragmentId: 1, eventIndex: 2}, fragmentIdComparer)]);\n let entry = getIndexFromIterable(timeline.entries, 0);\n // 2. add local reaction\n pendingEvents.append(new PendingEvent({data: {\n roomId,\n queueIndex: 1,\n eventType: \"m.reaction\",\n txnId: \"t123\",\n content: entry.annotate(\"👋\"),\n relatedEventId: entry.id\n }}));\n await poll(() => timeline.entries.length === 2);\n assert.equal(entry.pendingAnnotations.get(\"👋\").count, 1);\n const reactionEntry = getIndexFromIterable(timeline.entries, 1);\n // 3. add redaction to timeline\n pendingEvents.append(new PendingEvent({data: {\n roomId,\n queueIndex: 2,\n eventType: \"m.room.redaction\",\n txnId: \"t456\",\n content: {},\n relatedTxnId: reactionEntry.id\n }}));\n // TODO: await nextUpdate here with ListObserver, to ensure entry emits an update when pendingAnnotations changes\n await poll(() => timeline.entries.length === 3);\n assert.equal(entry.pendingAnnotations.get(\"👋\").count, 0);\n // 4. cancel redaction\n pendingEvents.remove(1);\n await poll(() => timeline.entries.length === 2);\n assert.equal(entry.pendingAnnotations.get(\"👋\").count, 1);\n // 5. cancel reaction\n pendingEvents.remove(0);\n await poll(() => timeline.entries.length === 1);\n assert(!entry.pendingAnnotations);\n },\n \"getOwnAnnotationEntry\": async assert => {\n const messageId = \"!abc\";\n const reactionId = \"!def\";\n // 1. put event and reaction into storage\n const storage = await createMockStorage();\n const txn = await storage.readWriteTxn([storage.storeNames.timelineEvents, storage.storeNames.timelineRelations]);\n txn.timelineEvents.tryInsert({\n event: withContent(createAnnotation(messageId, \"👋\"), createEvent(\"m.reaction\", reactionId, bob)),\n fragmentId: 1, eventIndex: 1, roomId\n }, new NullLogItem());\n txn.timelineRelations.add(roomId, messageId, ANNOTATION_RELATION_TYPE, reactionId);\n await txn.complete();\n // 2. setup the timeline\n const timeline = new Timeline({roomId, storage, closeCallback: () => {},\n fragmentIdComparer, pendingEvents: new ObservableArray(), clock: new MockClock()});\n await timeline.load(new User(bob), \"join\", new NullLogItem());\n // 3. get the own annotation out\n const reactionEntry = await timeline.getOwnAnnotationEntry(messageId, \"👋\");\n assert.equal(reactionEntry.id, reactionId);\n assert.equal(reactionEntry.relation.key, \"👋\");\n },\n \"remote reaction\": async assert => {\n const messageEntry = new EventEntry({\n event: withTextBody(\"hi bob!\", createEvent(\"m.room.message\", \"!abc\", alice)),\n fragmentId: 1, eventIndex: 2, roomId,\n annotations: { // aggregated like RelationWriter would\n \"👋\": {count: 1, me: true, firstTimestamp: 0}\n },\n }, fragmentIdComparer);\n // 2. setup timeline\n const pendingEvents = new ObservableArray();\n const timeline = new Timeline({roomId, storage: await createMockStorage(),\n closeCallback: () => {}, fragmentIdComparer, pendingEvents, clock: new MockClock()});\n await timeline.load(new User(bob), \"join\", new NullLogItem());\n timeline.entries.subscribe(new ListObserver());\n // 3. add message to timeline\n timeline.addEntries([messageEntry]);\n const entry = getIndexFromIterable(timeline.entries, 0);\n assert.equal(entry, messageEntry);\n assert.equal(entry.annotations[\"👋\"].count, 1);\n },\n \"remove remote reaction\": async assert => {\n // 1. setup timeline\n const pendingEvents = new ObservableArray();\n const timeline = new Timeline({roomId, storage: await createMockStorage(),\n closeCallback: () => { }, fragmentIdComparer, pendingEvents, clock: new MockClock()});\n await timeline.load(new User(bob), \"join\", new NullLogItem());\n timeline.entries.subscribe(new ListObserver());\n // 2. add message and reaction to timeline\n const messageEntry = new EventEntry({\n event: withTextBody(\"hi bob!\", createEvent(\"m.room.message\", \"!abc\", alice)),\n fragmentId: 1, eventIndex: 2, roomId,\n }, fragmentIdComparer);\n const reactionEntry = new EventEntry({\n event: withContent(createAnnotation(messageEntry.id, \"👋\"), createEvent(\"m.reaction\", \"!def\", bob)),\n fragmentId: 1, eventIndex: 3, roomId\n }, fragmentIdComparer);\n timeline.addEntries([messageEntry, reactionEntry]);\n // 3. redact reaction\n pendingEvents.append(new PendingEvent({data: {\n roomId,\n queueIndex: 1,\n eventType: \"m.room.redaction\",\n txnId: \"t123\",\n content: {},\n relatedEventId: reactionEntry.id\n }}));\n await poll(() => timeline.entries.length >= 3);\n assert.equal(messageEntry.pendingAnnotations.get(\"👋\").count, -1);\n },\n \"local reaction gets applied after remote echo is added to timeline\": async assert => {\n const messageEntry = new EventEntry({event: withTextBody(\"hi bob!\", withSender(alice, createEvent(\"m.room.message\", \"!abc\"))),\n fragmentId: 1, eventIndex: 2}, fragmentIdComparer);\n // 1. setup timeline\n const pendingEvents = new ObservableArray();\n const timeline = new Timeline({roomId, storage: await createMockStorage(),\n closeCallback: () => {}, fragmentIdComparer, pendingEvents, clock: new MockClock()});\n await timeline.load(new User(bob), \"join\", new NullLogItem());\n timeline.entries.subscribe(new ListObserver());\n // 2. add local reaction\n pendingEvents.append(new PendingEvent({data: {\n roomId,\n queueIndex: 1,\n eventType: \"m.reaction\",\n txnId: \"t123\",\n content: messageEntry.annotate(\"👋\"),\n relatedEventId: messageEntry.id\n }}));\n await poll(() => timeline.entries.length === 1);\n // 3. add remote reaction target\n timeline.addEntries([messageEntry]);\n await poll(() => timeline.entries.length === 2);\n const entry = getIndexFromIterable(timeline.entries, 0);\n assert.equal(entry, messageEntry);\n assert.equal(entry.pendingAnnotations.get(\"👋\").count, 1);\n },\n \"local reaction removal gets applied after remote echo is added to timeline with reaction not loaded\": async assert => {\n const messageId = \"!abc\";\n const reactionId = \"!def\";\n // 1. put reaction in storage\n const storage = await createMockStorage();\n const txn = await storage.readWriteTxn([storage.storeNames.timelineEvents, storage.storeNames.timelineRelations]);\n txn.timelineEvents.tryInsert({\n event: withContent(createAnnotation(messageId, \"👋\"), createEvent(\"m.reaction\", reactionId, bob)),\n fragmentId: 1, eventIndex: 3, roomId\n }, new NullLogItem());\n await txn.complete();\n // 2. setup timeline\n const pendingEvents = new ObservableArray();\n const timeline = new Timeline({roomId, storage, closeCallback: () => {},\n fragmentIdComparer, pendingEvents, clock: new MockClock()});\n await timeline.load(new User(bob), \"join\", new NullLogItem());\n timeline.entries.subscribe(new ListObserver());\n // 3. add local redaction for reaction\n pendingEvents.append(new PendingEvent({data: {\n roomId,\n queueIndex: 1,\n eventType: \"m.room.redaction\",\n txnId: \"t123\",\n content: {},\n relatedEventId: reactionId\n }}));\n await poll(() => timeline.entries.length === 1);\n // 4. add reaction target\n timeline.addEntries([new EventEntry({\n event: withTextBody(\"hi bob!\", createEvent(\"m.room.message\", messageId, alice)),\n fragmentId: 1, eventIndex: 2}, fragmentIdComparer)\n ]);\n await poll(() => timeline.entries.length === 2);\n // 5. check that redaction was linked to reaction target\n const entry = getIndexFromIterable(timeline.entries, 0);\n assert.equal(entry.pendingAnnotations.get(\"👋\").count, -1);\n },\n \"decrypted entry preserves content when receiving other update without decryption\": async assert => {\n // 1. create encrypted and decrypted entry\n const encryptedEntry = new EventEntry({\n event: withContent({ciphertext: \"abc\"}, createEvent(\"m.room.encrypted\", \"!abc\", alice)),\n fragmentId: 1, eventIndex: 1, roomId\n }, fragmentIdComparer);\n const decryptedEntry = encryptedEntry.clone();\n decryptedEntry.setDecryptionResult({\n event: withTextBody(\"hi bob!\", createEvent(\"m.room.message\", encryptedEntry.id, encryptedEntry.sender))\n });\n // 2. setup the timeline\n const timeline = new Timeline({roomId, storage: await createMockStorage(), closeCallback: () => {},\n fragmentIdComparer, pendingEvents: new ObservableArray(), clock: new MockClock()});\n await timeline.load(new User(alice), \"join\", new NullLogItem());\n timeline.addEntries([decryptedEntry]);\n const observer = new ListObserver();\n timeline.entries.subscribe(observer);\n // 3. replace the entry with one that is not decrypted\n // (as would happen when receiving a reaction,\n // as it does not rerun the decryption)\n // and check that the decrypted content is preserved\n timeline.replaceEntries([encryptedEntry]);\n const {value, type} = await observer.next();\n assert.equal(type, \"update\");\n assert.equal(value.eventType, \"m.room.message\");\n assert.equal(value.content.body, \"hi bob!\");\n },\n\n \"context entry is fetched from remoteEntries\": async assert => {\n const timeline = new Timeline({roomId, storage: await createMockStorage(), closeCallback: () => {},\n fragmentIdComparer, pendingEvents: new ObservableArray(), clock: new MockClock()});\n const entryA = new EventEntry({ event: withTextBody(\"foo\", createEvent(\"m.room.message\", \"event_id_1\", alice)) });\n const entryB = new EventEntry({ event: withReply(\"event_id_1\", createEvent(\"m.room.message\", \"event_id_2\", bob)), eventIndex: 2 });\n await timeline.load(new User(alice), \"join\", new NullLogItem());\n timeline.entries.subscribe({\n onAdd() {},\n });\n timeline.addEntries([entryA, entryB]);\n assert.deepEqual(entryB.contextEntry, entryA);\n },\n\n \"context entry is fetched from storage\": async assert => {\n const storage = await createMockStorage();\n const txn = await storage.readWriteTxn([storage.storeNames.timelineEvents, storage.storeNames.timelineRelations]);\n txn.timelineEvents.tryInsert({ event: withTextBody(\"foo\", createEvent(\"m.room.message\", \"event_id_1\", alice)), fragmentId: 1, eventIndex: 1, roomId });\n await txn.complete();\n const timeline = new Timeline({roomId, storage, closeCallback: () => {},\n fragmentIdComparer, pendingEvents: new ObservableArray(), clock: new MockClock()});\n const entryB = new EventEntry({ event: withReply(\"event_id_1\", createEvent(\"m.room.message\", \"event_id_2\", bob)), eventIndex: 2 });\n await timeline.load(new User(alice), \"join\", new NullLogItem());\n timeline.entries.subscribe({ onAdd: () => null, onUpdate: () => null });\n timeline.addEntries([entryB]);\n await poll(() => entryB.contextEntry);\n assert.strictEqual(entryB.contextEntry.id, \"event_id_1\");\n },\n\n \"context entry is fetched from hs\": async assert => {\n const timeline = new Timeline({roomId, storage: await createMockStorage(), closeCallback: () => {},\n fragmentIdComparer, pendingEvents: new ObservableArray(), clock: new MockClock(), hsApi});\n const entryB = new EventEntry({ event: withReply(\"event_id_1\", createEvent(\"m.room.message\", \"event_id_2\", bob)), eventIndex: 2 });\n await timeline.load(new User(alice), \"join\", new NullLogItem());\n timeline.entries.subscribe({ onAdd: () => null, onUpdate: () => null });\n timeline.addEntries([entryB]);\n await poll(() => entryB.contextEntry);\n assert.strictEqual(entryB.contextEntry.id, \"event_id_1\");\n },\n\n \"context entry has a list of entries to which it forms the context\": async assert => {\n const timeline = new Timeline({roomId, storage: await createMockStorage(), closeCallback: () => {},\n fragmentIdComparer, pendingEvents: new ObservableArray(), clock: new MockClock()});\n const entryA = new EventEntry({ event: withTextBody(\"foo\", createEvent(\"m.room.message\", \"event_id_1\", alice)), eventIndex: 1 });\n const entryB = new EventEntry({ event: withReply(\"event_id_1\", createEvent(\"m.room.message\", \"event_id_2\", bob)), eventIndex: 2 });\n const entryC = new EventEntry({ event: withReply(\"event_id_1\", createEvent(\"m.room.message\", \"event_id_3\", bob)), eventIndex: 3 });\n await timeline.load(new User(alice), \"join\", new NullLogItem());\n timeline.entries.subscribe({ onAdd: () => null, onUpdate: () => null });\n timeline.addEntries([entryA, entryB, entryC]);\n await poll(() => entryA.contextForEntries.length === 2);\n assert.deepEqual(entryA.contextForEntries, [entryB, entryC]);\n },\n\n \"context entry in contextEntryNotInTimeline gets updated based on incoming redaction\": async assert => {\n const timeline = new Timeline({roomId, storage: await createMockStorage(), closeCallback: () => {},\n fragmentIdComparer, pendingEvents: new ObservableArray(), clock: new MockClock(), hsApi});\n const entryB = new EventEntry({ event: withReply(\"event_id_1\", createEvent(\"m.room.message\", \"event_id_2\", bob)), eventIndex: 2 });\n await timeline.load(new User(alice), \"join\", new NullLogItem());\n timeline.entries.subscribe({ onAdd: () => null, onUpdate: () => null });\n timeline.addEntries([entryB]);\n await poll(() => entryB.contextEntry);\n const redactingEntry = new EventEntry({ event: withRedacts(\"event_id_1\", \"foo\", createEvent(\"m.room.redaction\", \"event_id_3\", alice)), eventIndex: 3 });\n timeline.addEntries([redactingEntry]);\n assert.strictEqual(entryB.contextEntry.isRedacted, true);\n },\n\n \"redaction of context entry triggers updates in other entries\": async assert => {\n const timeline = new Timeline({roomId, storage: await createMockStorage(), closeCallback: () => {},\n fragmentIdComparer, pendingEvents: new ObservableArray(), clock: new MockClock(), hsApi});\n const entryA = new EventEntry({ event: withTextBody(\"foo\", createEvent(\"m.room.message\", \"event_id_1\", alice)), eventIndex: 1, fragmentId: 1 });\n const entryB = new EventEntry({ event: withReply(\"event_id_1\", createEvent(\"m.room.message\", \"event_id_2\", bob)), eventIndex: 2, fragmentId: 1 });\n const entryC = new EventEntry({ event: withReply(\"event_id_1\", createEvent(\"m.room.message\", \"event_id_3\", bob)), eventIndex: 3, fragmentId: 1 });\n await timeline.load(new User(alice), \"join\", new NullLogItem());\n const bin = [];\n timeline.entries.subscribe({\n onUpdate: (index, e) => {\n bin.push(e.id);\n },\n onAdd: () => null,\n });\n timeline.addEntries([entryA, entryB, entryC]);\n const eventAClone = JSON.parse(JSON.stringify(entryA.event));\n redactEvent(withRedacts(\"event_id_1\", \"foo\", createEvent(\"m.room.redaction\", \"event_id_4\", alice)), eventAClone);\n const redactedEntry = new EventEntry({ event: eventAClone, eventIndex: 1, fragmentId: 1 });\n timeline.replaceEntries([redactedEntry]);\n assert.strictEqual(bin.includes(\"event_id_2\"), true);\n assert.strictEqual(bin.includes(\"event_id_3\"), true);\n },\n\n \"context entries fetched from storage/hs are moved to remoteEntries\": async assert => {\n const timeline = new Timeline({roomId, storage: await createMockStorage(), closeCallback: () => {},\n fragmentIdComparer, pendingEvents: new ObservableArray(), clock: new MockClock(), hsApi});\n const entryA = new EventEntry({ event: withTextBody(\"foo\", createEvent(\"m.room.message\", \"event_id_1\", alice)), eventIndex: 1 });\n const entryB = new EventEntry({ event: withReply(\"event_id_1\", createEvent(\"m.room.message\", \"event_id_2\", bob)), eventIndex: 2 });\n await timeline.load(new User(alice), \"join\", new NullLogItem());\n timeline.entries.subscribe({ onAdd: () => null, onUpdate: () => null });\n timeline.addEntries([entryB]);\n await poll(() => entryB.contextEntry);\n assert.strictEqual(timeline._contextEntriesNotInTimeline.has(entryA.id), true);\n timeline.addEntries([entryA]);\n assert.strictEqual(timeline._contextEntriesNotInTimeline.has(entryA.id), false);\n const movedEntry = timeline.remoteEntries[0];\n assert.deepEqual(movedEntry, entryA);\n assert.deepEqual(movedEntry.contextForEntries[0], entryB);\n assert.deepEqual(entryB.contextEntry, movedEntry);\n }\n };\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {RoomMember} from \"./RoomMember.js\";\n\nasync function loadMembers({roomId, storage, txn}) {\n if (!txn) {\n txn = await storage.readTxn([\n storage.storeNames.roomMembers,\n ]);\n }\n const memberDatas = await txn.roomMembers.getAll(roomId);\n return memberDatas.map(d => new RoomMember(d));\n}\n\nasync function fetchMembers({summary, syncToken, roomId, hsApi, storage, setChangedMembersMap}, log) {\n // if any members are changed by sync while we're fetching members,\n // they will end up here, so we check not to override them\n const changedMembersDuringSync = new Map();\n setChangedMembersMap(changedMembersDuringSync);\n \n const memberResponse = await hsApi.members(roomId, {at: syncToken}, {log}).response();\n\n const txn = await storage.readWriteTxn([\n storage.storeNames.roomSummary,\n storage.storeNames.roomMembers,\n ]);\n\n let summaryChanges;\n let members;\n \n try {\n summaryChanges = summary.writeHasFetchedMembers(true, txn);\n const {roomMembers} = txn;\n const memberEvents = memberResponse.chunk;\n if (!Array.isArray(memberEvents)) {\n throw new Error(\"malformed\");\n }\n log.set(\"members\", memberEvents.length);\n members = await Promise.all(memberEvents.map(async memberEvent => {\n const userId = memberEvent?.state_key;\n if (!userId) {\n throw new Error(\"malformed\");\n }\n // this member was changed during a sync that happened while calling /members\n // and thus is more recent, so don't overwrite\n const changedMember = changedMembersDuringSync.get(userId);\n if (changedMember) {\n return changedMember;\n } else {\n const member = RoomMember.fromMemberEvent(roomId, memberEvent);\n if (member) {\n roomMembers.set(member.serialize());\n }\n return member;\n }\n }));\n } catch (err) {\n // abort txn on any error\n txn.abort();\n throw err;\n } finally {\n // important this gets cleared\n // or otherwise Room remains in \"fetching-members\" mode\n setChangedMembersMap(null);\n }\n await txn.complete();\n summary.applyChanges(summaryChanges);\n return members;\n}\n\nexport async function fetchOrLoadMembers(options, logger) {\n const {summary} = options;\n if (!summary.data.hasFetchedMembers) {\n // we only want to log if we fetch members, so start or continue the optional log operation here\n return logger.wrapOrRun(options.log, \"fetchMembers\", log => fetchMembers(options, log));\n } else {\n return loadMembers(options);\n }\n}\n\nexport async function fetchOrLoadMember(options, logger) {\n const member = await loadMember(options);\n const {summary} = options;\n if (!summary.data.hasFetchedMembers && !member) {\n // We haven't fetched the memberlist yet; so ping the hs to see if this member does exist\n return logger.wrapOrRun(options.log, \"fetchMember\", log => fetchMember(options, log));\n }\n return member;\n}\n\nasync function loadMember({roomId, userId, storage}) {\n const txn = await storage.readTxn([storage.storeNames.roomMembers,]);\n const member = await txn.roomMembers.get(roomId, userId);\n return member? new RoomMember(member) : null;\n}\n\nasync function fetchMember({roomId, userId, hsApi, storage}, log) {\n let memberData;\n try {\n memberData = await hsApi.state(roomId, \"m.room.member\", userId, { log }).response();\n }\n catch (error) {\n if (error.name === \"HomeServerError\" && error.errcode === \"M_NOT_FOUND\") {\n return null;\n }\n throw error;\n }\n const member = new RoomMember({\n roomId,\n userId,\n membership: memberData.membership,\n avatarUrl: memberData.avatar_url,\n displayName: memberData.displayname,\n });\n const txn = await storage.readWriteTxn([storage.storeNames.roomMembers]);\n try {\n txn.roomMembers.set(member.serialize());\n }\n catch(e) {\n txn.abort();\n throw e;\n }\n await txn.complete();\n return member;\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport class RetainedValue {\n private readonly _freeCallback: () => void;\n private _retentionCount: number = 1;\n\n constructor(freeCallback: () => void) {\n this._freeCallback = freeCallback;\n }\n\n retain(): void {\n this._retentionCount += 1;\n }\n\n release(): void {\n this._retentionCount -= 1;\n if (this._retentionCount === 0) {\n this._freeCallback();\n }\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ObservableMap} from \"../../../observable/map/ObservableMap\";\nimport {RetainedValue} from \"../../../utils/RetainedValue\";\n\nexport class MemberList extends RetainedValue {\n constructor({members, closeCallback}) {\n super(closeCallback);\n this._members = new ObservableMap();\n for (const member of members) {\n this._members.add(member.userId, member);\n }\n }\n\n afterSync(memberChanges) {\n for (const [userId, memberChange] of memberChanges.entries()) {\n this._members.set(userId, memberChange.member);\n }\n }\n\n get members() {\n return this._members;\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {RoomMember} from \"./RoomMember.js\";\n\nexport function calculateRoomName(sortedMembers, summaryData, log) {\n const countWithoutMe = summaryData.joinCount + summaryData.inviteCount - 1;\n if (sortedMembers.length >= countWithoutMe) {\n if (sortedMembers.length > 1) {\n const lastMember = sortedMembers[sortedMembers.length - 1];\n const firstMembers = sortedMembers.slice(0, sortedMembers.length - 1);\n return firstMembers.map(m => m.name).join(\", \") + \" and \" + lastMember.name;\n } else {\n const otherMember = sortedMembers[0];\n if (otherMember) {\n return otherMember.name;\n } else {\n log.log({l: \"could get get other member name\", length: sortedMembers.length, otherMember: !!otherMember, otherMemberMembership: otherMember?.membership});\n return \"Unknown DM Name\";\n }\n }\n } else if (sortedMembers.length < countWithoutMe) {\n return sortedMembers.map(m => m.name).join(\", \") + ` and ${countWithoutMe} others`;\n } else {\n // Empty Room\n return null;\n }\n}\n\nexport class Heroes {\n constructor(roomId) {\n this._roomId = roomId;\n this._members = new Map();\n }\n\n /**\n * @param {string[]} newHeroes array of user ids\n * @param {Map<string, MemberChange>} memberChanges map of changed memberships\n * @param {Transaction} txn\n * @return {Promise}\n */\n async calculateChanges(newHeroes, memberChanges, txn) {\n const updatedHeroMembers = new Map();\n const removedUserIds = [];\n // remove non-present members\n for (const existingUserId of this._members.keys()) {\n if (newHeroes.indexOf(existingUserId) === -1) {\n removedUserIds.push(existingUserId);\n }\n }\n // update heroes with synced member changes\n for (const [userId, memberChange] of memberChanges.entries()) {\n if (this._members.has(userId) || newHeroes.indexOf(userId) !== -1) {\n updatedHeroMembers.set(userId, memberChange.member);\n }\n }\n // load member for new heroes from storage\n for (const userId of newHeroes) {\n if (!this._members.has(userId) && !updatedHeroMembers.has(userId)) {\n const memberData = await txn.roomMembers.get(this._roomId, userId);\n if (memberData) {\n const member = new RoomMember(memberData);\n updatedHeroMembers.set(member.userId, member);\n }\n }\n }\n return {updatedHeroMembers: updatedHeroMembers.values(), removedUserIds};\n }\n\n applyChanges({updatedHeroMembers, removedUserIds}, summaryData, log) {\n for (const userId of removedUserIds) {\n this._members.delete(userId);\n }\n for (const member of updatedHeroMembers) {\n this._members.set(member.userId, member);\n }\n const sortedMembers = Array.from(this._members.values()).sort((a, b) => a.name.localeCompare(b.name));\n this._roomName = calculateRoomName(sortedMembers, summaryData, log);\n }\n\n get roomName() {\n return this._roomName;\n }\n\n get roomAvatarUrl() {\n if (this._members.size === 1) {\n for (const member of this._members.values()) {\n return member.avatarUrl;\n }\n }\n return null;\n }\n\n /**\n * In DM rooms, we want the room's color to be\n * the same as the other user's color. Thus, if the room\n * only has one hero, we use their ID, instead\n * of the room's, to get the avatar color.\n *\n * @returns {?string} the ID of the single hero.\n */\n get roomAvatarColorId() {\n if (this._members.size === 1) {\n for (const member of this._members.keys()) {\n return member\n }\n }\n return null;\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseObservableValue} from \"../../observable/ObservableValue\";\n\nexport class ObservedEventMap {\n constructor(notifyEmpty) {\n this._map = new Map();\n this._notifyEmpty = notifyEmpty;\n }\n\n observe(eventId, eventEntry = null) {\n let observable = this._map.get(eventId);\n if (!observable) {\n observable = new ObservedEvent(this, eventEntry, eventId);\n this._map.set(eventId, observable);\n }\n return observable;\n }\n\n updateEvents(eventEntries) {\n for (let i = 0; i < eventEntries.length; i += 1) {\n const entry = eventEntries[i];\n const observable = this._map.get(entry.id);\n observable?.update(entry);\n }\n }\n\n _remove(id) {\n this._map.delete(id);\n if (this._map.size === 0) {\n this._notifyEmpty();\n }\n }\n}\n\nclass ObservedEvent extends BaseObservableValue {\n constructor(eventMap, entry, id) {\n super();\n this._eventMap = eventMap;\n this._entry = entry;\n this._id = id;\n // remove subscription in microtask after creating it\n // otherwise ObservedEvents would easily never get\n // removed if you never subscribe\n Promise.resolve().then(() => {\n if (!this.hasSubscriptions) {\n this._eventMap._remove(this._id);\n this._eventMap = null;\n }\n });\n }\n\n subscribe(handler) {\n if (!this._eventMap) {\n throw new Error(\"ObservedEvent expired, subscribe right after calling room.observeEvent()\");\n }\n return super.subscribe(handler);\n }\n\n onUnsubscribeLast() {\n this._eventMap._remove(this._id);\n this._eventMap = null;\n super.onUnsubscribeLast();\n }\n\n update(entry) {\n // entries are mostly updated in-place,\n // apart from when they are created,\n // but doesn't hurt to reassign\n this._entry = entry;\n this.emit(this._entry);\n }\n\n get() {\n return this._entry;\n }\n}\n","// these are helper functions if you can't assume you always have a log item (e.g. some code paths call with one set, others don't)\n// if you know you always have a log item, better to use the methods on the log item than these utility functions.\n\nimport {Instance as NullLoggerInstance} from \"./NullLogger\";\nimport type {FilterCreator, ILogItem, LabelOrValues, LogCallback} from \"./types\";\nimport {LogLevel} from \"./LogFilter\";\n\nexport function wrapOrRunNullLogger<T>(logItem: ILogItem | undefined, labelOrValues: LabelOrValues, callback: LogCallback<T>, logLevel?: LogLevel, filterCreator?: FilterCreator): T | Promise<T> {\n if (logItem) {\n return logItem.wrap(labelOrValues, callback, logLevel, filterCreator);\n } else {\n return NullLoggerInstance.run(null, callback);\n }\n}\n\nexport function ensureLogItem(logItem: ILogItem): ILogItem {\n return logItem || NullLoggerInstance.item;\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport const EVENT_TYPE = \"m.room.power_levels\";\n\nexport class PowerLevels {\n constructor({powerLevelEvent, createEvent, ownUserId, membership}) {\n this._plEvent = powerLevelEvent;\n this._createEvent = createEvent;\n this._ownUserId = ownUserId;\n this._membership = membership;\n }\n\n canRedactFromSender(userId) {\n if (userId === this._ownUserId && this._membership === \"join\") {\n return true;\n } else {\n return this.canRedact;\n }\n }\n\n canSendType(eventType) {\n return this._myLevel >= this._getEventTypeLevel(eventType);\n }\n\n get canRedact() {\n return this._myLevel >= this._getActionLevel(\"redact\");\n }\n\n get _myLevel() {\n if (this._membership !== \"join\") {\n return Number.MIN_SAFE_INTEGER;\n }\n return this.getUserLevel(this._ownUserId);\n }\n\n getUserLevel(userId) {\n if (this._plEvent) {\n let userLevel = this._plEvent.content?.users?.[userId];\n if (typeof userLevel !== \"number\") {\n userLevel = this._plEvent.content?.users_default;\n }\n if (typeof userLevel === \"number\") {\n return userLevel;\n }\n } else if (this._createEvent) {\n if (userId === this._createEvent.content?.creator) {\n return 100;\n }\n }\n return 0;\n }\n\n /** @param {string} action either \"invite\", \"kick\", \"ban\" or \"redact\". */\n _getActionLevel(action) {\n const level = this._plEvent?.content[action];\n if (typeof level === \"number\") {\n return level;\n } else {\n return 50;\n }\n }\n\n _getEventTypeLevel(eventType) {\n const level = this._plEvent?.content.events?.[eventType];\n if (typeof level === \"number\") {\n return level;\n } else {\n const level = this._plEvent?.content.events_default;\n if (typeof level === \"number\") {\n return level;\n } else {\n return 0;\n }\n }\n }\n}\n\nexport function tests() {\n const alice = \"@alice:hs.tld\";\n const bob = \"@bob:hs.tld\";\n const charly = \"@charly:hs.tld\";\n const createEvent = {content: {creator: alice}};\n const redactPowerLevelEvent = {content: {\n redact: 50,\n users: {\n [alice]: 50\n },\n users_default: 0\n }};\n const eventsPowerLevelEvent = {content: {\n events_default: 5,\n events: {\n \"m.room.message\": 45,\n \"m.room.topic\": 50,\n },\n users: {\n [alice]: 50,\n [bob]: 10\n },\n users_default: 0\n }};\n\n return {\n \"redact somebody else event with power level event\": assert => {\n const pl1 = new PowerLevels({powerLevelEvent: redactPowerLevelEvent, ownUserId: alice, membership: \"join\"});\n assert.equal(pl1.canRedact, true);\n const pl2 = new PowerLevels({powerLevelEvent: redactPowerLevelEvent, ownUserId: bob, membership: \"join\"});\n assert.equal(pl2.canRedact, false);\n },\n \"redact somebody else event with create event\": assert => {\n const pl1 = new PowerLevels({createEvent, ownUserId: alice, membership: \"join\"});\n assert.equal(pl1.canRedact, true);\n const pl2 = new PowerLevels({createEvent, ownUserId: bob, membership: \"join\"});\n assert.equal(pl2.canRedact, false);\n },\n \"redact own event\": assert => {\n const pl = new PowerLevels({ownUserId: alice, membership: \"join\"});\n assert.equal(pl.canRedactFromSender(alice), true);\n assert.equal(pl.canRedactFromSender(bob), false);\n },\n \"can send event without power levels\": assert => {\n const pl = new PowerLevels({createEvent, ownUserId: charly, membership: \"join\"});\n assert.equal(pl.canSendType(\"m.room.message\"), true);\n },\n \"can't send any event below events_default\": assert => {\n const pl = new PowerLevels({powerLevelEvent: eventsPowerLevelEvent, ownUserId: charly, membership: \"join\"});\n assert.equal(pl.canSendType(\"m.foo\"), false);\n },\n \"can't send event below events[type]\": assert => {\n const pl = new PowerLevels({powerLevelEvent: eventsPowerLevelEvent, ownUserId: bob, membership: \"join\"});\n assert.equal(pl.canSendType(\"m.foo\"), true);\n assert.equal(pl.canSendType(\"m.room.message\"), false);\n },\n \"can send event above or at events[type]\": assert => {\n const pl = new PowerLevels({powerLevelEvent: eventsPowerLevelEvent, ownUserId: alice, membership: \"join\"});\n assert.equal(pl.canSendType(\"m.room.message\"), true);\n assert.equal(pl.canSendType(\"m.room.topic\"), true);\n },\n \"can't redact or send in non-joined room'\": assert => {\n const pl = new PowerLevels({createEvent, ownUserId: alice, membership: \"leave\"});\n assert.equal(pl.canRedact, false);\n assert.equal(pl.canRedactFromSender(alice), false);\n assert.equal(pl.canSendType(\"m.room.message\"), false);\n },\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {EventEmitter} from \"../../utils/EventEmitter\";\nimport {RoomSummary} from \"./RoomSummary.js\";\nimport {GapWriter} from \"./timeline/persistence/GapWriter.js\";\nimport {RelationWriter} from \"./timeline/persistence/RelationWriter.js\";\nimport {Timeline} from \"./timeline/Timeline.js\";\nimport {FragmentIdComparer} from \"./timeline/FragmentIdComparer.js\";\nimport {WrappedError} from \"../error.js\"\nimport {fetchOrLoadMembers, fetchOrLoadMember} from \"./members/load.js\";\nimport {MemberList} from \"./members/MemberList.js\";\nimport {Heroes} from \"./members/Heroes.js\";\nimport {EventEntry} from \"./timeline/entries/EventEntry.js\";\nimport {ObservedEventMap} from \"./ObservedEventMap.js\";\nimport {DecryptionSource} from \"../e2ee/common.js\";\nimport {ensureLogItem} from \"../../logging/utils\";\nimport {PowerLevels} from \"./PowerLevels.js\";\nimport {RetainedObservableValue} from \"../../observable/ObservableValue\";\nimport {TimelineReader} from \"./timeline/persistence/TimelineReader\";\n\nconst EVENT_ENCRYPTED_TYPE = \"m.room.encrypted\";\n\nexport class BaseRoom extends EventEmitter {\n constructor({roomId, storage, hsApi, mediaRepository, emitCollectionChange, user, createRoomEncryption, getSyncToken, platform}) {\n super();\n this._roomId = roomId;\n this._storage = storage;\n this._hsApi = hsApi;\n this._mediaRepository = mediaRepository;\n this._summary = new RoomSummary(roomId);\n this._fragmentIdComparer = new FragmentIdComparer([]);\n this._emitCollectionChange = emitCollectionChange;\n this._timeline = null;\n this._user = user;\n this._changedMembersDuringSync = null;\n this._memberList = null;\n this._createRoomEncryption = createRoomEncryption;\n this._roomEncryption = null;\n this._getSyncToken = getSyncToken;\n this._platform = platform;\n this._observedEvents = null;\n this._powerLevels = null;\n this._powerLevelLoading = null;\n this._observedMembers = null;\n }\n\n async _eventIdsToEntries(eventIds, txn) {\n const retryEntries = [];\n await Promise.all(eventIds.map(async eventId => {\n const storageEntry = await txn.timelineEvents.getByEventId(this._roomId, eventId);\n if (storageEntry) {\n retryEntries.push(new EventEntry(storageEntry, this._fragmentIdComparer));\n }\n }));\n return retryEntries;\n }\n\n _getAdditionalTimelineRetryEntries(otherRetryEntries, roomKeys) {\n let retryTimelineEntries = this._roomEncryption.filterUndecryptedEventEntriesForKeys(this._timeline.remoteEntries, roomKeys);\n // filter out any entries already in retryEntries so we don't decrypt them twice\n const existingIds = otherRetryEntries.reduce((ids, e) => {ids.add(e.id); return ids;}, new Set());\n retryTimelineEntries = retryTimelineEntries.filter(e => !existingIds.has(e.id));\n return retryTimelineEntries;\n }\n\n /**\n * Used for retrying decryption from other sources than sync, like key backup.\n * @internal\n * @param {RoomKey} roomKey\n * @param {Array<string>} eventIds any event ids that should be retried. There might be more in the timeline though for this key.\n * @return {Promise}\n */\n async notifyRoomKey(roomKey, eventIds, log) {\n if (!this._roomEncryption) {\n return;\n }\n const txn = await this._storage.readTxn([\n this._storage.storeNames.timelineEvents,\n this._storage.storeNames.inboundGroupSessions,\n ]);\n let retryEntries = await this._eventIdsToEntries(eventIds, txn);\n if (this._timeline) {\n const retryTimelineEntries = this._getAdditionalTimelineRetryEntries(retryEntries, [roomKey]);\n retryEntries = retryEntries.concat(retryTimelineEntries);\n }\n if (retryEntries.length) {\n const decryptRequest = this._decryptEntries(DecryptionSource.Retry, retryEntries, txn, log);\n // this will close txn while awaiting decryption\n await decryptRequest.complete();\n\n this._timeline?.replaceEntries(retryEntries);\n // we would ideally write the room summary in the same txn as the groupSessionDecryptions in the\n // _decryptEntries entries and could even know which events have been decrypted for the first\n // time from DecryptionChanges.write and only pass those to the summary. As timeline changes\n // are not essential to the room summary, it's fine to write this in a separate txn for now.\n const changes = this._summary.data.applyTimelineEntries(retryEntries, false, false);\n if (await this._summary.writeAndApplyData(changes, this._storage)) {\n this._emitUpdate();\n }\n }\n }\n\n _setEncryption(roomEncryption) {\n if (roomEncryption && !this._roomEncryption) {\n this._roomEncryption = roomEncryption;\n if (this._timeline) {\n this._timeline.enableEncryption(this._decryptEntries.bind(this, DecryptionSource.Timeline));\n }\n return true;\n }\n return false;\n }\n\n /**\n * Used for decrypting when loading/filling the timeline, and retrying decryption,\n * not during sync, where it is split up during the multiple phases.\n */\n _decryptEntries(source, entries, inboundSessionTxn, log = null) {\n const request = new DecryptionRequest(async (r, log) => {\n if (!inboundSessionTxn) {\n inboundSessionTxn = await this._storage.readTxn([this._storage.storeNames.inboundGroupSessions]);\n }\n if (r.cancelled) return;\n const events = entries.filter(entry => {\n return entry.eventType === EVENT_ENCRYPTED_TYPE;\n }).map(entry => entry.event);\n r.preparation = await this._roomEncryption.prepareDecryptAll(events, null, source, inboundSessionTxn);\n if (r.cancelled) return;\n const changes = await r.preparation.decrypt();\n r.preparation = null;\n if (r.cancelled) return;\n const stores = [this._storage.storeNames.groupSessionDecryptions];\n const isTimelineOpen = this._isTimelineOpen;\n if (isTimelineOpen) {\n // read to fetch devices if timeline is open\n stores.push(this._storage.storeNames.deviceIdentities);\n }\n const writeTxn = await this._storage.readWriteTxn(stores);\n let decryption;\n try {\n decryption = await changes.write(writeTxn, log);\n if (isTimelineOpen) {\n await decryption.verifySenders(writeTxn);\n }\n } catch (err) {\n writeTxn.abort();\n throw err;\n }\n await writeTxn.complete();\n // TODO: log decryption errors here\n decryption.applyToEntries(entries);\n if (this._observedEvents) {\n this._observedEvents.updateEvents(entries);\n }\n }, ensureLogItem(log));\n return request;\n }\n\n // TODO: move this to Room\n async _getSyncRetryDecryptEntries(newKeys, roomEncryption, txn) {\n const entriesPerKey = await Promise.all(newKeys.map(async key => {\n const retryEventIds = await roomEncryption.getEventIdsForMissingKey(key, txn);\n if (retryEventIds) {\n return this._eventIdsToEntries(retryEventIds, txn);\n }\n }));\n let retryEntries = entriesPerKey.reduce((allEntries, entries) => entries ? allEntries.concat(entries) : allEntries, []);\n // If we have the timeline open, see if there are more entries for the new keys\n // as we only store missing session information for synced events, not backfilled.\n // We want to decrypt all events we can though if the user is looking\n // at them when the timeline is open\n if (this._timeline) {\n const retryTimelineEntries = this._getAdditionalTimelineRetryEntries(retryEntries, newKeys);\n // make copies so we don't modify the original entry in writeSync, before the afterSync stage\n const retryTimelineEntriesCopies = retryTimelineEntries.map(e => e.clone());\n // add to other retry entries\n retryEntries = retryEntries.concat(retryTimelineEntriesCopies);\n }\n return retryEntries;\n }\n\n /** @package */\n async load(summary, txn, log) {\n log.set(\"id\", this.id);\n try {\n // if called from sync, there is no summary yet\n if (summary) {\n this._summary.load(summary);\n }\n if (this._summary.data.encryption) {\n const roomEncryption = this._createRoomEncryption(this, this._summary.data.encryption);\n this._setEncryption(roomEncryption);\n }\n // need to load members for name?\n if (this._summary.data.needsHeroes) {\n this._heroes = new Heroes(this._roomId);\n const changes = await this._heroes.calculateChanges(this._summary.data.heroes, [], txn);\n this._heroes.applyChanges(changes, this._summary.data, log);\n }\n } catch (err) {\n throw new WrappedError(`Could not load room ${this._roomId}`, err);\n }\n }\n\n async observeMember(userId) {\n if (!this._observedMembers) {\n this._observedMembers = new Map();\n }\n const mapMember = this._observedMembers.get(userId);\n if (mapMember) {\n // Hit, we're already observing this member\n return mapMember;\n }\n // Miss, load from storage/hs and set in map\n const member = await fetchOrLoadMember({\n summary: this._summary,\n roomId: this._roomId,\n userId,\n storage: this._storage,\n hsApi: this._hsApi\n }, this._platform.logger);\n if (!member) {\n return null;\n }\n const observableMember = new RetainedObservableValue(member, () => this._observedMembers.delete(userId));\n this._observedMembers.set(userId, observableMember);\n return observableMember;\n }\n\n\n /** @public */\n async loadMemberList(txn = undefined, log = null) {\n if (this._memberList) {\n // TODO: also await fetchOrLoadMembers promise here\n this._memberList.retain();\n return this._memberList;\n } else {\n const members = await fetchOrLoadMembers({\n summary: this._summary,\n roomId: this._roomId,\n hsApi: this._hsApi,\n storage: this._storage,\n // pass in a transaction if we know we won't need to fetch (which would abort the transaction)\n // and we want to make this operation part of the larger transaction\n txn,\n syncToken: this._getSyncToken(),\n // to handle race between /members and /sync\n setChangedMembersMap: map => this._changedMembersDuringSync = map,\n log,\n }, this._platform.logger);\n this._memberList = new MemberList({\n members,\n closeCallback: () => { this._memberList = null; }\n });\n return this._memberList;\n }\n } \n\n /** @public */\n fillGap(fragmentEntry, amount, log = null) {\n // TODO move some/all of this out of BaseRoom\n return this._platform.logger.wrapOrRun(log, \"fillGap\", async log => {\n log.set(\"id\", this.id);\n log.set(\"fragment\", fragmentEntry.fragmentId);\n log.set(\"dir\", fragmentEntry.direction.asApiString());\n if (fragmentEntry.edgeReached) {\n log.set(\"edgeReached\", true);\n return;\n }\n const response = await this._hsApi.messages(this._roomId, {\n from: fragmentEntry.token,\n dir: fragmentEntry.direction.asApiString(),\n limit: amount,\n filter: {\n lazy_load_members: true,\n include_redundant_members: true,\n }\n }, {log}).response();\n\n const txn = await this._storage.readWriteTxn([\n this._storage.storeNames.pendingEvents,\n this._storage.storeNames.timelineEvents,\n this._storage.storeNames.timelineRelations,\n this._storage.storeNames.timelineFragments,\n ]);\n let extraGapFillChanges;\n let gapResult;\n try {\n // detect remote echos of pending messages in the gap\n extraGapFillChanges = await this._writeGapFill(response.chunk, txn, log);\n // write new events into gap\n const relationWriter = new RelationWriter({\n roomId: this._roomId,\n fragmentIdComparer: this._fragmentIdComparer,\n ownUserId: this._user.id,\n });\n const gapWriter = new GapWriter({\n roomId: this._roomId,\n storage: this._storage,\n fragmentIdComparer: this._fragmentIdComparer,\n relationWriter\n });\n gapResult = await gapWriter.writeFragmentFill(fragmentEntry, response, txn, log);\n } catch (err) {\n txn.abort();\n throw err;\n }\n await txn.complete();\n if (this._roomEncryption) {\n const decryptRequest = this._decryptEntries(DecryptionSource.Timeline, gapResult.entries, null, log);\n await decryptRequest.complete();\n }\n // once txn is committed, update in-memory state & emit events\n for (const fragment of gapResult.fragments) {\n this._fragmentIdComparer.add(fragment);\n }\n if (extraGapFillChanges) {\n this._applyGapFill(extraGapFillChanges);\n }\n if (this._timeline) {\n // these should not be added if not already there\n this._timeline.replaceEntries(gapResult.updatedEntries);\n this._timeline.addEntries(gapResult.entries);\n }\n });\n }\n\n /**\n allow sub classes to integrate in the gap fill lifecycle.\n JoinedRoom uses this update remote echos.\n */\n // eslint-disable-next-line no-unused-vars\n async _writeGapFill(chunk, txn, log) {}\n _applyGapFill() {}\n\n /** @public */\n get name() {\n if (this._heroes) {\n return this._heroes.roomName;\n }\n const summaryData = this._summary.data;\n if (summaryData.name) {\n return summaryData.name;\n }\n if (summaryData.canonicalAlias) {\n return summaryData.canonicalAlias;\n }\n return null;\n }\n\n /** @public */\n get id() {\n return this._roomId;\n }\n\n get avatarUrl() {\n if (this._summary.data.avatarUrl) {\n return this._summary.data.avatarUrl;\n } else if (this._heroes) {\n return this._heroes.roomAvatarUrl;\n }\n return null;\n }\n\n /**\n * Retrieve the identifier that should be used to color\n * this room's avatar. By default this is the room's\n * ID, but DM rooms should be the same color as their\n * user's avatar.\n */\n get avatarColorId() {\n return this._roomId;\n }\n\n get lastMessageTimestamp() {\n return this._summary.data.lastMessageTimestamp;\n }\n\n get isLowPriority() {\n const tags = this._summary.data.tags;\n return !!(tags && tags['m.lowpriority']);\n }\n\n get isEncrypted() {\n return !!this._summary.data.encryption;\n }\n\n get isJoined() {\n return this.membership === \"join\";\n }\n\n get isLeft() {\n return this.membership === \"leave\";\n }\n\n get canonicalAlias() {\n return this._summary.data.canonicalAlias;\n }\n\n get joinedMemberCount() {\n return this._summary.data.joinCount;\n }\n\n get mediaRepository() {\n return this._mediaRepository;\n }\n\n get membership() {\n return this._summary.data.membership;\n }\n\n isDirectMessageForUserId(userId) {\n if (this._summary.data.dmUserId === userId) {\n return true;\n } else {\n // fall back to considering any room a DM containing heroes (e.g. no name) and 2 members,\n // on of which the userId we're looking for.\n // We need this because we're not yet processing m.direct account data correctly.\n const {heroes, joinCount, inviteCount} = this._summary.data;\n if (heroes && heroes.includes(userId) && (joinCount + inviteCount) === 2) {\n return true;\n }\n }\n return false;\n }\n\n async _loadPowerLevels() {\n const txn = await this._storage.readTxn([this._storage.storeNames.roomState]);\n const powerLevelsState = await txn.roomState.get(this._roomId, \"m.room.power_levels\", \"\");\n if (powerLevelsState) {\n return new PowerLevels({\n powerLevelEvent: powerLevelsState.event,\n ownUserId: this._user.id,\n membership: this.membership\n });\n }\n const createState = await txn.roomState.get(this._roomId, \"m.room.create\", \"\");\n if (createState) {\n return new PowerLevels({\n createEvent: createState.event,\n ownUserId: this._user.id,\n membership: this.membership\n });\n } else {\n const membership = this.membership;\n return new PowerLevels({ownUserId: this._user.id, membership});\n }\n }\n\n /**\n * Get the PowerLevels of the room.\n * Always subscribe to the value returned by this method.\n * @returns {RetainedObservableValue} PowerLevels of the room\n */\n async observePowerLevels() {\n if (this._powerLevelLoading) { await this._powerLevelLoading; }\n let observable = this._powerLevels;\n if (!observable) {\n this._powerLevelLoading = this._loadPowerLevels();\n const powerLevels = await this._powerLevelLoading;\n observable = new RetainedObservableValue(powerLevels, () => { this._powerLevels = null; });\n this._powerLevels = observable;\n this._powerLevelLoading = null;\n }\n return observable;\n }\n\n enableKeyBackup(keyBackup) {\n this._roomEncryption?.enableKeyBackup(keyBackup);\n // TODO: do we really want to do this every time you open the app?\n if (this._timeline && keyBackup) {\n this._platform.logger.run(\"enableKeyBackup\", log => {\n return this._roomEncryption.restoreMissingSessionsFromBackup(this._timeline.remoteEntries, log);\n });\n }\n }\n\n get _isTimelineOpen() {\n return !!this._timeline;\n }\n\n _emitUpdate() {\n // once for event emitter listeners\n this.emit(\"change\");\n // and once for collection listeners\n this._emitCollectionChange(this);\n }\n\n /** @public */\n openTimeline(log = null) {\n return this._platform.logger.wrapOrRun(log, \"open timeline\", async log => {\n log.set(\"id\", this.id);\n if (this._timeline) {\n throw new Error(\"not dealing with load race here for now\");\n }\n this._timeline = new Timeline({\n roomId: this.id,\n storage: this._storage,\n fragmentIdComparer: this._fragmentIdComparer,\n pendingEvents: this._getPendingEvents(),\n closeCallback: () => {\n this._timeline = null;\n if (this._roomEncryption) {\n this._roomEncryption.notifyTimelineClosed();\n }\n },\n clock: this._platform.clock,\n logger: this._platform.logger,\n powerLevelsObservable: await this.observePowerLevels(),\n hsApi: this._hsApi\n });\n try {\n if (this._roomEncryption) {\n this._timeline.enableEncryption(this._decryptEntries.bind(this, DecryptionSource.Timeline));\n }\n await this._timeline.load(this._user, this.membership, log);\n } catch (err) {\n // this also clears this._timeline in the closeCallback\n this._timeline.dispose();\n throw err;\n }\n return this._timeline;\n });\n }\n\n /* allow subclasses to provide an observable list with pending events when opening the timeline */\n _getPendingEvents() { return null; }\n\n observeEvent(eventId) {\n if (!this._observedEvents) {\n this._observedEvents = new ObservedEventMap(() => {\n this._observedEvents = null;\n });\n }\n let entry = null;\n if (this._timeline) {\n entry = this._timeline.getByEventId(eventId);\n }\n const observable = this._observedEvents.observe(eventId, entry);\n if (!entry) {\n // update in the background\n this._readEventById(eventId).then(entry => {\n observable.update(entry);\n }).catch(err => {\n console.warn(`could not load event ${eventId} from storage`, err);\n });\n }\n return observable;\n }\n\n async _readEventById(eventId) {\n const reader = new TimelineReader({ roomId: this._roomId, storage: this._storage, fragmentIdComparer: this._fragmentIdComparer });\n const entry = await reader.readById(eventId);\n return entry;\n }\n\n dispose() {\n this._roomEncryption?.dispose();\n this._timeline?.dispose();\n }\n}\n\nclass DecryptionRequest {\n constructor(decryptFn, log) {\n this._cancelled = false;\n this.preparation = null;\n this._promise = log.wrap(\"decryptEntries\", log => decryptFn(this, log));\n }\n\n complete() {\n return this._promise;\n }\n\n get cancelled() {\n return this._cancelled;\n }\n\n dispose() {\n this._cancelled = true;\n if (this.preparation) {\n this.preparation.dispose();\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport function makeTxnId() {\n const n = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);\n const str = n.toString(16);\n return \"t\" + \"0\".repeat(14 - str.length) + str;\n}\n\nexport function isTxnId(txnId) {\n\treturn txnId.startsWith(\"t\") && txnId.length === 15;\n}\n\nexport function tests() {\n\treturn {\n\t\t\"isTxnId succeeds on result of makeTxnId\": assert => {\n\t\t\tassert(isTxnId(makeTxnId()));\n\t\t},\n\t\t\"isTxnId fails on event id\": assert => {\n\t\t\tassert(!isTxnId(\"$yS_n5n3cIO2aTtek0_2ZSlv-7g4YYR2zKrk2mFCW_rm\"));\n\t\t},\n\t}\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {SortedArray} from \"../../../observable/list/SortedArray\";\nimport {ConnectionError} from \"../../error.js\";\nimport {PendingEvent, SendStatus} from \"./PendingEvent.js\";\nimport {makeTxnId, isTxnId} from \"../../common.js\";\nimport {REDACTION_TYPE} from \"../common\";\nimport {getRelationFromContent, getRelationTarget, setRelationTarget, REACTION_TYPE, ANNOTATION_RELATION_TYPE} from \"../timeline/relations.js\";\n\nexport class SendQueue {\n constructor({roomId, storage, hsApi, pendingEvents}) {\n pendingEvents = pendingEvents || [];\n this._roomId = roomId;\n this._storage = storage;\n this._hsApi = hsApi;\n this._pendingEvents = new SortedArray((a, b) => a.queueIndex - b.queueIndex);\n this._pendingEvents.setManyUnsorted(pendingEvents.map(data => this._createPendingEvent(data)));\n this._isSending = false;\n this._offline = false;\n this._roomEncryption = null;\n this._currentQueueIndex = 0;\n }\n\n _createPendingEvent(data, attachments = null) {\n const pendingEvent = new PendingEvent({\n data,\n remove: () => this._removeEvent(pendingEvent),\n emitUpdate: params => this._pendingEvents.update(pendingEvent, params),\n attachments\n });\n return pendingEvent;\n }\n\n enableEncryption(roomEncryption) {\n this._roomEncryption = roomEncryption;\n }\n\n _sendLoop(log) {\n this._isSending = true;\n this._sendLoopLogItem = log.runDetached(\"send queue flush\", async log => {\n try {\n for (const pendingEvent of this._pendingEvents) {\n await log.wrap(\"send event\", async log => {\n log.set(\"queueIndex\", pendingEvent.queueIndex);\n try {\n this._currentQueueIndex = pendingEvent.queueIndex;\n await this._sendEvent(pendingEvent, log);\n } catch(err) {\n if (err instanceof ConnectionError) {\n this._offline = true;\n log.set(\"offline\", true);\n pendingEvent.setWaiting();\n } else {\n log.catch(err);\n const isPermanentError = err.name === \"HomeServerError\" && (\n err.statusCode === 400 || // bad request, must be a bug on our end\n err.statusCode === 403 || // forbidden\n err.statusCode === 404 // not found\n );\n if (isPermanentError) {\n log.set(\"remove\", true);\n await pendingEvent.abort();\n } else {\n pendingEvent.setError(err);\n }\n }\n } finally {\n this._currentQueueIndex = 0;\n }\n });\n }\n } finally {\n this._isSending = false;\n this._sendLoopLogItem = null;\n }\n });\n }\n\n async _sendEvent(pendingEvent, log) {\n if (pendingEvent.needsUpload) {\n await log.wrap(\"upload attachments\", log => pendingEvent.uploadAttachments(this._hsApi, log));\n await this._tryUpdateEvent(pendingEvent);\n }\n if (pendingEvent.needsEncryption) {\n pendingEvent.setEncrypting();\n const encryptionContent = pendingEvent.contentForEncryption;\n const {type, content} = await log.wrap(\"encrypt\", log => this._roomEncryption.encrypt(\n pendingEvent.eventType, encryptionContent, this._hsApi, log));\n pendingEvent.setEncrypted(type, content);\n await this._tryUpdateEvent(pendingEvent);\n }\n if (pendingEvent.needsSending) {\n await pendingEvent.send(this._hsApi, log);\n // we now have a remoteId, but this pending event may be removed at any point in the future\n // (or past, so can't assume it still exists) once the remote echo comes in.\n // So if we have any related events that need to resolve the relatedTxnId to a related event id,\n // they need to do so now.\n // We ensure this by writing the new remote id for the pending event and all related events\n // with unresolved relatedTxnId in the queue in one transaction.\n const txn = await this._storage.readWriteTxn([this._storage.storeNames.pendingEvents]);\n try {\n await this._tryUpdateEventWithTxn(pendingEvent, txn);\n await this._resolveRemoteIdInPendingRelations(\n pendingEvent.txnId, pendingEvent.remoteId, txn);\n } catch (err) {\n txn.abort();\n throw err;\n }\n await txn.complete();\n }\n }\n\n async _resolveRemoteIdInPendingRelations(txnId, remoteId, txn) {\n const relatedEventWithoutRemoteId = this._pendingEvents.array.filter(pe => {\n return pe.relatedTxnId === txnId && pe.relatedEventId !== remoteId;\n });\n for (const relatedPE of relatedEventWithoutRemoteId) {\n relatedPE.setRelatedEventId(remoteId);\n await this._tryUpdateEventWithTxn(relatedPE, txn);\n }\n return relatedEventWithoutRemoteId;\n }\n\n async removeRemoteEchos(events, txn, parentLog) {\n const removed = [];\n for (const event of events) {\n const txnId = event.unsigned && event.unsigned.transaction_id;\n let idx;\n if (txnId) {\n idx = this._pendingEvents.array.findIndex(pe => pe.txnId === txnId);\n } else {\n idx = this._pendingEvents.array.findIndex(pe => pe.remoteId === event.event_id);\n }\n if (idx !== -1) {\n const pendingEvent = this._pendingEvents.get(idx);\n const remoteId = event.event_id;\n parentLog.log({l: \"removeRemoteEcho\", queueIndex: pendingEvent.queueIndex, remoteId, txnId});\n txn.pendingEvents.remove(pendingEvent.roomId, pendingEvent.queueIndex);\n removed.push(pendingEvent);\n await this._resolveRemoteIdInPendingRelations(txnId, remoteId, txn);\n }\n }\n return removed;\n }\n\n async _removeEvent(pendingEvent) {\n let hasEvent = this._pendingEvents.array.indexOf(pendingEvent) !== -1;\n if (hasEvent) {\n const txn = await this._storage.readWriteTxn([this._storage.storeNames.pendingEvents]);\n try {\n txn.pendingEvents.remove(pendingEvent.roomId, pendingEvent.queueIndex);\n } catch (err) {\n txn.abort();\n }\n await txn.complete();\n // lookup index after async txn is complete,\n // to make sure we're not racing with anything\n const idx = this._pendingEvents.array.indexOf(pendingEvent);\n if (idx !== -1) {\n this._pendingEvents.remove(idx);\n }\n }\n pendingEvent.dispose();\n }\n\n emitRemovals(pendingEvents) {\n for (const pendingEvent of pendingEvents) {\n const idx = this._pendingEvents.array.indexOf(pendingEvent);\n if (idx !== -1) {\n this._pendingEvents.remove(idx);\n }\n pendingEvent.dispose();\n }\n }\n\n resumeSending(parentLog) {\n this._offline = false;\n if (this._pendingEvents.length) {\n parentLog.wrap(\"resumeSending\", log => {\n log.set(\"id\", this._roomId);\n log.set(\"pendingEvents\", this._pendingEvents.length);\n if (!this._isSending) {\n this._sendLoop(log);\n }\n if (this._sendLoopLogItem) {\n log.refDetached(this._sendLoopLogItem);\n }\n });\n }\n }\n\n async enqueueEvent(eventType, content, attachments, log) {\n const relation = getRelationFromContent(content);\n let relatedTxnId = null;\n if (relation) {\n const relationTarget = getRelationTarget(relation);\n if (isTxnId(relationTarget)) {\n relatedTxnId = relationTarget;\n setRelationTarget(relation, null);\n }\n if (relation.rel_type === ANNOTATION_RELATION_TYPE) {\n // Here we know the shape of the relation, and can use event_id safely\n const isAlreadyAnnotating = this._pendingEvents.array.some(pe => {\n const r = getRelationFromContent(pe.content);\n return pe.eventType === eventType && r && r.key === relation.key &&\n (pe.relatedTxnId === relatedTxnId || r.event_id === relation.event_id);\n });\n if (isAlreadyAnnotating) {\n log.set(\"already_annotating\", true);\n return;\n }\n }\n }\n await this._enqueueEvent(eventType, content, attachments, relatedTxnId, null, log);\n }\n\n async _enqueueEvent(eventType, content, attachments, relatedTxnId, relatedEventId, log) {\n const pendingEvent = await this._createAndStoreEvent(eventType, content, relatedTxnId, relatedEventId, attachments);\n this._pendingEvents.set(pendingEvent);\n log.set(\"queueIndex\", pendingEvent.queueIndex);\n log.set(\"pendingEvents\", this._pendingEvents.length);\n if (!this._isSending && !this._offline) {\n this._sendLoop(log);\n }\n if (this._sendLoopLogItem) {\n log.refDetached(this._sendLoopLogItem);\n }\n }\n\n async enqueueRedaction(eventIdOrTxnId, reason, log) {\n const isAlreadyRedacting = this._pendingEvents.array.some(pe => {\n return pe.eventType === REDACTION_TYPE &&\n (pe.relatedTxnId === eventIdOrTxnId || pe.relatedEventId === eventIdOrTxnId);\n });\n if (isAlreadyRedacting) {\n log.set(\"already_redacting\", true);\n return;\n }\n let relatedTxnId;\n let relatedEventId;\n if (isTxnId(eventIdOrTxnId)) {\n relatedTxnId = eventIdOrTxnId;\n const txnId = eventIdOrTxnId;\n const pe = this._pendingEvents.array.find(pe => pe.txnId === txnId);\n if (pe && !pe.remoteId && pe.status !== SendStatus.Sending) {\n // haven't started sending this event yet,\n // just remove it from the queue\n log.set(\"remove\", relatedTxnId);\n await pe.abort();\n return;\n } else if (pe) {\n relatedEventId = pe.remoteId;\n } else {\n // we don't have the pending event anymore,\n // the remote echo must have arrived in the meantime.\n // we could look for it in the timeline, but for now\n // we don't do anything as this race is quite unlikely\n // and a bit complicated to fix.\n return;\n }\n } else {\n relatedEventId = eventIdOrTxnId;\n const pe = this._pendingEvents.array.find(pe => pe.remoteId === relatedEventId);\n if (pe) {\n // also set the txn id just in case that an event id was passed\n // for relating to a pending event that is still waiting for the remote echo\n relatedTxnId = pe.txnId;\n }\n }\n log.set(\"relatedTxnId\", relatedTxnId);\n log.set(\"relatedEventId\", relatedEventId);\n await this._enqueueEvent(REDACTION_TYPE, {reason}, null, relatedTxnId, relatedEventId, log);\n }\n\n get pendingEvents() {\n return this._pendingEvents;\n }\n\n async _tryUpdateEvent(pendingEvent) {\n const txn = await this._storage.readWriteTxn([this._storage.storeNames.pendingEvents]);\n try {\n this._tryUpdateEventWithTxn(pendingEvent, txn);\n } catch (err) {\n txn.abort();\n throw err;\n }\n await txn.complete();\n }\n\n async _tryUpdateEventWithTxn(pendingEvent, txn) {\n // pendingEvent might have been removed already here\n // by a racing remote echo, so check first so we don't recreate it\n if (await txn.pendingEvents.exists(pendingEvent.roomId, pendingEvent.queueIndex)) {\n txn.pendingEvents.update(pendingEvent.data);\n }\n }\n\n async _createAndStoreEvent(eventType, content, relatedTxnId, relatedEventId, attachments) {\n const txn = await this._storage.readWriteTxn([this._storage.storeNames.pendingEvents]);\n let pendingEvent;\n try {\n const pendingEventsStore = txn.pendingEvents;\n const maxStorageQueueIndex = await pendingEventsStore.getMaxQueueIndex(this._roomId) || 0;\n // don't use the queueIndex of the pendingEvent currently waiting for /send to return\n // if the remote echo already removed the pendingEvent in storage, as the send loop\n // wouldn't be able to detect the remote echo already arrived and end up overwriting the new event\n const maxQueueIndex = Math.max(maxStorageQueueIndex, this._currentQueueIndex);\n const queueIndex = maxQueueIndex + 1;\n const needsEncryption = eventType !== REDACTION_TYPE &&\n eventType !== REACTION_TYPE &&\n !!this._roomEncryption;\n pendingEvent = this._createPendingEvent({\n roomId: this._roomId,\n queueIndex,\n eventType,\n content,\n relatedTxnId,\n relatedEventId,\n txnId: makeTxnId(),\n needsEncryption,\n needsUpload: !!attachments\n }, attachments);\n pendingEventsStore.add(pendingEvent.data);\n } catch (err) {\n txn.abort();\n throw err;\n }\n await txn.complete();\n return pendingEvent;\n }\n\n dispose() {\n for (const pe of this._pendingEvents) {\n pe.dispose();\n }\n }\n}\n\nimport {HomeServer as MockHomeServer} from \"../../../mocks/HomeServer.js\";\nimport {createMockStorage} from \"../../../mocks/Storage\";\nimport {ListObserver} from \"../../../mocks/ListObserver.js\";\nimport {NullLogger, NullLogItem} from \"../../../logging/NullLogger\";\nimport {createEvent, withTextBody, withTxnId} from \"../../../mocks/event.js\";\nimport {poll} from \"../../../mocks/poll.js\";\nimport {createAnnotation} from \"../timeline/relations.js\";\n\nexport function tests() {\n const logger = new NullLogger();\n return {\n \"enqueue second message when remote echo of first arrives before /send returns\": async assert => {\n const storage = await createMockStorage();\n const hs = new MockHomeServer();\n // 1. enqueue and start send event 1\n const queue = new SendQueue({roomId: \"!abc\", storage, hsApi: hs.api});\n const event1 = withTextBody(\"message 1\", createEvent(\"m.room.message\", \"$123\"));\n await logger.run(\"event1\", log => queue.enqueueEvent(event1.type, event1.content, null, log));\n assert.equal(queue.pendingEvents.length, 1);\n const sendRequest1 = hs.requests.send[0];\n // 2. receive remote echo, before /send has returned\n const remoteEcho = withTxnId(sendRequest1.arguments[2], event1);\n const txn = await storage.readWriteTxn([storage.storeNames.pendingEvents]);\n const removal = await logger.run(\"remote echo\", log => queue.removeRemoteEchos([remoteEcho], txn, log));\n await txn.complete();\n assert.equal(removal.length, 1);\n queue.emitRemovals(removal);\n assert.equal(queue.pendingEvents.length, 0);\n // 3. now enqueue event 2\n const event2 = withTextBody(\"message 2\", createEvent(\"m.room.message\", \"$456\"));\n await logger.run(\"event2\", log => queue.enqueueEvent(event2.type, event2.content, null, log));\n // even though the first pending event has been removed by the remote echo,\n // the second should get the next index, as the send loop is still blocking on the first one\n assert.equal(Array.from(queue.pendingEvents)[0].queueIndex, 2);\n // 4. send for event 1 comes back\n sendRequest1.respond({event_id: event1.event_id});\n // 5. now expect second send request for event 2\n const sendRequest2 = await poll(() => hs.requests.send[1]);\n sendRequest2.respond({event_id: event2.event_id});\n await poll(() => !queue._isSending);\n },\n \"redaction of pending event that hasn't started sending yet aborts it\": async assert => {\n const queue = new SendQueue({\n roomId: \"!abc\",\n storage: await createMockStorage(),\n hsApi: new MockHomeServer().api\n });\n // first, enqueue a message that will be attempted to send, but we don't respond\n await queue.enqueueEvent(\"m.room.message\", {body: \"hello!\"}, null, new NullLogItem());\n\n const observer = new ListObserver();\n queue.pendingEvents.subscribe(observer);\n await queue.enqueueEvent(\"m.room.message\", {body: \"...world\"}, null, new NullLogItem());\n let txnId;\n {\n const {type, index, value} = await observer.next();\n assert.equal(type, \"add\");\n assert.equal(index, 1);\n assert.equal(typeof value.txnId, \"string\");\n txnId = value.txnId;\n }\n await queue.enqueueRedaction(txnId, null, new NullLogItem());\n {\n const {type, value, index} = await observer.next();\n assert.equal(type, \"remove\");\n assert.equal(index, 1);\n assert.equal(txnId, value.txnId);\n }\n },\n \"duplicate redaction gets dropped\": async assert => {\n const queue = new SendQueue({\n roomId: \"!abc\",\n storage: await createMockStorage(),\n hsApi: new MockHomeServer().api\n });\n assert.equal(queue.pendingEvents.length, 0);\n await queue.enqueueRedaction(\"!event\", null, new NullLogItem());\n assert.equal(queue.pendingEvents.length, 1);\n await queue.enqueueRedaction(\"!event\", null, new NullLogItem());\n assert.equal(queue.pendingEvents.length, 1);\n },\n \"duplicate reaction gets dropped\": async assert => {\n const queue = new SendQueue({\n roomId: \"!abc\",\n storage: await createMockStorage(),\n hsApi: new MockHomeServer().api\n });\n assert.equal(queue.pendingEvents.length, 0);\n await queue.enqueueEvent(\"m.reaction\", createAnnotation(\"!target\", \"🚀\"), null, new NullLogItem());\n assert.equal(queue.pendingEvents.length, 1);\n await queue.enqueueEvent(\"m.reaction\", createAnnotation(\"!target\", \"👋\"), null, new NullLogItem());\n assert.equal(queue.pendingEvents.length, 2);\n await queue.enqueueEvent(\"m.reaction\", createAnnotation(\"!target\", \"🚀\"), null, new NullLogItem());\n assert.equal(queue.pendingEvents.length, 2);\n },\n \n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {encryptAttachment} from \"../e2ee/attachment.js\";\n\nexport class AttachmentUpload {\n constructor({filename, blob, platform}) {\n this._filename = filename;\n // need to keep around for local preview while uploading\n this._unencryptedBlob = blob;\n this._transferredBlob = this._unencryptedBlob;\n this._platform = platform;\n this._mxcUrl = null;\n this._encryptionInfo = null;\n this._uploadRequest = null;\n this._aborted = false;\n this._error = null;\n this._sentBytes = 0;\n }\n\n /** important to call after encrypt() if encryption is needed */\n get size() {\n return this._transferredBlob.size;\n }\n\n get sentBytes() {\n return this._sentBytes;\n }\n\n abort() {\n this._uploadRequest?.abort();\n }\n\n get localPreview() {\n return this._unencryptedBlob;\n }\n\n /** @internal */\n async encrypt() {\n if (this._encryptionInfo) {\n throw new Error(\"already encrypted\");\n }\n const {info, blob} = await encryptAttachment(this._platform, this._transferredBlob);\n this._transferredBlob = blob;\n this._encryptionInfo = info;\n }\n\n /** @internal */\n async upload(hsApi, progressCallback, log) {\n this._uploadRequest = hsApi.uploadAttachment(this._transferredBlob, this._filename, {\n uploadProgress: sentBytes => {\n this._sentBytes = sentBytes;\n progressCallback();\n },\n log\n });\n const {content_uri} = await this._uploadRequest.response();\n this._mxcUrl = content_uri;\n }\n\n /** @internal */\n applyToContent(urlPath, content) {\n if (!this._mxcUrl) {\n throw new Error(\"upload has not finished\");\n }\n let prefix = urlPath.substr(0, urlPath.lastIndexOf(\"url\"));\n setPath(`${prefix}info.size`, content, this._transferredBlob.size);\n setPath(`${prefix}info.mimetype`, content, this._unencryptedBlob.mimeType);\n if (this._encryptionInfo) {\n setPath(`${prefix}file`, content, Object.assign(this._encryptionInfo, {\n mimetype: this._unencryptedBlob.mimeType,\n url: this._mxcUrl\n }));\n } else {\n setPath(`${prefix}url`, content, this._mxcUrl);\n }\n }\n\n dispose() {\n this._unencryptedBlob.dispose();\n this._transferredBlob.dispose();\n }\n}\n\nfunction setPath(path, content, value) {\n const parts = path.split(\".\");\n let obj = content;\n for (let i = 0; i < (parts.length - 1); i += 1) {\n const key = parts[i];\n if (!obj[key]) {\n obj[key] = {};\n }\n obj = obj[key];\n }\n const propKey = parts[parts.length - 1];\n obj[propKey] = value;\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseRoom} from \"./BaseRoom.js\";\nimport {SyncWriter} from \"./timeline/persistence/SyncWriter.js\";\nimport {MemberWriter} from \"./timeline/persistence/MemberWriter.js\";\nimport {RelationWriter} from \"./timeline/persistence/RelationWriter.js\";\nimport {SendQueue} from \"./sending/SendQueue.js\";\nimport {WrappedError} from \"../error.js\"\nimport {Heroes} from \"./members/Heroes.js\";\nimport {AttachmentUpload} from \"./AttachmentUpload.js\";\nimport {DecryptionSource} from \"../e2ee/common.js\";\nimport {PowerLevels, EVENT_TYPE as POWERLEVELS_EVENT_TYPE } from \"./PowerLevels.js\";\n\nconst EVENT_ENCRYPTED_TYPE = \"m.room.encrypted\";\n\nexport class Room extends BaseRoom {\n constructor(options) {\n super(options);\n // TODO: pass pendingEvents to start like pendingOperations?\n const {pendingEvents} = options;\n const relationWriter = new RelationWriter({\n roomId: this.id,\n fragmentIdComparer: this._fragmentIdComparer,\n ownUserId: this._user.id\n });\n this._syncWriter = new SyncWriter({\n roomId: this.id,\n fragmentIdComparer: this._fragmentIdComparer,\n relationWriter,\n memberWriter: new MemberWriter(this.id)\n });\n this._sendQueue = new SendQueue({roomId: this.id, storage: this._storage, hsApi: this._hsApi, pendingEvents});\n }\n\n _setEncryption(roomEncryption) {\n if (super._setEncryption(roomEncryption)) {\n this._sendQueue.enableEncryption(this._roomEncryption);\n return true;\n }\n return false;\n }\n\n async prepareSync(roomResponse, membership, newKeys, txn, log) {\n log.set(\"id\", this.id);\n if (newKeys) {\n log.set(\"newKeys\", newKeys.length);\n }\n let summaryChanges = this._summary.data.applySyncResponse(roomResponse, membership, this._user.id);\n let roomEncryption = this._roomEncryption;\n // encryption is enabled in this sync\n if (!roomEncryption && summaryChanges.encryption) {\n log.set(\"enableEncryption\", true);\n roomEncryption = this._createRoomEncryption(this, summaryChanges.encryption);\n }\n\n let retryEntries;\n let decryptPreparation;\n if (roomEncryption) {\n let eventsToDecrypt = roomResponse?.timeline?.events || [];\n // when new keys arrive, also see if any older events can now be retried to decrypt\n if (newKeys) {\n // TODO: if a key is considered by roomEncryption.prepareDecryptAll to use for decryption,\n // key.eventIds will be set. We could somehow try to reuse that work, but retrying also needs\n // to happen if a key is not needed to decrypt this sync or there are indeed no encrypted messages\n // in this sync at all.\n retryEntries = await this._getSyncRetryDecryptEntries(newKeys, roomEncryption, txn);\n if (retryEntries.length) {\n log.set(\"retry\", retryEntries.length);\n eventsToDecrypt = eventsToDecrypt.concat(retryEntries.map(entry => entry.event));\n }\n }\n eventsToDecrypt = eventsToDecrypt.filter(event => {\n return event?.type === EVENT_ENCRYPTED_TYPE;\n });\n if (eventsToDecrypt.length) {\n decryptPreparation = await roomEncryption.prepareDecryptAll(\n eventsToDecrypt, newKeys, DecryptionSource.Sync, txn);\n }\n }\n\n return {\n roomEncryption,\n summaryChanges,\n decryptPreparation,\n decryptChanges: null,\n retryEntries\n };\n }\n\n async afterPrepareSync(preparation, parentLog) {\n if (preparation.decryptPreparation) {\n await parentLog.wrap(\"decrypt\", async log => {\n log.set(\"id\", this.id);\n preparation.decryptChanges = await preparation.decryptPreparation.decrypt();\n preparation.decryptPreparation = null;\n }, parentLog.level.Detail);\n }\n }\n\n /** @package */\n async writeSync(roomResponse, isInitialSync, {summaryChanges, decryptChanges, roomEncryption, retryEntries}, txn, log) {\n log.set(\"id\", this.id);\n const isRejoin = summaryChanges.isNewJoin(this._summary.data);\n if (isRejoin) {\n // remove all room state before calling syncWriter,\n // so no old state sticks around\n txn.roomState.removeAllForRoom(this.id);\n txn.roomMembers.removeAllForRoom(this.id);\n }\n const {entries: newEntries, updatedEntries, newLiveKey, memberChanges} =\n await log.wrap(\"syncWriter\", log => this._syncWriter.writeSync(\n roomResponse, isRejoin, summaryChanges.hasFetchedMembers, txn, log), log.level.Detail);\n if (decryptChanges) {\n const decryption = await log.wrap(\"decryptChanges\", log => decryptChanges.write(txn, log));\n log.set(\"decryptionResults\", decryption.results.size);\n log.set(\"decryptionErrors\", decryption.errors.size);\n if (this._isTimelineOpen) {\n await decryption.verifySenders(txn);\n }\n decryption.applyToEntries(newEntries);\n if (retryEntries?.length) {\n decryption.applyToEntries(retryEntries);\n updatedEntries.push(...retryEntries);\n }\n }\n log.set(\"newEntries\", newEntries.length);\n log.set(\"updatedEntries\", updatedEntries.length);\n let encryptionChanges;\n // pass member changes to device tracker\n if (roomEncryption) {\n encryptionChanges = await roomEncryption.writeSync(roomResponse, memberChanges, txn, log);\n log.set(\"shouldFlushKeyShares\", encryptionChanges.shouldFlush);\n }\n const allEntries = newEntries.concat(updatedEntries);\n // also apply (decrypted) timeline entries to the summary changes\n summaryChanges = summaryChanges.applyTimelineEntries(\n allEntries, isInitialSync, !this._isTimelineOpen, this._user.id);\n \n // if we've have left the room, remove the summary\n if (summaryChanges.membership !== \"join\") {\n txn.roomSummary.remove(this.id);\n } else {\n // write summary changes, and unset if nothing was actually changed\n summaryChanges = this._summary.writeData(summaryChanges, txn);\n }\n if (summaryChanges) {\n log.set(\"summaryChanges\", summaryChanges.changedKeys(this._summary.data));\n }\n // fetch new members while we have txn open,\n // but don't make any in-memory changes yet\n let heroChanges;\n // if any hero changes their display name, the summary in the room response\n // is also updated, which will trigger a RoomSummary update\n // and make summaryChanges non-falsy here\n if (summaryChanges?.needsHeroes) {\n // room name disappeared, open heroes\n if (!this._heroes) {\n this._heroes = new Heroes(this._roomId);\n }\n heroChanges = await this._heroes.calculateChanges(summaryChanges.heroes, memberChanges, txn);\n }\n let removedPendingEvents;\n if (Array.isArray(roomResponse.timeline?.events)) {\n removedPendingEvents = await this._sendQueue.removeRemoteEchos(roomResponse.timeline.events, txn, log);\n }\n const powerLevelsEvent = this._getPowerLevelsEvent(roomResponse);\n return {\n summaryChanges,\n roomEncryption,\n newEntries,\n updatedEntries,\n newLiveKey,\n removedPendingEvents,\n memberChanges,\n heroChanges,\n powerLevelsEvent,\n encryptionChanges,\n };\n }\n\n /**\n * @package\n * Called with the changes returned from `writeSync` to apply them and emit changes.\n * No storage or network operations should be done here.\n */\n afterSync(changes, log) {\n const {\n summaryChanges, newEntries, updatedEntries, newLiveKey,\n removedPendingEvents, memberChanges, powerLevelsEvent,\n heroChanges, roomEncryption, encryptionChanges\n } = changes;\n log.set(\"id\", this.id);\n this._syncWriter.afterSync(newLiveKey);\n this._setEncryption(roomEncryption);\n if (this._roomEncryption) {\n this._roomEncryption.afterSync(encryptionChanges);\n }\n if (memberChanges.size) {\n if (this._changedMembersDuringSync) {\n for (const [userId, memberChange] of memberChanges.entries()) {\n this._changedMembersDuringSync.set(userId, memberChange.member);\n }\n }\n if (this._memberList) {\n this._memberList.afterSync(memberChanges);\n }\n if (this._observedMembers) {\n this._updateObservedMembers(memberChanges);\n }\n if (this._timeline) {\n for (const [userId, memberChange] of memberChanges.entries()) {\n if (userId === this._user.id) {\n this._timeline.updateOwnMember(memberChange.member);\n break;\n }\n }\n }\n }\n let emitChange = false;\n if (summaryChanges) {\n this._summary.applyChanges(summaryChanges);\n if (!this._summary.data.needsHeroes) {\n this._heroes = null;\n }\n emitChange = true;\n }\n if (this._heroes && heroChanges) {\n const oldName = this.name;\n this._heroes.applyChanges(heroChanges, this._summary.data, log);\n if (oldName !== this.name) {\n emitChange = true;\n }\n }\n if (powerLevelsEvent) {\n this._updatePowerLevels(powerLevelsEvent);\n }\n if (emitChange) {\n this._emitUpdate();\n }\n if (this._timeline) {\n // these should not be added if not already there\n this._timeline.replaceEntries(updatedEntries);\n this._timeline.addEntries(newEntries);\n }\n if (this._observedEvents) {\n this._observedEvents.updateEvents(updatedEntries);\n this._observedEvents.updateEvents(newEntries);\n }\n if (removedPendingEvents) {\n this._sendQueue.emitRemovals(removedPendingEvents);\n }\n }\n\n _updateObservedMembers(memberChanges) {\n for (const [userId, memberChange] of memberChanges) {\n const observableMember = this._observedMembers.get(userId);\n if (observableMember) {\n observableMember.set(memberChange.member);\n }\n }\n }\n\n _getPowerLevelsEvent(roomResponse) {\n const isPowerlevelEvent = event => event.state_key === \"\" && event.type === POWERLEVELS_EVENT_TYPE;\n const powerLevelEvent = roomResponse.timeline?.events.find(isPowerlevelEvent) ?? roomResponse.state?.events.find(isPowerlevelEvent);\n return powerLevelEvent;\n }\n\n _updatePowerLevels(powerLevelEvent) {\n if (this._powerLevels) {\n const newPowerLevels = new PowerLevels({\n powerLevelEvent,\n ownUserId: this._user.id,\n membership: this.membership,\n });\n this._powerLevels.set(newPowerLevels);\n }\n }\n\n needsAfterSyncCompleted({encryptionChanges}) {\n return encryptionChanges?.shouldFlush;\n }\n\n /**\n * Only called if the result of writeSync had `needsAfterSyncCompleted` set.\n * Can be used to do longer running operations that resulted from the last sync,\n * like network operations.\n */\n async afterSyncCompleted(changes, log) {\n log.set(\"id\", this.id);\n if (this._roomEncryption) {\n await this._roomEncryption.flushPendingRoomKeyShares(this._hsApi, null, log);\n }\n }\n\n /** @package */\n start(pendingOperations, parentLog) {\n if (this._roomEncryption) {\n const roomKeyShares = pendingOperations?.get(\"share_room_key\");\n if (roomKeyShares) {\n // if we got interrupted last time sending keys to newly joined members\n parentLog.wrapDetached(\"flush room keys\", log => {\n log.set(\"id\", this.id);\n return this._roomEncryption.flushPendingRoomKeyShares(this._hsApi, roomKeyShares, log);\n });\n }\n }\n \n this._sendQueue.resumeSending(parentLog);\n }\n\n /** @package */\n async load(summary, txn, log) {\n try {\n await super.load(summary, txn, log);\n await this._syncWriter.load(txn, log);\n } catch (err) {\n throw new WrappedError(`Could not load room ${this._roomId}`, err);\n }\n }\n\n async _writeGapFill(gapChunk, txn, log) {\n const removedPendingEvents = await this._sendQueue.removeRemoteEchos(gapChunk, txn, log);\n return removedPendingEvents;\n }\n\n _applyGapFill(removedPendingEvents) {\n this._sendQueue.emitRemovals(removedPendingEvents);\n }\n\n /** @public */\n sendEvent(eventType, content, attachments, log = null) {\n return this._platform.logger.wrapOrRun(log, \"send\", log => {\n log.set(\"id\", this.id);\n return this._sendQueue.enqueueEvent(eventType, content, attachments, log);\n });\n }\n\n /** @public */\n sendRedaction(eventIdOrTxnId, reason, log = null) {\n return this._platform.logger.wrapOrRun(log, \"redact\", log => {\n log.set(\"id\", this.id);\n return this._sendQueue.enqueueRedaction(eventIdOrTxnId, reason, log);\n });\n }\n\n /** @public */\n async ensureMessageKeyIsShared(log = null) {\n if (!this._roomEncryption) {\n return;\n }\n return this._platform.logger.wrapOrRun(log, \"ensureMessageKeyIsShared\", log => {\n log.set(\"id\", this.id);\n return this._roomEncryption.ensureMessageKeyIsShared(this._hsApi, log);\n });\n }\n\n get avatarColorId() {\n return this._heroes?.roomAvatarColorId || this._roomId;\n }\n\n get isUnread() {\n return this._summary.data.isUnread;\n }\n\n get notificationCount() {\n return this._summary.data.notificationCount;\n }\n \n get highlightCount() {\n return this._summary.data.highlightCount;\n }\n\n get isTrackingMembers() {\n return this._summary.data.isTrackingMembers;\n }\n\n async _getLastEventId() {\n const lastKey = this._syncWriter.lastMessageKey;\n if (lastKey) {\n const txn = await this._storage.readTxn([\n this._storage.storeNames.timelineEvents,\n ]);\n const eventEntry = await txn.timelineEvents.get(this._roomId, lastKey);\n return eventEntry?.event?.event_id;\n }\n }\n\n async clearUnread(log = null) {\n if (this.isUnread || this.notificationCount) {\n return await this._platform.logger.wrapOrRun(log, \"clearUnread\", async log => {\n log.set(\"id\", this.id);\n const txn = await this._storage.readWriteTxn([\n this._storage.storeNames.roomSummary,\n ]);\n let data;\n try {\n data = this._summary.writeClearUnread(txn);\n } catch (err) {\n txn.abort();\n throw err;\n }\n await txn.complete();\n this._summary.applyChanges(data);\n this._emitUpdate();\n \n try {\n const lastEventId = await this._getLastEventId();\n if (lastEventId) {\n await this._hsApi.receipt(this._roomId, \"m.read\", lastEventId);\n }\n } catch (err) {\n // ignore ConnectionError\n if (err.name !== \"ConnectionError\") {\n throw err;\n }\n }\n });\n }\n }\n\n leave(log = null) {\n return this._platform.logger.wrapOrRun(log, \"leave room\", async log => {\n log.set(\"id\", this.id);\n await this._hsApi.leave(this.id, {log}).response();\n });\n }\n\n /* called by BaseRoom to pass pendingEvents when opening the timeline */\n _getPendingEvents() {\n return this._sendQueue.pendingEvents;\n }\n\n /** @package */\n writeIsTrackingMembers(value, txn) {\n return this._summary.writeIsTrackingMembers(value, txn);\n }\n\n /** @package */\n applyIsTrackingMembersChanges(changes) {\n this._summary.applyChanges(changes);\n }\n\n createAttachment(blob, filename) {\n return new AttachmentUpload({blob, filename, platform: this._platform});\n }\n\n dispose() {\n super.dispose();\n this._sendQueue.dispose();\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {reduceStateEvents} from \"./RoomSummary.js\";\nimport {BaseRoom} from \"./BaseRoom.js\";\nimport {RoomMember, EVENT_TYPE as MEMBER_EVENT_TYPE} from \"./members/RoomMember.js\";\n\nexport class ArchivedRoom extends BaseRoom {\n constructor(options) {\n super(options);\n // archived rooms are reference counted,\n // as they are not kept in memory when not needed\n this._releaseCallback = options.releaseCallback;\n this._forgetCallback = options.forgetCallback;\n this._retentionCount = 1;\n /**\n Some details from our own member event when being kicked or banned.\n We can't get this from the member store, because we don't store the reason field there.\n */\n this._kickDetails = null;\n this._kickedBy = null;\n }\n\n retain() {\n this._retentionCount += 1;\n }\n\n release() {\n this._retentionCount -= 1;\n if (this._retentionCount === 0) {\n this._releaseCallback();\n }\n }\n\n async _getKickAuthor(sender, txn) {\n const senderMember = await txn.roomMembers.get(this.id, sender);\n if (senderMember) {\n return new RoomMember(senderMember);\n } else {\n return RoomMember.fromUserId(this.id, sender, \"join\");\n }\n }\n \n async load(archivedRoomSummary, txn, log) {\n const {summary, kickDetails} = archivedRoomSummary;\n this._kickDetails = kickDetails;\n if (this._kickDetails) {\n this._kickedBy = await this._getKickAuthor(this._kickDetails.sender, txn);\n }\n return super.load(summary, txn, log);\n }\n\n /** @package */\n async writeSync(joinedSummaryData, roomResponse, membership, txn, log) {\n log.set(\"id\", this.id);\n if (membership === \"leave\") {\n const newKickDetails = findKickDetails(roomResponse, this._user.id);\n if (newKickDetails || joinedSummaryData) {\n const kickDetails = newKickDetails || this._kickDetails;\n let kickedBy;\n if (newKickDetails) {\n kickedBy = await this._getKickAuthor(newKickDetails.sender, txn);\n }\n const summaryData = joinedSummaryData || this._summary.data;\n txn.archivedRoomSummary.set({\n summary: summaryData.serialize(),\n kickDetails,\n });\n return {kickDetails, kickedBy, summaryData};\n }\n } else if (membership === \"join\") {\n txn.archivedRoomSummary.remove(this.id);\n }\n // always return object\n return {};\n }\n\n /**\n * @package\n * Called with the changes returned from `writeSync` to apply them and emit changes.\n * No storage or network operations should be done here.\n */\n afterSync({summaryData, kickDetails, kickedBy}, log) {\n log.set(\"id\", this.id);\n if (summaryData) {\n this._summary.applyChanges(summaryData);\n }\n if (kickDetails) {\n this._kickDetails = kickDetails;\n }\n if (kickedBy) {\n this._kickedBy = kickedBy;\n }\n this._emitUpdate();\n }\n\n get isKicked() {\n return this._kickDetails?.membership === \"leave\";\n }\n\n get isBanned() {\n return this._kickDetails?.membership === \"ban\";\n }\n\n get kickedBy() {\n return this._kickedBy;\n }\n\n get kickReason() {\n return this._kickDetails?.reason;\n }\n\n isArchived() {\n return true;\n }\n\n forget(log = null) {\n return this._platform.logger.wrapOrRun(log, \"forget room\", async log => {\n log.set(\"id\", this.id);\n await this._hsApi.forget(this.id, {log}).response();\n const storeNames = this._storage.storeNames;\n const txn = await this._storage.readWriteTxn([\n storeNames.roomState,\n storeNames.archivedRoomSummary,\n storeNames.roomMembers,\n storeNames.timelineEvents,\n storeNames.timelineFragments,\n storeNames.timelineRelations,\n storeNames.pendingEvents,\n storeNames.inboundGroupSessions,\n storeNames.groupSessionDecryptions,\n storeNames.operations,\n ]);\n\n txn.roomState.removeAllForRoom(this.id);\n txn.archivedRoomSummary.remove(this.id);\n txn.roomMembers.removeAllForRoom(this.id);\n txn.timelineEvents.removeAllForRoom(this.id);\n txn.timelineFragments.removeAllForRoom(this.id);\n txn.timelineRelations.removeAllForRoom(this.id);\n txn.pendingEvents.removeAllForRoom(this.id);\n txn.inboundGroupSessions.removeAllForRoom(this.id);\n txn.groupSessionDecryptions.removeAllForRoom(this.id);\n await txn.operations.removeAllForScope(this.id);\n\n await txn.complete();\n\n this._retentionCount = 0;\n this._releaseCallback();\n \n this._forgetCallback(this.id);\n });\n }\n\n join(log = null) {\n return this._platform.logger.wrapOrRun(log, \"rejoin archived room\", async log => {\n await this._hsApi.join(this.id, {log}).response();\n });\n }\n}\n\nfunction findKickDetails(roomResponse, ownUserId) {\n const kickEvent = reduceStateEvents(roomResponse, (kickEvent, event) => {\n if (event.type === MEMBER_EVENT_TYPE) {\n // did we get kicked?\n if (event.state_key === ownUserId && event.sender !== event.state_key) {\n kickEvent = event;\n }\n }\n return kickEvent;\n }, null);\n if (kickEvent) {\n return {\n // this is different from the room membership in the sync section, which can only be leave\n membership: kickEvent.content?.membership, // could be leave or ban\n reason: kickEvent.content?.reason,\n sender: kickEvent.sender,\n };\n }\n}\n\nexport function tests() {\n function createMemberEvent(sender, target, membership, reason) {\n return {\n sender,\n state_key: target,\n type: \"m.room.member\",\n content: { reason, membership }\n };\n }\n const bob = \"@bob:hs.tld\";\n const alice = \"@alice:hs.tld\";\n\n return {\n \"ban/kick sets kickDetails from state event\": assert => {\n const reason = \"Bye!\";\n const leaveEvent = createMemberEvent(alice, bob, \"ban\", reason);\n const kickDetails = findKickDetails({state: {events: [leaveEvent]}}, bob);\n assert.equal(kickDetails.membership, \"ban\");\n assert.equal(kickDetails.reason, reason);\n assert.equal(kickDetails.sender, alice);\n },\n \"ban/kick sets kickDetails from timeline state event, taking precedence over state\": assert => {\n const reason = \"Bye!\";\n const inviteEvent = createMemberEvent(alice, bob, \"invite\");\n const leaveEvent = createMemberEvent(alice, bob, \"ban\", reason);\n const kickDetails = findKickDetails({\n state: { events: [inviteEvent] },\n timeline: {events: [leaveEvent] }\n }, bob);\n assert.equal(kickDetails.membership, \"ban\");\n assert.equal(kickDetails.reason, reason);\n assert.equal(kickDetails.sender, alice);\n },\n \"leaving without being kicked doesn't produce kickDetails\": assert => {\n const leaveEvent = createMemberEvent(bob, bob, \"leave\");\n const kickDetails = findKickDetails({state: {events: [leaveEvent]}}, bob);\n assert.equal(kickDetails, null);\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport type {HomeServerApi} from \"./net/HomeServerApi\";\nimport type {ILogItem} from \"../logging/types\";\n\nexport async function loadProfiles(userIds: string[], hsApi: HomeServerApi, log: ILogItem): Promise<Profile[]> {\n const profiles = await Promise.all(userIds.map(async userId => {\n const response = await hsApi.profile(userId, {log}).response();\n return new Profile(userId, response.displayname as string, response.avatar_url as string);\n }));\n profiles.sort((a, b) => a.name.localeCompare(b.name));\n return profiles;\n}\n\nexport interface IProfile {\n get userId(): string;\n get displayName(): string | undefined;\n get avatarUrl(): string | undefined;\n get name(): string;\n}\n\nexport class Profile implements IProfile {\n constructor(\n public readonly userId: string,\n public readonly displayName: string,\n public readonly avatarUrl: string | undefined\n ) {}\n\n get name() { return this.displayName || this.userId; }\n}\n\nexport class UserIdProfile implements IProfile {\n constructor(public readonly userId: string) {}\n get displayName() { return undefined; }\n get name() { return this.userId; }\n get avatarUrl() { return undefined; }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {calculateRoomName} from \"./members/Heroes\";\nimport {createRoomEncryptionEvent} from \"../e2ee/common\";\nimport {MediaRepository} from \"../net/MediaRepository\";\nimport {EventEmitter} from \"../../utils/EventEmitter\";\nimport {AttachmentUpload} from \"./AttachmentUpload\";\nimport {loadProfiles, Profile, UserIdProfile} from \"../profile\";\nimport {RoomType} from \"./common\";\n\nimport type {HomeServerApi} from \"../net/HomeServerApi\";\nimport type {ILogItem} from \"../../logging/types\";\nimport type {Platform} from \"../../platform/web/Platform\";\nimport type {IBlobHandle} from \"../../platform/types/types\";\nimport type {User} from \"../User\";\nimport type {Storage} from \"../storage/idb/Storage\";\n\ntype CreateRoomPayload = {\n is_direct?: boolean;\n preset?: string;\n name?: string;\n topic?: string;\n invite?: string[];\n room_alias_name?: string;\n creation_content?: {\"m.federate\": boolean};\n initial_state: { type: string; state_key: string; content: Record<string, any> }[];\n power_level_content_override?: Record<string, any>;\n}\n\ntype ImageInfo = {\n w: number;\n h: number;\n mimetype: string;\n size: number;\n}\n\ntype Avatar = {\n info: ImageInfo;\n blob: IBlobHandle;\n name: string;\n}\n\ntype Options = {\n type: RoomType;\n isEncrypted?: boolean;\n isFederationDisabled?: boolean;\n name?: string;\n topic?: string;\n invites?: string[];\n avatar?: Avatar;\n alias?: string;\n powerLevelContentOverride?: Record<string, any>;\n}\n\nfunction defaultE2EEStatusForType(type: RoomType): boolean {\n switch (type) {\n case RoomType.DirectMessage:\n case RoomType.Private:\n return true;\n case RoomType.Public:\n return false;\n }\n}\n\nfunction presetForType(type: RoomType): string {\n switch (type) {\n case RoomType.DirectMessage:\n return \"trusted_private_chat\";\n case RoomType.Private:\n return \"private_chat\";\n case RoomType.Public:\n return \"public_chat\";\n }\n}\n\nexport class RoomBeingCreated extends EventEmitter<{change: never}> {\n private _roomId?: string;\n private profiles: Profile[] = [];\n\n public readonly isEncrypted: boolean;\n private _calculatedName: string;\n private _error?: Error;\n private _isCancelled = false;\n\n constructor(\n public readonly id: string,\n private readonly options: Options,\n private readonly updateCallback: (self: RoomBeingCreated, params: string | undefined) => void,\n public readonly mediaRepository: MediaRepository,\n public readonly platform: Platform,\n log: ILogItem\n ) {\n super();\n this.isEncrypted = options.isEncrypted === undefined ? defaultE2EEStatusForType(options.type) : options.isEncrypted;\n if (options.name) {\n this._calculatedName = options.name;\n } else {\n const summaryData = {\n joinCount: 1, // ourselves\n inviteCount: (options.invites?.length || 0)\n };\n const userIdProfiles = (options.invites || []).map(userId => new UserIdProfile(userId));\n this._calculatedName = calculateRoomName(userIdProfiles, summaryData, log);\n }\n }\n\n /** @internal */\n async create(hsApi: HomeServerApi, log: ILogItem): Promise<void> {\n try {\n let avatarEventContent;\n if (this.options.avatar) {\n const {avatar} = this.options;\n const attachment = new AttachmentUpload({filename: avatar.name, blob: avatar.blob, platform: this.platform});\n await attachment.upload(hsApi, () => {}, log);\n avatarEventContent = {\n info: avatar.info\n };\n attachment.applyToContent(\"url\", avatarEventContent);\n }\n const createOptions: CreateRoomPayload = {\n is_direct: this.options.type === RoomType.DirectMessage,\n preset: presetForType(this.options.type),\n initial_state: []\n };\n if (this.options.name) {\n createOptions.name = this.options.name;\n }\n if (this.options.topic) {\n createOptions.topic = this.options.topic;\n }\n if (this.options.invites) {\n createOptions.invite = this.options.invites;\n }\n if (this.options.alias) {\n createOptions.room_alias_name = this.options.alias;\n }\n if (this.options.isFederationDisabled === true) {\n createOptions.creation_content = {\n \"m.federate\": false\n };\n }\n if (this.options.powerLevelContentOverride) {\n createOptions.power_level_content_override = this.options.powerLevelContentOverride;\n }\n if (this.isEncrypted) {\n createOptions.initial_state.push(createRoomEncryptionEvent());\n }\n if (avatarEventContent) {\n createOptions.initial_state.push({\n type: \"m.room.avatar\",\n state_key: \"\",\n content: avatarEventContent\n });\n }\n const response = await hsApi.createRoom(createOptions, {log}).response();\n this._roomId = response[\"room_id\"];\n } catch (err) {\n this._error = err;\n }\n this.emitChange();\n }\n\n /** requests the profiles of the invitees if needed to give an accurate\n * estimated room name in case an explicit room name is not set.\n * The room is being created in the background whether this is called\n * or not, and this just gives a more accurate name while that request\n * is running. */\n /** @internal */\n async loadProfiles(hsApi: HomeServerApi, log: ILogItem): Promise<void> {\n try {\n // only load profiles if we need it for the room name and avatar\n if (!this.options.name && this.options.invites) {\n this.profiles = await loadProfiles(this.options.invites, hsApi, log);\n const summaryData = {\n joinCount: 1, // ourselves\n inviteCount: this.options.invites.length\n };\n this._calculatedName = calculateRoomName(this.profiles, summaryData, log);\n this.emitChange();\n }\n } catch (err) {} // swallow error, loading profiles is not essential\n }\n\n private emitChange(params?: string) {\n this.updateCallback(this, params);\n this.emit(\"change\");\n }\n\n get avatarColorId(): string { return this.options.invites?.[0] ?? this._roomId ?? this.id; }\n get avatarUrl(): string | undefined { return this.profiles?.[0]?.avatarUrl; }\n get avatarBlobUrl(): string | undefined { return this.options.avatar?.blob?.url; }\n get roomId(): string | undefined { return this._roomId; }\n get name() { return this._calculatedName; }\n get isBeingCreated(): boolean { return true; }\n get error(): Error | undefined { return this._error; }\n\n cancel() {\n if (!this._isCancelled) {\n this.dispose();\n this._isCancelled = true;\n this.emitChange(\"isCancelled\");\n }\n }\n // called from Session when updateCallback is invoked to remove it from the collection\n get isCancelled() { return this._isCancelled; }\n\n /** @internal */\n dispose() {\n if (this.options.avatar) {\n this.options.avatar.blob.dispose();\n }\n }\n\n async adjustDirectMessageMapIfNeeded(user: User, storage: Storage, hsApi: HomeServerApi, log: ILogItem): Promise<void> {\n if (!this.options.invites || this.options.type !== RoomType.DirectMessage) {\n return;\n }\n const userId = this.options.invites[0];\n const DM_MAP_TYPE = \"m.direct\";\n await log.wrap(\"set \" + DM_MAP_TYPE, async log => {\n try {\n const txn = await storage.readWriteTxn([storage.storeNames.accountData]);\n let mapEntry;\n try {\n mapEntry = await txn.accountData.get(DM_MAP_TYPE);\n if (!mapEntry) {\n mapEntry = {type: DM_MAP_TYPE, content: {}};\n }\n const map = mapEntry.content;\n let userRooms = map[userId];\n if (!userRooms) {\n map[userId] = userRooms = [];\n }\n // this is a new room id so no need to check if it's already there\n userRooms.push(this._roomId);\n txn.accountData.set(mapEntry);\n await txn.complete();\n } catch (err) {\n txn.abort();\n throw err;\n }\n await hsApi.setAccountData(user.id, DM_MAP_TYPE, mapEntry.content, {log}).response();\n } catch (err) {\n // we can't really do anything else but logging here\n log.catch(err);\n }\n });\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {EventEmitter} from \"../../utils/EventEmitter\";\nimport {SummaryData, processStateEvent} from \"./RoomSummary.js\";\nimport {Heroes} from \"./members/Heroes.js\";\nimport {MemberChange, RoomMember, EVENT_TYPE as MEMBER_EVENT_TYPE} from \"./members/RoomMember.js\";\n\nexport class Invite extends EventEmitter {\n constructor({roomId, user, hsApi, mediaRepository, emitCollectionRemove, emitCollectionUpdate, platform}) {\n super();\n this._roomId = roomId;\n this._user = user;\n this._hsApi = hsApi;\n this._emitCollectionRemove = emitCollectionRemove;\n this._emitCollectionUpdate = emitCollectionUpdate;\n this._mediaRepository = mediaRepository;\n this._platform = platform;\n this._inviteData = null;\n this._accepting = false;\n this._rejecting = false;\n this._accepted = false;\n this._rejected = false;\n }\n\n get isInvite() {\n return true;\n }\n\n get id() {\n return this._roomId;\n }\n\n get name() {\n return this._inviteData.name || this._inviteData.canonicalAlias;\n }\n\n get isDirectMessage() {\n return this._inviteData.isDirectMessage;\n }\n\n get avatarUrl() {\n return this._inviteData.avatarUrl;\n }\n\n /** @see BaseRoom.avatarColorId */\n get avatarColorId() {\n return this._inviteData.avatarColorId || this.id;\n }\n\n get timestamp() {\n return this._inviteData.timestamp;\n }\n\n get isEncrypted() {\n return this._inviteData.isEncrypted;\n }\n\n get inviter() {\n return this._inviter;\n }\n\n isDirectMessageForUserId(userId) {\n return this.isDirectMessage && this._inviter.userId === userId;\n }\n\n get isPublic() {\n return this._inviteData.joinRule === \"public\";\n }\n\n get canonicalAlias() {\n return this._inviteData.canonicalAlias;\n }\n\n async accept(log = null) {\n await this._platform.logger.wrapOrRun(log, \"acceptInvite\", async log => {\n this._accepting = true;\n this._emitChange(\"accepting\");\n await this._hsApi.join(this._roomId, {log}).response();\n });\n }\n\n async reject(log = null) {\n await this._platform.logger.wrapOrRun(log, \"rejectInvite\", async log => {\n this._rejecting = true;\n this._emitChange(\"rejecting\");\n await this._hsApi.leave(this._roomId, {log}).response();\n });\n }\n\n get accepting() {\n return this._accepting;\n }\n\n get accepted() {\n return this._accepted;\n }\n\n get rejecting() {\n return this._rejecting;\n }\n\n get rejected() {\n return this._rejected;\n }\n\n get mediaRepository() {\n return this._mediaRepository;\n }\n\n _emitChange(params) {\n this.emit(\"change\");\n this._emitCollectionUpdate(this, params);\n }\n\n load(inviteData, log) {\n log.set(\"id\", this.id);\n this._inviteData = inviteData;\n this._inviter = inviteData.inviter ? new RoomMember(inviteData.inviter) : null;\n }\n\n async writeSync(membership, roomResponse, txn, log) {\n if (membership === \"invite\") {\n log.set(\"id\", this.id);\n log.set(\"add\", true);\n const inviteState = roomResponse[\"invite_state\"]?.events;\n if (!Array.isArray(inviteState)) {\n return null;\n }\n const summaryData = this._createSummaryData(inviteState);\n let heroes;\n if (!summaryData.name && !summaryData.canonicalAlias) {\n heroes = await this._createHeroes(inviteState, log);\n }\n const myInvite = this._getMyInvite(inviteState);\n if (!myInvite) {\n return null;\n }\n const inviter = this._getInviter(myInvite, inviteState);\n const inviteData = this._createData(inviteState, myInvite, inviter, summaryData, heroes);\n txn.invites.set(inviteData);\n return {inviteData, inviter};\n } else {\n log.set(\"id\", this.id);\n log.set(\"membership\", membership);\n txn.invites.remove(this.id);\n return {removed: true, membership};\n }\n }\n\n afterSync(changes, log) {\n log.set(\"id\", this.id);\n if (changes) {\n if (changes.removed) {\n this._accepting = false;\n this._rejecting = false;\n if (changes.membership === \"join\") {\n this._accepted = true;\n } else {\n this._rejected = true;\n }\n this.emit(\"change\");\n } else {\n // no emit change, adding to the collection is done by sync\n this._inviteData = changes.inviteData;\n this._inviter = changes.inviter;\n }\n }\n }\n\n _createData(inviteState, myInvite, inviter, summaryData, heroes) {\n const name = heroes ? heroes.roomName : summaryData.name;\n const avatarUrl = heroes ? heroes.roomAvatarUrl : summaryData.avatarUrl;\n const avatarColorId = heroes?.roomAvatarColorId || this.id;\n return {\n roomId: this.id,\n isEncrypted: !!summaryData.encryption,\n isDirectMessage: summaryData.isDirectMessage,\n// type: \n name,\n avatarUrl,\n avatarColorId,\n canonicalAlias: summaryData.canonicalAlias,\n timestamp: this._platform.clock.now(),\n joinRule: this._getJoinRule(inviteState),\n inviter: inviter?.serialize(),\n };\n }\n\n _createSummaryData(inviteState) {\n return inviteState.reduce((data, event) => processStateEvent(data, event, this._user.id), new SummaryData(null, this.id));\n }\n\n async _createHeroes(inviteState, log) {\n const members = inviteState.filter(e => e.type === MEMBER_EVENT_TYPE);\n const otherMembers = members.filter(e => e.state_key !== this._user.id);\n const memberChanges = otherMembers.reduce((map, e) => {\n const member = RoomMember.fromMemberEvent(this.id, e);\n map.set(member.userId, new MemberChange(member, null));\n return map;\n }, new Map());\n const otherUserIds = otherMembers.map(e => e.state_key);\n const heroes = new Heroes(this.id);\n const changes = await heroes.calculateChanges(otherUserIds, memberChanges, null);\n // we don't get an actual lazy-loading m.heroes summary on invites,\n // so just count the members by hand\n const countSummary = new SummaryData(null, this.id);\n countSummary.joinCount = members.reduce((sum, e) => sum + (e.content?.membership === \"join\" ? 1 : 0), 0);\n countSummary.inviteCount = members.reduce((sum, e) => sum + (e.content?.membership === \"invite\" ? 1 : 0), 0);\n heroes.applyChanges(changes, countSummary, log);\n return heroes;\n }\n\n _getMyInvite(inviteState) {\n return inviteState.find(e => e.type === MEMBER_EVENT_TYPE && e.state_key === this._user.id);\n }\n\n _getInviter(myInvite, inviteState) {\n const inviterMemberEvent = inviteState.find(e => e.type === MEMBER_EVENT_TYPE && e.state_key === myInvite.sender);\n if (inviterMemberEvent) {\n return RoomMember.fromMemberEvent(this.id, inviterMemberEvent);\n }\n }\n\n _getJoinRule(inviteState) {\n const event = inviteState.find(e => e.type === \"m.room.join_rules\");\n if (event) {\n return event.content?.join_rule;\n }\n return null;\n }\n}\n\nimport {NullLogItem} from \"../../logging/NullLogger\";\nimport {Clock as MockClock} from \"../../mocks/Clock.js\";\nimport {default as roomInviteFixture} from \"../../fixtures/matrix/invites/room.js\";\nimport {default as dmInviteFixture} from \"../../fixtures/matrix/invites/dm.js\";\n\nexport function tests() {\n\n function createStorage() {\n const invitesMap = new Map();\n return {\n invitesMap,\n invites: {\n set(invite) {\n invitesMap.set(invite.roomId, invite);\n },\n remove(roomId) {\n invitesMap.delete(roomId);\n }\n }\n }\n }\n\n const roomId = \"!123:hs.tld\";\n const aliceAvatarUrl = \"mxc://hs.tld/def456\";\n const roomAvatarUrl = \"mxc://hs.tld/roomavatar\";\n\n return {\n \"invite for room has correct fields\": async assert => {\n const invite = new Invite({\n roomId,\n platform: {clock: new MockClock(1001)},\n user: {id: \"@bob:hs.tld\"}\n });\n const txn = createStorage();\n const changes = await invite.writeSync(\"invite\", roomInviteFixture, txn, new NullLogItem());\n assert.equal(txn.invitesMap.get(roomId).roomId, roomId);\n invite.afterSync(changes, new NullLogItem());\n assert.equal(invite.name, \"Invite example\");\n assert.equal(invite.avatarUrl, roomAvatarUrl);\n assert.equal(invite.isPublic, false);\n assert.equal(invite.timestamp, 1001);\n assert.equal(invite.isEncrypted, false);\n assert.equal(invite.isDirectMessage, false);\n assert(invite.inviter);\n assert.equal(invite.inviter.userId, \"@alice:hs.tld\");\n assert.equal(invite.inviter.displayName, \"Alice\");\n assert.equal(invite.inviter.avatarUrl, aliceAvatarUrl);\n },\n \"invite for encrypted DM has correct fields\": async assert => {\n const invite = new Invite({\n roomId,\n platform: {clock: new MockClock(1003)},\n user: {id: \"@bob:hs.tld\"}\n });\n const txn = createStorage();\n const changes = await invite.writeSync(\"invite\", dmInviteFixture, txn, new NullLogItem());\n assert.equal(txn.invitesMap.get(roomId).roomId, roomId);\n invite.afterSync(changes, new NullLogItem());\n assert.equal(invite.name, \"Alice\");\n assert.equal(invite.avatarUrl, aliceAvatarUrl);\n assert.equal(invite.timestamp, 1003);\n assert.equal(invite.isEncrypted, true);\n assert.equal(invite.isDirectMessage, true);\n assert(invite.inviter);\n assert.equal(invite.inviter.userId, \"@alice:hs.tld\");\n assert.equal(invite.inviter.displayName, \"Alice\");\n assert.equal(invite.inviter.avatarUrl, aliceAvatarUrl);\n },\n \"load persisted invite has correct fields\": async assert => {\n const writeInvite = new Invite({\n roomId,\n platform: {clock: new MockClock(1003)},\n user: {id: \"@bob:hs.tld\"}\n });\n const txn = createStorage();\n await writeInvite.writeSync(\"invite\", dmInviteFixture, txn, new NullLogItem());\n const invite = new Invite({roomId});\n invite.load(txn.invitesMap.get(roomId), new NullLogItem());\n assert.equal(invite.name, \"Alice\");\n assert.equal(invite.avatarUrl, aliceAvatarUrl);\n assert.equal(invite.timestamp, 1003);\n assert.equal(invite.isEncrypted, true);\n assert.equal(invite.isDirectMessage, true);\n assert(invite.inviter);\n assert.equal(invite.inviter.userId, \"@alice:hs.tld\");\n assert.equal(invite.inviter.displayName, \"Alice\");\n assert.equal(invite.inviter.avatarUrl, aliceAvatarUrl);\n },\n \"syncing join sets accepted\": async assert => {\n let changeEmitCount = 0;\n const invite = new Invite({\n roomId,\n platform: {clock: new MockClock(1003)},\n user: {id: \"@bob:hs.tld\"},\n });\n invite.on(\"change\", () => { changeEmitCount += 1; });\n const txn = createStorage();\n const changes = await invite.writeSync(\"invite\", dmInviteFixture, txn, new NullLogItem());\n assert.equal(txn.invitesMap.get(roomId).roomId, roomId);\n invite.afterSync(changes, new NullLogItem());\n const joinChanges = await invite.writeSync(\"join\", null, txn, new NullLogItem());\n assert.strictEqual(changeEmitCount, 0);\n invite.afterSync(joinChanges, new NullLogItem());\n assert.strictEqual(changeEmitCount, 1);\n assert.equal(txn.invitesMap.get(roomId), undefined);\n assert.equal(invite.rejected, false);\n assert.equal(invite.accepted, true);\n }\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport type {HomeServerApi} from \"../net/HomeServerApi.js\";\nimport type {ILogItem} from \"../../logging/types\";\n\nexport interface IPusherDescription {\n kind: \"http\" | \"email\" | \"null\";\n lang: string;\n device_display_name: string;\n app_display_name: string;\n app_id: string;\n pushkey: string;\n data: IPusherData;\n append?: boolean;\n profile_tag?: string;\n}\n\ninterface IPusherData {\n format?: string;\n url?: string;\n endpoint?: PushSubscriptionJSON[\"endpoint\"];\n keys?: PushSubscriptionJSON[\"keys\"];\n}\n\nexport class Pusher {\n private readonly _description: IPusherDescription;\n\n constructor(description: IPusherDescription) {\n this._description = description;\n }\n\n static httpPusher(host: string, appId: string, pushkey: string, data: IPusherData): Pusher {\n return new Pusher({\n kind: \"http\",\n append: true, // as pushkeys are shared between multiple users on one origin\n data: Object.assign({}, data, {url: host + \"/_matrix/push/v1/notify\"}),\n pushkey,\n app_id: appId,\n app_display_name: \"Hydrogen\",\n device_display_name: \"Hydrogen\",\n lang: \"en\"\n });\n }\n\n static createDefaultPayload(sessionId: string): {session_id: string} {\n return {session_id: sessionId};\n }\n\n async enable(hsApi: HomeServerApi, log: ILogItem): Promise<void> {\n try {\n log.set(\"endpoint\", new URL(this._description.data.endpoint!).host);\n } catch {\n log.set(\"endpoint\", null);\n }\n await hsApi.setPusher(this._description, {log}).response();\n }\n\n async disable(hsApi: HomeServerApi, log: ILogItem): Promise<void> {\n const deleteDescription = Object.assign({}, this._description, {kind: null});\n await hsApi.setPusher(deleteDescription, {log}).response();\n }\n\n serialize(): IPusherDescription {\n return this._description;\n }\n\n equals(pusher): boolean {\n if (this._description.app_id !== pusher._description.app_id) {\n return false;\n }\n if (this._description.pushkey !== pusher._description.pushkey) {\n return false;\n }\n return JSON.stringify(this._description.data) === JSON.stringify(pusher._description.data);\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport function groupBy<K, V>(array: V[], groupFn: (V) => K): Map<K, V[]> {\n return groupByWithCreator<K, V, V[]>(array, groupFn,\n () => {return [];},\n (array, value) => array.push(value)\n );\n}\n\nexport function groupByWithCreator<K, V, C>(array: V[], groupFn: (V) => K, createCollectionFn: () => C, addCollectionFn: (C, V) => void): Map<K, C> {\n return array.reduce((map, value) => {\n const key = groupFn(value);\n let collection = map.get(key);\n if (!collection) {\n collection = createCollectionFn();\n map.set(key, collection);\n }\n addCollectionFn(collection, value);\n return map;\n }, new Map<K, C>());\n}\n\nexport function countBy<V>(events: V[], mapper: (V) => string | number): { [key: string]: number } {\n return events.reduce((counts, event) => {\n const mappedValue = mapper(event);\n if (!counts[mappedValue]) {\n counts[mappedValue] = 1;\n } else {\n counts[mappedValue] += 1;\n }\n return counts;\n }, {});\n}\n\nexport function tests() {\n return {\n countBy: assert => {\n const counts = countBy([{type: \"foo\"}, {type: \"bar\"}, {type: \"foo\"}], o => o.type);\n assert.equal(Object.keys(counts).length, 2);\n assert.equal(counts.foo, 2);\n assert.equal(counts.bar, 1);\n }\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {OLM_ALGORITHM} from \"./e2ee/common.js\";\nimport {countBy, groupBy} from \"../utils/groupBy\";\n\nexport class DeviceMessageHandler {\n constructor({storage}) {\n this._storage = storage;\n this._olmDecryption = null;\n this._megolmDecryption = null;\n }\n\n enableEncryption({olmDecryption, megolmDecryption}) {\n this._olmDecryption = olmDecryption;\n this._megolmDecryption = megolmDecryption;\n }\n\n obtainSyncLock(toDeviceEvents) {\n return this._olmDecryption?.obtainDecryptionLock(toDeviceEvents);\n }\n\n async prepareSync(toDeviceEvents, lock, txn, log) {\n log.set(\"messageTypes\", countBy(toDeviceEvents, e => e.type));\n const encryptedEvents = toDeviceEvents.filter(e => e.type === \"m.room.encrypted\");\n if (!this._olmDecryption) {\n log.log(\"can't decrypt, encryption not enabled\", log.level.Warn);\n return;\n }\n // only know olm for now\n const olmEvents = encryptedEvents.filter(e => e.content?.algorithm === OLM_ALGORITHM);\n if (olmEvents.length) {\n const olmDecryptChanges = await this._olmDecryption.decryptAll(olmEvents, lock, txn);\n log.set(\"decryptedTypes\", countBy(olmDecryptChanges.results, r => r.event?.type));\n for (const err of olmDecryptChanges.errors) {\n log.child(\"decrypt_error\").catch(err);\n }\n const newRoomKeys = this._megolmDecryption.roomKeysFromDeviceMessages(olmDecryptChanges.results, log);\n return new SyncPreparation(olmDecryptChanges, newRoomKeys);\n }\n }\n\n /** check that prep is not undefined before calling this */\n async writeSync(prep, txn) {\n // write olm changes\n prep.olmDecryptChanges.write(txn);\n const didWriteValues = await Promise.all(prep.newRoomKeys.map(key => this._megolmDecryption.writeRoomKey(key, txn)));\n return didWriteValues.some(didWrite => !!didWrite);\n }\n}\n\nclass SyncPreparation {\n constructor(olmDecryptChanges, newRoomKeys) {\n this.olmDecryptChanges = olmDecryptChanges;\n this.newRoomKeys = newRoomKeys;\n this.newKeysByRoom = groupBy(newRoomKeys, r => r.roomId);\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport anotherjson from \"another-json\";\nimport {SESSION_E2EE_KEY_PREFIX, OLM_ALGORITHM, MEGOLM_ALGORITHM} from \"./common.js\";\n\n// use common prefix so it's easy to clear properties that are not e2ee related during session clear\nconst ACCOUNT_SESSION_KEY = SESSION_E2EE_KEY_PREFIX + \"olmAccount\";\nconst DEVICE_KEY_FLAG_SESSION_KEY = SESSION_E2EE_KEY_PREFIX + \"areDeviceKeysUploaded\";\nconst SERVER_OTK_COUNT_SESSION_KEY = SESSION_E2EE_KEY_PREFIX + \"serverOTKCount\";\n\nasync function initiallyStoreAccount(account, pickleKey, areDeviceKeysUploaded, serverOTKCount, storage) {\n const pickledAccount = account.pickle(pickleKey);\n const txn = await storage.readWriteTxn([\n storage.storeNames.session\n ]);\n try {\n // add will throw if the key already exists\n // we would not want to overwrite olmAccount here\n txn.session.add(ACCOUNT_SESSION_KEY, pickledAccount);\n txn.session.add(DEVICE_KEY_FLAG_SESSION_KEY, areDeviceKeysUploaded);\n txn.session.add(SERVER_OTK_COUNT_SESSION_KEY, serverOTKCount);\n } catch (err) {\n txn.abort();\n throw err;\n }\n await txn.complete();\n}\n\nexport class Account {\n static async load({olm, pickleKey, hsApi, userId, deviceId, olmWorker, txn}) {\n const pickledAccount = await txn.session.get(ACCOUNT_SESSION_KEY);\n if (pickledAccount) {\n const account = new olm.Account();\n const areDeviceKeysUploaded = await txn.session.get(DEVICE_KEY_FLAG_SESSION_KEY);\n account.unpickle(pickleKey, pickledAccount);\n const serverOTKCount = await txn.session.get(SERVER_OTK_COUNT_SESSION_KEY);\n return new Account({pickleKey, hsApi, account, userId,\n deviceId, areDeviceKeysUploaded, serverOTKCount, olm, olmWorker});\n }\n }\n\n static async adoptDehydratedDevice({olm, dehydratedDevice, pickleKey, hsApi, userId, olmWorker, storage}) {\n const account = dehydratedDevice.adoptUnpickledOlmAccount();\n const oneTimeKeys = JSON.parse(account.one_time_keys());\n // only one algorithm supported by olm atm, so hardcode its name\n const oneTimeKeysEntries = Object.entries(oneTimeKeys.curve25519);\n const serverOTKCount = oneTimeKeysEntries.length;\n const areDeviceKeysUploaded = true;\n await initiallyStoreAccount(account, pickleKey, areDeviceKeysUploaded, serverOTKCount, storage);\n return new Account({\n pickleKey, hsApi, account, userId,\n deviceId: dehydratedDevice.deviceId,\n areDeviceKeysUploaded, serverOTKCount, olm, olmWorker\n });\n }\n\n static async create({olm, pickleKey, hsApi, userId, deviceId, olmWorker, storage}) {\n const account = new olm.Account();\n if (olmWorker) {\n await olmWorker.createAccountAndOTKs(account, account.max_number_of_one_time_keys());\n } else {\n account.create();\n account.generate_one_time_keys(account.max_number_of_one_time_keys());\n }\n const areDeviceKeysUploaded = false;\n const serverOTKCount = 0;\n if (storage) {\n await initiallyStoreAccount(account, pickleKey, areDeviceKeysUploaded, serverOTKCount, storage);\n }\n return new Account({pickleKey, hsApi, account, userId,\n deviceId, areDeviceKeysUploaded, serverOTKCount, olm, olmWorker});\n }\n\n constructor({pickleKey, hsApi, account, userId, deviceId, areDeviceKeysUploaded, serverOTKCount, olm, olmWorker}) {\n this._olm = olm;\n this._pickleKey = pickleKey;\n this._hsApi = hsApi;\n this._account = account;\n this._userId = userId;\n this._deviceId = deviceId;\n this._areDeviceKeysUploaded = areDeviceKeysUploaded;\n this._serverOTKCount = serverOTKCount;\n this._olmWorker = olmWorker;\n this._identityKeys = JSON.parse(this._account.identity_keys());\n }\n\n get identityKeys() {\n return this._identityKeys;\n }\n\n setDeviceId(deviceId) {\n this._deviceId = deviceId;\n }\n\n async uploadKeys(storage, isDehydratedDevice, log) {\n const oneTimeKeys = JSON.parse(this._account.one_time_keys());\n // only one algorithm supported by olm atm, so hardcode its name\n const oneTimeKeysEntries = Object.entries(oneTimeKeys.curve25519);\n if (oneTimeKeysEntries.length || !this._areDeviceKeysUploaded) {\n const payload = {};\n if (!this._areDeviceKeysUploaded) {\n log.set(\"identity\", true);\n const identityKeys = JSON.parse(this._account.identity_keys());\n payload.device_keys = this._deviceKeysPayload(identityKeys);\n }\n if (oneTimeKeysEntries.length) {\n log.set(\"otks\", true);\n payload.one_time_keys = this._oneTimeKeysPayload(oneTimeKeysEntries);\n }\n const dehydratedDeviceId = isDehydratedDevice ? this._deviceId : undefined;\n const response = await this._hsApi.uploadKeys(dehydratedDeviceId, payload, {log}).response();\n this._serverOTKCount = response?.one_time_key_counts?.signed_curve25519;\n log.set(\"serverOTKCount\", this._serverOTKCount);\n // TODO: should we not modify this in the txn like we do elsewhere?\n // we'd have to pickle and unpickle the account to clone it though ...\n // and the upload has succeed at this point, so in-memory would be correct\n // but in-storage not if the txn fails. \n await this._updateSessionStorage(storage, sessionStore => {\n if (oneTimeKeysEntries.length) {\n this._account.mark_keys_as_published();\n sessionStore?.set(ACCOUNT_SESSION_KEY, this._account.pickle(this._pickleKey));\n sessionStore?.set(SERVER_OTK_COUNT_SESSION_KEY, this._serverOTKCount);\n }\n if (!this._areDeviceKeysUploaded) {\n this._areDeviceKeysUploaded = true;\n sessionStore?.set(DEVICE_KEY_FLAG_SESSION_KEY, this._areDeviceKeysUploaded);\n }\n });\n }\n }\n\n async generateOTKsIfNeeded(storage, log) {\n // We need to keep a pool of one time public keys on the server so that\n // other devices can start conversations with us. But we can only store\n // a finite number of private keys in the olm Account object.\n // To complicate things further there can be a delay between a device\n // claiming a public one time key from the server and it sending us a\n // message. We need to keep the corresponding private key locally until\n // we receive the message.\n // But that message might never arrive leaving us stuck with duff\n // private keys clogging up our local storage.\n // So we need some kind of engineering compromise to balance all of\n // these factors.\n \n // Check how many keys we can store in the Account object.\n const maxOTKs = this._account.max_number_of_one_time_keys();\n // Try to keep at most half that number on the server. This leaves the\n // rest of the slots free to hold keys that have been claimed from the\n // server but we haven't recevied a message for.\n // If we run out of slots when generating new keys then olm will\n // discard the oldest private keys first. This will eventually clean\n // out stale private keys that won't receive a message.\n const keyLimit = Math.floor(maxOTKs / 2);\n // does the server have insufficient OTKs?\n if (this._serverOTKCount < keyLimit) {\n const oneTimeKeys = JSON.parse(this._account.one_time_keys());\n const oneTimeKeysEntries = Object.entries(oneTimeKeys.curve25519);\n const unpublishedOTKCount = oneTimeKeysEntries.length;\n // we want to end up with maxOTKs / 2 key on the server,\n // so generate any on top of the remaining ones on the server and the unpublished ones\n // (we have generated before but haven't uploaded yet for some reason)\n // to get to that number.\n const newKeyCount = keyLimit - unpublishedOTKCount - this._serverOTKCount;\n if (newKeyCount > 0) {\n await log.wrap(\"generate otks\", log => {\n log.set(\"max\", maxOTKs);\n log.set(\"server\", this._serverOTKCount);\n log.set(\"unpublished\", unpublishedOTKCount);\n log.set(\"new\", newKeyCount);\n log.set(\"limit\", keyLimit);\n this._account.generate_one_time_keys(newKeyCount);\n this._updateSessionStorage(storage, sessionStore => {\n sessionStore.set(ACCOUNT_SESSION_KEY, this._account.pickle(this._pickleKey));\n });\n });\n }\n // even though we didn't generate any keys, we still have some unpublished ones that should be published\n return true;\n }\n return false;\n }\n\n createInboundOlmSession(senderKey, body) {\n const newSession = new this._olm.Session();\n try {\n newSession.create_inbound_from(this._account, senderKey, body);\n return newSession;\n } catch (err) {\n newSession.free();\n throw err;\n }\n }\n\n async createOutboundOlmSession(theirIdentityKey, theirOneTimeKey) {\n const newSession = new this._olm.Session();\n try {\n if (this._olmWorker) {\n await this._olmWorker.createOutboundOlmSession(this._account, newSession, theirIdentityKey, theirOneTimeKey);\n } else {\n newSession.create_outbound(this._account, theirIdentityKey, theirOneTimeKey);\n }\n return newSession;\n } catch (err) {\n newSession.free();\n throw err;\n }\n }\n\n writeRemoveOneTimeKey(session, txn) {\n // this is side-effecty and will have applied the change if the txn fails,\n // but don't want to clone the account for now\n // and it is not the worst thing to think we have used a OTK when\n // decrypting the message that actually used it threw for some reason.\n this._account.remove_one_time_keys(session);\n txn.session.set(ACCOUNT_SESSION_KEY, this._account.pickle(this._pickleKey));\n }\n\n writeSync(deviceOneTimeKeysCount, txn, log) {\n // we only upload signed_curve25519 otks\n const otkCount = deviceOneTimeKeysCount.signed_curve25519;\n if (Number.isSafeInteger(otkCount) && otkCount !== this._serverOTKCount) {\n txn.session.set(SERVER_OTK_COUNT_SESSION_KEY, otkCount);\n log.set(\"otkCount\", otkCount);\n return otkCount;\n }\n }\n\n afterSync(otkCount) {\n // could also be undefined\n if (Number.isSafeInteger(otkCount)) {\n this._serverOTKCount = otkCount;\n }\n }\n\n _deviceKeysPayload(identityKeys) {\n const obj = {\n user_id: this._userId,\n device_id: this._deviceId,\n algorithms: [OLM_ALGORITHM, MEGOLM_ALGORITHM],\n keys: {}\n };\n for (const [algorithm, pubKey] of Object.entries(identityKeys)) {\n obj.keys[`${algorithm}:${this._deviceId}`] = pubKey;\n }\n this.signObject(obj);\n return obj;\n }\n\n _oneTimeKeysPayload(oneTimeKeysEntries) {\n const obj = {};\n for (const [keyId, pubKey] of oneTimeKeysEntries) {\n const keyObj = {\n key: pubKey \n };\n this.signObject(keyObj);\n obj[`signed_curve25519:${keyId}`] = keyObj;\n }\n return obj;\n }\n\n async _updateSessionStorage(storage, callback) {\n if (storage) {\n const txn = await storage.readWriteTxn([\n storage.storeNames.session\n ]);\n try {\n await callback(txn.session);\n } catch (err) {\n txn.abort();\n throw err;\n }\n await txn.complete();\n } else {\n await callback(undefined);\n }\n }\n\n signObject(obj) {\n const sigs = obj.signatures || {};\n const unsigned = obj.unsigned;\n\n delete obj.signatures;\n delete obj.unsigned;\n\n sigs[this._userId] = sigs[this._userId] || {};\n sigs[this._userId][\"ed25519:\" + this._deviceId] = \n this._account.sign(anotherjson.stringify(obj));\n obj.signatures = sigs;\n if (unsigned !== undefined) {\n obj.unsigned = unsigned;\n }\n }\n\n pickleWithKey(key) {\n return this._account.pickle(key);\n }\n\n dispose() {\n this._account.free();\n this._account = undefined;\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport type {Platform} from \"../../platform/web/Platform.js\";\n\nexport type KeyDescriptionData = {\n algorithm: string;\n passphrase: {\n algorithm: string;\n iterations: number;\n salt: string;\n bits?: number;\n };\n mac: string;\n iv: string;\n}\n\nexport class KeyDescription {\n private readonly _id: string;\n private readonly _keyDescription: KeyDescriptionData;\n\n constructor(id: string, keyDescription: KeyDescriptionData) {\n this._id = id;\n this._keyDescription = keyDescription;\n }\n\n get id(): string {\n return this._id;\n }\n\n get passphraseParams(): KeyDescriptionData[\"passphrase\"] {\n return this._keyDescription?.passphrase;\n }\n\n get algorithm(): string {\n return this._keyDescription?.algorithm;\n }\n\n async isCompatible(key: Key, platform: Platform): Promise<boolean> {\n if (this.algorithm === \"m.secret_storage.v1.aes-hmac-sha2\") {\n const kd = this._keyDescription;\n if (kd.mac) {\n const otherMac = await calculateKeyMac(key.binaryKey, kd.iv, platform);\n return kd.mac === otherMac;\n } else if (kd.passphrase) {\n const kdOther = key.description._keyDescription;\n if (!kdOther.passphrase) {\n return false;\n }\n return kd.passphrase.algorithm === kdOther.passphrase.algorithm && \n kd.passphrase.iterations === kdOther.passphrase.iterations && \n kd.passphrase.salt === kdOther.passphrase.salt;\n }\n }\n return false;\n }\n}\n\nexport class Key {\n private readonly _keyDescription: KeyDescription;\n private readonly _binaryKey: Uint8Array;\n\n constructor(keyDescription: KeyDescription, binaryKey: Uint8Array) {\n this._keyDescription = keyDescription;\n this._binaryKey = binaryKey;\n }\n\n withDescription(description: KeyDescription): Key {\n return new Key(description, this._binaryKey);\n }\n\n get description(): KeyDescription {\n return this._keyDescription;\n }\n\n get id(): string {\n return this._keyDescription.id;\n }\n\n get binaryKey(): Uint8Array {\n return this._binaryKey;\n }\n\n get algorithm(): string {\n return this._keyDescription.algorithm;\n }\n}\n\nasync function calculateKeyMac(key: BufferSource, ivStr: string, platform: Platform): Promise<string> {\n const {crypto, encoding} = platform;\n const {utf8, base64} = encoding;\n const {derive, aes, hmac} = crypto;\n\n const iv = base64.decode(ivStr);\n\n // salt for HKDF, with 8 bytes of zeros\n const zerosalt = new Uint8Array(8);\n const ZERO_STR = \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\";\n \n const info = utf8.encode(\"\");\n const keybits = await derive.hkdf(key, zerosalt, info, \"SHA-256\", 512);\n const aesKey = keybits.slice(0, 32);\n const hmacKey = keybits.slice(32);\n const ciphertext = await aes.encryptCTR({key: aesKey, iv, data: utf8.encode(ZERO_STR)});\n const mac = await hmac.compute(hmacKey, ciphertext, \"SHA-256\");\n\n return base64.encode(mac);\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {Key} from \"./common\";\nimport type {KeyDescription} from \"./common\";\nimport type {Platform} from \"../../platform/web/Platform.js\";\n\nconst DEFAULT_ITERATIONS = 500000;\nconst DEFAULT_BITSIZE = 256;\n\n/**\n * @param {KeyDescription} keyDescription\n * @param {string} passphrase\n * @param {Platform} platform\n * @return {Key}\n */\nexport async function keyFromPassphrase(keyDescription: KeyDescription, passphrase: string, platform: Platform): Promise<Key> {\n const {passphraseParams} = keyDescription;\n if (!passphraseParams) {\n throw new Error(\"not a passphrase key\");\n }\n if (passphraseParams.algorithm !== \"m.pbkdf2\") {\n throw new Error(`Unsupported passphrase algorithm: ${passphraseParams.algorithm}`);\n }\n const {utf8} = platform.encoding;\n const keyBits = await platform.crypto.derive.pbkdf2(\n utf8.encode(passphrase),\n passphraseParams.iterations || DEFAULT_ITERATIONS,\n // salt is just a random string, not encoded in any way\n utf8.encode(passphraseParams.salt),\n \"SHA-512\",\n passphraseParams.bits || DEFAULT_BITSIZE);\n return new Key(keyDescription, keyBits);\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\nimport {Key} from \"./common\";\nimport {KeyDescription} from \"./common\";\nimport type {Platform} from \"../../platform/web/Platform.js\";\nimport type * as OlmNamespace from \"@matrix-org/olm\";\ntype Olm = typeof OlmNamespace;\n\nconst OLM_RECOVERY_KEY_PREFIX = [0x8B, 0x01] as const;\n\n/**\n * @param {Olm} olm\n * @param {KeyDescription} keyDescription\n * @param {string} recoveryKey\n * @return {Key}\n */\nexport function keyFromRecoveryKey(keyDescription: KeyDescription, recoveryKey: string, olm: Olm, platform: Platform): Key {\n const result = platform.encoding.base58.decode(recoveryKey.replace(/ /g, ''));\n\n let parity = 0;\n for (const b of result) {\n parity ^= b;\n }\n if (parity !== 0) {\n throw new Error(\"Incorrect parity\");\n }\n\n for (let i = 0; i < OLM_RECOVERY_KEY_PREFIX.length; ++i) {\n if (result[i] !== OLM_RECOVERY_KEY_PREFIX[i]) {\n throw new Error(\"Incorrect prefix\");\n }\n }\n\n if (\n result.length !==\n OLM_RECOVERY_KEY_PREFIX.length + olm.PRIVATE_KEY_LENGTH + 1\n ) {\n throw new Error(\"Incorrect length\");\n }\n\n const keyBits = Uint8Array.from(result.slice(\n OLM_RECOVERY_KEY_PREFIX.length,\n OLM_RECOVERY_KEY_PREFIX.length + olm.PRIVATE_KEY_LENGTH,\n ));\n\n return new Key(keyDescription, keyBits);\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {KeyDescription, Key} from \"./common\";\nimport {keyFromPassphrase} from \"./passphrase\";\nimport {keyFromRecoveryKey} from \"./recoveryKey\";\nimport {SESSION_E2EE_KEY_PREFIX} from \"../e2ee/common.js\";\nimport type {Storage} from \"../storage/idb/Storage\";\nimport type {Transaction} from \"../storage/idb/Transaction\";\nimport type {KeyDescriptionData} from \"./common\";\nimport type {Platform} from \"../../platform/web/Platform.js\";\nimport type * as OlmNamespace from \"@matrix-org/olm\"\n\ntype Olm = typeof OlmNamespace;\n\nconst SSSS_KEY = `${SESSION_E2EE_KEY_PREFIX}ssssKey`;\nconst BACKUPVERSION_KEY = `${SESSION_E2EE_KEY_PREFIX}keyBackupVersion`;\n\nexport enum KeyType {\n \"RecoveryKey\",\n \"Passphrase\"\n}\n\nasync function readDefaultKeyDescription(storage: Storage): Promise<KeyDescription | undefined> {\n const txn = await storage.readTxn([\n storage.storeNames.accountData\n ]);\n const defaultKeyEvent = await txn.accountData.get(\"m.secret_storage.default_key\");\n const id = defaultKeyEvent?.content?.key;\n if (!id) {\n return;\n }\n const keyAccountData = await txn.accountData.get(`m.secret_storage.key.${id}`);\n if (!keyAccountData) {\n return;\n }\n return new KeyDescription(id, keyAccountData.content as KeyDescriptionData);\n}\n\nexport async function writeKey(key: Key, keyBackupVersion: number, txn: Transaction): Promise<number | undefined> {\n const existingVersion: number | undefined = await txn.session.get(BACKUPVERSION_KEY);\n txn.session.set(BACKUPVERSION_KEY, keyBackupVersion);\n txn.session.set(SSSS_KEY, {id: key.id, binaryKey: key.binaryKey});\n return existingVersion;\n}\n\nexport async function readKey(txn: Transaction): Promise<Key | undefined> {\n const keyData = await txn.session.get(SSSS_KEY);\n if (!keyData) {\n return;\n }\n const keyAccountData = await txn.accountData.get(`m.secret_storage.key.${keyData.id}`);\n if (keyAccountData) {\n return new Key(new KeyDescription(keyData.id, keyAccountData.content as KeyDescriptionData), keyData.binaryKey);\n }\n}\n\n\nexport async function removeKey(txn: Transaction): Promise<void> {\n txn.session.remove(SSSS_KEY);\n}\n\nexport async function keyFromCredential(type: KeyType, credential: string, storage: Storage, platform: Platform, olm: Olm): Promise<Key> {\n const keyDescription = await readDefaultKeyDescription(storage);\n if (!keyDescription) {\n throw new Error(\"Could not find a default secret storage key in account data\");\n }\n return await keyFromCredentialAndDescription(type, credential, keyDescription, platform, olm);\n}\n\nexport async function keyFromCredentialAndDescription(type: KeyType, credential: string, keyDescription: KeyDescription, platform: Platform, olm: Olm): Promise<Key> {\n let key: Key;\n if (type === KeyType.Passphrase) {\n key = await keyFromPassphrase(keyDescription, credential, platform);\n } else if (type === KeyType.RecoveryKey) {\n key = keyFromRecoveryKey(keyDescription, credential, olm, platform);\n } else {\n throw new Error(`Invalid type: ${type}`);\n }\n return key;\n}\n\nexport async function keyFromDehydratedDeviceKey(key: Key, storage: Storage, platform: Platform): Promise<Key | undefined> {\n const keyDescription = await readDefaultKeyDescription(storage);\n if (await keyDescription?.isCompatible(key, platform)) {\n return key.withDescription(keyDescription!);\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nconst DEHYDRATION_LIBOLM_PICKLE_ALGORITHM = \"org.matrix.msc2697.v1.olm.libolm_pickle\"; \nimport {KeyDescription} from \"../ssss/common\";\nimport {keyFromCredentialAndDescription} from \"../ssss/index\";\n\nexport async function getDehydratedDevice(hsApi, olm, platform, log) {\n try {\n const response = await hsApi.getDehydratedDevice({log}).response();\n if (response.device_data.algorithm === DEHYDRATION_LIBOLM_PICKLE_ALGORITHM) {\n return new EncryptedDehydratedDevice(response, olm, platform);\n }\n } catch (err) {\n if (err.name !== \"HomeServerError\") {\n log.error = err;\n }\n return undefined;\n }\n}\n\nexport async function uploadAccountAsDehydratedDevice(account, hsApi, key, deviceDisplayName, log) {\n const response = await hsApi.createDehydratedDevice({\n device_data: {\n algorithm: DEHYDRATION_LIBOLM_PICKLE_ALGORITHM,\n account: account.pickleWithKey(key.binaryKey.slice()),\n passphrase: key.description?.passphraseParams || {},\n },\n initial_device_display_name: deviceDisplayName\n }).response();\n const deviceId = response.device_id;\n account.setDeviceId(deviceId);\n await account.uploadKeys(undefined, true, log);\n return deviceId;\n}\n\nclass EncryptedDehydratedDevice {\n constructor(dehydratedDevice, olm, platform) {\n this._dehydratedDevice = dehydratedDevice;\n this._olm = olm;\n this._platform = platform;\n }\n\n async decrypt(keyType, credential) {\n const keyDescription = new KeyDescription(\"dehydrated_device\", this._dehydratedDevice.device_data.passphrase);\n const key = await keyFromCredentialAndDescription(keyType, credential, keyDescription, this._platform, this._olm);\n const account = new this._olm.Account();\n try {\n const pickledAccount = this._dehydratedDevice.device_data.account;\n account.unpickle(key.binaryKey.slice(), pickledAccount);\n return new DehydratedDevice(this._dehydratedDevice, account, key);\n } catch (err) {\n account.free();\n if (err.message === \"OLM.BAD_ACCOUNT_KEY\") {\n return undefined;\n } else {\n throw err;\n }\n }\n }\n\n get deviceId() {\n return this._dehydratedDevice.device_id;\n }\n}\n\nclass DehydratedDevice {\n constructor(dehydratedDevice, account, key) {\n this._dehydratedDevice = dehydratedDevice;\n this._account = account;\n this._key = key;\n }\n\n async claim(hsApi, log) {\n try {\n const response = await hsApi.claimDehydratedDevice(this.deviceId, {log}).response();\n return response.success;\n } catch (err) {\n return false;\n }\n }\n\n // make it clear that ownership is transfered upon calling this\n adoptUnpickledOlmAccount() {\n const account = this._account;\n this._account = undefined;\n return account;\n }\n\n get deviceId() {\n return this._dehydratedDevice.device_id;\n }\n\n get key() {\n return this._key;\n }\n\n dispose() {\n this._account?.free();\n this._account = undefined;\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport interface ILock {\n release(): void;\n}\n\nexport class Lock implements ILock {\n private _promise?: Promise<void>;\n private _resolve?: (() => void);\n\n tryTake(): boolean {\n if (!this._promise) {\n this._promise = new Promise(resolve => {\n this._resolve = resolve;\n });\n return true;\n }\n return false;\n }\n\n async take(): Promise<void> {\n while(!this.tryTake()) {\n await this.released();\n }\n }\n\n get isTaken(): boolean {\n return !!this._promise;\n }\n\n release(): void {\n if (this._resolve) {\n this._promise = undefined;\n const resolve = this._resolve;\n this._resolve = undefined;\n resolve();\n }\n }\n\n released(): Promise<void> | undefined {\n return this._promise;\n }\n}\n\nexport class MultiLock implements ILock {\n\n constructor(public readonly locks: Lock[]) {\n }\n\n release(): void {\n for (const lock of this.locks) {\n lock.release();\n }\n }\n}\n\nexport function tests() {\n return {\n \"taking a lock twice returns false\": assert => {\n const lock = new Lock();\n assert.equal(lock.tryTake(), true);\n assert.equal(lock.isTaken, true);\n assert.equal(lock.tryTake(), false);\n },\n \"can take a released lock again\": assert => {\n const lock = new Lock();\n lock.tryTake();\n lock.release();\n assert.equal(lock.isTaken, false);\n assert.equal(lock.tryTake(), true);\n },\n \"2 waiting for lock, only first one gets it\": async assert => {\n const lock = new Lock();\n lock.tryTake();\n\n let first;\n lock.released()!.then(() => first = lock.tryTake());\n let second;\n lock.released()!.then(() => second = lock.tryTake());\n const promise = lock.released();\n lock.release();\n await promise;\n assert.strictEqual(first, true);\n assert.strictEqual(second, false);\n },\n \"await non-taken lock\": async assert => {\n const lock = new Lock();\n await lock.released();\n assert(true);\n }\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport type {OlmSessionEntry} from \"../../storage/idb/stores/OlmSessionStore\";\nimport type * as OlmNamespace from \"@matrix-org/olm\";\ntype Olm = typeof OlmNamespace;\n\nexport function createSessionEntry(olmSession: Olm.Session, senderKey: string, timestamp: number, pickleKey: string): OlmSessionEntry {\n return {\n session: olmSession.pickle(pickleKey),\n sessionId: olmSession.session_id(),\n senderKey,\n lastUsed: timestamp,\n };\n}\n\nexport class Session {\n public isModified: boolean;\n\n constructor(\n public readonly data: OlmSessionEntry,\n private readonly pickleKey: string,\n private readonly olm: Olm,\n public isNew: boolean = false\n ) {\n this.isModified = isNew;\n }\n\n static create(senderKey: string, olmSession: Olm.Session, olm: Olm, pickleKey: string, timestamp: number): Session {\n const data = createSessionEntry(olmSession, senderKey, timestamp, pickleKey);\n return new Session(data, pickleKey, olm, true);\n }\n\n get id(): string {\n return this.data.sessionId;\n }\n\n load(): Olm.Session {\n const session = new this.olm.Session();\n session.unpickle(this.pickleKey, this.data.session);\n return session;\n }\n\n unload(olmSession: Olm.Session): void {\n olmSession.free();\n }\n\n save(olmSession: Olm.Session): void {\n this.data.session = olmSession.pickle(this.pickleKey);\n this.isModified = true;\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n\n/**\n * @property {object} event the plaintext event (type and content property)\n * @property {string} senderCurve25519Key the curve25519 sender key of the olm event\n * @property {string} claimedEd25519Key The ed25519 fingerprint key retrieved from the decryption payload.\n * The sender of the olm event claims this is the ed25519 fingerprint key\n * that matches the curve25519 sender key.\n * The caller needs to check if this key does indeed match the senderKey\n * for a device with a valid signature returned from /keys/query,\n * see DeviceTracker\n */\n\nimport type {DeviceIdentity} from \"../storage/idb/stores/DeviceIdentityStore\";\n\ntype DecryptedEvent = {\n type?: string,\n content?: Record<string, any>\n}\n\nexport class DecryptionResult {\n private device?: DeviceIdentity;\n private roomTracked: boolean = true;\n\n constructor(\n public readonly event: DecryptedEvent,\n public readonly senderCurve25519Key: string,\n public readonly claimedEd25519Key: string\n ) {}\n\n setDevice(device: DeviceIdentity): void {\n this.device = device;\n }\n\n setRoomNotTrackedYet(): void {\n this.roomTracked = false;\n }\n\n get isVerified(): boolean {\n if (this.device) {\n const comesFromDevice = this.device.ed25519Key === this.claimedEd25519Key;\n return comesFromDevice;\n }\n return false;\n }\n\n get isUnverified(): boolean {\n if (this.device) {\n return !this.isVerified;\n } else if (this.isVerificationUnknown) {\n return false;\n } else {\n return true;\n }\n }\n\n get isVerificationUnknown(): boolean {\n // verification is unknown if we haven't yet fetched the devices for the room\n return !this.device && !this.roomTracked;\n }\n}\n","/*\nCopyright 2022 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport const enum OlmPayloadType {\n PreKey = 0,\n Normal = 1\n}\n\nexport type OlmMessage = {\n type?: OlmPayloadType,\n body?: string\n}\n\nexport type OlmEncryptedMessageContent = {\n algorithm?: \"m.olm.v1.curve25519-aes-sha2\"\n sender_key?: string,\n ciphertext?: {\n [deviceCurve25519Key: string]: OlmMessage\n }\n}\n\nexport type OlmEncryptedEvent = {\n type?: \"m.room.encrypted\",\n content?: OlmEncryptedMessageContent\n sender?: string\n}\n\nexport type OlmPayload = {\n type?: string;\n content?: Record<string, any>;\n sender?: string;\n recipient?: string;\n recipient_keys?: {ed25519?: string};\n keys?: {ed25519?: string};\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {DecryptionError} from \"../common.js\";\nimport {groupBy} from \"../../../utils/groupBy\";\nimport {MultiLock, ILock} from \"../../../utils/Lock\";\nimport {Session} from \"./Session\";\nimport {DecryptionResult} from \"../DecryptionResult\";\nimport {OlmPayloadType} from \"./types\";\n\nimport type {OlmMessage, OlmPayload} from \"./types\";\nimport type {Account} from \"../Account\";\nimport type {LockMap} from \"../../../utils/LockMap\";\nimport type {Transaction} from \"../../storage/idb/Transaction\";\nimport type {OlmEncryptedEvent} from \"./types\";\nimport type * as OlmNamespace from \"@matrix-org/olm\";\ntype Olm = typeof OlmNamespace;\n\nconst SESSION_LIMIT_PER_SENDER_KEY = 4;\n\ntype DecryptionResults = {\n results: DecryptionResult[],\n errors: DecryptionError[],\n senderKeyDecryption: SenderKeyDecryption\n};\n\ntype CreateAndDecryptResult = {\n session: Session,\n plaintext: string\n};\n\nfunction sortSessions(sessions: Session[]): void {\n sessions.sort((a, b) => {\n return b.data.lastUsed - a.data.lastUsed;\n });\n}\n\nexport class Decryption {\n constructor(\n private readonly account: Account,\n private readonly pickleKey: string,\n private readonly now: () => number,\n private readonly ownUserId: string,\n private readonly olm: Olm,\n private readonly senderKeyLock: LockMap<string>\n ) {}\n \n // we need to lock because both encryption and decryption can't be done in one txn,\n // so for them not to step on each other toes, we need to lock.\n // \n // the lock is release from 1 of 3 places, whichever comes first:\n // - decryptAll below fails (to release the lock as early as we can)\n // - DecryptionChanges.write succeeds\n // - Sync finishes the writeSync phase (or an error was thrown, in case we never get to DecryptionChanges.write) \n async obtainDecryptionLock(events: OlmEncryptedEvent[]): Promise<ILock> {\n const senderKeys = new Set<string>();\n for (const event of events) {\n const senderKey = event.content?.[\"sender_key\"];\n if (senderKey) {\n senderKeys.add(senderKey);\n }\n }\n // take a lock on all senderKeys so encryption or other calls to decryptAll (should not happen)\n // don't modify the sessions at the same time\n const locks = await Promise.all(Array.from(senderKeys).map(senderKey => {\n return this.senderKeyLock.takeLock(senderKey);\n }));\n return new MultiLock(locks);\n }\n\n // we need decryptAll because there is some parallelization we can do for decrypting different sender keys at once\n // but for the same sender key we need to do one by one\n // \n // also we want to store the room key, etc ... in the same txn as we remove the pending encrypted event\n // \n // so we need to decrypt events in a batch (so we can decide which ones can run in parallel and which one one by one)\n // and also can avoid side-effects before all can be stored this way\n // \n // doing it one by one would be possible, but we would lose the opportunity for parallelization\n // \n \n /**\n * It is importants the lock obtained from obtainDecryptionLock is for the same set of events as passed in here.\n * [decryptAll description]\n * @param {[type]} events\n * @return {Promise<DecryptionChanges>} [description]\n */\n async decryptAll(events: OlmEncryptedEvent[], lock: ILock, txn: Transaction): Promise<DecryptionChanges> {\n try {\n const eventsPerSenderKey = groupBy(events, (event: OlmEncryptedEvent) => event.content?.[\"sender_key\"]);\n const timestamp = this.now();\n // decrypt events for different sender keys in parallel\n const senderKeyOperations = await Promise.all(Array.from(eventsPerSenderKey.entries()).map(([senderKey, events]) => {\n return this._decryptAllForSenderKey(senderKey!, events, timestamp, txn);\n }));\n const results = senderKeyOperations.reduce((all, r) => all.concat(r.results), [] as DecryptionResult[]);\n const errors = senderKeyOperations.reduce((all, r) => all.concat(r.errors), [] as DecryptionError[]);\n const senderKeyDecryptions = senderKeyOperations.map(r => r.senderKeyDecryption);\n return new DecryptionChanges(senderKeyDecryptions, results, errors, this.account, lock);\n } catch (err) {\n // make sure the locks are release if something throws\n // otherwise they will be released in DecryptionChanges after having written\n // or after the writeSync phase in Sync\n lock.release();\n throw err;\n }\n }\n\n async _decryptAllForSenderKey(senderKey: string, events: OlmEncryptedEvent[], timestamp: number, readSessionsTxn: Transaction): Promise<DecryptionResults> {\n const sessions = await this._getSessions(senderKey, readSessionsTxn);\n const senderKeyDecryption = new SenderKeyDecryption(senderKey, sessions, timestamp);\n const results: DecryptionResult[] = [];\n const errors: DecryptionError[] = [];\n // events for a single senderKey need to be decrypted one by one\n for (const event of events) {\n try {\n const result = this._decryptForSenderKey(senderKeyDecryption, event, timestamp);\n results.push(result);\n } catch (err) {\n errors.push(err);\n }\n }\n return {results, errors, senderKeyDecryption};\n }\n\n _decryptForSenderKey(senderKeyDecryption: SenderKeyDecryption, event: OlmEncryptedEvent, timestamp: number): DecryptionResult {\n const senderKey = senderKeyDecryption.senderKey;\n const message = this._getMessageAndValidateEvent(event);\n let plaintext: string | undefined;\n try {\n plaintext = senderKeyDecryption.decrypt(message);\n } catch (err) {\n // TODO: is it ok that an error on one session prevents other sessions from being attempted?\n throw new DecryptionError(\"OLM_BAD_ENCRYPTED_MESSAGE\", event, {senderKey, error: err.message});\n }\n // could not decrypt with any existing session\n if (typeof plaintext !== \"string\" && message.type === OlmPayloadType.PreKey) {\n let createResult: CreateAndDecryptResult;\n try {\n createResult = this._createSessionAndDecrypt(senderKey, message, timestamp);\n } catch (error) {\n throw new DecryptionError(`Could not create inbound olm session: ${error.message}`, event, {senderKey, error});\n }\n senderKeyDecryption.addNewSession(createResult.session);\n plaintext = createResult.plaintext;\n }\n if (typeof plaintext === \"string\") {\n let payload: OlmPayload;\n try {\n payload = JSON.parse(plaintext);\n } catch (error) {\n throw new DecryptionError(\"PLAINTEXT_NOT_JSON\", event, {plaintext, error});\n }\n this._validatePayload(payload, event);\n return new DecryptionResult(payload, senderKey, payload.keys!.ed25519!);\n } else {\n throw new DecryptionError(\"OLM_NO_MATCHING_SESSION\", event,\n {knownSessionIds: senderKeyDecryption.sessions.map(s => s.id)});\n }\n }\n\n // only for pre-key messages after having attempted decryption with existing sessions\n _createSessionAndDecrypt(senderKey: string, message: OlmMessage, timestamp: number): CreateAndDecryptResult {\n let plaintext;\n // if we have multiple messages encrypted with the same new session,\n // this could create multiple sessions as the OTK isn't removed yet\n // (this only happens in DecryptionChanges.write)\n // This should be ok though as we'll first try to decrypt with the new session\n const olmSession = this.account.createInboundOlmSession(senderKey, message.body);\n try {\n plaintext = olmSession.decrypt(message.type, message.body);\n const session = Session.create(senderKey, olmSession, this.olm, this.pickleKey, timestamp);\n session.unload(olmSession);\n return {session, plaintext};\n } catch (err) {\n olmSession.free();\n throw err;\n }\n }\n\n _getMessageAndValidateEvent(event: OlmEncryptedEvent): OlmMessage {\n const ciphertext = event.content?.ciphertext;\n if (!ciphertext) {\n throw new DecryptionError(\"OLM_MISSING_CIPHERTEXT\", event);\n }\n const message = ciphertext?.[this.account.identityKeys.curve25519];\n if (!message) {\n throw new DecryptionError(\"OLM_NOT_INCLUDED_IN_RECIPIENTS\", event);\n }\n\n return message;\n }\n\n async _getSessions(senderKey: string, txn: Transaction): Promise<Session[]> {\n const sessionEntries = await txn.olmSessions.getAll(senderKey);\n // sort most recent used sessions first\n const sessions = sessionEntries.map(s => new Session(s, this.pickleKey, this.olm));\n sortSessions(sessions);\n return sessions;\n }\n\n _validatePayload(payload: OlmPayload, event: OlmEncryptedEvent): void {\n if (payload.sender !== event.sender) {\n throw new DecryptionError(\"OLM_FORWARDED_MESSAGE\", event, {sentBy: event.sender, encryptedBy: payload.sender});\n }\n if (payload.recipient !== this.ownUserId) {\n throw new DecryptionError(\"OLM_BAD_RECIPIENT\", event, {recipient: payload.recipient});\n }\n if (payload.recipient_keys?.ed25519 !== this.account.identityKeys.ed25519) {\n throw new DecryptionError(\"OLM_BAD_RECIPIENT_KEY\", event, {key: payload.recipient_keys?.ed25519});\n }\n // TODO: check room_id\n if (!payload.type) {\n throw new DecryptionError(\"missing type on payload\", event, {payload});\n }\n if (typeof payload.keys?.ed25519 !== \"string\") {\n throw new DecryptionError(\"Missing or invalid claimed ed25519 key on payload\", event, {payload});\n }\n }\n}\n\n// decryption helper for a single senderKey\nclass SenderKeyDecryption {\n constructor(\n public readonly senderKey: string,\n public readonly sessions: Session[],\n private readonly timestamp: number\n ) {}\n\n addNewSession(session: Session): void {\n // add at top as it is most recent\n this.sessions.unshift(session);\n }\n\n decrypt(message: OlmMessage): string | undefined {\n for (const session of this.sessions) {\n const plaintext = this.decryptWithSession(session, message);\n if (typeof plaintext === \"string\") {\n // keep them sorted so will try the same session first for other messages\n // and so we can assume the excess ones are at the end\n // if they grow too large\n sortSessions(this.sessions);\n return plaintext;\n }\n }\n }\n\n getModifiedSessions(): Session[] {\n return this.sessions.filter(session => session.isModified);\n }\n\n get hasNewSessions(): boolean {\n return this.sessions.some(session => session.isNew);\n }\n\n // this could internally dispatch to a web-worker\n // and is why we unpickle/pickle on each iteration\n // if this turns out to be a real cost for IE11,\n // we could look into adding a less expensive serialization mechanism\n // for olm sessions to libolm\n private decryptWithSession(session: Session, message: OlmMessage): string | undefined {\n if (message.type === undefined || message.body === undefined) {\n throw new Error(\"Invalid message without type or body\");\n }\n const olmSession = session.load();\n try {\n if (message.type === OlmPayloadType.PreKey && !olmSession.matches_inbound(message.body)) {\n return;\n }\n try {\n const plaintext = olmSession.decrypt(message.type as number, message.body!);\n session.save(olmSession);\n session.data.lastUsed = this.timestamp;\n return plaintext;\n } catch (err) {\n if (message.type === OlmPayloadType.PreKey) {\n throw new Error(`Error decrypting prekey message with existing session id ${session.id}: ${err.message}`);\n }\n // decryption failed, bail out\n return;\n }\n } finally {\n session.unload(olmSession);\n }\n }\n}\n\n/**\n * @property {Array<DecryptionResult>} results\n * @property {Array<DecryptionError>} errors see DecryptionError.event to retrieve the event that failed to decrypt.\n */\nclass DecryptionChanges {\n constructor(\n private readonly senderKeyDecryptions: SenderKeyDecryption[],\n public readonly results: DecryptionResult[],\n public readonly errors: DecryptionError[],\n private readonly account: Account,\n private readonly lock: ILock\n ) {}\n\n get hasNewSessions(): boolean {\n return this.senderKeyDecryptions.some(skd => skd.hasNewSessions);\n }\n\n write(txn: Transaction): void {\n try {\n for (const senderKeyDecryption of this.senderKeyDecryptions) {\n for (const session of senderKeyDecryption.getModifiedSessions()) {\n txn.olmSessions.set(session.data);\n if (session.isNew) {\n const olmSession = session.load();\n try {\n this.account.writeRemoveOneTimeKey(olmSession, txn);\n } finally {\n session.unload(olmSession);\n }\n }\n }\n if (senderKeyDecryption.sessions.length > SESSION_LIMIT_PER_SENDER_KEY) {\n const {senderKey, sessions} = senderKeyDecryption;\n // >= because index is zero-based\n for (let i = sessions.length - 1; i >= SESSION_LIMIT_PER_SENDER_KEY ; i -= 1) {\n const session = sessions[i];\n txn.olmSessions.remove(senderKey, session.id);\n }\n }\n }\n } finally {\n this.lock.release();\n }\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {groupByWithCreator} from \"../../../utils/groupBy\";\nimport {verifyEd25519Signature, OLM_ALGORITHM} from \"../common.js\";\nimport {createSessionEntry} from \"./Session\";\n\nimport type {OlmMessage, OlmPayload, OlmEncryptedMessageContent} from \"./types\";\nimport type {Account} from \"../Account\";\nimport type {LockMap} from \"../../../utils/LockMap\";\nimport type {Storage} from \"../../storage/idb/Storage\";\nimport type {Transaction} from \"../../storage/idb/Transaction\";\nimport type {DeviceIdentity} from \"../../storage/idb/stores/DeviceIdentityStore\";\nimport type {HomeServerApi} from \"../../net/HomeServerApi\";\nimport type {ILogItem} from \"../../../logging/types\";\nimport type * as OlmNamespace from \"@matrix-org/olm\";\ntype Olm = typeof OlmNamespace;\n\ntype ClaimedOTKResponse = {\n [userId: string]: {\n [deviceId: string]: {\n [algorithmAndOtk: string]: {\n key: string,\n signatures: {\n [userId: string]: {\n [algorithmAndDevice: string]: string\n }\n }\n }\n }\n }\n};\n\nfunction findFirstSessionId(sessionIds) {\n return sessionIds.reduce((first, sessionId) => {\n if (!first || sessionId < first) {\n return sessionId;\n } else {\n return first;\n }\n }, null);\n}\n\nconst OTK_ALGORITHM = \"signed_curve25519\";\n// only encrypt this amount of olm messages at once otherwise we run out of wasm memory\n// with all the sessions loaded at the same time\n// See https://github.com/vector-im/hydrogen-web/issues/150 as well, which indicates the limit is 44,\n// but let's take a conservative limit as the megolm session cache also takes space\nconst MAX_BATCH_SIZE = 20;\n\nexport class Encryption {\n constructor(\n private readonly account: Account,\n private readonly pickleKey: string,\n private readonly olm: Olm,\n private readonly storage: Storage,\n private readonly now: () => number,\n private readonly ownUserId: string,\n private readonly olmUtil: Olm.Utility,\n private readonly senderKeyLock: LockMap<string>\n ) {}\n\n async encrypt(type: string, content: Record<string, any>, devices: DeviceIdentity[], hsApi: HomeServerApi, log: ILogItem): Promise<EncryptedMessage[]> {\n let messages: EncryptedMessage[] = [];\n for (let i = 0; i < devices.length ; i += MAX_BATCH_SIZE) {\n const batchDevices = devices.slice(i, i + MAX_BATCH_SIZE);\n const batchMessages = await this._encryptForMaxDevices(type, content, batchDevices, hsApi, log);\n messages = messages.concat(batchMessages);\n }\n return messages;\n }\n\n async _encryptForMaxDevices(type: string, content: Record<string, any>, devices: DeviceIdentity[], hsApi: HomeServerApi, log: ILogItem): Promise<EncryptedMessage[]> {\n // TODO: see if we can only hold some of the locks until after the /keys/claim call (if needed) \n // take a lock on all senderKeys so decryption and other calls to encrypt (should not happen)\n // don't modify the sessions at the same time\n const locks = await Promise.all(devices.map(device => {\n return this.senderKeyLock.takeLock(device.curve25519Key);\n }));\n try {\n const {\n devicesWithoutSession,\n existingEncryptionTargets,\n } = await this._findExistingSessions(devices);\n \n const timestamp = this.now(); \n\n let encryptionTargets: EncryptionTarget[] = [];\n try {\n if (devicesWithoutSession.length) {\n const newEncryptionTargets = await log.wrap(\"create sessions\", log => this._createNewSessions(\n devicesWithoutSession, hsApi, timestamp, log));\n encryptionTargets = encryptionTargets.concat(newEncryptionTargets);\n }\n await this._loadSessions(existingEncryptionTargets);\n encryptionTargets = encryptionTargets.concat(existingEncryptionTargets);\n const encryptLog = {l: \"encrypt\", targets: encryptionTargets.length};\n const messages = log.wrap(encryptLog, () => encryptionTargets.map(target => {\n const encryptedContent = this._encryptForDevice(type, content, target);\n return new EncryptedMessage(encryptedContent, target.device);\n }));\n await this._storeSessions(encryptionTargets, timestamp);\n return messages;\n } finally {\n for (const target of encryptionTargets) {\n target.dispose();\n }\n }\n } finally {\n for (const lock of locks) {\n lock.release();\n }\n }\n }\n\n async _findExistingSessions(devices: DeviceIdentity[]): Promise<{devicesWithoutSession: DeviceIdentity[], existingEncryptionTargets: EncryptionTarget[]}> {\n const txn = await this.storage.readTxn([this.storage.storeNames.olmSessions]);\n const sessionIdsForDevice = await Promise.all(devices.map(async device => {\n return await txn.olmSessions.getSessionIds(device.curve25519Key);\n }));\n const devicesWithoutSession = devices.filter((_, i) => {\n const sessionIds = sessionIdsForDevice[i];\n return !(sessionIds?.length);\n });\n\n const existingEncryptionTargets = devices.map((device, i) => {\n const sessionIds = sessionIdsForDevice[i];\n if (sessionIds?.length > 0) {\n const sessionId = findFirstSessionId(sessionIds);\n return EncryptionTarget.fromSessionId(device, sessionId);\n }\n }).filter(target => !!target) as EncryptionTarget[];\n\n return {devicesWithoutSession, existingEncryptionTargets};\n }\n\n _encryptForDevice(type: string, content: Record<string, any>, target: EncryptionTarget): OlmEncryptedMessageContent {\n const {session, device} = target;\n const plaintext = JSON.stringify(this._buildPlainTextMessageForDevice(type, content, device));\n const message = session!.encrypt(plaintext);\n const encryptedContent = {\n algorithm: OLM_ALGORITHM,\n sender_key: this.account.identityKeys.curve25519,\n ciphertext: {\n [device.curve25519Key]: message\n }\n };\n return encryptedContent;\n }\n\n _buildPlainTextMessageForDevice(type: string, content: Record<string, any>, device: DeviceIdentity): OlmPayload {\n return {\n keys: {\n \"ed25519\": this.account.identityKeys.ed25519\n },\n recipient_keys: {\n \"ed25519\": device.ed25519Key\n },\n recipient: device.userId,\n sender: this.ownUserId,\n content,\n type\n }\n }\n\n async _createNewSessions(devicesWithoutSession: DeviceIdentity[], hsApi: HomeServerApi, timestamp: number, log: ILogItem): Promise<EncryptionTarget[]> {\n const newEncryptionTargets = await log.wrap(\"claim\", log => this._claimOneTimeKeys(hsApi, devicesWithoutSession, log));\n try {\n for (const target of newEncryptionTargets) {\n const {device, oneTimeKey} = target;\n target.session = await this.account.createOutboundOlmSession(device.curve25519Key, oneTimeKey);\n }\n await this._storeSessions(newEncryptionTargets, timestamp);\n } catch (err) {\n for (const target of newEncryptionTargets) {\n target.dispose();\n }\n throw err;\n }\n return newEncryptionTargets;\n }\n\n async _claimOneTimeKeys(hsApi: HomeServerApi, deviceIdentities: DeviceIdentity[], log: ILogItem): Promise<EncryptionTarget[]> {\n // create a Map<userId, Map<deviceId, deviceIdentity>>\n const devicesByUser = groupByWithCreator(deviceIdentities,\n (device: DeviceIdentity) => device.userId,\n (): Map<string, DeviceIdentity> => new Map(),\n (deviceMap: Map<string, DeviceIdentity>, device: DeviceIdentity) => deviceMap.set(device.deviceId, device)\n );\n const oneTimeKeys = Array.from(devicesByUser.entries()).reduce((usersObj, [userId, deviceMap]) => {\n usersObj[userId] = Array.from(deviceMap.values()).reduce((devicesObj, device) => {\n devicesObj[device.deviceId] = OTK_ALGORITHM;\n return devicesObj;\n }, {});\n return usersObj;\n }, {});\n const claimResponse = await hsApi.claimKeys({\n timeout: 10000,\n one_time_keys: oneTimeKeys\n }, {log}).response();\n if (Object.keys(claimResponse.failures).length) {\n log.log({l: \"failures\", servers: Object.keys(claimResponse.failures)}, log.level.Warn);\n }\n const userKeyMap = claimResponse?.[\"one_time_keys\"] as ClaimedOTKResponse;\n return this._verifyAndCreateOTKTargets(userKeyMap, devicesByUser, log);\n }\n\n _verifyAndCreateOTKTargets(userKeyMap: ClaimedOTKResponse, devicesByUser: Map<string, Map<string, DeviceIdentity>>, log: ILogItem): EncryptionTarget[] {\n const verifiedEncryptionTargets: EncryptionTarget[] = [];\n for (const [userId, userSection] of Object.entries(userKeyMap)) {\n for (const [deviceId, deviceSection] of Object.entries(userSection)) {\n const [firstPropName, keySection] = Object.entries(deviceSection)[0];\n const [keyAlgorithm] = firstPropName.split(\":\");\n if (keyAlgorithm === OTK_ALGORITHM) {\n const device = devicesByUser.get(userId)?.get(deviceId);\n if (device) {\n const isValidSignature = verifyEd25519Signature(\n this.olmUtil, userId, deviceId, device.ed25519Key, keySection, log);\n if (isValidSignature) {\n const target = EncryptionTarget.fromOTK(device, keySection.key);\n verifiedEncryptionTargets.push(target);\n }\n }\n }\n } \n }\n return verifiedEncryptionTargets;\n }\n\n async _loadSessions(encryptionTargets: EncryptionTarget[]): Promise<void> {\n const txn = await this.storage.readTxn([this.storage.storeNames.olmSessions]);\n // given we run loading in parallel, there might still be some\n // storage requests that will finish later once one has failed.\n // those should not allocate a session anymore.\n let failed = false;\n try {\n await Promise.all(encryptionTargets.map(async encryptionTarget => {\n const sessionEntry = await txn.olmSessions.get(\n encryptionTarget.device.curve25519Key, encryptionTarget.sessionId!);\n if (sessionEntry && !failed) {\n const olmSession = new this.olm.Session();\n olmSession.unpickle(this.pickleKey, sessionEntry.session);\n encryptionTarget.session = olmSession;\n }\n }));\n } catch (err) {\n failed = true;\n // clean up the sessions that did load\n for (const target of encryptionTargets) {\n target.dispose();\n }\n throw err;\n }\n }\n\n async _storeSessions(encryptionTargets: EncryptionTarget[], timestamp: number): Promise<void> {\n const txn = await this.storage.readWriteTxn([this.storage.storeNames.olmSessions]);\n try {\n for (const target of encryptionTargets) {\n const sessionEntry = createSessionEntry(\n target.session!, target.device.curve25519Key, timestamp, this.pickleKey);\n txn.olmSessions.set(sessionEntry);\n }\n } catch (err) {\n txn.abort();\n throw err;\n }\n await txn.complete();\n }\n}\n\n// just a container needed to encrypt a message for a recipient device\n// it is constructed with either a oneTimeKey\n// (and later converted to a session) in case of a new session\n// or an existing session\nclass EncryptionTarget {\n \n public session: Olm.Session | null = null;\n\n constructor(\n public readonly device: DeviceIdentity, \n public readonly oneTimeKey: string | null,\n public readonly sessionId: string | null\n ) {}\n\n static fromOTK(device: DeviceIdentity, oneTimeKey: string): EncryptionTarget {\n return new EncryptionTarget(device, oneTimeKey, null);\n }\n\n static fromSessionId(device: DeviceIdentity, sessionId: string): EncryptionTarget {\n return new EncryptionTarget(device, null, sessionId);\n }\n\n dispose(): void {\n if (this.session) {\n this.session.free();\n }\n }\n}\n\nclass EncryptedMessage {\n constructor(\n public readonly content: OlmEncryptedMessageContent,\n public readonly device: DeviceIdentity\n ) {}\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {DecryptionError} from \"../../common.js\";\n\nexport class DecryptionChanges {\n constructor(roomId, results, errors, replayEntries) {\n this._roomId = roomId;\n this._results = results;\n this._errors = errors;\n this._replayEntries = replayEntries;\n }\n\n /**\n * @type MegolmBatchDecryptionResult\n * @property {Map<string, DecryptionResult>} results a map of event id to decryption result\n * @property {Map<string, Error>} errors event id -> errors\n * \n * Handle replay attack detection, and return result\n * @param {[type]} txn [description]\n * @return {MegolmBatchDecryptionResult}\n */\n async write(txn) {\n await Promise.all(this._replayEntries.map(async replayEntry => {\n try {\n this._handleReplayAttack(this._roomId, replayEntry, txn);\n } catch (err) {\n this._errors.set(replayEntry.eventId, err);\n }\n }));\n return {\n results: this._results,\n errors: this._errors\n };\n }\n\n // need to handle replay attack because\n // if we redecrypted the same message twice and showed it again\n // then it could be a malicious server admin replaying the word “yes”\n // to make you respond to a msg you didnt say “yes” to, or something\n async _handleReplayAttack(roomId, replayEntry, txn) {\n const {messageIndex, sessionId, eventId, timestamp} = replayEntry;\n const decryption = await txn.groupSessionDecryptions.get(roomId, sessionId, messageIndex);\n\n if (decryption && decryption.eventId !== eventId) {\n // the one with the newest timestamp should be the attack\n const decryptedEventIsBad = decryption.timestamp < timestamp;\n const badEventId = decryptedEventIsBad ? eventId : decryption.eventId;\n // discard result\n this._results.delete(eventId);\n\n throw new DecryptionError(\"MEGOLM_REPLAYED_INDEX\", event, {\n messageIndex,\n badEventId,\n otherEventId: decryption.eventId\n });\n }\n\n if (!decryption) {\n txn.groupSessionDecryptions.set(roomId, sessionId, messageIndex, {\n eventId,\n timestamp\n });\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport function mergeMap<K, V>(src: Map<K, V> | undefined, dst: Map<K, V>): void {\n if (src) {\n for (const [key, value] of src.entries()) {\n dst.set(key, value);\n }\n }\n}\n\nexport function tests() {\n return {\n \"mergeMap with src\": assert => {\n const src = new Map();\n src.set(1, \"a\");\n const dst = new Map();\n dst.set(2, \"b\");\n mergeMap(src, dst);\n assert.equal(dst.get(1), \"a\");\n assert.equal(dst.get(2), \"b\");\n assert.equal(src.get(2), null);\n },\n \"mergeMap without src doesn't fail\": () => {\n mergeMap(undefined, new Map());\n }\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {DecryptionChanges} from \"./DecryptionChanges.js\";\nimport {mergeMap} from \"../../../../utils/mergeMap\";\n\n/**\n * Class that contains all the state loaded from storage to decrypt the given events\n */\nexport class DecryptionPreparation {\n constructor(roomId, sessionDecryptions, errors) {\n this._roomId = roomId;\n this._sessionDecryptions = sessionDecryptions;\n this._initialErrors = errors;\n }\n\n async decrypt() {\n try {\n const errors = this._initialErrors;\n const results = new Map();\n const replayEntries = [];\n await Promise.all(this._sessionDecryptions.map(async sessionDecryption => {\n const sessionResult = await sessionDecryption.decryptAll();\n mergeMap(sessionResult.errors, errors);\n mergeMap(sessionResult.results, results);\n replayEntries.push(...sessionResult.replayEntries);\n }));\n return new DecryptionChanges(this._roomId, results, errors, replayEntries);\n } finally {\n this.dispose();\n }\n }\n\n dispose() {\n for (const sd of this._sessionDecryptions) {\n sd.dispose();\n }\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport type {TimelineEvent} from \"../../../storage/types\";\n\nexport class ReplayDetectionEntry {\n public readonly sessionId: string;\n public readonly messageIndex: number;\n public readonly event: TimelineEvent;\n\n constructor(sessionId: string, messageIndex: number, event: TimelineEvent) {\n this.sessionId = sessionId;\n this.messageIndex = messageIndex;\n this.event = event;\n }\n\n get eventId(): string {\n return this.event.event_id;\n }\n\n get timestamp(): number {\n return this.event.origin_server_ts;\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {DecryptionResult} from \"../../DecryptionResult\";\nimport {DecryptionError} from \"../../common.js\";\nimport {ReplayDetectionEntry} from \"./ReplayDetectionEntry\";\nimport type {RoomKey} from \"./RoomKey\";\nimport type {KeyLoader, OlmDecryptionResult} from \"./KeyLoader\";\nimport type {OlmWorker} from \"../../OlmWorker\";\nimport type {TimelineEvent} from \"../../../storage/types\";\n\ninterface DecryptAllResult {\n readonly results: Map<string, DecryptionResult>;\n readonly errors?: Map<string, Error>;\n readonly replayEntries: ReplayDetectionEntry[];\n}\n/**\n * Does the actual decryption of all events for a given megolm session in a batch\n */\nexport class SessionDecryption {\n private key: RoomKey;\n private events: TimelineEvent[];\n private keyLoader: KeyLoader;\n private olmWorker?: OlmWorker;\n private decryptionRequests?: any[];\n\n constructor(key: RoomKey, events: TimelineEvent[], olmWorker: OlmWorker | undefined, keyLoader: KeyLoader) {\n this.key = key;\n this.events = events;\n this.olmWorker = olmWorker;\n this.keyLoader = keyLoader;\n this.decryptionRequests = olmWorker ? [] : undefined;\n }\n\n async decryptAll(): Promise<DecryptAllResult> {\n const replayEntries: ReplayDetectionEntry[] = [];\n const results: Map<string, DecryptionResult> = new Map();\n let errors: Map<string, Error> | undefined;\n\n await this.keyLoader.useKey(this.key, async session => {\n for (const event of this.events) {\n try {\n const ciphertext = event.content.ciphertext as string;\n let decryptionResult: OlmDecryptionResult | undefined;\n // TODO: pass all cipthertexts in one go to the megolm worker and don't deserialize the key until in the worker?\n if (this.olmWorker) {\n const request = this.olmWorker.megolmDecrypt(session, ciphertext);\n this.decryptionRequests!.push(request);\n decryptionResult = await request.response();\n } else {\n decryptionResult = session.decrypt(ciphertext) as OlmDecryptionResult;\n }\n const {plaintext} = decryptionResult!;\n let payload;\n try {\n payload = JSON.parse(plaintext);\n } catch (err) {\n throw new DecryptionError(\"PLAINTEXT_NOT_JSON\", event, {plaintext, err});\n }\n if (payload.room_id !== this.key.roomId) {\n throw new DecryptionError(\"MEGOLM_WRONG_ROOM\", event,\n {encryptedRoomId: payload.room_id, eventRoomId: this.key.roomId});\n }\n replayEntries.push(new ReplayDetectionEntry(this.key.sessionId, decryptionResult!.message_index, event));\n const result = new DecryptionResult(payload, this.key.senderKey, this.key.claimedEd25519Key);\n results.set(event.event_id, result);\n } catch (err) {\n // ignore AbortError from cancelling decryption requests in dispose method\n if (err.name === \"AbortError\") {\n return;\n }\n if (!errors) {\n errors = new Map();\n }\n errors.set(event.event_id, err);\n }\n }\n });\n\n return {results, errors, replayEntries};\n }\n\n dispose() {\n if (this.decryptionRequests) {\n for (const r of this.decryptionRequests) {\n r.abort();\n }\n }\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {groupByWithCreator} from \"../../../../utils/groupBy\";\nimport type {TimelineEvent} from \"../../../storage/types\";\n\nfunction getSenderKey(event: TimelineEvent): string | undefined {\n return event.content?.[\"sender_key\"];\n}\n\nfunction getSessionId(event: TimelineEvent): string | undefined {\n return event.content?.[\"session_id\"];\n}\n\nfunction getCiphertext(event: TimelineEvent): string | undefined {\n return event.content?.ciphertext;\n}\n\nexport function validateEvent(event: TimelineEvent) {\n return typeof getSenderKey(event) === \"string\" &&\n typeof getSessionId(event) === \"string\" &&\n typeof getCiphertext(event) === \"string\";\n}\n\nexport class SessionKeyGroup {\n public readonly events: TimelineEvent[];\n constructor() {\n this.events = [];\n }\n\n get senderKey(): string | undefined {\n return getSenderKey(this.events[0]!);\n }\n\n get sessionId(): string | undefined {\n return getSessionId(this.events[0]!);\n }\n}\n\nexport function groupEventsBySession(events: TimelineEvent[]): Map<string, SessionKeyGroup> {\n return groupByWithCreator<string, TimelineEvent, SessionKeyGroup>(events,\n (event: TimelineEvent) => `${getSenderKey(event)}|${getSessionId(event)}`,\n () => new SessionKeyGroup(),\n (group: SessionKeyGroup, event: TimelineEvent) => group.events.push(event)\n );\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BackupStatus, KeySource} from \"../../../storage/idb/stores/InboundGroupSessionStore\";\nimport type {InboundGroupSessionEntry} from \"../../../storage/idb/stores/InboundGroupSessionStore\";\nimport type {Transaction} from \"../../../storage/idb/Transaction\";\nimport type {DecryptionResult} from \"../../DecryptionResult\";\nimport type {KeyLoader} from \"./KeyLoader\";\nimport type * as OlmNamespace from \"@matrix-org/olm\";\ntype Olm = typeof OlmNamespace;\n\nexport abstract class RoomKey {\n private _isBetter: boolean | undefined;\n\n isForSession(roomId: string, senderKey: string, sessionId: string) {\n return this.roomId === roomId && this.senderKey === senderKey && this.sessionId === sessionId;\n }\n\n abstract get roomId(): string;\n abstract get senderKey(): string;\n abstract get sessionId(): string;\n abstract get claimedEd25519Key(): string;\n abstract get serializationKey(): string;\n abstract get serializationType(): string;\n abstract get eventIds(): string[] | undefined;\n abstract loadInto(session: Olm.InboundGroupSession, pickleKey: string): void;\n /* Whether the key has been checked against storage (or is from storage)\n * to be the better key for a given session. Given that all keys are checked to be better\n * as part of writing, we can trust that when this returns true, it really is the best key\n * available between storage and cached keys in memory. This is why keys with this field set to\n * true are used by the key loader to return cached keys. Also see KeyOperation.isBest there. */\n get isBetter(): boolean | undefined { return this._isBetter; }\n // should only be set in key.checkBetterThanKeyInStorage\n set isBetter(value: boolean | undefined) { this._isBetter = value; }\n}\n\nexport function isBetterThan(newSession: Olm.InboundGroupSession, existingSession: Olm.InboundGroupSession) {\n return newSession.first_known_index() < existingSession.first_known_index();\n}\n\nexport abstract class IncomingRoomKey extends RoomKey {\n private _eventIds?: string[];\n \n checkBetterThanKeyInStorage(loader: KeyLoader, txn: Transaction): Promise<boolean> {\n return this._checkBetterThanKeyInStorage(loader, undefined, txn);\n }\n\n async write(loader: KeyLoader, txn: Transaction): Promise<boolean> {\n // we checked already and we had a better session in storage, so don't write\n let pickledSession: string | undefined;\n if (this.isBetter === undefined) {\n // if this key wasn't used to decrypt any messages in the same sync,\n // we haven't checked if this is the best key yet,\n // so do that now to not overwrite a better key.\n // while we have the key deserialized, also pickle it to store it later on here.\n await this._checkBetterThanKeyInStorage(loader, (session, pickleKey) => {\n pickledSession = session.pickle(pickleKey);\n }, txn);\n }\n if (this.isBetter === false) {\n return false;\n }\n // before calling write in parallel, we need to check loader.running is false so we are sure our transaction will not be closed\n if (!pickledSession) {\n pickledSession = await loader.useKey(this, (session, pickleKey) => session.pickle(pickleKey));\n }\n const sessionEntry = {\n roomId: this.roomId,\n senderKey: this.senderKey,\n sessionId: this.sessionId,\n session: pickledSession,\n backup: this.backupStatus,\n source: this.keySource,\n claimedKeys: {\"ed25519\": this.claimedEd25519Key},\n };\n txn.inboundGroupSessions.set(sessionEntry);\n return true;\n }\n\n get eventIds() { return this._eventIds; }\n\n private async _checkBetterThanKeyInStorage(loader: KeyLoader, callback: (((session: Olm.InboundGroupSession, pickleKey: string) => void) | undefined), txn: Transaction): Promise<boolean> {\n if (this.isBetter !== undefined) {\n return this.isBetter;\n }\n let existingKey = loader.getCachedKey(this.roomId, this.senderKey, this.sessionId);\n if (!existingKey) {\n const storageKey = await keyFromStorage(this.roomId, this.senderKey, this.sessionId, txn);\n // store the event ids that can be decrypted with this key\n // before we overwrite them if called from `write`.\n if (storageKey) {\n if (storageKey.hasSession) {\n existingKey = storageKey;\n } else if (storageKey.eventIds) {\n this._eventIds = storageKey.eventIds;\n }\n }\n }\n if (existingKey) {\n const key = existingKey;\n await loader.useKey(this, async newSession => {\n await loader.useKey(key, (existingSession, pickleKey) => {\n // set isBetter as soon as possible, on both keys compared, \n // as it is is used to determine whether a key can be used for the cache\n this.isBetter = isBetterThan(newSession, existingSession);\n key.isBetter = !this.isBetter;\n if (this.isBetter && callback) {\n callback(newSession, pickleKey);\n }\n });\n });\n } else {\n // no previous key, so we're the best \\o/\n this.isBetter = true;\n }\n return this.isBetter!;\n }\n\n protected get backupStatus(): BackupStatus {\n return BackupStatus.NotBackedUp;\n }\n\n protected abstract get keySource(): KeySource;\n}\n\nclass DeviceMessageRoomKey extends IncomingRoomKey {\n private _decryptionResult: DecryptionResult;\n\n constructor(decryptionResult: DecryptionResult) {\n super();\n this._decryptionResult = decryptionResult;\n }\n\n get roomId() { return this._decryptionResult.event.content?.[\"room_id\"]; }\n get senderKey() { return this._decryptionResult.senderCurve25519Key; }\n get sessionId() { return this._decryptionResult.event.content?.[\"session_id\"]; }\n get claimedEd25519Key() { return this._decryptionResult.claimedEd25519Key; }\n get serializationKey(): string { return this._decryptionResult.event.content?.[\"session_key\"]; }\n get serializationType(): string { return \"create\"; }\n protected get keySource(): KeySource { return KeySource.DeviceMessage; }\n\n loadInto(session) {\n session.create(this.serializationKey);\n }\n}\n\n// a room key we send out ourselves,\n// here adapted to write it as an incoming key\n// as we don't send it to ourself with a to_device msg\nexport class OutboundRoomKey extends IncomingRoomKey {\n private _sessionKey: string;\n\n constructor(\n private readonly _roomId: string,\n private readonly outboundSession: Olm.OutboundGroupSession,\n private readonly identityKeys: {[algo: string]: string}\n ) {\n super();\n // this is a new key, so always better than what might be in storage, no need to check\n this.isBetter = true;\n // cache this, as it is used by key loader to find a matching key and\n // this calls into WASM so is not just reading a prop\n this._sessionKey = this.outboundSession.session_key();\n }\n\n get roomId(): string { return this._roomId; }\n get senderKey(): string { return this.identityKeys.curve25519; }\n get sessionId(): string { return this.outboundSession.session_id(); }\n get claimedEd25519Key(): string { return this.identityKeys.ed25519; }\n get serializationKey(): string { return this._sessionKey; }\n get serializationType(): string { return \"create\"; }\n protected get keySource(): KeySource { return KeySource.Outbound; }\n\n loadInto(session: Olm.InboundGroupSession) {\n session.create(this.serializationKey);\n }\n}\n\nclass BackupRoomKey extends IncomingRoomKey {\n constructor(private _roomId: string, private _sessionId: string, private _backupInfo: object) {\n super();\n }\n\n get roomId() { return this._roomId; }\n get senderKey() { return this._backupInfo[\"sender_key\"]; }\n get sessionId() { return this._sessionId; }\n get claimedEd25519Key() { return this._backupInfo[\"sender_claimed_keys\"]?.[\"ed25519\"]; }\n get serializationKey(): string { return this._backupInfo[\"session_key\"]; }\n get serializationType(): string { return \"import_session\"; }\n protected get keySource(): KeySource { return KeySource.Backup; }\n\n loadInto(session) {\n session.import_session(this.serializationKey);\n }\n\n protected get backupStatus(): BackupStatus {\n return BackupStatus.BackedUp;\n }\n}\n\nexport class StoredRoomKey extends RoomKey {\n private storageEntry: InboundGroupSessionEntry;\n\n constructor(storageEntry: InboundGroupSessionEntry) {\n super();\n this.isBetter = true; // usually the key in storage is the best until checks prove otherwise\n this.storageEntry = storageEntry;\n }\n\n get roomId() { return this.storageEntry.roomId; }\n get senderKey() { return this.storageEntry.senderKey; }\n get sessionId() { return this.storageEntry.sessionId; }\n get claimedEd25519Key() { return this.storageEntry.claimedKeys![\"ed25519\"]; }\n get eventIds() { return this.storageEntry.eventIds; }\n get serializationKey(): string { return this.storageEntry.session || \"\"; }\n get serializationType(): string { return \"unpickle\"; }\n \n loadInto(session, pickleKey) {\n session.unpickle(pickleKey, this.serializationKey);\n }\n\n get hasSession() {\n // sessions are stored before they are received\n // to keep track of events that need it to be decrypted.\n // This is used to retry decryption of those events once the session is received.\n return !!this.serializationKey;\n }\n}\n\nexport function keyFromDeviceMessage(dr: DecryptionResult): DeviceMessageRoomKey | undefined {\n const sessionKey = dr.event.content?.[\"session_key\"];\n const key = new DeviceMessageRoomKey(dr);\n if (\n typeof key.roomId === \"string\" && \n typeof key.sessionId === \"string\" && \n typeof key.senderKey === \"string\" &&\n typeof sessionKey === \"string\"\n ) {\n return key;\n }\n}\n\n/*\nsessionInfo is a response from key backup and has the following keys:\n algorithm\n forwarding_curve25519_key_chain\n sender_claimed_keys\n sender_key\n session_key\n */\nexport function keyFromBackup(roomId, sessionId, backupInfo): BackupRoomKey | undefined {\n const sessionKey = backupInfo[\"session_key\"];\n const senderKey = backupInfo[\"sender_key\"];\n // TODO: can we just trust this?\n const claimedEd25519Key = backupInfo[\"sender_claimed_keys\"]?.[\"ed25519\"];\n\n if (\n typeof roomId === \"string\" && \n typeof sessionId === \"string\" && \n typeof senderKey === \"string\" &&\n typeof sessionKey === \"string\" &&\n typeof claimedEd25519Key === \"string\"\n ) {\n return new BackupRoomKey(roomId, sessionId, backupInfo);\n }\n}\n\nexport async function keyFromStorage(roomId: string, senderKey: string, sessionId: string, txn: Transaction): Promise<StoredRoomKey | undefined> {\n const existingSessionEntry = await txn.inboundGroupSessions.get(roomId, senderKey, sessionId);\n if (existingSessionEntry) {\n return new StoredRoomKey(existingSessionEntry);\n }\n return;\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {DecryptionError} from \"../common.js\";\nimport {DecryptionPreparation} from \"./decryption/DecryptionPreparation.js\";\nimport {SessionDecryption} from \"./decryption/SessionDecryption\";\nimport {MEGOLM_ALGORITHM} from \"../common.js\";\nimport {validateEvent, groupEventsBySession} from \"./decryption/utils\";\nimport {keyFromStorage, keyFromDeviceMessage, keyFromBackup} from \"./decryption/RoomKey\";\nimport type {RoomKey, IncomingRoomKey} from \"./decryption/RoomKey\";\nimport type {KeyLoader} from \"./decryption/KeyLoader\";\nimport type {OlmWorker} from \"../OlmWorker\";\nimport type {Transaction} from \"../../storage/idb/Transaction\";\nimport type {TimelineEvent} from \"../../storage/types\";\nimport type {DecryptionResult} from \"../DecryptionResult\";\nimport type {ILogItem} from \"../../../logging/types\";\n\nexport class Decryption {\n private keyLoader: KeyLoader;\n private olmWorker?: OlmWorker;\n\n constructor(keyLoader: KeyLoader, olmWorker: OlmWorker | undefined) {\n this.keyLoader = keyLoader;\n this.olmWorker = olmWorker;\n }\n\n async addMissingKeyEventIds(roomId, senderKey, sessionId, eventIds, txn) {\n let sessionEntry = await txn.inboundGroupSessions.get(roomId, senderKey, sessionId);\n // we never want to overwrite an existing key\n if (sessionEntry?.session) {\n return;\n }\n if (sessionEntry) {\n const uniqueEventIds = new Set(sessionEntry.eventIds);\n for (const id of eventIds) {\n uniqueEventIds.add(id);\n }\n sessionEntry.eventIds = Array.from(uniqueEventIds);\n } else {\n sessionEntry = {roomId, senderKey, sessionId, eventIds};\n }\n txn.inboundGroupSessions.set(sessionEntry);\n }\n\n async getEventIdsForMissingKey(roomId, senderKey, sessionId, txn) {\n const sessionEntry = await txn.inboundGroupSessions.get(roomId, senderKey, sessionId);\n if (sessionEntry && !sessionEntry.session) {\n return sessionEntry.eventIds;\n }\n }\n\n async hasSession(roomId, senderKey, sessionId, txn) {\n const sessionEntry = await txn.inboundGroupSessions.get(roomId, senderKey, sessionId);\n const isValidSession = typeof sessionEntry?.session === \"string\";\n return isValidSession;\n }\n\n /**\n * Reads all the state from storage to be able to decrypt the given events.\n * Decryption can then happen outside of a storage transaction.\n * @param {[type]} roomId [description]\n * @param {[type]} events [description]\n * @param {RoomKey[]?} newKeys keys as returned from extractRoomKeys, but not yet committed to storage. May be undefined.\n * @param {[type]} sessionCache [description]\n * @param {[type]} txn [description]\n * @return {DecryptionPreparation}\n */\n async prepareDecryptAll(roomId: string, events: TimelineEvent[], newKeys: IncomingRoomKey[] | undefined, txn: Transaction) {\n const errors = new Map();\n const validEvents: TimelineEvent[] = [];\n\n for (const event of events) {\n if (validateEvent(event)) {\n validEvents.push(event);\n } else {\n errors.set(event.event_id, new DecryptionError(\"MEGOLM_INVALID_EVENT\", event))\n }\n }\n\n const eventsBySession = groupEventsBySession(validEvents);\n\n const sessionDecryptions: SessionDecryption[] = [];\n await Promise.all(Array.from(eventsBySession.values()).map(async group => {\n const key = await this.getRoomKey(roomId, group.senderKey!, group.sessionId!, newKeys, txn);\n if (key) {\n sessionDecryptions.push(new SessionDecryption(key, group.events, this.olmWorker, this.keyLoader));\n } else {\n for (const event of group.events) {\n errors.set(event.event_id, new DecryptionError(\"MEGOLM_NO_SESSION\", event));\n }\n }\n }));\n\n return new DecryptionPreparation(roomId, sessionDecryptions, errors);\n }\n\n private async getRoomKey(roomId: string, senderKey: string, sessionId: string, newKeys: IncomingRoomKey[] | undefined, txn: Transaction): Promise<RoomKey | undefined> {\n if (newKeys) {\n const key = newKeys.find(k => k.isForSession(roomId, senderKey, sessionId));\n if (key && await key.checkBetterThanKeyInStorage(this.keyLoader, txn)) {\n return key;\n }\n }\n // look only in the cache after looking into newKeys as it may contains that are better\n const cachedKey = this.keyLoader.getCachedKey(roomId, senderKey, sessionId);\n if (cachedKey) {\n return cachedKey;\n }\n const storageKey = await keyFromStorage(roomId, senderKey, sessionId, txn);\n if (storageKey && storageKey.serializationKey) {\n return storageKey;\n }\n }\n\n /**\n * Writes the key as an inbound group session if there is not already a better key in the store\n */\n writeRoomKey(key: IncomingRoomKey, txn: Transaction): Promise<boolean> {\n return key.write(this.keyLoader, txn);\n }\n\n /**\n * Extracts room keys from decrypted device messages.\n * The key won't be persisted yet, you need to call RoomKey.write for that.\n */\n roomKeysFromDeviceMessages(decryptionResults: DecryptionResult[], log: ILogItem): IncomingRoomKey[] {\n const keys: IncomingRoomKey[] = [];\n for (const dr of decryptionResults) {\n if (dr.event?.type !== \"m.room_key\" || dr.event.content?.algorithm !== MEGOLM_ALGORITHM) {\n continue;\n }\n log.wrap(\"room_key\", log => {\n const key = keyFromDeviceMessage(dr);\n if (key) {\n log.set(\"roomId\", key.roomId);\n log.set(\"id\", key.sessionId);\n keys.push(key);\n } else {\n log.logLevel = log.level.Warn;\n log.set(\"invalid\", true);\n }\n }, log.level.Detail);\n }\n return keys;\n }\n\n roomKeyFromBackup(roomId: string, sessionId: string, sessionInfo: string): IncomingRoomKey | undefined {\n return keyFromBackup(roomId, sessionId, sessionInfo);\n }\n\n dispose() {\n this.keyLoader.dispose();\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {isBetterThan, IncomingRoomKey} from \"./RoomKey\";\nimport {BaseLRUCache} from \"../../../../utils/LRUCache\";\nimport type {RoomKey} from \"./RoomKey\";\nimport type * as OlmNamespace from \"@matrix-org/olm\";\ntype Olm = typeof OlmNamespace;\n\nexport declare class OlmDecryptionResult {\n readonly plaintext: string;\n readonly message_index: number;\n}\n\n/*\nBecause Olm only has very limited memory available when compiled to wasm,\nwe limit the amount of sessions held in memory.\n*/\nexport class KeyLoader extends BaseLRUCache<KeyOperation> {\n\n private pickleKey: string;\n private olm: Olm;\n private resolveUnusedOperation?: () => void;\n private operationBecomesUnusedPromise?: Promise<void>;\n\n constructor(olm: Olm, pickleKey: string, limit: number) {\n super(limit);\n this.pickleKey = pickleKey;\n this.olm = olm;\n }\n\n getCachedKey(roomId: string, senderKey: string, sessionId: string): RoomKey | undefined {\n const idx = this.findCachedKeyIndex(roomId, senderKey, sessionId);\n if (idx !== -1) {\n return this._getByIndexAndMoveUp(idx)!.key;\n }\n }\n\n async useKey<T>(key: RoomKey, callback: (session: Olm.InboundGroupSession, pickleKey: string) => Promise<T> | T): Promise<T> {\n const keyOp = await this.allocateOperation(key);\n try {\n return await callback(keyOp.session, this.pickleKey);\n } finally {\n this.releaseOperation(keyOp);\n }\n }\n\n get running() {\n return this._entries.some(op => op.refCount !== 0);\n }\n\n dispose() {\n for (let i = 0; i < this._entries.length; i += 1) {\n this._entries[i].dispose();\n }\n // remove all entries\n this._entries.splice(0, this._entries.length);\n }\n\n private async allocateOperation(key: RoomKey): Promise<KeyOperation> {\n let idx;\n while((idx = this.findIndexForAllocation(key)) === -1) {\n await this.operationBecomesUnused();\n }\n if (idx < this.size) {\n const op = this._getByIndexAndMoveUp(idx)!;\n // cache hit\n if (op.isForKey(key)) {\n op.refCount += 1;\n return op;\n } else {\n // refCount should be 0 here\n op.refCount = 1;\n op.key = key;\n key.loadInto(op.session, this.pickleKey);\n }\n return op;\n } else {\n // create new operation\n const session = new this.olm.InboundGroupSession();\n key.loadInto(session, this.pickleKey);\n const op = new KeyOperation(key, session);\n this._set(op);\n return op;\n }\n }\n\n private releaseOperation(op: KeyOperation) {\n op.refCount -= 1;\n if (op.refCount <= 0 && this.resolveUnusedOperation) {\n this.resolveUnusedOperation();\n // promise is resolved now, we'll need a new one for next await so clear\n this.operationBecomesUnusedPromise = this.resolveUnusedOperation = undefined;\n }\n }\n\n private operationBecomesUnused(): Promise<void> {\n if (!this.operationBecomesUnusedPromise) {\n this.operationBecomesUnusedPromise = new Promise(resolve => {\n this.resolveUnusedOperation = resolve;\n });\n }\n return this.operationBecomesUnusedPromise;\n }\n\n private findIndexForAllocation(key: RoomKey) {\n let idx = this.findIndexSameKey(key); // cache hit\n if (idx === -1) {\n if (this.size < this.limit) {\n idx = this.size;\n } else {\n idx = this.findIndexSameSessionUnused(key);\n if (idx === -1) {\n idx = this.findIndexOldestUnused();\n }\n }\n }\n return idx;\n }\n\n private findCachedKeyIndex(roomId: string, senderKey: string, sessionId: string): number {\n return this._entries.reduce((bestIdx, op, i, arr) => {\n const bestOp = bestIdx === -1 ? undefined : arr[bestIdx];\n // only operations that are the \"best\" for their session can be used, see comment on isBest\n if (op.isBest === true && op.isForSameSession(roomId, senderKey, sessionId)) {\n if (!bestOp || op.isBetter(bestOp)) {\n return i;\n }\n }\n return bestIdx;\n }, -1);\n }\n\n private findIndexSameKey(key: RoomKey): number {\n return this._entries.findIndex(op => {\n return op.isForSameSession(key.roomId, key.senderKey, key.sessionId) && op.isForKey(key);\n });\n }\n\n private findIndexSameSessionUnused(key: RoomKey): number {\n return this._entries.reduce((worstIdx, op, i, arr) => {\n const worst = worstIdx === -1 ? undefined : arr[worstIdx];\n // we try to pick the worst operation to overwrite, so the best one stays in the cache\n if (op.refCount === 0 && op.isForSameSession(key.roomId, key.senderKey, key.sessionId)) {\n if (!worst || !op.isBetter(worst)) {\n return i;\n }\n }\n return worstIdx;\n }, -1);\n }\n\n private findIndexOldestUnused(): number {\n for (let i = this._entries.length - 1; i >= 0; i -= 1) {\n const op = this._entries[i];\n if (op.refCount === 0) {\n return i;\n }\n }\n return -1;\n }\n}\n\nclass KeyOperation {\n session: Olm.InboundGroupSession;\n key: RoomKey;\n refCount: number;\n\n constructor(key: RoomKey, session: Olm.InboundGroupSession) {\n this.key = key;\n this.session = session;\n this.refCount = 1;\n }\n\n isForSameSession(roomId: string, senderKey: string, sessionId: string): boolean {\n return this.key.roomId === roomId && this.key.senderKey === senderKey && this.key.sessionId === sessionId;\n }\n\n // assumes isForSameSession is true\n isBetter(other: KeyOperation) {\n return isBetterThan(this.session, other.session);\n }\n\n isForKey(key: RoomKey) {\n return this.key.serializationKey === key.serializationKey &&\n this.key.serializationType === key.serializationType;\n }\n\n dispose() {\n this.session.free();\n this.session = undefined as any;\n }\n\n /** returns whether the key for this operation has been checked at some point against storage\n * and was determined to be the better key, undefined if it hasn't been checked yet.\n * Only keys that are the best keys can be returned by getCachedKey as returning a cache hit\n * will usually not check for a better session in storage. Also see RoomKey.isBetter. */\n get isBest(): boolean | undefined {\n return this.key.isBetter;\n }\n}\n\nimport {KeySource} from \"../../../storage/idb/stores/InboundGroupSessionStore\";\n\n\nexport function tests() {\n let instances = 0;\n\n class MockRoomKey extends IncomingRoomKey {\n private _roomId: string;\n private _senderKey: string;\n private _sessionId: string;\n private _firstKnownIndex: number;\n\n constructor(roomId: string, senderKey: string, sessionId: string, firstKnownIndex: number) {\n super();\n this._roomId = roomId;\n this._senderKey = senderKey;\n this._sessionId = sessionId;\n this._firstKnownIndex = firstKnownIndex;\n }\n\n get roomId(): string { return this._roomId; }\n get senderKey(): string { return this._senderKey; }\n get sessionId(): string { return this._sessionId; }\n get claimedEd25519Key(): string { return \"claimedEd25519Key\"; }\n get serializationKey(): string { return `key-${this.sessionId}-${this._firstKnownIndex}`; }\n get serializationType(): string { return \"type\"; }\n get eventIds(): string[] | undefined { return undefined; }\n get keySource(): KeySource { return KeySource.DeviceMessage; }\n\n loadInto(session: Olm.InboundGroupSession) {\n const mockSession = session as MockInboundSession;\n mockSession.sessionId = this.sessionId;\n mockSession.firstKnownIndex = this._firstKnownIndex;\n }\n }\n\n class MockInboundSession {\n public sessionId: string = \"\";\n public firstKnownIndex: number = 0;\n\n constructor() {\n instances += 1;\n }\n\n free(): void { instances -= 1; }\n pickle(key: string | Uint8Array): string { return `${this.sessionId}-pickled-session`; }\n unpickle(key: string | Uint8Array, pickle: string) {}\n create(session_key: string): string { return `${this.sessionId}-created-session`; }\n import_session(session_key: string): string { return \"\"; }\n decrypt(message: string): OlmDecryptionResult { return {} as OlmDecryptionResult; }\n session_id(): string { return this.sessionId; }\n first_known_index(): number { return this.firstKnownIndex; }\n export_session(message_index: number): string { return `${this.sessionId}-exported-session`; }\n }\n\n const PICKLE_KEY = \"🥒🔑\";\n const olm = {InboundGroupSession: MockInboundSession};\n const roomId = \"!abc:hs.tld\";\n const aliceSenderKey = \"abc\";\n const bobSenderKey = \"def\";\n const sessionId1 = \"s123\";\n const sessionId2 = \"s456\";\n \n return {\n \"load key gives correct session\": async assert => {\n const loader = new KeyLoader(olm as any as Olm, PICKLE_KEY, 2);\n let callback1Called = false;\n let callback2Called = false;\n const p1 = loader.useKey(new MockRoomKey(roomId, aliceSenderKey, sessionId1, 1), async session => {\n callback1Called = true;\n assert.equal(session.session_id(), sessionId1);\n assert.equal(session.first_known_index(), 1);\n await Promise.resolve(); // make sure they are busy in parallel\n });\n const p2 = loader.useKey(new MockRoomKey(roomId, aliceSenderKey, sessionId2, 2), async session => {\n callback2Called = true;\n assert.equal(session.session_id(), sessionId2);\n assert.equal(session.first_known_index(), 2);\n await Promise.resolve(); // make sure they are busy in parallel\n });\n assert.equal(loader.size, 2);\n await Promise.all([p1, p2]);\n assert(callback1Called);\n assert(callback2Called);\n },\n \"keys with different first index are kept separate\": async assert => {\n const loader = new KeyLoader(olm as any as Olm, PICKLE_KEY, 2);\n let callback1Called = false;\n let callback2Called = false;\n const p1 = loader.useKey(new MockRoomKey(roomId, aliceSenderKey, sessionId1, 1), async session => {\n callback1Called = true;\n assert.equal(session.session_id(), sessionId1);\n assert.equal(session.first_known_index(), 1);\n await Promise.resolve(); // make sure they are busy in parallel\n });\n const p2 = loader.useKey(new MockRoomKey(roomId, aliceSenderKey, sessionId1, 2), async session => {\n callback2Called = true;\n assert.equal(session.session_id(), sessionId1);\n assert.equal(session.first_known_index(), 2);\n await Promise.resolve(); // make sure they are busy in parallel\n });\n assert.equal(loader.size, 2);\n await Promise.all([p1, p2]);\n assert(callback1Called);\n assert(callback2Called);\n },\n \"useKey blocks as long as no free sessions are available\": async assert => {\n const loader = new KeyLoader(olm as any as Olm, PICKLE_KEY, 1);\n let resolve;\n let callbackCalled = false;\n loader.useKey(new MockRoomKey(roomId, aliceSenderKey, sessionId1, 1), async session => {\n await new Promise(r => resolve = r);\n });\n await Promise.resolve();\n assert.equal(loader.size, 1);\n const promise = loader.useKey(new MockRoomKey(roomId, aliceSenderKey, sessionId2, 1), session => {\n callbackCalled = true;\n });\n assert.equal(callbackCalled, false);\n resolve();\n await promise;\n assert.equal(callbackCalled, true);\n },\n \"cache hit while key in use, then replace (check refCount works properly)\": async assert => {\n const loader = new KeyLoader(olm as any as Olm, PICKLE_KEY, 1);\n let resolve1, resolve2;\n const key1 = new MockRoomKey(roomId, aliceSenderKey, sessionId1, 1);\n const p1 = loader.useKey(key1, async session => {\n await new Promise(r => resolve1 = r);\n });\n const p2 = loader.useKey(key1, async session => {\n await new Promise(r => resolve2 = r);\n });\n await Promise.resolve();\n assert.equal(loader.size, 1);\n assert.equal(loader.running, true);\n resolve1();\n await p1;\n assert.equal(loader.running, true);\n resolve2();\n await p2;\n assert.equal(loader.running, false);\n let callbackCalled = false;\n await loader.useKey(new MockRoomKey(roomId, aliceSenderKey, sessionId2, 1), async session => {\n callbackCalled = true;\n assert.equal(session.session_id(), sessionId2);\n assert.equal(session.first_known_index(), 1);\n });\n assert.equal(loader.size, 1);\n assert.equal(callbackCalled, true);\n },\n \"cache hit while key not in use\": async assert => {\n const loader = new KeyLoader(olm as any as Olm, PICKLE_KEY, 2);\n let resolve1, resolve2, invocations = 0;\n const key1 = new MockRoomKey(roomId, aliceSenderKey, sessionId1, 1);\n await loader.useKey(key1, async session => { invocations += 1; });\n key1.isBetter = true;\n assert.equal(loader.size, 1);\n const cachedKey = loader.getCachedKey(roomId, aliceSenderKey, sessionId1)!;\n assert.equal(cachedKey, key1);\n await loader.useKey(cachedKey, async session => { invocations += 1; });\n assert.equal(loader.size, 1);\n assert.equal(invocations, 2);\n },\n \"dispose calls free on all sessions\": async assert => {\n instances = 0;\n const loader = new KeyLoader(olm as any as Olm, PICKLE_KEY, 2);\n await loader.useKey(new MockRoomKey(roomId, aliceSenderKey, sessionId1, 1), async session => {});\n await loader.useKey(new MockRoomKey(roomId, aliceSenderKey, sessionId2, 1), async session => {});\n assert.equal(instances, 2);\n assert.equal(loader.size, 2);\n loader.dispose();\n assert.strictEqual(instances, 0, \"instances\");\n assert.strictEqual(loader.size, 0, \"loader.size\");\n },\n \"checkBetterThanKeyInStorage false with cache\": async assert => {\n const loader = new KeyLoader(olm as any as Olm, PICKLE_KEY, 2);\n const key1 = new MockRoomKey(roomId, aliceSenderKey, sessionId1, 2);\n await loader.useKey(key1, async session => {});\n // fake we've checked with storage that this is the best key,\n // and as long is it remains the best key with newly added keys,\n // it will be returned from getCachedKey (as called from checkBetterThanKeyInStorage)\n key1.isBetter = true;\n const key2 = new MockRoomKey(roomId, aliceSenderKey, sessionId1, 3);\n // this will hit cache of key 1 so we pass in null as txn\n const isBetter = await key2.checkBetterThanKeyInStorage(loader, null as any);\n assert.strictEqual(isBetter, false);\n assert.strictEqual(key2.isBetter, false);\n },\n \"checkBetterThanKeyInStorage true with cache\": async assert => {\n const loader = new KeyLoader(olm as any as Olm, PICKLE_KEY, 2);\n const key1 = new MockRoomKey(roomId, aliceSenderKey, sessionId1, 2);\n key1.isBetter = true; // fake we've check with storage so far (not including key2) this is the best key\n await loader.useKey(key1, async session => {});\n const key2 = new MockRoomKey(roomId, aliceSenderKey, sessionId1, 1);\n // this will hit cache of key 1 so we pass in null as txn\n const isBetter = await key2.checkBetterThanKeyInStorage(loader, null as any);\n assert.strictEqual(isBetter, true);\n assert.strictEqual(key2.isBetter, true);\n },\n \"prefer to remove worst key for a session from cache\": async assert => {\n const loader = new KeyLoader(olm as any as Olm, PICKLE_KEY, 2);\n const key1 = new MockRoomKey(roomId, aliceSenderKey, sessionId1, 2);\n await loader.useKey(key1, async session => {});\n key1.isBetter = true; // set to true just so it gets returned from getCachedKey\n const key2 = new MockRoomKey(roomId, aliceSenderKey, sessionId1, 4);\n await loader.useKey(key2, async session => {});\n const key3 = new MockRoomKey(roomId, aliceSenderKey, sessionId1, 3);\n await loader.useKey(key3, async session => {});\n assert.strictEqual(loader.getCachedKey(roomId, aliceSenderKey, sessionId1), key1);\n },\n }\n}\n","/*\nCopyright 2022 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {MEGOLM_ALGORITHM} from \"../../common\";\nimport type {RoomKey} from \"../decryption/RoomKey\";\n\nimport type {BaseBackupInfo, SignatureMap, SessionKeyInfo} from \"./types\";\nimport type * as OlmNamespace from \"@matrix-org/olm\";\ntype Olm = typeof OlmNamespace;\n\nexport const Algorithm = \"m.megolm_backup.v1.curve25519-aes-sha2\";\n\nexport type BackupInfo = BaseBackupInfo & {\n algorithm: typeof Algorithm,\n auth_data: AuthData,\n}\n\ntype AuthData = {\n public_key: string,\n signatures: SignatureMap\n}\n\nexport type SessionData = {\n ciphertext: string,\n mac: string,\n ephemeral: string,\n}\n\nexport class BackupEncryption {\n constructor(\n private encryption?: Olm.PkEncryption,\n private decryption?: Olm.PkDecryption\n ) {}\n\n static fromAuthData(authData: AuthData, privateKey: Uint8Array, olm: Olm): BackupEncryption {\n const expectedPubKey = authData.public_key;\n const decryption = new olm.PkDecryption();\n const encryption = new olm.PkEncryption();\n try {\n const pubKey = decryption.init_with_private_key(privateKey);\n if (pubKey !== expectedPubKey) {\n throw new Error(`Bad backup key, public key does not match. Calculated ${pubKey} but expected ${expectedPubKey}`);\n }\n encryption.set_recipient_key(pubKey);\n } catch(err) {\n decryption.free();\n throw err;\n }\n return new BackupEncryption(encryption, decryption);\n }\n\n decryptRoomKey(sessionData: SessionData): SessionKeyInfo {\n const sessionInfo = this.decryption!.decrypt(\n sessionData.ephemeral,\n sessionData.mac,\n sessionData.ciphertext,\n );\n return JSON.parse(sessionInfo) as SessionKeyInfo;\n }\n\n encryptRoomKey(key: RoomKey, sessionKey: string): SessionData {\n const sessionInfo: SessionKeyInfo = {\n algorithm: MEGOLM_ALGORITHM,\n sender_key: key.senderKey,\n sender_claimed_keys: {ed25519: key.claimedEd25519Key},\n forwarding_curve25519_key_chain: [],\n session_key: sessionKey\n };\n return this.encryption!.encrypt(JSON.stringify(sessionInfo)) as SessionData;\n }\n\n dispose() {\n this.decryption?.free();\n this.decryption = undefined;\n this.encryption?.free();\n this.encryption = undefined;\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {StoreNames} from \"../../../storage/common\";\nimport {StoredRoomKey, keyFromBackup} from \"../decryption/RoomKey\";\nimport {MEGOLM_ALGORITHM} from \"../../common\";\nimport * as Curve25519 from \"./Curve25519\";\nimport {AbortableOperation} from \"../../../../utils/AbortableOperation\";\nimport {ObservableValue} from \"../../../../observable/ObservableValue\";\n\nimport {SetAbortableFn} from \"../../../../utils/AbortableOperation\";\nimport type {BackupInfo, SessionData, SessionKeyInfo, SessionInfo, KeyBackupPayload} from \"./types\";\nimport type {HomeServerApi} from \"../../../net/HomeServerApi\";\nimport type {IncomingRoomKey, RoomKey} from \"../decryption/RoomKey\";\nimport type {KeyLoader} from \"../decryption/KeyLoader\";\nimport type {SecretStorage} from \"../../../ssss/SecretStorage\";\nimport type {Storage} from \"../../../storage/idb/Storage\";\nimport type {ILogItem} from \"../../../../logging/types\";\nimport type {Platform} from \"../../../../platform/web/Platform\";\nimport type {Transaction} from \"../../../storage/idb/Transaction\";\nimport type * as OlmNamespace from \"@matrix-org/olm\";\ntype Olm = typeof OlmNamespace;\n\nconst KEYS_PER_REQUEST = 200;\n\nexport class KeyBackup {\n public readonly operationInProgress = new ObservableValue<AbortableOperation<Promise<void>, Progress> | undefined>(undefined);\n\n private _stopped = false;\n private _needsNewKey = false;\n private _hasBackedUpAllKeys = false;\n private _error?: Error;\n\n constructor(\n private readonly backupInfo: BackupInfo,\n private readonly crypto: Curve25519.BackupEncryption,\n private readonly hsApi: HomeServerApi,\n private readonly keyLoader: KeyLoader,\n private readonly storage: Storage,\n private readonly platform: Platform,\n private readonly maxDelay: number = 10000\n ) {}\n\n get hasStopped(): boolean { return this._stopped; }\n get error(): Error | undefined { return this._error; }\n get version(): string { return this.backupInfo.version; }\n get needsNewKey(): boolean { return this._needsNewKey; }\n get hasBackedUpAllKeys(): boolean { return this._hasBackedUpAllKeys; }\n\n async getRoomKey(roomId: string, sessionId: string, log: ILogItem): Promise<IncomingRoomKey | undefined> {\n const sessionResponse = await this.hsApi.roomKeyForRoomAndSession(this.backupInfo.version, roomId, sessionId, {log}).response();\n if (!sessionResponse.session_data) {\n return;\n }\n const sessionKeyInfo = this.crypto.decryptRoomKey(sessionResponse.session_data as SessionData);\n if (sessionKeyInfo?.algorithm === MEGOLM_ALGORITHM) {\n return keyFromBackup(roomId, sessionId, sessionKeyInfo);\n } else if (sessionKeyInfo?.algorithm) {\n log.set(\"unknown algorithm\", sessionKeyInfo.algorithm);\n }\n }\n\n markAllForBackup(txn: Transaction): Promise<number> {\n return txn.inboundGroupSessions.markAllAsNotBackedUp();\n }\n\n flush(log: ILogItem): void {\n if (!this.operationInProgress.get()) {\n log.wrapDetached(\"flush key backup\", async log => {\n if (this._needsNewKey) {\n log.set(\"needsNewKey\", this._needsNewKey);\n return;\n }\n this._stopped = false;\n this._error = undefined;\n this._hasBackedUpAllKeys = false;\n const operation = this._runFlushOperation(log);\n this.operationInProgress.set(operation);\n try {\n await operation.result;\n this._hasBackedUpAllKeys = true;\n } catch (err) {\n this._stopped = true;\n if (err.name === \"HomeServerError\" && (err.errcode === \"M_WRONG_ROOM_KEYS_VERSION\" || err.errcode === \"M_NOT_FOUND\")) {\n log.set(\"wrong_version\", true);\n this._needsNewKey = true;\n } else {\n // TODO should really also use AbortError in storage\n if (err.name !== \"AbortError\" || (err.name === \"StorageError\" && err.errcode === \"AbortError\")) {\n this._error = err;\n }\n }\n log.catch(err);\n }\n this.operationInProgress.set(undefined);\n });\n }\n }\n\n private _runFlushOperation(log: ILogItem): AbortableOperation<Promise<void>, Progress> {\n return new AbortableOperation(async (setAbortable, setProgress) => {\n let total = 0;\n let amountFinished = 0;\n while (true) {\n const waitMs = this.platform.random() * this.maxDelay;\n const timeout = this.platform.clock.createTimeout(waitMs);\n setAbortable(timeout);\n await timeout.elapsed();\n const txn = await this.storage.readTxn([StoreNames.inboundGroupSessions]);\n setAbortable(txn);\n // fetch total again on each iteration as while we are flushing, sync might be adding keys\n total = amountFinished + await txn.inboundGroupSessions.countNonBackedUpSessions();\n setProgress(new Progress(total, amountFinished));\n const keysNeedingBackup = (await txn.inboundGroupSessions.getFirstNonBackedUpSessions(KEYS_PER_REQUEST))\n .map(entry => new StoredRoomKey(entry));\n if (keysNeedingBackup.length === 0) {\n log.set(\"total\", total);\n return;\n }\n const payload = await this.encodeKeysForBackup(keysNeedingBackup);\n const uploadRequest = this.hsApi.uploadRoomKeysToBackup(this.backupInfo.version, payload, {log});\n setAbortable(uploadRequest);\n await uploadRequest.response();\n await this.markKeysAsBackedUp(keysNeedingBackup, setAbortable);\n amountFinished += keysNeedingBackup.length;\n setProgress(new Progress(total, amountFinished));\n }\n });\n }\n\n private async encodeKeysForBackup(roomKeys: RoomKey[]): Promise<KeyBackupPayload> {\n const payload: KeyBackupPayload = { rooms: {} };\n const payloadRooms = payload.rooms;\n for (const key of roomKeys) {\n let roomPayload = payloadRooms[key.roomId];\n if (!roomPayload) {\n roomPayload = payloadRooms[key.roomId] = { sessions: {} };\n }\n roomPayload.sessions[key.sessionId] = await this.encodeRoomKey(key);\n }\n return payload;\n }\n\n private async markKeysAsBackedUp(roomKeys: RoomKey[], setAbortable: SetAbortableFn) {\n const txn = await this.storage.readWriteTxn([\n StoreNames.inboundGroupSessions,\n ]);\n setAbortable(txn);\n try {\n await Promise.all(roomKeys.map(key => {\n return txn.inboundGroupSessions.markAsBackedUp(key.roomId, key.senderKey, key.sessionId);\n }));\n } catch (err) {\n txn.abort();\n throw err;\n }\n await txn.complete();\n }\n\n private async encodeRoomKey(roomKey: RoomKey): Promise<SessionInfo> {\n return await this.keyLoader.useKey(roomKey, session => {\n const firstMessageIndex = session.first_known_index();\n const sessionKey = session.export_session(firstMessageIndex);\n return {\n first_message_index: firstMessageIndex,\n forwarded_count: 0,\n is_verified: false,\n session_data: this.crypto.encryptRoomKey(roomKey, sessionKey)\n };\n });\n }\n\n dispose() {\n this.crypto.dispose();\n }\n\n static async fromSecretStorage(platform: Platform, olm: Olm, secretStorage: SecretStorage, hsApi: HomeServerApi, keyLoader: KeyLoader, storage: Storage, txn: Transaction): Promise<KeyBackup | undefined> {\n const base64PrivateKey = await secretStorage.readSecret(\"m.megolm_backup.v1\", txn);\n if (base64PrivateKey) {\n const privateKey = new Uint8Array(platform.encoding.base64.decode(base64PrivateKey));\n const backupInfo = await hsApi.roomKeysVersion().response() as BackupInfo;\n if (backupInfo.algorithm === Curve25519.Algorithm) {\n const crypto = Curve25519.BackupEncryption.fromAuthData(backupInfo.auth_data, privateKey, olm);\n return new KeyBackup(backupInfo, crypto, hsApi, keyLoader, storage, platform);\n } else {\n throw new Error(`Unknown backup algorithm: ${backupInfo.algorithm}`);\n }\n }\n }\n}\n\nexport class Progress {\n constructor(\n public readonly total: number,\n public readonly finished: number\n ) {}\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {MEGOLM_ALGORITHM} from \"../common.js\";\nimport {OutboundRoomKey} from \"./decryption/RoomKey\";\n\nexport class Encryption {\n constructor({pickleKey, olm, account, keyLoader, storage, now, ownDeviceId}) {\n this._pickleKey = pickleKey;\n this._olm = olm;\n this._account = account;\n this._keyLoader = keyLoader;\n this._storage = storage;\n this._now = now;\n this._ownDeviceId = ownDeviceId;\n }\n\n discardOutboundSession(roomId, txn) {\n txn.outboundGroupSessions.remove(roomId);\n }\n\n async createRoomKeyMessage(roomId, txn) {\n let sessionEntry = await txn.outboundGroupSessions.get(roomId);\n if (sessionEntry) {\n const session = new this._olm.OutboundGroupSession();\n try {\n session.unpickle(this._pickleKey, sessionEntry.session);\n return this._createRoomKeyMessage(session, roomId);\n } finally {\n session.free();\n }\n }\n }\n\n createWithheldMessage(roomMessage, code, reason) {\n return {\n algorithm: roomMessage.algorithm,\n code,\n reason,\n room_id: roomMessage.room_id,\n sender_key: this._account.identityKeys.curve25519,\n session_id: roomMessage.session_id\n };\n }\n\n async ensureOutboundSession(roomId, encryptionParams) {\n let session = new this._olm.OutboundGroupSession();\n try {\n const txn = await this._storage.readWriteTxn([\n this._storage.storeNames.inboundGroupSessions,\n this._storage.storeNames.outboundGroupSessions,\n ]);\n let roomKeyMessage;\n try {\n let sessionEntry = await txn.outboundGroupSessions.get(roomId);\n roomKeyMessage = await this._readOrCreateSession(session, sessionEntry, roomId, encryptionParams, txn);\n if (roomKeyMessage) {\n this._writeSession(this._now(), session, roomId, txn);\n }\n } catch (err) {\n txn.abort();\n throw err;\n }\n await txn.complete();\n return roomKeyMessage;\n } finally {\n session.free();\n }\n }\n\n async _readOrCreateSession(session, sessionEntry, roomId, encryptionParams, txn) {\n if (sessionEntry) {\n session.unpickle(this._pickleKey, sessionEntry.session);\n }\n if (!sessionEntry || this._needsToRotate(session, sessionEntry.createdAt, encryptionParams)) {\n // in the case of rotating, recreate a session as we already unpickled into it\n if (sessionEntry) {\n session.free();\n session = new this._olm.OutboundGroupSession();\n }\n session.create();\n const roomKeyMessage = this._createRoomKeyMessage(session, roomId);\n const roomKey = new OutboundRoomKey(roomId, session, this._account.identityKeys);\n await roomKey.write(this._keyLoader, txn);\n return roomKeyMessage;\n }\n }\n\n _writeSession(createdAt, session, roomId, txn) {\n txn.outboundGroupSessions.set({\n roomId,\n session: session.pickle(this._pickleKey),\n createdAt,\n });\n }\n\n /**\n * Encrypts a message with megolm\n * @param {string} roomId \n * @param {string} type event type to encrypt\n * @param {string} content content to encrypt\n * @param {object} encryptionParams the content of the m.room.encryption event\n * @return {Promise<EncryptionResult>}\n */\n async encrypt(roomId, type, content, encryptionParams) {\n let session = new this._olm.OutboundGroupSession();\n try {\n const txn = await this._storage.readWriteTxn([\n this._storage.storeNames.inboundGroupSessions,\n this._storage.storeNames.outboundGroupSessions,\n ]);\n let roomKeyMessage;\n let encryptedContent;\n try {\n let sessionEntry = await txn.outboundGroupSessions.get(roomId);\n roomKeyMessage = await this._readOrCreateSession(session, sessionEntry, roomId, encryptionParams, txn);\n encryptedContent = this._encryptContent(roomId, session, type, content);\n // update timestamp when a new session is created\n const createdAt = roomKeyMessage ? this._now() : sessionEntry.createdAt;\n this._writeSession(createdAt, session, roomId, txn);\n\n } catch (err) {\n txn.abort();\n throw err;\n }\n await txn.complete();\n return new EncryptionResult(encryptedContent, roomKeyMessage);\n } finally {\n if (session) {\n session.free();\n }\n }\n }\n\n _needsToRotate(session, createdAt, encryptionParams) {\n let rotationPeriodMs = 604800000; // default\n if (Number.isSafeInteger(encryptionParams?.rotation_period_ms)) {\n rotationPeriodMs = encryptionParams?.rotation_period_ms;\n }\n let rotationPeriodMsgs = 100; // default\n if (Number.isSafeInteger(encryptionParams?.rotation_period_msgs)) {\n rotationPeriodMsgs = encryptionParams?.rotation_period_msgs;\n }\n\n if (this._now() > (createdAt + rotationPeriodMs)) {\n return true;\n }\n if (session.message_index() >= rotationPeriodMsgs) {\n return true;\n } \n }\n\n _encryptContent(roomId, session, type, content) {\n const plaintext = JSON.stringify({\n room_id: roomId,\n type,\n content\n });\n const ciphertext = session.encrypt(plaintext);\n\n const encryptedContent = {\n algorithm: MEGOLM_ALGORITHM,\n sender_key: this._account.identityKeys.curve25519,\n ciphertext,\n session_id: session.session_id(),\n device_id: this._ownDeviceId\n };\n\n return encryptedContent;\n }\n\n _createRoomKeyMessage(session, roomId) {\n return {\n room_id: roomId,\n session_id: session.session_id(),\n session_key: session.session_key(),\n algorithm: MEGOLM_ALGORITHM,\n // chain_index is ignored by element-web if not all clients\n // but let's send it anyway, as element-web does so\n chain_index: session.message_index()\n }\n }\n}\n\n/**\n * @property {object?} roomKeyMessage if encrypting this message\n * created a new outbound session,\n * this contains the content of the m.room_key message\n * that should be sent out over olm.\n * @property {object} content the encrypted message as the content of\n * the m.room.encrypted event that should be sent out \n */\nclass EncryptionResult {\n constructor(content, roomKeyMessage) {\n this.content = content;\n this.roomKeyMessage = roomKeyMessage;\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {LogLevel, LogFilter} from \"./LogFilter\";\nimport type {BaseLogger} from \"./BaseLogger\";\nimport type {ISerializedItem, ILogItem, LogItemValues, LabelOrValues, FilterCreator, LogCallback} from \"./types\";\n\nexport class LogItem implements ILogItem {\n public readonly start: number;\n public logLevel: LogLevel;\n public error?: Error;\n public end?: number;\n private _values: LogItemValues;\n private _logger: BaseLogger;\n private _filterCreator?: FilterCreator;\n private _children?: Array<LogItem>;\n\n constructor(labelOrValues: LabelOrValues, logLevel: LogLevel, logger: BaseLogger, filterCreator?: FilterCreator) {\n this._logger = logger;\n this.start = logger._now();\n // (l)abel\n this._values = typeof labelOrValues === \"string\" ? {l: labelOrValues} : labelOrValues;\n this.logLevel = logLevel;\n this._filterCreator = filterCreator;\n }\n\n /** start a new root log item and run it detached mode, see BaseLogger.runDetached */\n runDetached(labelOrValues: LabelOrValues, callback: LogCallback<unknown>, logLevel?: LogLevel, filterCreator?: FilterCreator): ILogItem {\n return this._logger.runDetached(labelOrValues, callback, logLevel, filterCreator);\n }\n\n /** start a new detached root log item and log a reference to it from this item */\n wrapDetached(labelOrValues: LabelOrValues, callback: LogCallback<unknown>, logLevel?: LogLevel, filterCreator?: FilterCreator): void {\n this.refDetached(this.runDetached(labelOrValues, callback, logLevel, filterCreator));\n }\n\n /** logs a reference to a different log item, usually obtained from runDetached.\n This is useful if the referenced operation can't be awaited. */\n refDetached(logItem: ILogItem, logLevel?: LogLevel): void {\n logItem.ensureRefId();\n this.log({ref: logItem.values.refId}, logLevel);\n }\n\n ensureRefId(): void {\n if (!this._values.refId) {\n this.set(\"refId\", this._logger._createRefId());\n }\n }\n\n /**\n * Creates a new child item and runs it in `callback`.\n */\n wrap<T>(labelOrValues: LabelOrValues, callback: LogCallback<T>, logLevel?: LogLevel, filterCreator?: FilterCreator): T {\n const item = this.child(labelOrValues, logLevel, filterCreator);\n return item.run(callback);\n }\n\n get duration(): number | undefined {\n if (this.end) {\n return this.end - this.start;\n } else {\n return undefined;\n }\n }\n\n durationWithoutType(type: string): number | undefined {\n const durationOfType = this.durationOfType(type);\n if (this.duration && durationOfType) {\n return this.duration - durationOfType;\n }\n }\n\n durationOfType(type: string): number | undefined {\n if (this._values.t === type) {\n return this.duration;\n } else if (this._children) {\n return this._children.reduce((sum, c) => {\n const duration = c.durationOfType(type);\n return sum + (duration ?? 0);\n }, 0);\n } else {\n return 0;\n }\n }\n \n /**\n * Creates a new child item that finishes immediately\n * Finished items should not be modified anymore as they can be serialized\n * at any stage, but using `set` on the return value in a synchronous way should still be safe.\n */\n log(labelOrValues: LabelOrValues, logLevel?: LogLevel): ILogItem {\n const item = this.child(labelOrValues, logLevel);\n item.end = item.start;\n return item;\n }\n\n set(key: string | object, value?: unknown): ILogItem {\n if(typeof key === \"object\") {\n const values = key;\n Object.assign(this._values, values);\n } else {\n this._values[key] = value;\n }\n return this;\n }\n\n serialize(filter: LogFilter, parentStartTime: number | undefined, forced: boolean): ISerializedItem | undefined {\n if (this._filterCreator) {\n try {\n filter = this._filterCreator(new LogFilter(filter), this);\n } catch (err) {\n console.error(\"Error creating log filter\", err);\n }\n }\n let children: Array<ISerializedItem> | null = null;\n if (this._children) {\n children = this._children.reduce((array: Array<ISerializedItem>, c) => {\n const s = c.serialize(filter, this.start, false);\n if (s) {\n if (array === null) {\n array = [];\n }\n array.push(s);\n }\n return array;\n }, null);\n }\n if (filter && !filter.filter(this, children)) {\n return;\n }\n // in (v)alues, (l)abel and (t)ype are also reserved.\n const item: ISerializedItem = {\n // (s)tart\n s: typeof parentStartTime === \"number\" ? this.start - parentStartTime : this.start,\n // (d)uration\n d: this.duration,\n // (v)alues\n v: this._values,\n // (l)evel\n l: this.logLevel\n };\n if (this.error) {\n // (e)rror\n item.e = {\n stack: this.error.stack,\n name: this.error.name,\n message: this.error.message.split(\"\\n\")[0]\n };\n }\n if (forced) {\n item.f = true; //(f)orced\n }\n if (children) {\n // (c)hildren\n item.c = children;\n }\n return item;\n }\n\n /**\n * You probably want to use `wrap` instead of this.\n * \n * Runs a callback passing this log item,\n * recording the timing and any error.\n *\n * callback can return a Promise.\n *\n * Should only be called once.\n * \n * @param {Function} callback [description]\n * @return {[type]} [description]\n */\n run<T>(callback: LogCallback<T>): T {\n if (this.end !== undefined) {\n console.trace(\"log item is finished, additional logs will likely not be recorded\");\n }\n try {\n const result = callback(this);\n if (result instanceof Promise) {\n return result.then(promiseResult => {\n this.finish();\n return promiseResult;\n }, err => {\n throw this.catch(err);\n }) as unknown as T;\n } else {\n this.finish();\n return result;\n }\n } catch (err) {\n throw this.catch(err);\n }\n }\n\n /**\n * finished the item, recording the end time. After finishing, an item can't be modified anymore as it will be persisted.\n * @internal shouldn't typically be called by hand. allows to force finish if a promise is still running when closing the app\n */\n finish(): void {\n if (this.end === undefined) {\n if (this._children) {\n for(const c of this._children) {\n c.finish();\n }\n }\n this.end = this._logger._now();\n }\n }\n\n // expose log level without needing import everywhere\n get level(): typeof LogLevel {\n return LogLevel;\n }\n\n catch(err: Error): Error {\n this.error = err;\n this.logLevel = LogLevel.Error;\n this.finish();\n return err;\n }\n\n child(labelOrValues: LabelOrValues, logLevel?: LogLevel, filterCreator?: FilterCreator): LogItem {\n if (this.end) {\n console.trace(\"log item is finished, additional logs will likely not be recorded\");\n }\n if (!logLevel) {\n logLevel = this.logLevel || LogLevel.Info;\n }\n const item = new LogItem(labelOrValues, logLevel, this._logger, filterCreator);\n if (!this._children) {\n this._children = [];\n }\n this._children.push(item);\n return item;\n }\n\n get logger(): BaseLogger {\n return this._logger;\n }\n\n get values(): LogItemValues {\n return this._values;\n }\n\n get children(): Array<LogItem> | undefined {\n return this._children;\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {LogItem} from \"./LogItem\";\nimport {LogLevel, LogFilter} from \"./LogFilter\";\nimport type {ILogger, ILogExport, FilterCreator, LabelOrValues, LogCallback, ILogItem, ISerializedItem} from \"./types\";\nimport type {Platform} from \"../platform/web/Platform.js\";\n\nexport abstract class BaseLogger implements ILogger {\n protected _openItems: Set<LogItem> = new Set();\n protected _platform: Platform;\n protected _serializedTransformer: (item: ISerializedItem) => ISerializedItem;\n\n constructor({platform, serializedTransformer = (item: ISerializedItem) => item}) {\n this._platform = platform;\n this._serializedTransformer = serializedTransformer;\n }\n\n log(labelOrValues: LabelOrValues, logLevel: LogLevel = LogLevel.Info): void {\n const item = new LogItem(labelOrValues, logLevel, this);\n item.end = item.start;\n this._persistItem(item, undefined, false);\n }\n\n /** if item is a log item, wrap the callback in a child of it, otherwise start a new root log item. */\n wrapOrRun<T>(item: ILogItem | undefined, labelOrValues: LabelOrValues, callback: LogCallback<T>, logLevel?: LogLevel, filterCreator?: FilterCreator): T {\n if (item) {\n return item.wrap(labelOrValues, callback, logLevel, filterCreator);\n } else {\n return this.run(labelOrValues, callback, logLevel, filterCreator);\n }\n }\n\n /** run a callback in detached mode,\n where the (async) result or errors are not propagated but still logged.\n Useful to pair with LogItem.refDetached.\n\n @return {ILogItem} the log item added, useful to pass to LogItem.refDetached */\n runDetached<T>(labelOrValues: LabelOrValues, callback: LogCallback<T>, logLevel?: LogLevel, filterCreator?: FilterCreator): ILogItem {\n if (!logLevel) {\n logLevel = LogLevel.Info;\n }\n const item = new LogItem(labelOrValues, logLevel, this);\n this._run(item, callback, logLevel, false /* don't throw, nobody is awaiting */, filterCreator);\n return item;\n }\n\n /** run a callback wrapped in a log operation.\n Errors and duration are transparently logged, also for async operations.\n Whatever the callback returns is returned here. */\n run<T>(labelOrValues: LabelOrValues, callback: LogCallback<T>, logLevel?: LogLevel, filterCreator?: FilterCreator): T {\n if (logLevel === undefined) {\n logLevel = LogLevel.Info;\n }\n const item = new LogItem(labelOrValues, logLevel, this);\n return this._run(item, callback, logLevel, true, filterCreator);\n }\n\n _run<T>(item: LogItem, callback: LogCallback<T>, logLevel: LogLevel, wantResult: true, filterCreator?: FilterCreator): T;\n // we don't return if we don't throw, as we don't have anything to return when an error is caught but swallowed for the fire-and-forget case.\n _run<T>(item: LogItem, callback: LogCallback<T>, logLevel: LogLevel, wantResult: false, filterCreator?: FilterCreator): void;\n _run<T>(item: LogItem, callback: LogCallback<T>, logLevel: LogLevel, wantResult: boolean, filterCreator?: FilterCreator): T | void {\n this._openItems.add(item);\n\n const finishItem = () => {\n let filter = new LogFilter();\n if (filterCreator) {\n try {\n filter = filterCreator(filter, item);\n } catch (err) {\n console.error(\"Error while creating log filter\", err);\n }\n } else {\n // if not filter is specified, filter out anything lower than the initial log level\n filter = filter.minLevel(logLevel);\n }\n try {\n this._persistItem(item, filter, false);\n } catch (err) {\n console.error(\"Could not persist log item\", err);\n }\n this._openItems.delete(item);\n };\n\n try {\n let result = item.run(callback);\n if (result instanceof Promise) {\n result = result.then(promiseResult => {\n finishItem();\n return promiseResult;\n }, err => {\n finishItem();\n if (wantResult) {\n throw err;\n }\n }) as unknown as T;\n if (wantResult) {\n return result;\n }\n } else {\n finishItem();\n if(wantResult) {\n return result;\n }\n }\n } catch (err) {\n finishItem();\n if (wantResult) {\n throw err;\n }\n }\n }\n\n _finishOpenItems() {\n for (const openItem of this._openItems) {\n openItem.finish();\n try {\n // for now, serialize with an all-permitting filter\n // as the createFilter function would get a distorted image anyway\n // about the duration of the item, etc ...\n // true for force finish\n this._persistItem(openItem, new LogFilter(), true);\n } catch (err) {\n console.error(\"Could not serialize log item\", err);\n }\n }\n this._openItems.clear();\n }\n\n abstract _persistItem(item: LogItem, filter?: LogFilter, forced?: boolean): void;\n\n abstract export(): Promise<ILogExport | undefined>;\n\n // expose log level without needing \n get level(): typeof LogLevel {\n return LogLevel;\n }\n\n _now(): number {\n return this._platform.clock.now();\n }\n\n _createRefId(): number {\n return Math.round(this._platform.random() * Number.MAX_SAFE_INTEGER);\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\nimport {BaseLogger} from \"./BaseLogger\";\nimport {LogItem} from \"./LogItem\";\nimport type {ILogItem, LogItemValues, ILogExport} from \"./types\";\n\nexport class ConsoleLogger extends BaseLogger {\n _persistItem(item: LogItem): void {\n printToConsole(item);\n }\n\n async export(): Promise<ILogExport | undefined> {\n return undefined;\n }\n}\n\nconst excludedKeysFromTable = [\"l\", \"id\"];\nfunction filterValues(values: LogItemValues): LogItemValues | null {\n return Object.entries(values)\n .filter(([key]) => !excludedKeysFromTable.includes(key))\n .reduce((obj: LogItemValues, [key, value]) => {\n obj = obj || {};\n obj[key] = value;\n return obj;\n }, null);\n}\n\nfunction printToConsole(item: LogItem): void {\n const label = `${itemCaption(item)} (${item.duration}ms)`;\n const filteredValues = filterValues(item.values);\n const shouldGroup = item.children || filteredValues;\n if (shouldGroup) {\n if (item.error) {\n console.group(label);\n } else {\n console.groupCollapsed(label);\n }\n if (item.error) {\n console.error(item.error);\n }\n } else {\n if (item.error) {\n console.error(item.error);\n } else {\n console.log(label);\n }\n }\n if (filteredValues) {\n console.table(filteredValues);\n }\n if (item.children) {\n for(const c of item.children) {\n printToConsole(c);\n }\n }\n if (shouldGroup) {\n console.groupEnd();\n }\n}\n\nfunction itemCaption(item: ILogItem): string {\n if (item.values.t === \"network\") {\n return `${item.values.method} ${item.values.url}`;\n } else if (item.values.l && typeof item.values.id !== \"undefined\") {\n return `${item.values.l} ${item.values.id}`;\n } else if (item.values.l && typeof item.values.status !== \"undefined\") {\n return `${item.values.l} (${item.values.status})`;\n } else if (item.values.l && item.error) {\n return `${item.values.l} failed`;\n } else if (typeof item.values.ref !== \"undefined\") {\n return `ref ${item.values.ref}`;\n } else {\n return item.values.l || item.values.type;\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {MEGOLM_ALGORITHM, DecryptionSource} from \"./common.js\";\nimport {groupEventsBySession} from \"./megolm/decryption/utils\";\nimport {mergeMap} from \"../../utils/mergeMap\";\nimport {groupBy} from \"../../utils/groupBy\";\nimport {makeTxnId} from \"../common.js\";\nimport {iterateResponseStateEvents} from \"../room/common\";\n\nconst ENCRYPTED_TYPE = \"m.room.encrypted\";\nconst ROOM_HISTORY_VISIBILITY_TYPE = \"m.room.history_visibility\";\n// how often ensureMessageKeyIsShared can check if it needs to\n// create a new outbound session\n// note that encrypt could still create a new session\nconst MIN_PRESHARE_INTERVAL = 60 * 1000; // 1min\n\n// TODO: this class is a good candidate for splitting up into encryption and decryption, there doesn't seem to be much overlap\nexport class RoomEncryption {\n constructor({room, deviceTracker, olmEncryption, megolmEncryption, megolmDecryption, encryptionParams, storage, keyBackup, notifyMissingMegolmSession, clock}) {\n this._room = room;\n this._deviceTracker = deviceTracker;\n this._olmEncryption = olmEncryption;\n this._megolmEncryption = megolmEncryption;\n this._megolmDecryption = megolmDecryption;\n // content of the m.room.encryption event\n this._encryptionParams = encryptionParams;\n // caches devices to verify events\n this._senderDeviceCache = new Map();\n this._storage = storage;\n this._keyBackup = keyBackup;\n this._notifyMissingMegolmSession = notifyMissingMegolmSession;\n this._clock = clock;\n this._isFlushingRoomKeyShares = false;\n this._lastKeyPreShareTime = null;\n this._keySharePromise = null;\n this._historyVisibility = undefined;\n this._disposed = false;\n }\n\n enableKeyBackup(keyBackup) {\n if (this._keyBackup && !!keyBackup) {\n return;\n }\n this._keyBackup = keyBackup;\n }\n\n async restoreMissingSessionsFromBackup(entries, log) {\n const events = entries.filter(e => e.isEncrypted && !e.isDecrypted && e.event).map(e => e.event);\n const eventsBySession = groupEventsBySession(events);\n const groups = Array.from(eventsBySession.values());\n const txn = await this._storage.readTxn([this._storage.storeNames.inboundGroupSessions]);\n const hasSessions = await Promise.all(groups.map(async group => {\n return this._megolmDecryption.hasSession(this._room.id, group.senderKey, group.sessionId, txn);\n }));\n const missingSessions = groups.filter((_, i) => !hasSessions[i]);\n if (missingSessions.length) {\n // start with last sessions which should be for the last items in the timeline\n for (var i = missingSessions.length - 1; i >= 0; i--) {\n const session = missingSessions[i];\n await log.wrap(\"session\", log => this._requestMissingSessionFromBackup(session.senderKey, session.sessionId, log));\n }\n }\n }\n\n notifyTimelineClosed() {\n this._senderDeviceCache = new Map(); // purge the sender device cache\n }\n\n async writeSync(roomResponse, memberChanges, txn, log) {\n let historyVisibility = await this._loadHistoryVisibilityIfNeeded(this._historyVisibility, txn);\n const addedMembers = [];\n const removedMembers = [];\n // update the historyVisibility if needed\n await iterateResponseStateEvents(roomResponse, event => {\n // TODO: can the same state event appear twice? Hence we would be rewriting the useridentities twice...\n // we'll see in the logs\n if(event.state_key === \"\" && event.type === ROOM_HISTORY_VISIBILITY_TYPE) {\n const newHistoryVisibility = event?.content?.history_visibility;\n if (newHistoryVisibility !== historyVisibility) {\n return log.wrap({\n l: \"history_visibility changed\",\n from: historyVisibility,\n to: newHistoryVisibility\n }, async log => {\n historyVisibility = newHistoryVisibility;\n const result = await this._deviceTracker.writeHistoryVisibility(this._room, historyVisibility, txn, log);\n addedMembers.push(...result.added);\n removedMembers.push(...result.removed);\n });\n }\n }\n });\n // process member changes\n if (memberChanges.size) {\n const result = await this._deviceTracker.writeMemberChanges(\n this._room, memberChanges, historyVisibility, txn);\n addedMembers.push(...result.added);\n removedMembers.push(...result.removed);\n }\n // discard key if somebody (including ourselves) left\n if (removedMembers.length) {\n log.log({\n l: \"discardOutboundSession\",\n leftUsers: removedMembers,\n });\n this._megolmEncryption.discardOutboundSession(this._room.id, txn);\n }\n let shouldFlush = false;\n // add room to userIdentities if needed, and share the current key with them\n if (addedMembers.length) {\n shouldFlush = await this._addShareRoomKeyOperationForMembers(addedMembers, txn, log);\n }\n return {shouldFlush, historyVisibility};\n }\n\n afterSync({historyVisibility}) {\n this._historyVisibility = historyVisibility;\n }\n\n async _loadHistoryVisibilityIfNeeded(historyVisibility, txn = undefined) {\n if (!historyVisibility) {\n if (!txn) {\n txn = await this._storage.readTxn([this._storage.storeNames.roomState]);\n }\n const visibilityEntry = await txn.roomState.get(this._room.id, ROOM_HISTORY_VISIBILITY_TYPE, \"\");\n if (visibilityEntry) {\n return visibilityEntry.event?.content?.history_visibility;\n }\n }\n return historyVisibility;\n }\n\n async prepareDecryptAll(events, newKeys, source, txn) {\n const errors = new Map();\n const validEvents = [];\n for (const event of events) {\n if (event.redacted_because || event.unsigned?.redacted_because) {\n continue;\n }\n if (event.content?.algorithm !== MEGOLM_ALGORITHM) {\n errors.set(event.event_id, new Error(\"Unsupported algorithm: \" + event.content?.algorithm));\n }\n validEvents.push(event);\n }\n const preparation = await this._megolmDecryption.prepareDecryptAll(\n this._room.id, validEvents, newKeys, txn);\n return new DecryptionPreparation(preparation, errors, source, this, events);\n }\n\n async _processDecryptionResults(events, results, errors, source, txn, log) {\n const missingSessionEvents = events.filter(event => {\n const error = errors.get(event.event_id);\n return error?.code === \"MEGOLM_NO_SESSION\";\n });\n if (!missingSessionEvents.length) {\n return;\n }\n // store missing event ids if received from sync\n const missingEventsBySession = groupEventsBySession(missingSessionEvents);\n if (source === DecryptionSource.Sync) {\n await Promise.all(Array.from(missingEventsBySession.values()).map(async group => {\n const eventIds = group.events.map(e => e.event_id);\n return this._megolmDecryption.addMissingKeyEventIds(\n this._room.id, group.senderKey, group.sessionId, eventIds, txn);\n }));\n }\n \n if (!this._keyBackup) {\n return;\n }\n\n log.wrapDetached(\"check key backup\", async log => {\n // if the message came from sync, wait 10s to see if the room key arrives late,\n // and only after that proceed to request from backup\n log.set(\"source\", source);\n log.set(\"events\", missingSessionEvents.length);\n log.set(\"sessions\", missingEventsBySession.size);\n if (source === DecryptionSource.Sync) {\n await this._clock.createTimeout(10000).elapsed();\n if (this._disposed) {\n return;\n }\n // now check which sessions have been received already\n const txn = await this._storage.readTxn([this._storage.storeNames.inboundGroupSessions]);\n await Promise.all(Array.from(missingEventsBySession).map(async ([key, group]) => {\n if (await this._megolmDecryption.hasSession(this._room.id, group.senderKey, group.sessionId, txn)) {\n missingEventsBySession.delete(key);\n }\n }));\n }\n await Promise.all(Array.from(missingEventsBySession.values()).map(group => {\n return log.wrap(\"session\", log => this._requestMissingSessionFromBackup(group.senderKey, group.sessionId, log));\n }));\n });\n }\n\n async _verifyDecryptionResult(result, txn) {\n let device = this._senderDeviceCache.get(result.senderCurve25519Key);\n if (!device) {\n device = await this._deviceTracker.getDeviceByCurve25519Key(result.senderCurve25519Key, txn);\n this._senderDeviceCache.set(result.senderCurve25519Key, device);\n }\n if (device) {\n result.setDevice(device);\n } else if (!this._room.isTrackingMembers) {\n result.setRoomNotTrackedYet();\n }\n }\n\n async _requestMissingSessionFromBackup(senderKey, sessionId, log) {\n // show prompt to enable secret storage\n if (!this._keyBackup) {\n log.set(\"enabled\", false);\n this._notifyMissingMegolmSession();\n return;\n }\n log.set(\"id\", sessionId);\n log.set(\"senderKey\", senderKey);\n try {\n const roomKey = await this._keyBackup.getRoomKey(this._room.id, sessionId, log);\n if (roomKey) {\n if (roomKey.senderKey !== senderKey) {\n log.set(\"wrong_sender_key\", roomKey.senderKey);\n log.logLevel = log.level.Warn;\n return;\n }\n let keyIsBestOne = false;\n let retryEventIds;\n const txn = await this._storage.readWriteTxn([this._storage.storeNames.inboundGroupSessions]);\n try {\n keyIsBestOne = await this._megolmDecryption.writeRoomKey(roomKey, txn);\n log.set(\"isBetter\", keyIsBestOne);\n if (keyIsBestOne) {\n retryEventIds = roomKey.eventIds;\n }\n } catch (err) {\n txn.abort();\n throw err;\n }\n await txn.complete();\n if (keyIsBestOne) {\n await log.wrap(\"retryDecryption\", log => this._room.notifyRoomKey(roomKey, retryEventIds || [], log));\n }\n }\n } catch (err) {\n if (!(err.name === \"HomeServerError\" && err.errcode === \"M_NOT_FOUND\")) {\n log.set(\"not_found\", true);\n } else {\n log.error = err;\n log.logLevel = log.level.Error;\n }\n }\n }\n\n /**\n * @param {RoomKey} roomKeys\n * @param {Transaction} txn\n * @return {Promise<Array<string>>} the event ids that should be retried to decrypt\n */\n getEventIdsForMissingKey(roomKey, txn) {\n return this._megolmDecryption.getEventIdsForMissingKey(this._room.id, roomKey.senderKey, roomKey.sessionId, txn);\n }\n\n /** shares the encryption key for the next message if needed */\n async ensureMessageKeyIsShared(hsApi, log) {\n if (this._lastKeyPreShareTime?.measure() < MIN_PRESHARE_INTERVAL) {\n return;\n }\n this._lastKeyPreShareTime = this._clock.createMeasure();\n try {\n this._keySharePromise = (async () => {\n const roomKeyMessage = await this._megolmEncryption.ensureOutboundSession(this._room.id, this._encryptionParams);\n if (roomKeyMessage) {\n this._keyBackup?.flush(log);\n await log.wrap(\"share key\", log => this._shareNewRoomKey(roomKeyMessage, hsApi, log));\n }\n })();\n await this._keySharePromise;\n } finally {\n this._keySharePromise = null;\n }\n }\n\n async encrypt(type, content, hsApi, log) {\n // ensureMessageKeyIsShared is still running,\n // wait for it to create and share a key if needed\n if (this._keySharePromise) {\n log.set(\"waitForRunningKeyShare\", true);\n await this._keySharePromise;\n }\n const megolmResult = await log.wrap(\"megolm encrypt\", () => this._megolmEncryption.encrypt(this._room.id, type, content, this._encryptionParams));\n if (megolmResult.roomKeyMessage) {\n this._keyBackup?.flush(log);\n await log.wrap(\"share key\", log => this._shareNewRoomKey(megolmResult.roomKeyMessage, hsApi, log));\n }\n return {\n type: ENCRYPTED_TYPE,\n content: megolmResult.content\n };\n }\n\n needsToShareKeys(memberChanges) {\n for (const m of memberChanges.values()) {\n if (m.hasJoined) {\n return true;\n }\n }\n return false;\n }\n\n async _shareNewRoomKey(roomKeyMessage, hsApi, log) {\n this._historyVisibility = await this._loadHistoryVisibilityIfNeeded(this._historyVisibility);\n await this._deviceTracker.trackRoom(this._room, this._historyVisibility, log);\n const devices = await this._deviceTracker.devicesForTrackedRoom(this._room.id, hsApi, log);\n const userIds = Array.from(devices.reduce((set, device) => set.add(device.userId), new Set()));\n \n let writeOpTxn = await this._storage.readWriteTxn([this._storage.storeNames.operations]);\n let operation;\n try {\n operation = this._writeRoomKeyShareOperation(roomKeyMessage, userIds, writeOpTxn);\n } catch (err) {\n writeOpTxn.abort();\n throw err;\n }\n // TODO: at this point we have the room key stored, and the rest is sort of optional\n // it would be nice if we could signal SendQueue that any error from here on is non-fatal and\n // return the encrypted payload.\n await this._processShareRoomKeyOperation(operation, hsApi, log);\n }\n\n async _addShareRoomKeyOperationForMembers(userIds, txn, log) {\n const roomKeyMessage = await this._megolmEncryption.createRoomKeyMessage(\n this._room.id, txn);\n if (roomKeyMessage) {\n log.log({\n l: \"share key for new members\", userIds,\n id: roomKeyMessage.session_id,\n chain_index: roomKeyMessage.chain_index\n });\n this._writeRoomKeyShareOperation(roomKeyMessage, userIds, txn);\n return true;\n }\n return false;\n }\n\n async flushPendingRoomKeyShares(hsApi, operations, log) {\n // this has to be reentrant as it can be called from Room.start while still running\n if (this._isFlushingRoomKeyShares) {\n return;\n }\n this._isFlushingRoomKeyShares = true;\n try {\n if (!operations) {\n const txn = await this._storage.readTxn([this._storage.storeNames.operations]);\n operations = await txn.operations.getAllByTypeAndScope(\"share_room_key\", this._room.id);\n }\n for (const operation of operations) {\n // just to be sure\n if (operation.type !== \"share_room_key\") {\n continue;\n }\n await log.wrap(\"operation\", log => this._processShareRoomKeyOperation(operation, hsApi, log));\n }\n } finally {\n this._isFlushingRoomKeyShares = false;\n }\n }\n\n _writeRoomKeyShareOperation(roomKeyMessage, userIds, txn) {\n const id = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString();\n const operation = {\n id,\n type: \"share_room_key\",\n scope: this._room.id,\n userIds,\n roomKeyMessage,\n };\n txn.operations.add(operation);\n return operation;\n }\n\n async _processShareRoomKeyOperation(operation, hsApi, log) {\n log.set(\"id\", operation.id);\n this._historyVisibility = await this._loadHistoryVisibilityIfNeeded(this._historyVisibility);\n await this._deviceTracker.trackRoom(this._room, this._historyVisibility, log);\n const devices = await this._deviceTracker.devicesForRoomMembers(this._room.id, operation.userIds, hsApi, log);\n const messages = await log.wrap(\"olm encrypt\", log => this._olmEncryption.encrypt(\n \"m.room_key\", operation.roomKeyMessage, devices, hsApi, log));\n const missingDevices = devices.filter(d => !messages.some(m => m.device === d));\n await log.wrap(\"send\", log => this._sendMessagesToDevices(ENCRYPTED_TYPE, messages, hsApi, log));\n if (missingDevices.length) {\n await log.wrap(\"missingDevices\", async log => {\n log.set(\"devices\", missingDevices.map(d => d.deviceId));\n const unsentUserIds = operation.userIds.filter(userId => missingDevices.some(d => d.userId === userId));\n log.set(\"unsentUserIds\", unsentUserIds);\n operation.userIds = unsentUserIds;\n // first remove the users that we've sent the keys already from the operation,\n // so if anything fails, we don't send them again\n await this._updateOperationsStore(operations => operations.update(operation));\n // now, let the devices we could not claim their key\n const withheldMessage = this._megolmEncryption.createWithheldMessage(operation.roomKeyMessage, \"m.no_olm\", \"OTKs exhausted\");\n await this._sendSharedMessageToDevices(\"org.matrix.room_key.withheld\", withheldMessage, missingDevices, hsApi, log);\n });\n }\n await this._updateOperationsStore(operations => operations.remove(operation.id));\n }\n\n async _updateOperationsStore(callback) {\n const writeTxn = await this._storage.readWriteTxn([this._storage.storeNames.operations]);\n try {\n callback(writeTxn.operations);\n } catch (err) {\n writeTxn.abort();\n throw err;\n }\n await writeTxn.complete();\n }\n\n async _sendSharedMessageToDevices(type, message, devices, hsApi, log) {\n const devicesByUser = groupBy(devices, device => device.userId);\n const payload = {\n messages: Array.from(devicesByUser.entries()).reduce((userMap, [userId, devices]) => {\n userMap[userId] = devices.reduce((deviceMap, device) => {\n deviceMap[device.deviceId] = message;\n return deviceMap;\n }, {});\n return userMap;\n }, {})\n };\n const txnId = makeTxnId();\n await hsApi.sendToDevice(type, payload, txnId, {log}).response();\n }\n\n async _sendMessagesToDevices(type, messages, hsApi, log) {\n log.set(\"messages\", messages.length);\n const messagesByUser = groupBy(messages, message => message.device.userId);\n const payload = {\n messages: Array.from(messagesByUser.entries()).reduce((userMap, [userId, messages]) => {\n userMap[userId] = messages.reduce((deviceMap, message) => {\n deviceMap[message.device.deviceId] = message.content;\n return deviceMap;\n }, {});\n return userMap;\n }, {})\n };\n const txnId = makeTxnId();\n await hsApi.sendToDevice(type, payload, txnId, {log}).response();\n }\n\n filterUndecryptedEventEntriesForKeys(entries, keys) {\n return entries.filter(entry => {\n if (entry.isEncrypted && !entry.isDecrypted) {\n const {event} = entry;\n if (event) {\n const senderKey = event.content?.[\"sender_key\"];\n const sessionId = event.content?.[\"session_id\"];\n return keys.some(key => senderKey === key.senderKey && sessionId === key.sessionId);\n }\n }\n return false;\n });\n }\n\n dispose() {\n this._disposed = true;\n }\n}\n\n/**\n * wrappers around megolm decryption classes to be able to post-process\n * the decryption results before turning them\n */\nclass DecryptionPreparation {\n constructor(megolmDecryptionPreparation, extraErrors, source, roomEncryption, events) {\n this._megolmDecryptionPreparation = megolmDecryptionPreparation;\n this._extraErrors = extraErrors;\n this._source = source;\n this._roomEncryption = roomEncryption;\n this._events = events;\n }\n\n async decrypt() {\n return new DecryptionChanges(\n await this._megolmDecryptionPreparation.decrypt(),\n this._extraErrors,\n this._source,\n this._roomEncryption,\n this._events);\n }\n\n dispose() {\n this._megolmDecryptionPreparation.dispose();\n }\n}\n\nclass DecryptionChanges {\n constructor(megolmDecryptionChanges, extraErrors, source, roomEncryption, events) {\n this._megolmDecryptionChanges = megolmDecryptionChanges;\n this._extraErrors = extraErrors;\n this._source = source;\n this._roomEncryption = roomEncryption;\n this._events = events;\n }\n\n async write(txn, log) {\n const {results, errors} = await this._megolmDecryptionChanges.write(txn);\n mergeMap(this._extraErrors, errors);\n await this._roomEncryption._processDecryptionResults(this._events, results, errors, this._source, txn, log);\n return new BatchDecryptionResult(results, errors, this._roomEncryption);\n }\n}\n\nclass BatchDecryptionResult {\n constructor(results, errors, roomEncryption) {\n this.results = results;\n this.errors = errors;\n this._roomEncryption = roomEncryption;\n }\n\n applyToEntries(entries) {\n for (const entry of entries) {\n const result = this.results.get(entry.id);\n if (result) {\n entry.setDecryptionResult(result);\n } else {\n const error = this.errors.get(entry.id);\n if (error) {\n entry.setDecryptionError(error);\n }\n }\n }\n }\n\n verifySenders(txn) {\n return Promise.all(Array.from(this.results.values()).map(result => {\n return this._roomEncryption._verifyDecryptionResult(result, txn);\n }));\n }\n}\n\nimport {createMockStorage} from \"../../mocks/Storage\";\nimport {Clock as MockClock} from \"../../mocks/Clock\";\nimport {poll} from \"../../mocks/poll\";\nimport {Instance as NullLoggerInstance} from \"../../logging/NullLogger\";\nimport {ConsoleLogger} from \"../../logging/ConsoleLogger\";\nimport {HomeServer as MockHomeServer} from \"../../mocks/HomeServer.js\";\n\nexport function tests() {\n const roomId = \"!abc:hs.tld\";\n return {\n \"ensureMessageKeyIsShared tracks room and passes correct history visibility to deviceTracker\": async assert => {\n const storage = await createMockStorage();\n const megolmMock = {\n async ensureOutboundSession() { return { }; }\n };\n const olmMock = {\n async encrypt() { return []; }\n }\n let isRoomTracked = false;\n let isDevicesRequested = false;\n const deviceTracker = {\n async trackRoom(room, historyVisibility) {\n // only assert on first call\n if (isRoomTracked) { return; }\n assert(!isDevicesRequested);\n assert.equal(room.id, roomId);\n assert.equal(historyVisibility, \"invited\");\n isRoomTracked = true;\n },\n async devicesForTrackedRoom() {\n assert(isRoomTracked);\n isDevicesRequested = true;\n return [];\n },\n async devicesForRoomMembers() {\n return [];\n }\n }\n const writeTxn = await storage.readWriteTxn([storage.storeNames.roomState]);\n writeTxn.roomState.set(roomId, {state_key: \"\", type: ROOM_HISTORY_VISIBILITY_TYPE, content: {\n history_visibility: \"invited\"\n }});\n await writeTxn.complete();\n const roomEncryption = new RoomEncryption({\n room: {id: roomId},\n megolmEncryption: megolmMock,\n olmEncryption: olmMock,\n storage,\n deviceTracker,\n clock: new MockClock()\n });\n const homeServer = new MockHomeServer();\n const promise = roomEncryption.ensureMessageKeyIsShared(homeServer.api, NullLoggerInstance.item);\n // need to poll because sendToDevice isn't first async step\n const request = await poll(() => homeServer.requests.sendToDevice?.[0]);\n request.respond({});\n await promise;\n assert(isRoomTracked);\n assert(isDevicesRequested);\n },\n \"encrypt tracks room and passes correct history visibility to deviceTracker\": async assert => {\n const storage = await createMockStorage();\n const megolmMock = {\n async encrypt() { return { roomKeyMessage: {} }; }\n };\n const olmMock = {\n async encrypt() { return []; }\n }\n let isRoomTracked = false;\n let isDevicesRequested = false;\n const deviceTracker = {\n async trackRoom(room, historyVisibility) {\n // only assert on first call\n if (isRoomTracked) { return; }\n assert(!isDevicesRequested);\n assert.equal(room.id, roomId);\n assert.equal(historyVisibility, \"invited\");\n isRoomTracked = true;\n },\n async devicesForTrackedRoom() {\n assert(isRoomTracked);\n isDevicesRequested = true;\n return [];\n },\n async devicesForRoomMembers() {\n return [];\n }\n }\n const writeTxn = await storage.readWriteTxn([storage.storeNames.roomState]);\n writeTxn.roomState.set(roomId, {state_key: \"\", type: ROOM_HISTORY_VISIBILITY_TYPE, content: {\n history_visibility: \"invited\"\n }});\n await writeTxn.complete();\n const roomEncryption = new RoomEncryption({\n room: {id: roomId},\n megolmEncryption: megolmMock,\n olmEncryption: olmMock,\n storage,\n deviceTracker\n });\n const homeServer = new MockHomeServer();\n const promise = roomEncryption.encrypt(\"m.room.message\", {body: \"hello\"}, homeServer.api, NullLoggerInstance.item);\n // need to poll because sendToDevice isn't first async step\n const request = await poll(() => homeServer.requests.sendToDevice?.[0]);\n request.respond({});\n await promise;\n assert(isRoomTracked);\n assert(isDevicesRequested);\n },\n \"writeSync passes correct history visibility to deviceTracker\": async assert => {\n const storage = await createMockStorage();\n let isMemberChangesCalled = false;\n const deviceTracker = {\n async writeMemberChanges(room, memberChanges, historyVisibility, txn) {\n assert.equal(historyVisibility, \"invited\");\n isMemberChangesCalled = true;\n return {removed: [], added: []};\n },\n async devicesForRoomMembers() {\n return [];\n }\n }\n const writeTxn = await storage.readWriteTxn([storage.storeNames.roomState]);\n writeTxn.roomState.set(roomId, {state_key: \"\", type: ROOM_HISTORY_VISIBILITY_TYPE, content: {\n history_visibility: \"invited\"\n }});\n const memberChanges = new Map([[\"@alice:hs.tld\", {}]]);\n const roomEncryption = new RoomEncryption({\n room: {id: roomId},\n storage,\n deviceTracker\n });\n const roomResponse = {};\n const txn = await storage.readWriteTxn([storage.storeNames.roomState]);\n await roomEncryption.writeSync(roomResponse, memberChanges, txn, NullLoggerInstance.item);\n assert(isMemberChangesCalled);\n },\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {verifyEd25519Signature, SIGNATURE_ALGORITHM} from \"./common.js\";\nimport {HistoryVisibility, shouldShareKey} from \"./common.js\";\nimport {RoomMember} from \"../room/members/RoomMember.js\";\n\nconst TRACKING_STATUS_OUTDATED = 0;\nconst TRACKING_STATUS_UPTODATE = 1;\n\nfunction addRoomToIdentity(identity, userId, roomId) {\n if (!identity) {\n identity = {\n userId: userId,\n roomIds: [roomId],\n deviceTrackingStatus: TRACKING_STATUS_OUTDATED,\n };\n return identity;\n } else {\n if (!identity.roomIds.includes(roomId)) {\n identity.roomIds.push(roomId);\n return identity;\n }\n }\n}\n\n// map 1 device from /keys/query response to DeviceIdentity\nfunction deviceKeysAsDeviceIdentity(deviceSection) {\n const deviceId = deviceSection[\"device_id\"];\n const userId = deviceSection[\"user_id\"];\n return {\n userId,\n deviceId,\n ed25519Key: deviceSection.keys[`ed25519:${deviceId}`],\n curve25519Key: deviceSection.keys[`curve25519:${deviceId}`],\n algorithms: deviceSection.algorithms,\n displayName: deviceSection.unsigned?.device_display_name,\n };\n}\n\nexport class DeviceTracker {\n constructor({storage, getSyncToken, olmUtil, ownUserId, ownDeviceId}) {\n this._storage = storage;\n this._getSyncToken = getSyncToken;\n this._identityChangedForRoom = null;\n this._olmUtil = olmUtil;\n this._ownUserId = ownUserId;\n this._ownDeviceId = ownDeviceId;\n }\n\n async writeDeviceChanges(changed, txn, log) {\n const {userIdentities} = txn;\n // TODO: should we also look at left here to handle this?:\n // the usual problem here is that you share a room with a user,\n // go offline, the remote user leaves the room, changes their devices,\n // then rejoins the room you share (or another room).\n // At which point you come online, all of this happens in the gap, \n // and you don't notice that they ever left, \n // and so the client doesn't invalidate their device cache for the user\n log.set(\"changed\", changed.length);\n await Promise.all(changed.map(async userId => {\n const user = await userIdentities.get(userId);\n if (user) {\n log.log({l: \"outdated\", id: userId});\n user.deviceTrackingStatus = TRACKING_STATUS_OUTDATED;\n userIdentities.set(user);\n }\n }));\n }\n\n /** @return Promise<{added: string[], removed: string[]}> the user ids for who the room was added or removed to the userIdentity,\n * and with who a key should be now be shared\n **/\n async writeMemberChanges(room, memberChanges, historyVisibility, txn) {\n const added = [];\n const removed = [];\n await Promise.all(Array.from(memberChanges.values()).map(async memberChange => {\n // keys should now be shared with this member?\n // add the room to the userIdentity if so\n if (shouldShareKey(memberChange.membership, historyVisibility)) {\n if (await this._addRoomToUserIdentity(memberChange.roomId, memberChange.userId, txn)) {\n added.push(memberChange.userId);\n }\n } else if (shouldShareKey(memberChange.previousMembership, historyVisibility)) {\n // try to remove room we were previously sharing the key with the member but not anymore\n const {roomId} = memberChange;\n // if we left the room, remove room from all user identities in the room\n if (memberChange.userId === this._ownUserId) {\n const userIds = await txn.roomMembers.getAllUserIds(roomId);\n await Promise.all(userIds.map(userId => {\n return this._removeRoomFromUserIdentity(roomId, userId, txn);\n }));\n } else {\n await this._removeRoomFromUserIdentity(roomId, memberChange.userId, txn);\n }\n removed.push(memberChange.userId);\n }\n }));\n return {added, removed};\n }\n\n async trackRoom(room, historyVisibility, log) {\n if (room.isTrackingMembers || !room.isEncrypted) {\n return;\n }\n const memberList = await room.loadMemberList(undefined, log);\n const txn = await this._storage.readWriteTxn([\n this._storage.storeNames.roomSummary,\n this._storage.storeNames.userIdentities,\n ]);\n try {\n let isTrackingChanges;\n try {\n isTrackingChanges = room.writeIsTrackingMembers(true, txn);\n const members = Array.from(memberList.members.values());\n log.set(\"members\", members.length);\n await Promise.all(members.map(async member => {\n if (shouldShareKey(member.membership, historyVisibility)) {\n await this._addRoomToUserIdentity(member.roomId, member.userId, txn);\n }\n }));\n } catch (err) {\n txn.abort();\n throw err;\n }\n await txn.complete();\n room.applyIsTrackingMembersChanges(isTrackingChanges);\n } finally {\n memberList.release();\n }\n }\n\n async writeHistoryVisibility(room, historyVisibility, syncTxn, log) {\n const added = [];\n const removed = [];\n if (room.isTrackingMembers && room.isEncrypted) {\n await log.wrap(\"rewriting userIdentities\", async log => {\n const memberList = await room.loadMemberList(syncTxn, log);\n try {\n const members = Array.from(memberList.members.values());\n log.set(\"members\", members.length);\n await Promise.all(members.map(async member => {\n if (shouldShareKey(member.membership, historyVisibility)) {\n if (await this._addRoomToUserIdentity(member.roomId, member.userId, syncTxn)) {\n added.push(member.userId);\n }\n } else {\n if (await this._removeRoomFromUserIdentity(member.roomId, member.userId, syncTxn)) {\n removed.push(member.userId);\n }\n }\n }));\n } finally {\n memberList.release();\n }\n });\n }\n return {added, removed};\n }\n\n async _addRoomToUserIdentity(roomId, userId, txn) {\n const {userIdentities} = txn;\n const identity = await userIdentities.get(userId);\n const updatedIdentity = addRoomToIdentity(identity, userId, roomId);\n if (updatedIdentity) {\n userIdentities.set(updatedIdentity);\n return true;\n }\n return false;\n }\n\n async _removeRoomFromUserIdentity(roomId, userId, txn) {\n const {userIdentities, deviceIdentities} = txn;\n const identity = await userIdentities.get(userId);\n if (identity) {\n identity.roomIds = identity.roomIds.filter(id => id !== roomId);\n // no more encrypted rooms with this user, remove\n if (identity.roomIds.length === 0) {\n userIdentities.remove(userId);\n deviceIdentities.removeAllForUser(userId);\n } else {\n userIdentities.set(identity);\n }\n return true;\n }\n return false;\n }\n\n async _queryKeys(userIds, hsApi, log) {\n // TODO: we need to handle the race here between /sync and /keys/query just like we need to do for the member list ...\n // there are multiple requests going out for /keys/query though and only one for /members\n\n const deviceKeyResponse = await hsApi.queryKeys({\n \"timeout\": 10000,\n \"device_keys\": userIds.reduce((deviceKeysMap, userId) => {\n deviceKeysMap[userId] = [];\n return deviceKeysMap;\n }, {}),\n \"token\": this._getSyncToken()\n }, {log}).response();\n\n const verifiedKeysPerUser = log.wrap(\"verify\", log => this._filterVerifiedDeviceKeys(deviceKeyResponse[\"device_keys\"], log));\n const txn = await this._storage.readWriteTxn([\n this._storage.storeNames.userIdentities,\n this._storage.storeNames.deviceIdentities,\n ]);\n let deviceIdentities;\n try {\n const devicesIdentitiesPerUser = await Promise.all(verifiedKeysPerUser.map(async ({userId, verifiedKeys}) => {\n const deviceIdentities = verifiedKeys.map(deviceKeysAsDeviceIdentity);\n return await this._storeQueriedDevicesForUserId(userId, deviceIdentities, txn);\n }));\n deviceIdentities = devicesIdentitiesPerUser.reduce((all, devices) => all.concat(devices), []);\n log.set(\"devices\", deviceIdentities.length);\n } catch (err) {\n txn.abort();\n throw err;\n }\n await txn.complete();\n return deviceIdentities;\n }\n\n async _storeQueriedDevicesForUserId(userId, deviceIdentities, txn) {\n const knownDeviceIds = await txn.deviceIdentities.getAllDeviceIds(userId);\n // delete any devices that we know off but are not in the response anymore.\n // important this happens before checking if the ed25519 key changed,\n // otherwise we would end up deleting existing devices with changed keys.\n for (const deviceId of knownDeviceIds) {\n if (deviceIdentities.every(di => di.deviceId !== deviceId)) {\n txn.deviceIdentities.remove(userId, deviceId);\n }\n }\n\n // all the device identities as we will have them in storage\n const allDeviceIdentities = [];\n const deviceIdentitiesToStore = [];\n // filter out devices that have changed their ed25519 key since last time we queried them\n await Promise.all(deviceIdentities.map(async deviceIdentity => {\n if (knownDeviceIds.includes(deviceIdentity.deviceId)) {\n const existingDevice = await txn.deviceIdentities.get(deviceIdentity.userId, deviceIdentity.deviceId);\n if (existingDevice.ed25519Key !== deviceIdentity.ed25519Key) {\n allDeviceIdentities.push(existingDevice);\n return;\n }\n }\n allDeviceIdentities.push(deviceIdentity);\n deviceIdentitiesToStore.push(deviceIdentity);\n }));\n // store devices\n for (const deviceIdentity of deviceIdentitiesToStore) {\n txn.deviceIdentities.set(deviceIdentity);\n }\n // mark user identities as up to date\n const identity = await txn.userIdentities.get(userId);\n identity.deviceTrackingStatus = TRACKING_STATUS_UPTODATE;\n txn.userIdentities.set(identity);\n\n return allDeviceIdentities;\n }\n\n /**\n * @return {Array<{userId, verifiedKeys: Array<DeviceSection>>}\n */\n _filterVerifiedDeviceKeys(keyQueryDeviceKeysResponse, parentLog) {\n const curve25519Keys = new Set();\n const verifiedKeys = Object.entries(keyQueryDeviceKeysResponse).map(([userId, keysByDevice]) => {\n const verifiedEntries = Object.entries(keysByDevice).filter(([deviceId, deviceKeys]) => {\n const deviceIdOnKeys = deviceKeys[\"device_id\"];\n const userIdOnKeys = deviceKeys[\"user_id\"];\n if (userIdOnKeys !== userId) {\n return false;\n }\n if (deviceIdOnKeys !== deviceId) {\n return false;\n }\n const ed25519Key = deviceKeys.keys?.[`ed25519:${deviceId}`];\n const curve25519Key = deviceKeys.keys?.[`curve25519:${deviceId}`];\n if (typeof ed25519Key !== \"string\" || typeof curve25519Key !== \"string\") {\n return false;\n }\n if (curve25519Keys.has(curve25519Key)) {\n parentLog.log({\n l: \"ignore device with duplicate curve25519 key\",\n keys: deviceKeys\n }, parentLog.level.Warn);\n return false;\n }\n curve25519Keys.add(curve25519Key);\n const isValid = this._hasValidSignature(deviceKeys, parentLog);\n if (!isValid) {\n parentLog.log({\n l: \"ignore device with invalid signature\",\n keys: deviceKeys\n }, parentLog.level.Warn);\n }\n return isValid;\n });\n const verifiedKeys = verifiedEntries.map(([, deviceKeys]) => deviceKeys);\n return {userId, verifiedKeys};\n });\n return verifiedKeys;\n }\n\n _hasValidSignature(deviceSection, parentLog) {\n const deviceId = deviceSection[\"device_id\"];\n const userId = deviceSection[\"user_id\"];\n const ed25519Key = deviceSection?.keys?.[`${SIGNATURE_ALGORITHM}:${deviceId}`];\n return verifyEd25519Signature(this._olmUtil, userId, deviceId, ed25519Key, deviceSection, parentLog);\n }\n\n /**\n * Gives all the device identities for a room that is already tracked.\n * Assumes room is already tracked. Call `trackRoom` first if unsure.\n * @param {String} roomId [description]\n * @return {[type]} [description]\n */\n async devicesForTrackedRoom(roomId, hsApi, log) {\n const txn = await this._storage.readTxn([\n this._storage.storeNames.roomMembers,\n this._storage.storeNames.userIdentities,\n ]);\n\n // because we don't have multiEntry support in IE11, we get a set of userIds that is pretty close to what we\n // need as a good first filter (given that non-join memberships will be in there). After fetching the identities,\n // we check which ones have the roomId for the room we're looking at.\n \n // So, this will also contain non-joined memberships\n const userIds = await txn.roomMembers.getAllUserIds(roomId);\n\n return await this._devicesForUserIds(roomId, userIds, txn, hsApi, log);\n }\n\n async devicesForRoomMembers(roomId, userIds, hsApi, log) {\n const txn = await this._storage.readTxn([\n this._storage.storeNames.userIdentities,\n ]);\n return await this._devicesForUserIds(roomId, userIds, txn, hsApi, log);\n }\n\n /**\n * @param {string} roomId [description]\n * @param {Array<string>} userIds a set of user ids to try and find the identity for. Will be check to belong to roomId.\n * @param {Transaction} userIdentityTxn to read the user identities\n * @param {HomeServerApi} hsApi\n * @return {Array<DeviceIdentity>}\n */\n async _devicesForUserIds(roomId, userIds, userIdentityTxn, hsApi, log) {\n const allMemberIdentities = await Promise.all(userIds.map(userId => userIdentityTxn.userIdentities.get(userId)));\n const identities = allMemberIdentities.filter(identity => {\n // identity will be missing for any userIds that don't have \n // membership join in any of your encrypted rooms\n return identity && identity.roomIds.includes(roomId);\n });\n const upToDateIdentities = identities.filter(i => i.deviceTrackingStatus === TRACKING_STATUS_UPTODATE);\n const outdatedIdentities = identities.filter(i => i.deviceTrackingStatus === TRACKING_STATUS_OUTDATED);\n log.set(\"uptodate\", upToDateIdentities.length);\n log.set(\"outdated\", outdatedIdentities.length);\n let queriedDevices;\n if (outdatedIdentities.length) {\n // TODO: ignore the race between /sync and /keys/query for now,\n // where users could get marked as outdated or added/removed from the room while\n // querying keys\n queriedDevices = await this._queryKeys(outdatedIdentities.map(i => i.userId), hsApi, log);\n }\n\n const deviceTxn = await this._storage.readTxn([\n this._storage.storeNames.deviceIdentities,\n ]);\n const devicesPerUser = await Promise.all(upToDateIdentities.map(identity => {\n return deviceTxn.deviceIdentities.getAllForUserId(identity.userId);\n }));\n let flattenedDevices = devicesPerUser.reduce((all, devicesForUser) => all.concat(devicesForUser), []);\n if (queriedDevices && queriedDevices.length) {\n flattenedDevices = flattenedDevices.concat(queriedDevices);\n }\n // filter out our own device\n const devices = flattenedDevices.filter(device => {\n const isOwnDevice = device.userId === this._ownUserId && device.deviceId === this._ownDeviceId;\n return !isOwnDevice;\n });\n return devices;\n }\n\n async getDeviceByCurve25519Key(curve25519Key, txn) {\n return await txn.deviceIdentities.getByCurve25519Key(curve25519Key);\n }\n}\n\nimport {createMockStorage} from \"../../mocks/Storage\";\nimport {Instance as NullLoggerInstance} from \"../../logging/NullLogger\";\nimport {MemberChange} from \"../room/members/RoomMember\";\n\nexport function tests() {\n\n function createUntrackedRoomMock(roomId, joinedUserIds, invitedUserIds = []) {\n return {\n id: roomId,\n isTrackingMembers: false,\n isEncrypted: true,\n loadMemberList: () => {\n const joinedMembers = joinedUserIds.map(userId => {return RoomMember.fromUserId(roomId, userId, \"join\");});\n const invitedMembers = invitedUserIds.map(userId => {return RoomMember.fromUserId(roomId, userId, \"invite\");});\n const members = joinedMembers.concat(invitedMembers);\n const memberMap = members.reduce((map, member) => {\n map.set(member.userId, member);\n return map;\n }, new Map());\n return {members: memberMap, release() {}}\n },\n writeIsTrackingMembers(isTrackingMembers) {\n if (this.isTrackingMembers !== isTrackingMembers) {\n return isTrackingMembers;\n }\n return undefined;\n },\n applyIsTrackingMembersChanges(isTrackingMembers) {\n if (isTrackingMembers !== undefined) {\n this.isTrackingMembers = isTrackingMembers;\n }\n },\n }\n }\n\n function createQueryKeysHSApiMock(createKey = (algorithm, userId, deviceId) => `${algorithm}:${userId}:${deviceId}:key`) {\n return {\n queryKeys(payload) {\n const {device_keys: deviceKeys} = payload;\n const userKeys = Object.entries(deviceKeys).reduce((userKeys, [userId, deviceIds]) => {\n if (deviceIds.length === 0) {\n deviceIds = [\"device1\"];\n }\n userKeys[userId] = deviceIds.filter(d => d === \"device1\").reduce((deviceKeys, deviceId) => {\n deviceKeys[deviceId] = {\n \"algorithms\": [\n \"m.olm.v1.curve25519-aes-sha2\",\n \"m.megolm.v1.aes-sha2\"\n ],\n \"device_id\": deviceId,\n \"keys\": {\n [`curve25519:${deviceId}`]: createKey(\"curve25519\", userId, deviceId),\n [`ed25519:${deviceId}`]: createKey(\"ed25519\", userId, deviceId),\n },\n \"signatures\": {\n [userId]: {\n [`ed25519:${deviceId}`]: `ed25519:${userId}:${deviceId}:signature`\n }\n },\n \"unsigned\": {\n \"device_display_name\": `${userId} Phone`\n },\n \"user_id\": userId\n };\n return deviceKeys;\n }, {});\n return userKeys;\n }, {});\n const response = {device_keys: userKeys};\n return {\n async response() {\n return response;\n }\n };\n }\n };\n }\n\n async function writeMemberListToStorage(room, storage) {\n const txn = await storage.readWriteTxn([\n storage.storeNames.roomMembers,\n ]);\n const memberList = await room.loadMemberList(txn);\n try {\n for (const member of memberList.members.values()) {\n txn.roomMembers.set(member.serialize());\n }\n } catch (err) {\n txn.abort();\n throw err;\n } finally {\n memberList.release();\n }\n await txn.complete();\n }\n\n const roomId = \"!abc:hs.tld\";\n\n return {\n \"trackRoom only writes joined members with history visibility of joined\": async assert => {\n const storage = await createMockStorage();\n const tracker = new DeviceTracker({\n storage,\n getSyncToken: () => \"token\",\n olmUtil: {ed25519_verify: () => {}}, // valid if it does not throw\n ownUserId: \"@alice:hs.tld\",\n ownDeviceId: \"ABCD\",\n });\n const room = createUntrackedRoomMock(roomId, [\"@alice:hs.tld\", \"@bob:hs.tld\"], [\"@charly:hs.tld\"]);\n await tracker.trackRoom(room, HistoryVisibility.Joined, NullLoggerInstance.item);\n const txn = await storage.readTxn([storage.storeNames.userIdentities]);\n assert.deepEqual(await txn.userIdentities.get(\"@alice:hs.tld\"), {\n userId: \"@alice:hs.tld\",\n roomIds: [roomId],\n deviceTrackingStatus: TRACKING_STATUS_OUTDATED\n });\n assert.deepEqual(await txn.userIdentities.get(\"@bob:hs.tld\"), {\n userId: \"@bob:hs.tld\",\n roomIds: [roomId],\n deviceTrackingStatus: TRACKING_STATUS_OUTDATED\n });\n assert.equal(await txn.userIdentities.get(\"@charly:hs.tld\"), undefined);\n },\n \"getting devices for tracked room yields correct keys\": async assert => {\n const storage = await createMockStorage();\n const tracker = new DeviceTracker({\n storage,\n getSyncToken: () => \"token\",\n olmUtil: {ed25519_verify: () => {}}, // valid if it does not throw\n ownUserId: \"@alice:hs.tld\",\n ownDeviceId: \"ABCD\",\n });\n const room = createUntrackedRoomMock(roomId, [\"@alice:hs.tld\", \"@bob:hs.tld\"]);\n await tracker.trackRoom(room, HistoryVisibility.Joined, NullLoggerInstance.item);\n const hsApi = createQueryKeysHSApiMock();\n const devices = await tracker.devicesForRoomMembers(roomId, [\"@alice:hs.tld\", \"@bob:hs.tld\"], hsApi, NullLoggerInstance.item);\n assert.equal(devices.length, 2);\n assert.equal(devices.find(d => d.userId === \"@alice:hs.tld\").ed25519Key, \"ed25519:@alice:hs.tld:device1:key\");\n assert.equal(devices.find(d => d.userId === \"@bob:hs.tld\").ed25519Key, \"ed25519:@bob:hs.tld:device1:key\");\n },\n \"device with changed key is ignored\": async assert => {\n const storage = await createMockStorage();\n const tracker = new DeviceTracker({\n storage,\n getSyncToken: () => \"token\",\n olmUtil: {ed25519_verify: () => {}}, // valid if it does not throw\n ownUserId: \"@alice:hs.tld\",\n ownDeviceId: \"ABCD\",\n });\n const room = createUntrackedRoomMock(roomId, [\"@alice:hs.tld\", \"@bob:hs.tld\"]);\n await tracker.trackRoom(room, HistoryVisibility.Joined, NullLoggerInstance.item);\n const hsApi = createQueryKeysHSApiMock();\n // query devices first time\n await tracker.devicesForRoomMembers(roomId, [\"@alice:hs.tld\", \"@bob:hs.tld\"], hsApi, NullLoggerInstance.item);\n const txn = await storage.readWriteTxn([storage.storeNames.userIdentities]);\n // mark alice as outdated, so keys will be fetched again\n tracker.writeDeviceChanges([\"@alice:hs.tld\"], txn, NullLoggerInstance.item);\n await txn.complete();\n const hsApiWithChangedAliceKey = createQueryKeysHSApiMock((algo, userId, deviceId) => {\n return `${algo}:${userId}:${deviceId}:${userId === \"@alice:hs.tld\" ? \"newKey\" : \"key\"}`;\n });\n const devices = await tracker.devicesForRoomMembers(roomId, [\"@alice:hs.tld\", \"@bob:hs.tld\"], hsApiWithChangedAliceKey, NullLoggerInstance.item);\n assert.equal(devices.length, 2);\n assert.equal(devices.find(d => d.userId === \"@alice:hs.tld\").ed25519Key, \"ed25519:@alice:hs.tld:device1:key\");\n assert.equal(devices.find(d => d.userId === \"@bob:hs.tld\").ed25519Key, \"ed25519:@bob:hs.tld:device1:key\");\n const txn2 = await storage.readTxn([storage.storeNames.deviceIdentities]);\n // also check the modified key was not stored\n assert.equal((await txn2.deviceIdentities.get(\"@alice:hs.tld\", \"device1\")).ed25519Key, \"ed25519:@alice:hs.tld:device1:key\");\n },\n \"change history visibility from joined to invited adds invitees\": async assert => {\n const storage = await createMockStorage();\n const tracker = new DeviceTracker({\n storage,\n getSyncToken: () => \"token\",\n olmUtil: {ed25519_verify: () => {}}, // valid if it does not throw\n ownUserId: \"@alice:hs.tld\",\n ownDeviceId: \"ABCD\",\n });\n // alice is joined, bob is invited\n const room = await createUntrackedRoomMock(roomId, \n [\"@alice:hs.tld\"], [\"@bob:hs.tld\"]);\n await tracker.trackRoom(room, HistoryVisibility.Joined, NullLoggerInstance.item);\n const txn = await storage.readWriteTxn([storage.storeNames.userIdentities, storage.storeNames.deviceIdentities]);\n assert.equal(await txn.userIdentities.get(\"@bob:hs.tld\"), undefined);\n const {added, removed} = await tracker.writeHistoryVisibility(room, HistoryVisibility.Invited, txn, NullLoggerInstance.item);\n assert.equal((await txn.userIdentities.get(\"@bob:hs.tld\")).userId, \"@bob:hs.tld\");\n assert.deepEqual(added, [\"@bob:hs.tld\"]);\n assert.deepEqual(removed, []);\n },\n \"change history visibility from invited to joined removes invitees\": async assert => {\n const storage = await createMockStorage();\n const tracker = new DeviceTracker({\n storage,\n getSyncToken: () => \"token\",\n olmUtil: {ed25519_verify: () => {}}, // valid if it does not throw\n ownUserId: \"@alice:hs.tld\",\n ownDeviceId: \"ABCD\",\n });\n // alice is joined, bob is invited\n const room = await createUntrackedRoomMock(roomId, \n [\"@alice:hs.tld\"], [\"@bob:hs.tld\"]);\n await tracker.trackRoom(room, HistoryVisibility.Invited, NullLoggerInstance.item);\n const txn = await storage.readWriteTxn([storage.storeNames.userIdentities, storage.storeNames.deviceIdentities]);\n assert.equal((await txn.userIdentities.get(\"@bob:hs.tld\")).userId, \"@bob:hs.tld\");\n const {added, removed} = await tracker.writeHistoryVisibility(room, HistoryVisibility.Joined, txn, NullLoggerInstance.item);\n assert.equal(await txn.userIdentities.get(\"@bob:hs.tld\"), undefined);\n assert.deepEqual(added, []);\n assert.deepEqual(removed, [\"@bob:hs.tld\"]);\n },\n \"adding invitee with history visibility of invited adds room to userIdentities\": async assert => {\n const storage = await createMockStorage();\n const tracker = new DeviceTracker({\n storage,\n getSyncToken: () => \"token\",\n olmUtil: {ed25519_verify: () => {}}, // valid if it does not throw\n ownUserId: \"@alice:hs.tld\",\n ownDeviceId: \"ABCD\",\n });\n const room = await createUntrackedRoomMock(roomId, [\"@alice:hs.tld\"]);\n await tracker.trackRoom(room, HistoryVisibility.Invited, NullLoggerInstance.item);\n const txn = await storage.readWriteTxn([storage.storeNames.userIdentities, storage.storeNames.deviceIdentities]);\n // inviting a new member\n const inviteChange = new MemberChange(RoomMember.fromUserId(roomId, \"@bob:hs.tld\", \"invite\"));\n const {added, removed} = await tracker.writeMemberChanges(room, [inviteChange], HistoryVisibility.Invited, txn);\n assert.deepEqual(added, [\"@bob:hs.tld\"]);\n assert.deepEqual(removed, []);\n assert.equal((await txn.userIdentities.get(\"@bob:hs.tld\")).userId, \"@bob:hs.tld\");\n },\n \"adding invitee with history visibility of joined doesn't add room\": async assert => {\n const storage = await createMockStorage();\n const tracker = new DeviceTracker({\n storage,\n getSyncToken: () => \"token\",\n olmUtil: {ed25519_verify: () => {}}, // valid if it does not throw\n ownUserId: \"@alice:hs.tld\",\n ownDeviceId: \"ABCD\",\n });\n const room = await createUntrackedRoomMock(roomId, [\"@alice:hs.tld\"]);\n await tracker.trackRoom(room, HistoryVisibility.Joined, NullLoggerInstance.item);\n const txn = await storage.readWriteTxn([storage.storeNames.userIdentities, storage.storeNames.deviceIdentities]);\n // inviting a new member\n const inviteChange = new MemberChange(RoomMember.fromUserId(roomId, \"@bob:hs.tld\", \"invite\"));\n const memberChanges = new Map([[inviteChange.userId, inviteChange]]);\n const {added, removed} = await tracker.writeMemberChanges(room, memberChanges, HistoryVisibility.Joined, txn);\n assert.deepEqual(added, []);\n assert.deepEqual(removed, []);\n assert.equal(await txn.userIdentities.get(\"@bob:hs.tld\"), undefined);\n },\n \"getting all devices after changing history visibility now includes invitees\": async assert => {\n const storage = await createMockStorage();\n const tracker = new DeviceTracker({\n storage,\n getSyncToken: () => \"token\",\n olmUtil: {ed25519_verify: () => {}}, // valid if it does not throw\n ownUserId: \"@alice:hs.tld\",\n ownDeviceId: \"ABCD\",\n });\n const room = createUntrackedRoomMock(roomId, [\"@alice:hs.tld\"], [\"@bob:hs.tld\"]);\n await tracker.trackRoom(room, HistoryVisibility.Invited, NullLoggerInstance.item);\n const hsApi = createQueryKeysHSApiMock();\n // write memberlist from room mock to mock storage,\n // as devicesForTrackedRoom reads directly from roomMembers store.\n await writeMemberListToStorage(room, storage);\n const devices = await tracker.devicesForTrackedRoom(roomId, hsApi, NullLoggerInstance.item);\n assert.equal(devices.length, 2);\n assert.equal(devices.find(d => d.userId === \"@alice:hs.tld\").ed25519Key, \"ed25519:@alice:hs.tld:device1:key\");\n assert.equal(devices.find(d => d.userId === \"@bob:hs.tld\").ed25519Key, \"ed25519:@bob:hs.tld:device1:key\");\n },\n \"rejecting invite with history visibility of invited removes room from user identity\": async assert => {\n const storage = await createMockStorage();\n const tracker = new DeviceTracker({\n storage,\n getSyncToken: () => \"token\",\n olmUtil: {ed25519_verify: () => {}}, // valid if it does not throw\n ownUserId: \"@alice:hs.tld\",\n ownDeviceId: \"ABCD\",\n });\n // alice is joined, bob is invited\n const room = await createUntrackedRoomMock(roomId, [\"@alice:hs.tld\"], [\"@bob:hs.tld\"]);\n await tracker.trackRoom(room, HistoryVisibility.Invited, NullLoggerInstance.item);\n const txn = await storage.readWriteTxn([storage.storeNames.userIdentities, storage.storeNames.deviceIdentities]);\n // reject invite\n const inviteChange = new MemberChange(RoomMember.fromUserId(roomId, \"@bob:hs.tld\", \"leave\"), \"invite\");\n const memberChanges = new Map([[inviteChange.userId, inviteChange]]);\n const {added, removed} = await tracker.writeMemberChanges(room, memberChanges, HistoryVisibility.Invited, txn);\n assert.deepEqual(added, []);\n assert.deepEqual(removed, [\"@bob:hs.tld\"]);\n assert.equal(await txn.userIdentities.get(\"@bob:hs.tld\"), undefined);\n },\n \"remove room from user identity sharing multiple rooms with us preserves other room\": async assert => {\n const storage = await createMockStorage();\n const tracker = new DeviceTracker({\n storage,\n getSyncToken: () => \"token\",\n olmUtil: {ed25519_verify: () => {}}, // valid if it does not throw\n ownUserId: \"@alice:hs.tld\",\n ownDeviceId: \"ABCD\",\n });\n // alice is joined, bob is invited\n const room1 = await createUntrackedRoomMock(\"!abc:hs.tld\", [\"@alice:hs.tld\", \"@bob:hs.tld\"]);\n const room2 = await createUntrackedRoomMock(\"!def:hs.tld\", [\"@alice:hs.tld\", \"@bob:hs.tld\"]);\n await tracker.trackRoom(room1, HistoryVisibility.Joined, NullLoggerInstance.item);\n await tracker.trackRoom(room2, HistoryVisibility.Joined, NullLoggerInstance.item);\n const txn1 = await storage.readTxn([storage.storeNames.userIdentities]);\n assert.deepEqual((await txn1.userIdentities.get(\"@bob:hs.tld\")).roomIds, [\"!abc:hs.tld\", \"!def:hs.tld\"]);\n const leaveChange = new MemberChange(RoomMember.fromUserId(room2.id, \"@bob:hs.tld\", \"leave\"), \"join\");\n const memberChanges = new Map([[leaveChange.userId, leaveChange]]);\n const txn2 = await storage.readWriteTxn([storage.storeNames.userIdentities, storage.storeNames.deviceIdentities]);\n await tracker.writeMemberChanges(room2, memberChanges, HistoryVisibility.Joined, txn2);\n await txn2.complete();\n const txn3 = await storage.readTxn([storage.storeNames.userIdentities]);\n assert.deepEqual((await txn3.userIdentities.get(\"@bob:hs.tld\")).roomIds, [\"!abc:hs.tld\"]);\n },\n \"add room to user identity sharing multiple rooms with us preserves other room\": async assert => {\n const storage = await createMockStorage();\n const tracker = new DeviceTracker({\n storage,\n getSyncToken: () => \"token\",\n olmUtil: {ed25519_verify: () => {}}, // valid if it does not throw\n ownUserId: \"@alice:hs.tld\",\n ownDeviceId: \"ABCD\",\n });\n // alice is joined, bob is invited\n const room1 = await createUntrackedRoomMock(\"!abc:hs.tld\", [\"@alice:hs.tld\", \"@bob:hs.tld\"]);\n const room2 = await createUntrackedRoomMock(\"!def:hs.tld\", [\"@alice:hs.tld\", \"@bob:hs.tld\"]);\n await tracker.trackRoom(room1, HistoryVisibility.Joined, NullLoggerInstance.item);\n const txn1 = await storage.readTxn([storage.storeNames.userIdentities]);\n assert.deepEqual((await txn1.userIdentities.get(\"@bob:hs.tld\")).roomIds, [\"!abc:hs.tld\"]);\n await tracker.trackRoom(room2, HistoryVisibility.Joined, NullLoggerInstance.item);\n const txn2 = await storage.readTxn([storage.storeNames.userIdentities]);\n assert.deepEqual((await txn2.userIdentities.get(\"@bob:hs.tld\")).roomIds, [\"!abc:hs.tld\", \"!def:hs.tld\"]);\n },\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {Lock} from \"./Lock\";\n\nexport class LockMap<T> {\n private readonly _map: Map<T, Lock> = new Map();\n\n async takeLock(key: T): Promise<Lock> {\n let lock = this._map.get(key);\n if (lock) {\n await lock.take();\n } else {\n lock = new Lock();\n lock.tryTake();\n this._map.set(key, lock);\n }\n // don't leave old locks lying around\n lock.released()!.then(() => {\n // give others a chance to take the lock first\n Promise.resolve().then(() => {\n if (!lock!.isTaken) {\n this._map.delete(key);\n }\n });\n });\n return lock;\n }\n}\n\nexport function tests() {\n return {\n \"taking a lock on the same key blocks\": async assert => {\n const lockMap = new LockMap();\n const lock = await lockMap.takeLock(\"foo\");\n let second = false;\n const prom = lockMap.takeLock(\"foo\").then(() => {\n second = true;\n });\n assert.equal(second, false);\n // do a delay to make sure prom does not resolve on its own\n await Promise.resolve();\n lock.release();\n await prom;\n assert.equal(second, true);\n },\n \"lock is not cleaned up with second request\": async assert => {\n const lockMap = new LockMap();\n const lock = await lockMap.takeLock(\"foo\");\n let ranSecond = false;\n const prom = lockMap.takeLock(\"foo\").then(returnedLock => {\n ranSecond = true;\n assert.equal(returnedLock.isTaken, true);\n // peek into internals, naughty\n // @ts-ignore\n assert.equal(lockMap._map.get(\"foo\"), returnedLock);\n });\n lock.release();\n await prom;\n // double delay to make sure cleanup logic ran\n await Promise.resolve();\n await Promise.resolve();\n assert.equal(ranSecond, true);\n },\n \"lock is cleaned up without other request\": async assert => {\n const lockMap = new LockMap();\n const lock = await lockMap.takeLock(\"foo\");\n await Promise.resolve();\n lock.release();\n // double delay to make sure cleanup logic ran\n await Promise.resolve();\n await Promise.resolve();\n // @ts-ignore\n assert.equal(lockMap._map.has(\"foo\"), false);\n },\n \n };\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\nimport type {Key} from \"./common\";\nimport type {Platform} from \"../../platform/web/Platform.js\";\nimport type {Transaction} from \"../storage/idb/Transaction\";\n\ntype EncryptedData = {\n iv: string;\n ciphertext: string;\n mac: string;\n}\n\nexport class SecretStorage {\n private readonly _key: Key;\n private readonly _platform: Platform;\n\n constructor({key, platform}: {key: Key, platform: Platform}) {\n this._key = key;\n this._platform = platform;\n }\n\n async readSecret(name: string, txn: Transaction): Promise<string | undefined> {\n const accountData = await txn.accountData.get(name);\n if (!accountData) {\n return;\n }\n const encryptedData = accountData?.content?.encrypted?.[this._key.id] as EncryptedData;\n if (!encryptedData) {\n throw new Error(`Secret ${accountData.type} is not encrypted for key ${this._key.id}`);\n }\n\n if (this._key.algorithm === \"m.secret_storage.v1.aes-hmac-sha2\") {\n return await this._decryptAESSecret(accountData.type, encryptedData);\n } else {\n throw new Error(`Unsupported algorithm for key ${this._key.id}: ${this._key.algorithm}`);\n }\n }\n\n async _decryptAESSecret(type: string, encryptedData: EncryptedData): Promise<string> {\n const {base64, utf8} = this._platform.encoding;\n // now derive the aes and mac key from the 4s key\n const hkdfKey = await this._platform.crypto.derive.hkdf(\n this._key.binaryKey,\n new Uint8Array(8).buffer, //zero salt\n utf8.encode(type), // info\n \"SHA-256\",\n 512 // 512 bits or 64 bytes\n );\n const aesKey = hkdfKey.slice(0, 32);\n const hmacKey = hkdfKey.slice(32);\n const ciphertextBytes = base64.decode(encryptedData.ciphertext);\n\n const isVerified = await this._platform.crypto.hmac.verify(\n hmacKey, base64.decode(encryptedData.mac),\n ciphertextBytes, \"SHA-256\");\n\n if (!isVerified) {\n throw new Error(\"Bad MAC\");\n }\n\n const plaintextBytes = await this._platform.crypto.aes.decryptCTR({\n key: aesKey,\n iv: base64.decode(encryptedData.iv),\n data: ciphertextBytes\n });\n\n return utf8.decode(plaintextBytes);\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020, 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {Room} from \"./room/Room.js\";\nimport {ArchivedRoom} from \"./room/ArchivedRoom.js\";\nimport {RoomStatus} from \"./room/common\";\nimport {RoomBeingCreated} from \"./room/RoomBeingCreated\";\nimport {Invite} from \"./room/Invite.js\";\nimport {Pusher} from \"./push/Pusher\";\nimport { ObservableMap } from \"../observable/index.js\";\nimport {User} from \"./User.js\";\nimport {DeviceMessageHandler} from \"./DeviceMessageHandler.js\";\nimport {Account as E2EEAccount} from \"./e2ee/Account.js\";\nimport {uploadAccountAsDehydratedDevice} from \"./e2ee/Dehydration.js\";\nimport {Decryption as OlmDecryption} from \"./e2ee/olm/Decryption\";\nimport {Encryption as OlmEncryption} from \"./e2ee/olm/Encryption\";\nimport {Decryption as MegOlmDecryption} from \"./e2ee/megolm/Decryption\";\nimport {KeyLoader as MegOlmKeyLoader} from \"./e2ee/megolm/decryption/KeyLoader\";\nimport {KeyBackup} from \"./e2ee/megolm/keybackup/KeyBackup\";\nimport {Encryption as MegOlmEncryption} from \"./e2ee/megolm/Encryption.js\";\nimport {MEGOLM_ALGORITHM} from \"./e2ee/common.js\";\nimport {RoomEncryption} from \"./e2ee/RoomEncryption.js\";\nimport {DeviceTracker} from \"./e2ee/DeviceTracker.js\";\nimport {LockMap} from \"../utils/LockMap\";\nimport {groupBy} from \"../utils/groupBy\";\nimport {\n keyFromCredential as ssssKeyFromCredential,\n readKey as ssssReadKey,\n writeKey as ssssWriteKey,\n removeKey as ssssRemoveKey,\n keyFromDehydratedDeviceKey as createSSSSKeyFromDehydratedDeviceKey\n} from \"./ssss/index\";\nimport {SecretStorage} from \"./ssss/SecretStorage\";\nimport {ObservableValue, RetainedObservableValue} from \"../observable/ObservableValue\";\n\nconst PICKLE_KEY = \"DEFAULT_KEY\";\nconst PUSHER_KEY = \"pusher\";\n\nexport class Session {\n // sessionInfo contains deviceId, userId and homeserver\n constructor({storage, hsApi, sessionInfo, olm, olmWorker, platform, mediaRepository}) {\n this._platform = platform;\n this._storage = storage;\n this._hsApi = hsApi;\n this._mediaRepository = mediaRepository;\n this._syncInfo = null;\n this._sessionInfo = sessionInfo;\n this._rooms = new ObservableMap();\n this._roomUpdateCallback = (room, params) => this._rooms.update(room.id, params);\n this._activeArchivedRooms = new Map();\n this._invites = new ObservableMap();\n this._inviteUpdateCallback = (invite, params) => this._invites.update(invite.id, params);\n this._roomsBeingCreatedUpdateCallback = (rbc, params) => {\n if (rbc.isCancelled) {\n this._roomsBeingCreated.remove(rbc.id);\n } else {\n this._roomsBeingCreated.update(rbc.id, params)\n }\n };\n this._roomsBeingCreated = new ObservableMap();\n this._user = new User(sessionInfo.userId);\n this._deviceMessageHandler = new DeviceMessageHandler({storage});\n this._olm = olm;\n this._olmUtil = null;\n this._e2eeAccount = null;\n this._deviceTracker = null;\n this._olmEncryption = null;\n this._keyLoader = null;\n this._megolmEncryption = null;\n this._megolmDecryption = null;\n this._getSyncToken = () => this.syncToken;\n this._olmWorker = olmWorker;\n this._keyBackup = new ObservableValue(undefined);\n this._observedRoomStatus = new Map();\n\n if (olm) {\n this._olmUtil = new olm.Utility();\n this._deviceTracker = new DeviceTracker({\n storage,\n getSyncToken: this._getSyncToken,\n olmUtil: this._olmUtil,\n ownUserId: sessionInfo.userId,\n ownDeviceId: sessionInfo.deviceId,\n });\n }\n this._createRoomEncryption = this._createRoomEncryption.bind(this);\n this._forgetArchivedRoom = this._forgetArchivedRoom.bind(this);\n this.needsKeyBackup = new ObservableValue(false);\n }\n\n get fingerprintKey() {\n return this._e2eeAccount?.identityKeys.ed25519;\n }\n\n get hasSecretStorageKey() {\n return this._hasSecretStorageKey;\n }\n\n get deviceId() {\n return this._sessionInfo.deviceId;\n }\n\n get userId() {\n return this._sessionInfo.userId;\n }\n\n // called once this._e2eeAccount is assigned\n _setupEncryption() {\n // TODO: this should all go in a wrapper in e2ee/ that is bootstrapped by passing in the account\n // and can create RoomEncryption objects and handle encrypted to_device messages and device list changes.\n const senderKeyLock = new LockMap();\n const olmDecryption = new OlmDecryption(\n this._e2eeAccount,\n PICKLE_KEY,\n this._platform.clock.now,\n this._user.id,\n this._olm,\n senderKeyLock\n );\n this._olmEncryption = new OlmEncryption(\n this._e2eeAccount,\n PICKLE_KEY,\n this._olm,\n this._storage,\n this._platform.clock.now,\n this._user.id,\n this._olmUtil,\n senderKeyLock\n );\n this._keyLoader = new MegOlmKeyLoader(this._olm, PICKLE_KEY, 20);\n this._megolmEncryption = new MegOlmEncryption({\n account: this._e2eeAccount,\n pickleKey: PICKLE_KEY,\n olm: this._olm,\n storage: this._storage,\n keyLoader: this._keyLoader,\n now: this._platform.clock.now,\n ownDeviceId: this._sessionInfo.deviceId,\n });\n this._megolmDecryption = new MegOlmDecryption(this._keyLoader, this._olmWorker);\n this._deviceMessageHandler.enableEncryption({olmDecryption, megolmDecryption: this._megolmDecryption});\n }\n\n _createRoomEncryption(room, encryptionParams) {\n // TODO: this will actually happen when users start using the e2ee version for the first time\n\n // this should never happen because either a session was already synced once\n // and thus an e2ee account was created as well and _setupEncryption is called from load\n // OR\n // this is a new session and loading it will load zero rooms, thus not calling this method.\n // in this case _setupEncryption is called from beforeFirstSync, right after load,\n // so any incoming synced rooms won't be there yet\n if (!this._olmEncryption) {\n throw new Error(\"creating room encryption before encryption got globally enabled\");\n }\n // only support megolm\n if (encryptionParams.algorithm !== MEGOLM_ALGORITHM) {\n return null;\n }\n return new RoomEncryption({\n room,\n deviceTracker: this._deviceTracker,\n olmEncryption: this._olmEncryption,\n megolmEncryption: this._megolmEncryption,\n megolmDecryption: this._megolmDecryption,\n storage: this._storage,\n keyBackup: this._keyBackup?.get(),\n encryptionParams,\n notifyMissingMegolmSession: () => {\n if (!this._keyBackup.get()) {\n this.needsKeyBackup.set(true)\n }\n },\n clock: this._platform.clock\n });\n }\n\n /**\n * Enable secret storage by providing the secret storage credential.\n * This will also see if there is a megolm key backup and try to enable that if so.\n * \n * @param {string} type either \"passphrase\" or \"recoverykey\"\n * @param {string} credential either the passphrase or the recovery key, depending on the type\n * @return {Promise} resolves or rejects after having tried to enable secret storage\n */\n enableSecretStorage(type, credential, log = undefined) {\n return this._platform.logger.wrapOrRun(log, \"enable secret storage\", async log => {\n if (!this._olm) {\n throw new Error(\"olm required\");\n }\n if (this._keyBackup.get()) {\n this._keyBackup.get().dispose();\n this._keyBackup.set(null);\n }\n const key = await ssssKeyFromCredential(type, credential, this._storage, this._platform, this._olm);\n // and create key backup, which needs to read from accountData\n const readTxn = await this._storage.readTxn([\n this._storage.storeNames.accountData,\n ]);\n if (await this._createKeyBackup(key, readTxn, log)) {\n // only after having read a secret, write the key\n // as we only find out if it was good if the MAC verification succeeds\n await this._writeSSSSKey(key, log);\n this._keyBackup.get().flush(log);\n return key;\n } else {\n throw new Error(\"Could not read key backup with the given key\");\n }\n });\n }\n\n async _writeSSSSKey(key, log) {\n // we're going to write the 4S key, and also the backup version.\n // this way, we can detect when we enter a key for a new backup version\n // and mark all inbound sessions to be backed up again\n const keyBackup = this._keyBackup.get();\n if (!keyBackup) {\n return;\n }\n const backupVersion = keyBackup.version;\n const writeTxn = await this._storage.readWriteTxn([\n this._storage.storeNames.session,\n this._storage.storeNames.inboundGroupSessions,\n ]);\n try {\n const previousBackupVersion = await ssssWriteKey(key, backupVersion, writeTxn);\n log.set(\"previousBackupVersion\", previousBackupVersion);\n log.set(\"backupVersion\", backupVersion);\n if (!!previousBackupVersion && previousBackupVersion !== backupVersion) {\n const amountMarked = await keyBackup.markAllForBackup(writeTxn);\n log.set(\"amountMarkedForBackup\", amountMarked);\n }\n } catch (err) {\n writeTxn.abort();\n throw err;\n }\n await writeTxn.complete();\n }\n\n async disableSecretStorage() {\n const writeTxn = await this._storage.readWriteTxn([\n this._storage.storeNames.session,\n ]);\n try {\n ssssRemoveKey(writeTxn);\n } catch (err) {\n writeTxn.abort();\n throw err;\n }\n await writeTxn.complete();\n if (this._keyBackup.get()) {\n for (const room of this._rooms.values()) {\n if (room.isEncrypted) {\n room.enableKeyBackup(undefined);\n }\n }\n this._keyBackup.get().dispose();\n this._keyBackup.set(null);\n }\n }\n\n _createKeyBackup(ssssKey, txn, log) {\n return log.wrap(\"enable key backup\", async log => {\n try {\n const secretStorage = new SecretStorage({key: ssssKey, platform: this._platform});\n const keyBackup = await KeyBackup.fromSecretStorage(\n this._platform,\n this._olm,\n secretStorage,\n this._hsApi,\n this._keyLoader,\n this._storage,\n txn\n );\n if (keyBackup) {\n for (const room of this._rooms.values()) {\n if (room.isEncrypted) {\n room.enableKeyBackup(keyBackup);\n }\n }\n this._keyBackup.set(keyBackup);\n return true;\n }\n } catch (err) {\n log.catch(err);\n }\n return false;\n });\n }\n\n /**\n * @type {ObservableValue<KeyBackup | undefined | null}\n * - `undefined` means, we're not done with catchup sync yet and haven't checked yet if key backup is configured\n * - `null` means we've checked and key backup hasn't been configured correctly or at all.\n */\n get keyBackup() {\n return this._keyBackup;\n }\n\n get hasIdentity() {\n return !!this._e2eeAccount;\n }\n\n /** @internal */\n async createIdentity(log) {\n if (this._olm) {\n if (!this._e2eeAccount) {\n this._e2eeAccount = await this._createNewAccount(this._sessionInfo.deviceId, this._storage);\n log.set(\"keys\", this._e2eeAccount.identityKeys);\n this._setupEncryption();\n }\n await this._e2eeAccount.generateOTKsIfNeeded(this._storage, log);\n await log.wrap(\"uploadKeys\", log => this._e2eeAccount.uploadKeys(this._storage, false, log));\n }\n }\n\n /** @internal */\n async dehydrateIdentity(dehydratedDevice, log) {\n log.set(\"deviceId\", dehydratedDevice.deviceId);\n if (!this._olm) {\n log.set(\"no_olm\", true);\n return false;\n }\n if (dehydratedDevice.deviceId !== this.deviceId) {\n log.set(\"wrong_device\", true);\n return false;\n }\n if (this._e2eeAccount) {\n log.set(\"account_already_setup\", true);\n return false;\n }\n if (!await dehydratedDevice.claim(this._hsApi, log)) {\n log.set(\"already_claimed\", true);\n return false;\n }\n this._e2eeAccount = await E2EEAccount.adoptDehydratedDevice({\n dehydratedDevice,\n hsApi: this._hsApi,\n olm: this._olm,\n pickleKey: PICKLE_KEY,\n userId: this._sessionInfo.userId,\n olmWorker: this._olmWorker,\n deviceId: this.deviceId,\n storage: this._storage,\n });\n log.set(\"keys\", this._e2eeAccount.identityKeys);\n this._setupEncryption();\n return true;\n }\n\n _createNewAccount(deviceId, storage = undefined) {\n // storage is optional and if omitted the account won't be persisted (useful for dehydrating devices)\n return E2EEAccount.create({\n hsApi: this._hsApi,\n olm: this._olm,\n pickleKey: PICKLE_KEY,\n userId: this._sessionInfo.userId,\n olmWorker: this._olmWorker,\n deviceId,\n storage,\n });\n }\n\n setupDehydratedDevice(key, log = null) {\n return this._platform.logger.wrapOrRun(log, \"setupDehydratedDevice\", async log => {\n const dehydrationAccount = await this._createNewAccount(\"temp-device-id\");\n try {\n const deviceId = await uploadAccountAsDehydratedDevice(\n dehydrationAccount, this._hsApi, key, \"Dehydrated device\", log);\n log.set(\"deviceId\", deviceId);\n return deviceId;\n } finally {\n dehydrationAccount.dispose();\n }\n });\n }\n\n /** @internal */\n async load(log) {\n const txn = await this._storage.readTxn([\n this._storage.storeNames.session,\n this._storage.storeNames.roomSummary,\n this._storage.storeNames.invites,\n this._storage.storeNames.roomMembers,\n this._storage.storeNames.timelineEvents,\n this._storage.storeNames.timelineFragments,\n this._storage.storeNames.pendingEvents,\n ]);\n // restore session object\n this._syncInfo = await txn.session.get(\"sync\");\n // restore e2ee account, if any\n if (this._olm) {\n this._e2eeAccount = await E2EEAccount.load({\n hsApi: this._hsApi,\n olm: this._olm,\n pickleKey: PICKLE_KEY,\n userId: this._sessionInfo.userId,\n deviceId: this._sessionInfo.deviceId,\n olmWorker: this._olmWorker,\n txn\n });\n if (this._e2eeAccount) {\n log.set(\"keys\", this._e2eeAccount.identityKeys);\n this._setupEncryption();\n }\n }\n const pendingEventsByRoomId = await this._getPendingEventsByRoom(txn);\n // load invites\n const invites = await txn.invites.getAll();\n const inviteLoadPromise = Promise.all(invites.map(async inviteData => {\n const invite = this.createInvite(inviteData.roomId);\n log.wrap(\"invite\", log => invite.load(inviteData, log));\n this._invites.add(invite.id, invite);\n }));\n // load rooms\n const rooms = await txn.roomSummary.getAll();\n const roomLoadPromise = Promise.all(rooms.map(async summary => {\n const room = this.createJoinedRoom(summary.roomId, pendingEventsByRoomId.get(summary.roomId));\n await log.wrap(\"room\", log => room.load(summary, txn, log));\n this._rooms.add(room.id, room);\n }));\n // load invites and rooms in parallel\n await Promise.all([inviteLoadPromise, roomLoadPromise]);\n for (const [roomId, invite] of this.invites) {\n const room = this.rooms.get(roomId);\n if (room) {\n room.setInvite(invite);\n }\n }\n }\n\n dispose() {\n this._olmWorker?.dispose();\n this._olmWorker = undefined;\n this._keyBackup.get()?.dispose();\n this._keyBackup.set(undefined);\n this._megolmDecryption?.dispose();\n this._megolmDecryption = undefined;\n this._e2eeAccount?.dispose();\n this._e2eeAccount = undefined;\n for (const room of this._rooms.values()) {\n room.dispose();\n }\n this._rooms = undefined;\n }\n\n /**\n * @internal called from session container when coming back online and catchup syncs have finished.\n * @param {Object} lastVersionResponse a response from /versions, which is polled while offline,\n * and useful to store so we can later tell what capabilities\n * our homeserver has.\n */\n async start(lastVersionResponse, dehydratedDevice, log) {\n if (lastVersionResponse) {\n // store /versions response\n const txn = await this._storage.readWriteTxn([\n this._storage.storeNames.session\n ]);\n txn.session.set(\"serverVersions\", lastVersionResponse);\n // TODO: what can we do if this throws?\n await txn.complete();\n }\n // enable session backup, this requests the latest backup version\n if (!this._keyBackup.get()) {\n if (dehydratedDevice) {\n await log.wrap(\"SSSSKeyFromDehydratedDeviceKey\", async log => {\n const ssssKey = await createSSSSKeyFromDehydratedDeviceKey(dehydratedDevice.key, this._storage, this._platform);\n if (ssssKey) {\n log.set(\"success\", true);\n await this._writeSSSSKey(ssssKey);\n }\n });\n }\n const txn = await this._storage.readTxn([\n this._storage.storeNames.session,\n this._storage.storeNames.accountData,\n ]);\n // try set up session backup if we stored the ssss key\n const ssssKey = await ssssReadKey(txn);\n if (ssssKey) {\n // txn will end here as this does a network request\n if (await this._createKeyBackup(ssssKey, txn, log)) {\n this._keyBackup.get()?.flush(log);\n }\n }\n if (!this._keyBackup.get()) {\n // null means key backup isn't configured yet\n // as opposed to undefined, which means we're still checking\n this._keyBackup.set(null);\n }\n }\n // restore unfinished operations, like sending out room keys\n const opsTxn = await this._storage.readWriteTxn([\n this._storage.storeNames.operations\n ]);\n const operations = await opsTxn.operations.getAll();\n const operationsByScope = groupBy(operations, o => o.scope);\n\n for (const room of this._rooms.values()) {\n let roomOperationsByType;\n const roomOperations = operationsByScope.get(room.id);\n if (roomOperations) {\n roomOperationsByType = groupBy(roomOperations, r => r.type);\n }\n room.start(roomOperationsByType, log);\n }\n }\n\n async _getPendingEventsByRoom(txn) {\n const pendingEvents = await txn.pendingEvents.getAll();\n return pendingEvents.reduce((groups, pe) => {\n const group = groups.get(pe.roomId);\n if (group) {\n group.push(pe);\n } else {\n groups.set(pe.roomId, [pe]);\n }\n return groups;\n }, new Map());\n }\n\n get rooms() {\n return this._rooms;\n }\n\n findDirectMessageForUserId(userId) {\n for (const [,room] of this._rooms) {\n if (room.isDirectMessageForUserId(userId)) {\n return room;\n }\n }\n for (const [,invite] of this._invites) {\n if (invite.isDirectMessageForUserId(userId)) {\n return invite;\n }\n }\n }\n\n /** @internal */\n createJoinedRoom(roomId, pendingEvents) {\n return new Room({\n roomId,\n getSyncToken: this._getSyncToken,\n storage: this._storage,\n emitCollectionChange: this._roomUpdateCallback,\n hsApi: this._hsApi,\n mediaRepository: this._mediaRepository,\n pendingEvents,\n user: this._user,\n createRoomEncryption: this._createRoomEncryption,\n platform: this._platform\n });\n }\n\n /** @internal */\n _createArchivedRoom(roomId) {\n const room = new ArchivedRoom({\n roomId,\n getSyncToken: this._getSyncToken,\n storage: this._storage,\n emitCollectionChange: () => {},\n releaseCallback: () => this._activeArchivedRooms.delete(roomId),\n forgetCallback: this._forgetArchivedRoom,\n hsApi: this._hsApi,\n mediaRepository: this._mediaRepository,\n user: this._user,\n createRoomEncryption: this._createRoomEncryption,\n platform: this._platform\n });\n this._activeArchivedRooms.set(roomId, room);\n return room;\n }\n\n get invites() {\n return this._invites;\n }\n\n /** @internal */\n createInvite(roomId) {\n return new Invite({\n roomId,\n hsApi: this._hsApi,\n emitCollectionUpdate: this._inviteUpdateCallback,\n mediaRepository: this._mediaRepository,\n user: this._user,\n platform: this._platform,\n });\n }\n\n get roomsBeingCreated() {\n return this._roomsBeingCreated;\n }\n\n createRoom(options) {\n let roomBeingCreated;\n this._platform.logger.runDetached(\"create room\", async log => {\n const id = `local-${Math.floor(this._platform.random() * Number.MAX_SAFE_INTEGER)}`;\n roomBeingCreated = new RoomBeingCreated(\n id, options, this._roomsBeingCreatedUpdateCallback,\n this._mediaRepository, this._platform, log);\n this._roomsBeingCreated.set(id, roomBeingCreated);\n const promises = [roomBeingCreated.create(this._hsApi, log)];\n const loadProfiles = options.loadProfiles !== false; // default to true\n if (loadProfiles) {\n promises.push(roomBeingCreated.loadProfiles(this._hsApi, log));\n }\n await Promise.all(promises);\n // we should now know the roomId, check if the room was synced before we received\n // the room id. Replace the room being created with the synced room.\n if (roomBeingCreated.roomId) {\n if (this.rooms.get(roomBeingCreated.roomId)) {\n this._tryReplaceRoomBeingCreated(roomBeingCreated.roomId, log);\n }\n await roomBeingCreated.adjustDirectMessageMapIfNeeded(this._user, this._storage, this._hsApi, log);\n }\n });\n return roomBeingCreated;\n }\n\n async obtainSyncLock(syncResponse) {\n const toDeviceEvents = syncResponse.to_device?.events;\n if (Array.isArray(toDeviceEvents) && toDeviceEvents.length) {\n return await this._deviceMessageHandler.obtainSyncLock(toDeviceEvents);\n }\n }\n\n async prepareSync(syncResponse, lock, txn, log) {\n const toDeviceEvents = syncResponse.to_device?.events;\n if (Array.isArray(toDeviceEvents) && toDeviceEvents.length) {\n return await log.wrap(\"deviceMsgs\", log => this._deviceMessageHandler.prepareSync(toDeviceEvents, lock, txn, log));\n }\n }\n\n /** @internal */\n async writeSync(syncResponse, syncFilterId, preparation, txn, log) {\n const changes = {\n syncInfo: null,\n e2eeAccountChanges: null\n };\n const syncToken = syncResponse.next_batch;\n if (syncToken !== this.syncToken) {\n const syncInfo = {token: syncToken, filterId: syncFilterId};\n // don't modify `this` because transaction might still fail\n txn.session.set(\"sync\", syncInfo);\n changes.syncInfo = syncInfo;\n }\n\n const deviceOneTimeKeysCount = syncResponse.device_one_time_keys_count;\n if (this._e2eeAccount && deviceOneTimeKeysCount) {\n changes.e2eeAccountChanges = this._e2eeAccount.writeSync(deviceOneTimeKeysCount, txn, log);\n }\n \n const deviceLists = syncResponse.device_lists;\n if (this._deviceTracker && Array.isArray(deviceLists?.changed) && deviceLists.changed.length) {\n await log.wrap(\"deviceLists\", log => this._deviceTracker.writeDeviceChanges(deviceLists.changed, txn, log));\n }\n\n if (preparation) {\n changes.hasNewRoomKeys = await log.wrap(\"deviceMsgs\", log => this._deviceMessageHandler.writeSync(preparation, txn, log));\n }\n\n // store account data\n const accountData = syncResponse[\"account_data\"];\n if (Array.isArray(accountData?.events)) {\n for (const event of accountData.events) {\n if (typeof event.type === \"string\") {\n txn.accountData.set(event);\n }\n }\n }\n return changes;\n }\n\n /** @internal */\n afterSync({syncInfo, e2eeAccountChanges}) {\n if (syncInfo) {\n // sync transaction succeeded, modify object state now\n this._syncInfo = syncInfo;\n }\n if (this._e2eeAccount) {\n this._e2eeAccount.afterSync(e2eeAccountChanges);\n }\n }\n\n /** @internal */\n async afterSyncCompleted(changes, isCatchupSync, log) {\n // we don't start uploading one-time keys until we've caught up with\n // to-device messages, to help us avoid throwing away one-time-keys that we\n // are about to receive messages for\n // (https://github.com/vector-im/riot-web/issues/2782).\n if (!isCatchupSync) {\n const needsToUploadOTKs = await this._e2eeAccount.generateOTKsIfNeeded(this._storage, log);\n if (needsToUploadOTKs) {\n await log.wrap(\"uploadKeys\", log => this._e2eeAccount.uploadKeys(this._storage, false, log));\n }\n }\n if (changes.hasNewRoomKeys) {\n this._keyBackup.get()?.flush(log);\n }\n }\n\n _tryReplaceRoomBeingCreated(roomId, log) {\n for (const [,roomBeingCreated] of this._roomsBeingCreated) {\n if (roomBeingCreated.roomId === roomId) {\n const observableStatus = this._observedRoomStatus.get(roomBeingCreated.id);\n if (observableStatus) {\n log.log(`replacing room being created`)\n .set(\"localId\", roomBeingCreated.id)\n .set(\"roomId\", roomBeingCreated.roomId);\n observableStatus.set(observableStatus.get() | RoomStatus.Replaced);\n }\n roomBeingCreated.dispose();\n this._roomsBeingCreated.remove(roomBeingCreated.id);\n return;\n }\n }\n }\n\n applyRoomCollectionChangesAfterSync(inviteStates, roomStates, archivedRoomStates, log) {\n // update the collections after sync\n for (const rs of roomStates) {\n if (rs.shouldAdd) {\n this._rooms.add(rs.id, rs.room);\n this._tryReplaceRoomBeingCreated(rs.id, log);\n } else if (rs.shouldRemove) {\n this._rooms.remove(rs.id);\n }\n }\n for (const is of inviteStates) {\n if (is.shouldAdd) {\n this._invites.add(is.id, is.invite);\n } else if (is.shouldRemove) {\n this._invites.remove(is.id);\n }\n }\n // now all the collections are updated, update the room status\n // so any listeners to the status will find the collections\n // completely up to date\n if (this._observedRoomStatus.size !== 0) {\n for (const ars of archivedRoomStates) {\n if (ars.shouldAdd) {\n this._observedRoomStatus.get(ars.id)?.set(RoomStatus.Archived);\n }\n }\n for (const rs of roomStates) {\n if (rs.shouldAdd) {\n this._observedRoomStatus.get(rs.id)?.set(RoomStatus.Joined);\n }\n }\n for (const is of inviteStates) {\n const statusObservable = this._observedRoomStatus.get(is.id);\n if (statusObservable) {\n const withInvited = statusObservable.get() | RoomStatus.Invited;\n if (is.shouldAdd) {\n statusObservable.set(withInvited);\n } else if (is.shouldRemove) {\n const withoutInvited = withInvited ^ RoomStatus.Invited;\n statusObservable.set(withoutInvited);\n }\n }\n }\n }\n }\n\n _forgetArchivedRoom(roomId) {\n const statusObservable = this._observedRoomStatus.get(roomId);\n if (statusObservable) {\n statusObservable.set((statusObservable.get() | RoomStatus.Archived) ^ RoomStatus.Archived);\n }\n }\n\n /** @internal */\n get syncToken() {\n return this._syncInfo?.token;\n }\n\n /** @internal */\n get syncFilterId() {\n return this._syncInfo?.filterId;\n }\n\n get user() {\n return this._user;\n }\n\n get mediaRepository() {\n return this._mediaRepository;\n }\n\n enablePushNotifications(enable) {\n if (enable) {\n return this._enablePush();\n } else {\n return this._disablePush();\n }\n }\n\n async _enablePush() {\n return this._platform.logger.run(\"enablePush\", async log => {\n const defaultPayload = Pusher.createDefaultPayload(this._sessionInfo.id);\n const pusher = await this._platform.notificationService.enablePush(Pusher, defaultPayload);\n if (!pusher) {\n log.set(\"no_pusher\", true);\n return false;\n }\n await pusher.enable(this._hsApi, log);\n // store pusher data, so we know we enabled it across reloads,\n // and we can disable it without too much hassle\n const txn = await this._storage.readWriteTxn([this._storage.storeNames.session]);\n txn.session.set(PUSHER_KEY, pusher.serialize());\n await txn.complete();\n return true;\n });\n }\n\n\n async _disablePush() {\n return this._platform.logger.run(\"disablePush\", async log => {\n await this._platform.notificationService.disablePush();\n const readTxn = await this._storage.readTxn([this._storage.storeNames.session]);\n const pusherData = await readTxn.session.get(PUSHER_KEY);\n if (!pusherData) {\n // we've disabled push in the notif service at least\n return true;\n }\n const pusher = new Pusher(pusherData);\n await pusher.disable(this._hsApi, log);\n const txn = await this._storage.readWriteTxn([this._storage.storeNames.session]);\n txn.session.remove(PUSHER_KEY);\n await txn.complete();\n return true;\n });\n }\n\n async arePushNotificationsEnabled() {\n if (!await this._platform.notificationService.isPushEnabled()) {\n return false;\n }\n const readTxn = await this._storage.readTxn([this._storage.storeNames.session]);\n const pusherData = await readTxn.session.get(PUSHER_KEY);\n return !!pusherData;\n }\n\n async checkPusherEnabledOnHomeserver() {\n const readTxn = await this._storage.readTxn([this._storage.storeNames.session]);\n const pusherData = await readTxn.session.get(PUSHER_KEY);\n if (!pusherData) {\n return false;\n }\n const myPusher = new Pusher(pusherData);\n const serverPushersData = await this._hsApi.getPushers().response();\n const serverPushers = (serverPushersData?.pushers || []).map(data => new Pusher(data));\n return serverPushers.some(p => p.equals(myPusher));\n }\n\n async getRoomStatus(roomId) {\n const isBeingCreated = !!this._roomsBeingCreated.get(roomId);\n if (isBeingCreated) {\n return RoomStatus.BeingCreated;\n }\n const isJoined = !!this._rooms.get(roomId);\n if (isJoined) {\n return RoomStatus.Joined;\n } else {\n const isInvited = !!this._invites.get(roomId);\n const txn = await this._storage.readTxn([this._storage.storeNames.archivedRoomSummary]);\n const isArchived = await txn.archivedRoomSummary.has(roomId);\n if (isInvited && isArchived) {\n return RoomStatus.Invited | RoomStatus.Archived;\n } else if (isInvited) {\n return RoomStatus.Invited;\n } else if (isArchived) {\n return RoomStatus.Archived;\n } else {\n return RoomStatus.None;\n }\n }\n }\n\n async observeRoomStatus(roomId) {\n let observable = this._observedRoomStatus.get(roomId);\n if (!observable) {\n const status = await this.getRoomStatus(roomId);\n observable = new RetainedObservableValue(status, () => {\n this._observedRoomStatus.delete(roomId);\n });\n\n this._observedRoomStatus.set(roomId, observable);\n }\n return observable;\n }\n\n /**\n Creates an empty (summary isn't loaded) the archived room if it isn't\n loaded already, assuming sync will either remove it (when rejoining) or\n write a full summary adopting it from the joined room when leaving\n \n @internal\n */\n createOrGetArchivedRoomForSync(roomId) {\n let archivedRoom = this._activeArchivedRooms.get(roomId);\n if (archivedRoom) {\n archivedRoom.retain();\n } else {\n archivedRoom = this._createArchivedRoom(roomId);\n }\n return archivedRoom;\n }\n\n loadArchivedRoom(roomId, log = null) {\n return this._platform.logger.wrapOrRun(log, \"loadArchivedRoom\", async log => {\n log.set(\"id\", roomId);\n const activeArchivedRoom = this._activeArchivedRooms.get(roomId);\n if (activeArchivedRoom) {\n activeArchivedRoom.retain();\n return activeArchivedRoom;\n }\n const txn = await this._storage.readTxn([\n this._storage.storeNames.archivedRoomSummary,\n this._storage.storeNames.roomMembers,\n ]);\n const summary = await txn.archivedRoomSummary.get(roomId);\n if (summary) {\n const room = this._createArchivedRoom(roomId);\n await room.load(summary, txn, log);\n return room;\n }\n });\n }\n\n joinRoom(roomIdOrAlias, log = null) {\n return this._platform.logger.wrapOrRun(log, \"joinRoom\", async log => {\n const body = await this._hsApi.joinIdOrAlias(roomIdOrAlias, {log}).response();\n return body.room_id;\n });\n }\n}\n\nexport function tests() {\n function createStorageMock(session, pendingEvents = []) {\n return {\n readTxn() {\n return {\n session: {\n get(key) {\n return Promise.resolve(session[key]);\n }\n },\n pendingEvents: {\n getAll() {\n return Promise.resolve(pendingEvents);\n }\n },\n roomSummary: {\n getAll() {\n return Promise.resolve([]);\n }\n },\n invites: {\n getAll() {\n return Promise.resolve([]);\n }\n }\n };\n },\n storeNames: {}\n };\n }\n\n return {\n \"session data is not modified until after sync\": async (assert) => {\n const session = new Session({storage: createStorageMock({\n sync: {token: \"a\", filterId: 5}\n }), sessionInfo: {userId: \"\"}});\n await session.load();\n let syncSet = false;\n const syncTxn = {\n session: {\n set(key, value) {\n if (key === \"sync\") {\n assert.equal(value.token, \"b\");\n assert.equal(value.filterId, 6);\n syncSet = true;\n }\n }\n }\n };\n const newSessionData = await session.writeSync({next_batch: \"b\"}, 6, null, syncTxn, {});\n assert(syncSet);\n assert.equal(session.syncToken, \"a\");\n assert.equal(session.syncFilterId, 5);\n session.afterSync(newSessionData);\n assert.equal(session.syncToken, \"b\");\n assert.equal(session.syncFilterId, 6);\n }\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ILogItem} from \"../../logging/types\";\nimport {ILoginMethod} from \"./LoginMethod\";\nimport {HomeServerApi} from \"../net/HomeServerApi.js\";\n\nexport class PasswordLoginMethod implements ILoginMethod {\n private readonly _username: string;\n private readonly _password: string;\n public readonly homeserver: string;\n\n constructor({username, password, homeserver}: {username: string, password: string, homeserver: string}) {\n this._username = username;\n this._password = password;\n this.homeserver = homeserver;\n }\n\n async login(hsApi: HomeServerApi, deviceName: string, log: ILogItem): Promise<Record<string, any>> {\n return await hsApi.passwordLogin(this._username, this._password, deviceName, {log}).response();\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {makeTxnId} from \"../common.js\";\nimport {ILogItem} from \"../../logging/types\";\nimport {ILoginMethod} from \"./LoginMethod\";\nimport {HomeServerApi} from \"../net/HomeServerApi.js\";\n\nexport class TokenLoginMethod implements ILoginMethod {\n private readonly _loginToken: string;\n public readonly homeserver: string;\n\n constructor({ homeserver, loginToken }: { homeserver: string, loginToken: string}) {\n this.homeserver = homeserver;\n this._loginToken = loginToken;\n }\n\n async login(hsApi: HomeServerApi, deviceName: string, log: ILogItem): Promise<Record<string, any>> {\n return await hsApi.tokenLogin(this._loginToken, makeTxnId(), deviceName, {log}).response();\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport class SSOLoginHelper{\n private _homeserver: string;\n\n constructor(homeserver: string) {\n this._homeserver = homeserver;\n }\n\n get homeserver(): string { return this._homeserver; }\n\n createSSORedirectURL(returnURL: string): string {\n return `${this._homeserver}/_matrix/client/r0/login/sso/redirect?redirectUrl=${returnURL}`;\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport type {AuthenticationData, RegistrationParams} from \"../types\";\n\nexport abstract class BaseRegistrationStage {\n protected _session: string;\n protected _nextStage: BaseRegistrationStage;\n protected readonly _params?: Record<string, any>\n\n constructor(session: string, params?: RegistrationParams) {\n this._session = session;\n this._params = params;\n }\n\n /**\n * eg: m.login.recaptcha or m.login.dummy\n */\n abstract get type(): string;\n\n /**\n * This method should return auth part that must be provided to\n * /register endpoint to successfully complete this stage\n */\n /** @internal */\n abstract generateAuthenticationData(): AuthenticationData;\n\n setNextStage(stage: BaseRegistrationStage) {\n this._nextStage = stage;\n }\n\n get nextStage(): BaseRegistrationStage {\n return this._nextStage;\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {AuthenticationData} from \"../types\";\nimport {BaseRegistrationStage} from \"./BaseRegistrationStage\";\n\nexport class DummyAuth extends BaseRegistrationStage {\n generateAuthenticationData(): AuthenticationData {\n return {\n session: this._session,\n type: this.type,\n }; \n }\n\n get type(): string {\n return \"m.login.dummy\";\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {AuthenticationData} from \"../types\";\nimport {BaseRegistrationStage} from \"./BaseRegistrationStage\";\n\nexport class TermsAuth extends BaseRegistrationStage {\n generateAuthenticationData(): AuthenticationData {\n return {\n session: this._session,\n type: this.type,\n // No other auth data needed for m.login.terms\n }; \n }\n\n get type(): string {\n return \"m.login.terms\";\n }\n\n get privacyPolicy() {\n return this._params?.policies[\"privacy_policy\"];\n }\n\n get termsOfService() {\n return this._params?.policies[\"terms_of_service\"];\n }\n}\n","/*\nCopyright 2022 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {AuthenticationData, RegistrationParams} from \"../types\";\nimport {BaseRegistrationStage} from \"./BaseRegistrationStage\";\n\nexport class TokenAuth extends BaseRegistrationStage {\n private _token?: string;\n private readonly _type: string;\n\n constructor(session: string, params: RegistrationParams | undefined, type: string) {\n super(session, params);\n this._type = type;\n }\n\n\n generateAuthenticationData(): AuthenticationData {\n if (!this._token) {\n throw new Error(\"No token provided for TokenAuth\");\n }\n return {\n session: this._session,\n type: this._type,\n token: this._token,\n }; \n }\n\n setToken(token: string) {\n this._token = token;\n }\n\n get type(): string {\n return this._type;\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport type {HomeServerApi} from \"../net/HomeServerApi\";\nimport type {BaseRegistrationStage} from \"./stages/BaseRegistrationStage\";\nimport {DummyAuth} from \"./stages/DummyAuth\";\nimport {TermsAuth} from \"./stages/TermsAuth\";\nimport {TokenAuth} from \"./stages/TokenAuth\";\nimport type {\n AccountDetails,\n RegistrationFlow,\n RegistrationResponseMoreDataNeeded,\n RegistrationResponse,\n RegistrationResponseSuccess,\n RegistrationParams,\n} from \"./types\";\n\ntype FlowSelector = (flows: RegistrationFlow[]) => RegistrationFlow | void;\n\nexport class Registration {\n private readonly _hsApi: HomeServerApi;\n private readonly _accountDetails: AccountDetails;\n private readonly _flowSelector: FlowSelector;\n private _sessionInfo?: RegistrationResponseSuccess\n\n constructor(hsApi: HomeServerApi, accountDetails: AccountDetails, flowSelector?: FlowSelector) {\n this._hsApi = hsApi;\n this._accountDetails = accountDetails;\n this._flowSelector = flowSelector ?? (flows => flows[0]);\n }\n\n async start(): Promise<BaseRegistrationStage> {\n const response = await this._hsApi.register(\n this._accountDetails.username,\n this._accountDetails.password,\n this._accountDetails.initialDeviceDisplayName,\n undefined,\n this._accountDetails.inhibitLogin).response();\n return this.parseStagesFromResponse(response);\n }\n\n /**\n * Finish a registration stage, return value is:\n * - the next stage if this stage was completed successfully\n * - undefined if registration is completed\n */\n async submitStage(stage: BaseRegistrationStage): Promise<BaseRegistrationStage | undefined> {\n const auth = stage.generateAuthenticationData();\n const { username, password, initialDeviceDisplayName, inhibitLogin } = this._accountDetails;\n const request = this._hsApi.register(username, password, initialDeviceDisplayName, auth, inhibitLogin);\n const response = await request.response();\n const status = await request.responseCode();\n const registrationResponse: RegistrationResponse = { ...response, status };\n return this.parseRegistrationResponse(registrationResponse, stage);\n }\n\n private parseStagesFromResponse(response: RegistrationResponseMoreDataNeeded): BaseRegistrationStage {\n const { session, params } = response;\n const flow = this._flowSelector(response.flows);\n if (!flow) {\n throw new Error(\"flowSelector did not return any flow!\");\n }\n let firstStage: BaseRegistrationStage | undefined;\n let lastStage: BaseRegistrationStage | undefined;\n for (const stage of flow.stages) {\n const registrationStage = this._createRegistrationStage(stage, session, params);\n if (!firstStage) {\n firstStage = registrationStage;\n lastStage = registrationStage;\n } else {\n lastStage!.setNextStage(registrationStage);\n lastStage = registrationStage;\n }\n }\n return firstStage!;\n }\n\n private async parseRegistrationResponse(response: RegistrationResponse, currentStage: BaseRegistrationStage) {\n switch (response.status) {\n case 200:\n this._sessionInfo = response;\n return undefined;\n case 401:\n if (response.completed?.includes(currentStage.type)) {\n return currentStage.nextStage;\n }\n else {\n throw new Error(\"This stage could not be completed!\");\n }\n }\n }\n\n private _createRegistrationStage(type: string, session: string, params?: RegistrationParams) {\n switch (type) {\n case \"m.login.dummy\":\n return new DummyAuth(session, params?.[type]);\n case \"m.login.terms\":\n return new TermsAuth(session, params?.[type]);\n case \"org.matrix.msc3231.login.registration_token\":\n case \"m.login.registration_token\":\n return new TokenAuth(session, params?.[type], type);\n default:\n throw new Error(`Unknown stage: ${type}`);\n }\n }\n\n get sessionInfo(): RegistrationResponseSuccess | undefined {\n return this._sessionInfo;\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020, 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {createEnum} from \"../utils/enum\";\nimport {lookupHomeserver} from \"./well-known.js\";\nimport {AbortableOperation} from \"../utils/AbortableOperation\";\nimport {ObservableValue} from \"../observable/ObservableValue\";\nimport {HomeServerApi} from \"./net/HomeServerApi\";\nimport {Reconnector, ConnectionStatus} from \"./net/Reconnector\";\nimport {ExponentialRetryDelay} from \"./net/ExponentialRetryDelay\";\nimport {MediaRepository} from \"./net/MediaRepository\";\nimport {RequestScheduler} from \"./net/RequestScheduler\";\nimport {Sync, SyncStatus} from \"./Sync.js\";\nimport {Session} from \"./Session.js\";\nimport {PasswordLoginMethod} from \"./login/PasswordLoginMethod\";\nimport {TokenLoginMethod} from \"./login/TokenLoginMethod\";\nimport {SSOLoginHelper} from \"./login/SSOLoginHelper\";\nimport {getDehydratedDevice} from \"./e2ee/Dehydration.js\";\nimport {Registration} from \"./registration/Registration\";\n\nexport const LoadStatus = createEnum(\n \"NotLoading\",\n \"Login\",\n \"LoginFailed\",\n \"QueryAccount\", // check for dehydrated device after login\n \"AccountSetup\", // asked to restore from dehydrated device if present, call sc.accountSetup.finish() to progress to the next stage\n \"Loading\",\n \"SessionSetup\", // upload e2ee keys, ...\n \"Migrating\", // not used atm, but would fit here\n \"FirstSync\",\n \"Error\",\n \"Ready\",\n);\n\nexport const LoginFailure = createEnum(\n \"Connection\",\n \"Credentials\",\n \"Unknown\",\n);\n\nexport class Client {\n constructor(platform) {\n this._platform = platform;\n this._sessionStartedByReconnector = false;\n this._status = new ObservableValue(LoadStatus.NotLoading);\n this._error = null;\n this._loginFailure = null;\n this._reconnector = null;\n this._session = null;\n this._sync = null;\n this._sessionId = null;\n this._storage = null;\n this._requestScheduler = null;\n this._olmPromise = platform.loadOlm();\n this._workerPromise = platform.loadOlmWorker();\n this._accountSetup = undefined;\n }\n\n createNewSessionId() {\n return (Math.floor(this._platform.random() * Number.MAX_SAFE_INTEGER)).toString();\n }\n\n get sessionId() {\n return this._sessionId;\n }\n\n async startWithExistingSession(sessionId) {\n if (this._status.get() !== LoadStatus.NotLoading) {\n return;\n }\n this._status.set(LoadStatus.Loading);\n await this._platform.logger.run(\"load session\", async log => {\n log.set(\"id\", sessionId);\n try {\n const sessionInfo = await this._platform.sessionInfoStorage.get(sessionId);\n if (!sessionInfo) {\n throw new Error(\"Invalid session id: \" + sessionId);\n }\n await this._loadSessionInfo(sessionInfo, null, log);\n log.set(\"status\", this._status.get());\n } catch (err) {\n log.catch(err);\n this._error = err;\n this._status.set(LoadStatus.Error);\n }\n });\n }\n\n // TODO: When converted to typescript this should return the same type\n // as this._loginOptions is in LoginViewModel.ts (LoginOptions).\n _parseLoginOptions(options, homeserver) {\n /*\n Take server response and return new object which has two props password and sso which\n implements LoginMethod\n */\n const flows = options.flows;\n const result = {homeserver};\n for (const flow of flows) {\n if (flow.type === \"m.login.password\") {\n result.password = (username, password) => new PasswordLoginMethod({homeserver, username, password});\n }\n else if (flow.type === \"m.login.sso\" && flows.find(flow => flow.type === \"m.login.token\")) {\n result.sso = new SSOLoginHelper(homeserver);\n }\n else if (flow.type === \"m.login.token\") {\n result.token = loginToken => new TokenLoginMethod({homeserver, loginToken});\n }\n }\n return result;\n }\n\n queryLogin(homeserver) {\n return new AbortableOperation(async setAbortable => {\n homeserver = await lookupHomeserver(homeserver, (url, options) => {\n return setAbortable(this._platform.request(url, options));\n });\n const hsApi = new HomeServerApi({homeserver, request: this._platform.request});\n const response = await setAbortable(hsApi.getLoginFlows()).response();\n return this._parseLoginOptions(response, homeserver);\n });\n }\n\n async startRegistration(homeserver, username, password, initialDeviceDisplayName, flowSelector) {\n const request = this._platform.request;\n const hsApi = new HomeServerApi({homeserver, request});\n const registration = new Registration(hsApi, {\n username,\n password,\n initialDeviceDisplayName,\n },\n flowSelector);\n return registration;\n }\n\n async startWithLogin(loginMethod, {inspectAccountSetup} = {}) {\n const currentStatus = this._status.get();\n if (currentStatus !== LoadStatus.LoginFailed &&\n currentStatus !== LoadStatus.NotLoading &&\n currentStatus !== LoadStatus.Error) {\n return;\n }\n this._resetStatus();\n await this._platform.logger.run(\"login\", async log => {\n this._status.set(LoadStatus.Login);\n const clock = this._platform.clock;\n let sessionInfo;\n try {\n const request = this._platform.request;\n const hsApi = new HomeServerApi({homeserver: loginMethod.homeserver, request});\n const loginData = await loginMethod.login(hsApi, \"Hydrogen\", log);\n const sessionId = this.createNewSessionId();\n sessionInfo = {\n id: sessionId,\n deviceId: loginData.device_id,\n userId: loginData.user_id,\n homeServer: loginMethod.homeserver, // deprecate this over time\n homeserver: loginMethod.homeserver,\n accessToken: loginData.access_token,\n lastUsed: clock.now()\n };\n log.set(\"id\", sessionId);\n } catch (err) {\n this._error = err;\n if (err.name === \"HomeServerError\") {\n if (err.errcode === \"M_FORBIDDEN\") {\n this._loginFailure = LoginFailure.Credentials;\n } else {\n this._loginFailure = LoginFailure.Unknown;\n }\n log.set(\"loginFailure\", this._loginFailure);\n this._status.set(LoadStatus.LoginFailed);\n } else if (err.name === \"ConnectionError\") {\n this._loginFailure = LoginFailure.Connection;\n this._status.set(LoadStatus.LoginFailed);\n } else {\n this._status.set(LoadStatus.Error);\n }\n return;\n }\n let dehydratedDevice;\n if (inspectAccountSetup) {\n dehydratedDevice = await this._inspectAccountAfterLogin(sessionInfo, log);\n if (dehydratedDevice) {\n sessionInfo.deviceId = dehydratedDevice.deviceId;\n }\n }\n await this._platform.sessionInfoStorage.add(sessionInfo);\n // loading the session can only lead to\n // LoadStatus.Error in case of an error,\n // so separate try/catch\n try {\n await this._loadSessionInfo(sessionInfo, dehydratedDevice, log);\n log.set(\"status\", this._status.get());\n } catch (err) {\n log.catch(err);\n // free olm Account that might be contained\n dehydratedDevice?.dispose();\n this._error = err;\n this._status.set(LoadStatus.Error);\n }\n });\n }\n\n async _loadSessionInfo(sessionInfo, dehydratedDevice, log) {\n log.set(\"appVersion\", this._platform.version);\n const clock = this._platform.clock;\n this._sessionStartedByReconnector = false;\n this._status.set(LoadStatus.Loading);\n this._reconnector = new Reconnector({\n onlineStatus: this._platform.onlineStatus,\n retryDelay: new ExponentialRetryDelay(clock.createTimeout),\n createMeasure: clock.createMeasure\n });\n const hsApi = new HomeServerApi({\n homeserver: sessionInfo.homeServer,\n accessToken: sessionInfo.accessToken,\n request: this._platform.request,\n reconnector: this._reconnector,\n });\n this._sessionId = sessionInfo.id;\n this._storage = await this._platform.storageFactory.create(sessionInfo.id, log);\n // no need to pass access token to session\n const filteredSessionInfo = {\n id: sessionInfo.id,\n deviceId: sessionInfo.deviceId,\n userId: sessionInfo.userId,\n homeserver: sessionInfo.homeServer,\n };\n const olm = await this._olmPromise;\n let olmWorker = null;\n if (this._workerPromise) {\n olmWorker = await this._workerPromise;\n }\n this._requestScheduler = new RequestScheduler({hsApi, clock});\n this._requestScheduler.start();\n const mediaRepository = new MediaRepository({\n homeserver: sessionInfo.homeServer,\n platform: this._platform,\n });\n this._session = new Session({\n storage: this._storage,\n sessionInfo: filteredSessionInfo,\n hsApi: this._requestScheduler.hsApi,\n olm,\n olmWorker,\n mediaRepository,\n platform: this._platform,\n });\n await this._session.load(log);\n if (dehydratedDevice) {\n await log.wrap(\"dehydrateIdentity\", log => this._session.dehydrateIdentity(dehydratedDevice, log));\n await this._session.setupDehydratedDevice(dehydratedDevice.key, log);\n } else if (!this._session.hasIdentity) {\n this._status.set(LoadStatus.SessionSetup);\n await log.wrap(\"createIdentity\", log => this._session.createIdentity(log));\n }\n\n this._sync = new Sync({hsApi: this._requestScheduler.hsApi, storage: this._storage, session: this._session, logger: this._platform.logger});\n // notify sync and session when back online\n this._reconnectSubscription = this._reconnector.connectionStatus.subscribe(state => {\n if (state === ConnectionStatus.Online) {\n this._platform.logger.runDetached(\"reconnect\", async log => {\n // needs to happen before sync and session or it would abort all requests\n this._requestScheduler.start();\n this._sync.start();\n this._sessionStartedByReconnector = true;\n const d = dehydratedDevice;\n dehydratedDevice = undefined;\n await log.wrap(\"session start\", log => this._session.start(this._reconnector.lastVersionsResponse, d, log));\n });\n }\n });\n await log.wrap(\"wait first sync\", () => this._waitForFirstSync());\n if (this._isDisposed) {\n return;\n }\n this._status.set(LoadStatus.Ready);\n\n // if the sync failed, and then the reconnector\n // restored the connection, it would have already\n // started to session, so check first\n // to prevent an extra /versions request\n if (!this._sessionStartedByReconnector) {\n const lastVersionsResponse = await hsApi.versions({timeout: 10000, log}).response();\n if (this._isDisposed) {\n return;\n }\n const d = dehydratedDevice;\n dehydratedDevice = undefined;\n // log as ref as we don't want to await it\n await log.wrap(\"session start\", log => this._session.start(lastVersionsResponse, d, log));\n }\n }\n\n async _waitForFirstSync() {\n this._sync.start();\n this._status.set(LoadStatus.FirstSync);\n // only transition into Ready once the first sync has succeeded\n this._waitForFirstSyncHandle = this._sync.status.waitFor(s => {\n if (s === SyncStatus.Stopped) {\n // keep waiting if there is a ConnectionError\n // as the reconnector above will call\n // sync.start again to retry in this case\n return this._sync.error?.name !== \"ConnectionError\";\n }\n return s === SyncStatus.Syncing;\n });\n try {\n await this._waitForFirstSyncHandle.promise;\n if (this._sync.status.get() === SyncStatus.Stopped && this._sync.error) {\n throw this._sync.error;\n }\n } catch (err) {\n // if dispose is called from stop, bail out\n if (err.name === \"AbortError\") {\n return;\n }\n throw err;\n } finally {\n this._waitForFirstSyncHandle = null;\n }\n }\n\n _inspectAccountAfterLogin(sessionInfo, log) {\n return log.wrap(\"inspectAccount\", async log => {\n this._status.set(LoadStatus.QueryAccount);\n const hsApi = new HomeServerApi({\n homeserver: sessionInfo.homeServer,\n accessToken: sessionInfo.accessToken,\n request: this._platform.request,\n });\n const olm = await this._olmPromise;\n let encryptedDehydratedDevice;\n try {\n encryptedDehydratedDevice = await getDehydratedDevice(hsApi, olm, this._platform, log);\n } catch (err) {\n if (err.name === \"HomeServerError\") {\n log.set(\"not_supported\", true);\n } else {\n throw err;\n }\n }\n if (encryptedDehydratedDevice) {\n let resolveStageFinish;\n const promiseStageFinish = new Promise(r => resolveStageFinish = r);\n this._accountSetup = new AccountSetup(encryptedDehydratedDevice, resolveStageFinish);\n this._status.set(LoadStatus.AccountSetup);\n await promiseStageFinish;\n const dehydratedDevice = this._accountSetup?._dehydratedDevice;\n this._accountSetup = null;\n return dehydratedDevice;\n }\n });\n }\n\n get accountSetup() {\n return this._accountSetup;\n }\n\n get loadStatus() {\n return this._status;\n }\n\n get loadError() {\n return this._error;\n }\n\n get loginFailure() {\n return this._loginFailure;\n }\n\n /** only set at loadStatus InitialSync, CatchupSync or Ready */\n get sync() {\n return this._sync;\n }\n\n /** only set at loadStatus InitialSync, CatchupSync or Ready */\n get session() {\n return this._session;\n }\n\n get reconnector() {\n return this._reconnector;\n }\n\n get _isDisposed() {\n return !this._reconnector;\n }\n\n startLogout(sessionId) {\n return this._platform.logger.run(\"logout\", async log => {\n this._sessionId = sessionId;\n log.set(\"id\", this._sessionId);\n const sessionInfo = await this._platform.sessionInfoStorage.get(this._sessionId);\n if (!sessionInfo) {\n throw new Error(`Could not find session for id ${this._sessionId}`);\n }\n try {\n const hsApi = new HomeServerApi({\n homeserver: sessionInfo.homeServer,\n accessToken: sessionInfo.accessToken,\n request: this._platform.request\n });\n await hsApi.logout({log}).response();\n } catch (err) {}\n await this.deleteSession(log);\n });\n }\n\n dispose() {\n if (this._reconnectSubscription) {\n this._reconnectSubscription();\n this._reconnectSubscription = null;\n }\n this._reconnector = null;\n if (this._requestScheduler) {\n this._requestScheduler.stop();\n this._requestScheduler = null;\n }\n if (this._sync) {\n this._sync.stop();\n this._sync = null;\n }\n if (this._session) {\n this._session.dispose();\n this._session = null;\n }\n if (this._waitForFirstSyncHandle) {\n this._waitForFirstSyncHandle.dispose();\n this._waitForFirstSyncHandle = null;\n }\n if (this._storage) {\n this._storage.close();\n this._storage = null;\n }\n }\n\n async deleteSession(log) {\n if (this._sessionId) {\n // need to dispose first, so the storage is closed,\n // and also first sync finishing won't call Session.start anymore,\n // which assumes that the storage works.\n this.dispose();\n // if one fails, don't block the other from trying\n // also, run in parallel\n await Promise.all([\n log.wrap(\"storageFactory\", () => this._platform.storageFactory.delete(this._sessionId)),\n log.wrap(\"sessionInfoStorage\", () => this._platform.sessionInfoStorage.delete(this._sessionId)),\n ]);\n this._sessionId = null;\n }\n }\n\n _resetStatus() {\n this._status.set(LoadStatus.NotLoading);\n this._error = null;\n this._loginFailure = null;\n }\n}\n\nclass AccountSetup {\n constructor(encryptedDehydratedDevice, finishStage) {\n this._encryptedDehydratedDevice = encryptedDehydratedDevice;\n this._dehydratedDevice = undefined;\n this._finishStage = finishStage;\n }\n\n get encryptedDehydratedDevice() {\n return this._encryptedDehydratedDevice;\n }\n\n finish(dehydratedDevice) {\n this._dehydratedDevice = dehydratedDevice;\n this._finishStage();\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2022 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// ViewModel should just be an eventemitter, not an ObservableValue\n// as in some cases it would really be more convenient to have multiple events (like telling the timeline to scroll down)\n// we do need to return a disposable from EventEmitter.on, or at least have a method here to easily track a subscription to an EventEmitter\n\nimport {EventEmitter} from \"../utils/EventEmitter\";\nimport {Disposables} from \"../utils/Disposables\";\n\nimport type {Disposable} from \"../utils/Disposables\";\nimport type {Platform} from \"../platform/web/Platform\";\nimport type {Clock} from \"../platform/web/dom/Clock\";\nimport type {ILogger} from \"../logging/types\";\nimport type {Navigation} from \"./navigation/Navigation\";\nimport type {SegmentType} from \"./navigation/index\";\nimport type {IURLRouter} from \"./navigation/URLRouter\";\n\nexport type Options<T extends object = SegmentType> = {\n platform: Platform;\n logger: ILogger;\n urlCreator: IURLRouter<T>;\n navigation: Navigation<T>;\n emitChange?: (params: any) => void;\n}\n\n\nexport class ViewModel<N extends object = SegmentType, O extends Options<N> = Options<N>> extends EventEmitter<{change: never}> {\n private disposables?: Disposables;\n private _isDisposed = false;\n private _options: Readonly<O>;\n\n constructor(options: Readonly<O>) {\n super();\n this._options = options;\n }\n\n childOptions<T extends Object>(explicitOptions: T): T & Options<N> {\n return Object.assign({}, this._options, explicitOptions);\n }\n\n get options(): Readonly<O> { return this._options; }\n\n // makes it easier to pass through dependencies of a sub-view model\n getOption<N extends keyof O>(name: N): O[N] {\n return this._options[name];\n }\n\n observeNavigation<T extends keyof N>(type: T, onChange: (value: N[T], type: T) => void): void {\n const segmentObservable = this.navigation.observe(type);\n const unsubscribe = segmentObservable.subscribe((value: N[T]) => {\n onChange(value, type);\n });\n this.track(unsubscribe);\n }\n\n track<D extends Disposable>(disposable: D): D {\n if (!this.disposables) {\n this.disposables = new Disposables();\n }\n return this.disposables.track(disposable);\n }\n\n untrack(disposable: Disposable): undefined {\n if (this.disposables) {\n return this.disposables.untrack(disposable);\n }\n return undefined;\n }\n\n dispose(): void {\n if (this.disposables) {\n this.disposables.dispose();\n }\n this._isDisposed = true;\n }\n\n get isDisposed(): boolean {\n return this._isDisposed;\n }\n\n disposeTracked(disposable: Disposable | undefined): undefined {\n if (this.disposables) {\n return this.disposables.disposeTracked(disposable);\n }\n return undefined;\n }\n\n // TODO: this will need to support binding\n // if any of the expr is a function, assume the function is a binding, and return a binding function ourselves\n //\n // translated string should probably always be bindings, unless we're fine with a refresh when changing the language?\n // we probably are, if we're using routing with a url, we could just refresh.\n i18n(parts: TemplateStringsArray, ...expr: any[]): string {\n // just concat for now\n let result = \"\";\n for (let i = 0; i < parts.length; ++i) {\n result = result + parts[i];\n if (i < expr.length) {\n result = result + expr[i];\n }\n }\n return result;\n }\n\n emitChange(changedProps: any): void {\n if (this._options.emitChange) {\n this._options.emitChange(changedProps);\n } else {\n this.emit(\"change\", changedProps);\n }\n }\n\n get platform(): Platform {\n return this._options.platform;\n }\n\n get clock(): Clock {\n return this._options.platform.clock;\n }\n\n get logger(): ILogger {\n return this.platform.logger;\n }\n\n get urlCreator(): IURLRouter<N> {\n return this._options.urlCreator;\n }\n\n get navigation(): Navigation<N> {\n // typescript needs a little help here\n return this._options.navigation as unknown as Navigation<N>;\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { Platform } from \"../platform/web/Platform\";\nimport { MediaRepository } from \"../matrix/net/MediaRepository\";\n\nexport function avatarInitials(name: string): string {\n let firstChar = name.charAt(0);\n if (firstChar === \"!\" || firstChar === \"@\" || firstChar === \"#\") {\n firstChar = name.charAt(1);\n }\n return firstChar.toUpperCase();\n}\n\n/**\n * calculates a numeric hash for a given string\n *\n * @param {string} str string to hash\n *\n * @return {number}\n */\nfunction hashCode(str: string): number {\n let hash = 0;\n let i: number;\n let chr: number;\n if (str.length === 0) {\n return hash;\n }\n for (i = 0; i < str.length; i++) {\n chr = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + chr;\n hash |= 0;\n }\n return Math.abs(hash);\n}\n\nexport function getIdentifierColorNumber(id: string): number {\n return (hashCode(id) % 8) + 1;\n}\n\nexport function getAvatarHttpUrl(avatarUrl: string, cssSize: number, platform: Platform, mediaRepository: MediaRepository): string | null {\n if (avatarUrl) {\n const imageSize = cssSize * platform.devicePixelRatio;\n return mediaRepository.mxcUrlThumbnail(avatarUrl, imageSize, imageSize, \"crop\");\n }\n return null;\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020, 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {avatarInitials, getIdentifierColorNumber, getAvatarHttpUrl} from \"../../avatar\";\nimport {ViewModel} from \"../../ViewModel\";\n\nconst KIND_ORDER = [\"roomBeingCreated\", \"invite\", \"room\"];\n\nexport class BaseTileViewModel extends ViewModel {\n constructor(options) {\n super(options);\n this._isOpen = false;\n this._hidden = false;\n }\n\n get hidden() {\n return this._hidden;\n }\n\n set hidden(value) {\n if (value !== this._hidden) {\n this._hidden = value;\n this.emitChange(\"hidden\");\n }\n }\n\n close() {\n if (this._isOpen) {\n this._isOpen = false;\n this.emitChange(\"isOpen\");\n }\n }\n\n open() {\n if (!this._isOpen) {\n this._isOpen = true;\n this.emitChange(\"isOpen\");\n }\n }\n\n get isOpen() {\n return this._isOpen;\n }\n\n compare(other) {\n if (other.kind !== this.kind) {\n return KIND_ORDER.indexOf(this.kind) - KIND_ORDER.indexOf(other.kind);\n }\n return 0;\n }\n\n // Avatar view model contract\n get avatarLetter() {\n return avatarInitials(this.name);\n }\n\n get avatarColorNumber() {\n return getIdentifierColorNumber(this._avatarSource.avatarColorId);\n }\n\n avatarUrl(size) {\n return getAvatarHttpUrl(this._avatarSource.avatarUrl, size, this.platform, this._avatarSource.mediaRepository);\n }\n\n get avatarTitle() {\n return this.name;\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseTileViewModel} from \"./BaseTileViewModel.js\";\n\nexport class RoomTileViewModel extends BaseTileViewModel {\n constructor(options) {\n super(options);\n const {room} = options;\n this._room = room;\n this._url = this.urlCreator.openRoomActionUrl(this._room.id);\n }\n\n get kind() {\n return \"room\";\n }\n\n get url() {\n return this._url;\n }\n\n /** very important that sorting order is stable and that comparing\n * to itself always returns 0, otherwise SortedMapList will\n * remove the wrong children, etc ... */\n compare(other) {\n const parentComparison = super.compare(other);\n if (parentComparison !== 0) {\n return parentComparison;\n }\n /*\n put unread rooms first\n then put rooms with a timestamp first, and sort by name\n then sort by name for rooms without a timestamp\n */\n const myRoom = this._room;\n const theirRoom = other._room;\n\n if (myRoom.isLowPriority !== theirRoom.isLowPriority) {\n if (myRoom.isLowPriority) {\n return 1;\n }\n return -1;\n }\n const myTimestamp = myRoom.lastMessageTimestamp;\n const theirTimestamp = theirRoom.lastMessageTimestamp;\n const myTimestampValid = Number.isSafeInteger(myTimestamp);\n const theirTimestampValid = Number.isSafeInteger(theirTimestamp);\n // if either does not have a timestamp, put the one with a timestamp first\n if (myTimestampValid !== theirTimestampValid) {\n if (!theirTimestampValid) {\n return -1;\n }\n return 1;\n }\n const timeDiff = theirTimestamp - myTimestamp;\n if (timeDiff === 0 || !theirTimestampValid || !myTimestampValid) {\n // sort alphabetically\n const nameCmp = this.name.localeCompare(other.name);\n if (nameCmp === 0) {\n return this._room.id.localeCompare(other._room.id);\n }\n return nameCmp;\n }\n return timeDiff;\n }\n\n get isUnread() {\n return this._room.isUnread;\n }\n\n get name() {\n return this._room.name || this.i18n`Empty Room`;\n }\n\n get badgeCount() {\n return this._room.notificationCount;\n }\n\n get isHighlighted() {\n return this._room.highlightCount !== 0;\n }\n\n get _avatarSource() {\n return this._room;\n }\n}\n","/*\nCopyright 2020, 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport function comparePrimitive(a, b) {\n if (a === b) {\n return 0;\n } else {\n return a < b ? -1 : 1;\n }\n}\n","/*\nCopyright 2020, 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseTileViewModel} from \"./BaseTileViewModel.js\";\nimport {comparePrimitive} from \"./common\";\n\nexport class InviteTileViewModel extends BaseTileViewModel {\n constructor(options) {\n super(options);\n const {invite} = options;\n this._invite = invite;\n this._url = this.urlCreator.openRoomActionUrl(this._invite.id);\n }\n\n get busy() { return this._invite.accepting || this._invite.rejecting; }\n get kind() { return \"invite\"; }\n get url() { return this._url; }\n get name() { return this._invite.name; }\n get isHighlighted() { return true; }\n get isUnread() { return true; }\n get badgeCount() { return this.i18n`!`; }\n get _avatarSource() { return this._invite; }\n\n /** very important that sorting order is stable and that comparing\n * to itself always returns 0, otherwise SortedMapList will\n * remove the wrong children, etc ... */\n compare(other) {\n const parentComparison = super.compare(other);\n if (parentComparison !== 0) {\n return parentComparison;\n }\n const timeDiff = other._invite.timestamp - this._invite.timestamp;\n if (timeDiff !== 0) {\n return timeDiff;\n }\n return comparePrimitive(this._invite.id, other._invite.id);\n }\n}\n\nexport function tests() {\n return {\n \"test compare with timestamp\": assert => {\n const urlCreator = {openRoomActionUrl() { return \"\";}}\n const vm1 = new InviteTileViewModel({invite: {timestamp: 500, id: \"1\"}, urlCreator});\n const vm2 = new InviteTileViewModel({invite: {timestamp: 250, id: \"2\"}, urlCreator});\n assert(vm1.compare(vm2) < 0);\n assert(vm2.compare(vm1) > 0);\n assert.equal(vm1.compare(vm1), 0);\n },\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020, 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseTileViewModel} from \"./BaseTileViewModel.js\";\nimport {comparePrimitive} from \"./common\";\n\nexport class RoomBeingCreatedTileViewModel extends BaseTileViewModel {\n constructor(options) {\n super(options);\n const {roomBeingCreated} = options;\n this._roomBeingCreated = roomBeingCreated;\n this._url = this.urlCreator.openRoomActionUrl(this._roomBeingCreated.id);\n }\n\n get busy() { return !this._roomBeingCreated.error; }\n get kind() { return \"roomBeingCreated\"; }\n get isHighlighted() { return !this.busy; }\n get badgeCount() { return !this.busy && this.i18n`Failed`; }\n get url() { return this._url; }\n get name() { return this._roomBeingCreated.name; }\n get _avatarSource() { return this._roomBeingCreated; }\n\n /** very important that sorting order is stable and that comparing\n * to itself always returns 0, otherwise SortedMapList will\n * remove the wrong children, etc ... */\n compare(other) {\n const parentCmp = super.compare(other);\n if (parentCmp !== 0) {\n return parentCmp;\n }\n const nameCmp = comparePrimitive(this.name, other.name);\n if (nameCmp === 0) {\n return comparePrimitive(this._roomBeingCreated.id, other._roomBeingCreated.id);\n } else {\n return nameCmp;\n }\n }\n\n avatarUrl(size) {\n // allow blob url which doesn't need mxc => http resolution\n return this._roomBeingCreated.avatarBlobUrl ?? super.avatarUrl(size);\n }\n}\n\nexport function tests() {\n return {\n \"test compare with names\": assert => {\n const urlCreator = {openRoomActionUrl() { return \"\";}}\n const vm1 = new RoomBeingCreatedTileViewModel({roomBeingCreated: {name: \"A\", id: \"1\"}, urlCreator});\n const vm2 = new RoomBeingCreatedTileViewModel({roomBeingCreated: {name: \"B\", id: \"2\"}, urlCreator});\n assert(vm1.compare(vm2) < 0);\n assert(vm2.compare(vm1) > 0);\n assert.equal(vm1.compare(vm1), 0);\n },\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport class RoomFilter {\n constructor(query) {\n this._parts = query.split(\" \").map(s => s.toLowerCase().trim());\n }\n\n matches(roomTileVM) {\n const name = roomTileVM.name.toLowerCase();\n return this._parts.every(p => name.includes(p));\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseObservableMap} from \"./BaseObservableMap\";\n\nexport class ApplyMap extends BaseObservableMap {\n constructor(source, apply) {\n super();\n this._source = source;\n this._apply = apply;\n this._subscription = null;\n }\n\n hasApply() {\n return !!this._apply;\n }\n\n setApply(apply) {\n this._apply = apply;\n if (apply) {\n this.applyOnce(this._apply);\n }\n }\n\n applyOnce(apply) {\n for (const [key, value] of this._source) {\n apply(key, value);\n }\n }\n\n onAdd(key, value) {\n if (this._apply) {\n this._apply(key, value);\n }\n this.emitAdd(key, value);\n }\n\n onRemove(key, value) {\n this.emitRemove(key, value);\n }\n\n onUpdate(key, value, params) {\n if (this._apply) {\n this._apply(key, value, params);\n }\n this.emitUpdate(key, value, params);\n }\n\n onSubscribeFirst() {\n this._subscription = this._source.subscribe(this);\n if (this._apply) {\n this.applyOnce(this._apply);\n }\n super.onSubscribeFirst();\n }\n\n onUnsubscribeLast() {\n super.onUnsubscribeLast();\n this._subscription = this._subscription();\n }\n\n onReset() {\n if (this._apply) {\n this.applyOnce(this._apply);\n }\n this.emitReset();\n }\n\n [Symbol.iterator]() {\n return this._source[Symbol.iterator]();\n }\n\n get size() {\n return this._source.size;\n }\n\n get(key) {\n return this._source.get(key);\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseObservableValue, ObservableValue} from \"../../observable/ObservableValue\";\n\n\ntype AllowsChild<T> = (parent: Segment<T> | undefined, child: Segment<T>) => boolean;\n\n/**\n * OptionalValue is basically stating that if SegmentType[type] = true:\n * - Allow this type to be optional\n * - Give it a default value of undefined\n * - Also allow it to be true\n * This lets us do:\n * const s: Segment<SegmentType> = new Segment(\"create-room\");\n * instead of\n * const s: Segment<SegmentType> = new Segment(\"create-room\", undefined);\n */\nexport type OptionalValue<T> = T extends true? [(undefined | true)?]: [T];\n\nexport class Navigation<T extends object> {\n private readonly _allowsChild: AllowsChild<T>;\n private _path: Path<T>;\n private readonly _observables: Map<keyof T, SegmentObservable<T>> = new Map();\n private readonly _pathObservable: ObservableValue<Path<T>>;\n\n constructor(allowsChild: AllowsChild<T>) {\n this._allowsChild = allowsChild;\n this._path = new Path([], allowsChild);\n this._pathObservable = new ObservableValue(this._path);\n }\n\n get pathObservable(): ObservableValue<Path<T>> {\n return this._pathObservable;\n }\n\n get path(): Path<T> {\n return this._path;\n }\n\n push<K extends keyof T>(type: K, ...value: OptionalValue<T[K]>): void {\n const newPath = this.path.with(new Segment(type, ...value));\n if (newPath) {\n this.applyPath(newPath);\n }\n }\n\n applyPath(path: Path<T>): void {\n // Path is not exported, so you can only create a Path through Navigation,\n // so we assume it respects the allowsChild rules\n const oldPath = this._path;\n this._path = path;\n // clear values not in the new path in reverse order of path\n for (let i = oldPath.segments.length - 1; i >= 0; i -= 1) {\n const segment = oldPath.segments[i];\n if (!this._path.get(segment.type)) {\n const observable = this._observables.get(segment.type);\n observable?.emitIfChanged();\n }\n }\n // change values in order of path\n for (const segment of this._path.segments) {\n const observable = this._observables.get(segment.type);\n observable?.emitIfChanged();\n }\n // to observe the whole path having changed\n // Since paths are immutable,\n // we can just use set here which will compare the references\n this._pathObservable.set(this._path);\n }\n\n observe(type: keyof T): SegmentObservable<T> {\n let observable = this._observables.get(type);\n if (!observable) {\n observable = new SegmentObservable(this, type);\n this._observables.set(type, observable);\n }\n return observable;\n }\n\n pathFrom(segments: Segment<any>[]): Path<T> {\n let parent: Segment<any> | undefined;\n let i: number;\n for (i = 0; i < segments.length; i += 1) {\n if (!this._allowsChild(parent, segments[i])) {\n return new Path(segments.slice(0, i), this._allowsChild);\n }\n parent = segments[i];\n }\n return new Path(segments, this._allowsChild);\n }\n\n segment<K extends keyof T>(type: K, ...value: OptionalValue<T[K]>): Segment<T> {\n return new Segment(type, ...value);\n }\n}\n\nfunction segmentValueEqual<T>(a?: T[keyof T], b?: T[keyof T]): boolean {\n if (a === b) {\n return true;\n }\n // allow (sparse) arrays\n if (Array.isArray(a) && Array.isArray(b)) {\n const len = Math.max(a.length, b.length);\n for (let i = 0; i < len; i += 1) {\n if (a[i] !== b[i]) {\n return false;\n }\n }\n return true;\n }\n return false;\n}\n\n\nexport class Segment<T, K extends keyof T = any> {\n public value: T[K];\n\n constructor(public type: K, ...value: OptionalValue<T[K]>) {\n this.value = (value[0] === undefined ? true : value[0]) as unknown as T[K];\n }\n}\n\nclass Path<T> {\n private readonly _segments: Segment<T, any>[];\n private readonly _allowsChild: AllowsChild<T>;\n\n constructor(segments: Segment<T>[] = [], allowsChild: AllowsChild<T>) {\n this._segments = segments;\n this._allowsChild = allowsChild;\n }\n\n clone(): Path<T> {\n return new Path(this._segments.slice(), this._allowsChild);\n }\n\n with(segment: Segment<T>): Path<T> | undefined {\n let index = this._segments.length - 1;\n do {\n if (this._allowsChild(this._segments[index], segment)) {\n // pop the elements that didn't allow the new segment as a child\n const newSegments = this._segments.slice(0, index + 1);\n newSegments.push(segment);\n return new Path(newSegments, this._allowsChild);\n }\n index -= 1;\n } while(index >= -1);\n // allow -1 as well so we check if the segment is allowed as root\n return undefined;\n }\n\n until(type: keyof T): Path<T> {\n const index = this._segments.findIndex(s => s.type === type);\n if (index !== -1) {\n return new Path(this._segments.slice(0, index + 1), this._allowsChild)\n }\n return new Path([], this._allowsChild);\n }\n\n get(type: keyof T): Segment<T> | undefined {\n return this._segments.find(s => s.type === type);\n }\n\n replace(segment: Segment<T>): Path<T> | undefined {\n const index = this._segments.findIndex(s => s.type === segment.type);\n if (index !== -1) {\n const parent = this._segments[index - 1];\n if (this._allowsChild(parent, segment)) {\n const child = this._segments[index + 1];\n if (!child || this._allowsChild(segment, child)) {\n const newSegments = this._segments.slice();\n newSegments[index] = segment;\n return new Path(newSegments, this._allowsChild);\n }\n }\n }\n return undefined;\n }\n\n get segments(): Segment<T>[] {\n return this._segments;\n }\n}\n\n/**\n * custom observable so it always returns what is in navigation.path, even if we haven't emitted the change yet.\n * This ensures that observers of a segment can also read the most recent value of other segments.\n */\nclass SegmentObservable<T extends object> extends BaseObservableValue<T[keyof T] | undefined> {\n private readonly _navigation: Navigation<T>;\n private _type: keyof T;\n private _lastSetValue?: T[keyof T];\n \n constructor(navigation: Navigation<T>, type: keyof T) {\n super();\n this._navigation = navigation;\n this._type = type;\n this._lastSetValue = navigation.path.get(type)?.value;\n }\n\n get(): T[keyof T] | undefined {\n const path = this._navigation.path;\n const segment = path.get(this._type);\n const value = segment?.value;\n return value;\n }\n\n emitIfChanged(): void {\n const newValue = this.get();\n if (!segmentValueEqual<T>(newValue, this._lastSetValue)) {\n this._lastSetValue = newValue;\n this.emit(newValue);\n }\n }\n}\n\nexport type {Path};\n\nexport function tests() {\n\n function createMockNavigation() {\n return new Navigation((parent, {type}) => {\n switch (parent?.type) {\n case undefined:\n return type === \"1\" || type === \"2\";\n case \"1\":\n return type === \"1.1\";\n case \"1.1\":\n return type === \"1.1.1\";\n case \"2\":\n return type === \"2.1\" || type === \"2.2\";\n default:\n return false;\n }\n });\n }\n\n function observeTypes(nav, types) {\n const changes: {type:string, value:any}[] = [];\n for (const type of types) {\n nav.observe(type).subscribe(value => {\n changes.push({type, value});\n });\n }\n return changes;\n }\n\n type SegmentType = {\n \"foo\": number;\n \"bar\": number;\n \"baz\": number;\n }\n\n return {\n \"applying a path emits an event on the observable\": assert => {\n const nav = createMockNavigation();\n const path = nav.pathFrom([\n new Segment(\"2\", 7),\n new Segment(\"2.2\", 8),\n ]);\n assert.equal(path.segments.length, 2);\n let changes = observeTypes(nav, [\"2\", \"2.2\"]);\n nav.applyPath(path);\n assert.equal(changes.length, 2);\n assert.equal(changes[0].type, \"2\");\n assert.equal(changes[0].value, 7);\n assert.equal(changes[1].type, \"2.2\");\n assert.equal(changes[1].value, 8);\n },\n \"path.get\": assert => {\n const path = new Path<SegmentType>([new Segment(\"foo\", 5), new Segment(\"bar\", 6)], () => true);\n assert.equal(path.get(\"foo\")!.value, 5);\n assert.equal(path.get(\"bar\")!.value, 6);\n },\n \"path.replace success\": assert => {\n const path = new Path<SegmentType>([new Segment(\"foo\", 5), new Segment(\"bar\", 6)], () => true);\n const newPath = path.replace(new Segment(\"foo\", 1));\n assert.equal(newPath!.get(\"foo\")!.value, 1);\n assert.equal(newPath!.get(\"bar\")!.value, 6);\n },\n \"path.replace not found\": assert => {\n const path = new Path<SegmentType>([new Segment(\"foo\", 5), new Segment(\"bar\", 6)], () => true);\n const newPath = path.replace(new Segment(\"baz\", 1));\n assert.equal(newPath, null);\n }\n };\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport type {History} from \"../../platform/web/dom/History.js\";\nimport type {Navigation, Segment, Path, OptionalValue} from \"./Navigation\";\nimport type {SubscriptionHandle} from \"../../observable/BaseObservable\";\n\ntype ParseURLPath<T> = (urlPath: string, currentNavPath: Path<T>, defaultSessionId?: string) => Segment<T>[];\ntype StringifyPath<T> = (path: Path<T>) => string;\n\nexport interface IURLRouter<T> {\n attach(): void;\n dispose(): void;\n pushUrl(url: string): void;\n tryRestoreLastUrl(): boolean;\n urlForSegments(segments: Segment<T>[]): string | undefined;\n urlForSegment<K extends keyof T>(type: K, ...value: OptionalValue<T[K]>): string | undefined;\n urlUntilSegment(type: keyof T): string;\n urlForPath(path: Path<T>): string;\n openRoomActionUrl(roomId: string): string;\n createSSOCallbackURL(): string;\n normalizeUrl(): void;\n}\n\nexport class URLRouter<T extends {session: string | boolean}> implements IURLRouter<T> {\n private readonly _history: History;\n private readonly _navigation: Navigation<T>;\n private readonly _parseUrlPath: ParseURLPath<T>;\n private readonly _stringifyPath: StringifyPath<T>;\n private _subscription?: SubscriptionHandle;\n private _pathSubscription?: SubscriptionHandle;\n private _isApplyingUrl: boolean = false;\n private _defaultSessionId?: string;\n\n constructor(history: History, navigation: Navigation<T>, parseUrlPath: ParseURLPath<T>, stringifyPath: StringifyPath<T>) {\n this._history = history;\n this._navigation = navigation;\n this._parseUrlPath = parseUrlPath;\n this._stringifyPath = stringifyPath;\n this._defaultSessionId = this._getLastSessionId();\n }\n\n private _getLastSessionId(): string | undefined {\n const navPath = this._urlAsNavPath(this._history.getLastSessionUrl() || \"\");\n const sessionId = navPath.get(\"session\")?.value;\n if (typeof sessionId === \"string\") {\n return sessionId;\n }\n return undefined;\n }\n\n attach(): void {\n this._subscription = this._history.subscribe(url => this._applyUrl(url));\n // subscribe to path before applying initial url\n // so redirects in _applyNavPathToHistory are reflected in url bar\n this._pathSubscription = this._navigation.pathObservable.subscribe(path => this._applyNavPathToHistory(path));\n this._applyUrl(this._history.get());\n }\n\n dispose(): void {\n if (this._subscription) { this._subscription = this._subscription(); }\n if (this._pathSubscription) { this._pathSubscription = this._pathSubscription(); }\n }\n\n private _applyNavPathToHistory(path: Path<T>): void {\n const url = this.urlForPath(path);\n if (url !== this._history.get()) {\n if (this._isApplyingUrl) {\n // redirect\n this._history.replaceUrlSilently(url);\n } else {\n this._history.pushUrlSilently(url);\n }\n }\n }\n\n private _applyNavPathToNavigation(navPath: Path<T>): void {\n // this will cause _applyNavPathToHistory to be called,\n // so set a flag whether this request came from ourselves\n // (in which case it is a redirect if the url does not match the current one)\n this._isApplyingUrl = true;\n this._navigation.applyPath(navPath);\n this._isApplyingUrl = false;\n }\n\n private _urlAsNavPath(url: string): Path<T> {\n const urlPath = this._history.urlAsPath(url);\n return this._navigation.pathFrom(this._parseUrlPath(urlPath, this._navigation.path, this._defaultSessionId));\n }\n\n private _applyUrl(url: string): void {\n const navPath = this._urlAsNavPath(url);\n this._applyNavPathToNavigation(navPath);\n }\n\n pushUrl(url: string): void {\n this._history.pushUrl(url);\n }\n\n tryRestoreLastUrl(): boolean {\n const lastNavPath = this._urlAsNavPath(this._history.getLastSessionUrl() || \"\");\n if (lastNavPath.segments.length !== 0) {\n this._applyNavPathToNavigation(lastNavPath);\n return true;\n }\n return false;\n }\n\n urlForSegments(segments: Segment<T>[]): string | undefined {\n let path: Path<T> | undefined = this._navigation.path;\n for (const segment of segments) {\n path = path.with(segment);\n if (!path) {\n return;\n }\n }\n return this.urlForPath(path);\n }\n\n urlForSegment<K extends keyof T>(type: K, ...value: OptionalValue<T[K]>): string | undefined {\n return this.urlForSegments([this._navigation.segment(type, ...value)]);\n }\n\n urlUntilSegment(type: keyof T): string {\n return this.urlForPath(this._navigation.path.until(type));\n }\n\n urlForPath(path: Path<T>): string {\n return this._history.pathAsUrl(this._stringifyPath(path));\n }\n\n openRoomActionUrl(roomId: string): string {\n // not a segment to navigation knowns about, so append it manually\n const urlPath = `${this._stringifyPath(this._navigation.path.until(\"session\"))}/open-room/${roomId}`;\n return this._history.pathAsUrl(urlPath);\n }\n\n createSSOCallbackURL(): string {\n return window.location.origin;\n }\n\n normalizeUrl(): void {\n // Remove any queryParameters from the URL\n // Gets rid of the loginToken after SSO\n this._history.replaceUrlSilently(`${window.location.origin}/${window.location.hash}`);\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {Navigation, Segment} from \"./Navigation\";\nimport {URLRouter} from \"./URLRouter\";\nimport type {Path, OptionalValue} from \"./Navigation\";\n\nexport type SegmentType = {\n \"login\": true;\n \"session\": string | boolean;\n \"sso\": string;\n \"logout\": true;\n \"room\": string;\n \"rooms\": string[];\n \"settings\": true;\n \"create-room\": true;\n \"empty-grid-tile\": number;\n \"lightbox\": string;\n \"right-panel\": true;\n \"details\": true;\n \"members\": true;\n \"member\": string;\n};\n\nexport function createNavigation(): Navigation<SegmentType> {\n return new Navigation(allowsChild);\n}\n\nexport function createRouter({history, navigation}: {history: History, navigation: Navigation<SegmentType>}): URLRouter<SegmentType> {\n return new URLRouter(history, navigation, parseUrlPath, stringifyPath);\n}\n\nfunction allowsChild(parent: Segment<SegmentType> | undefined, child: Segment<SegmentType>): boolean {\n const {type} = child;\n switch (parent?.type) {\n case undefined:\n // allowed root segments\n return type === \"login\" || type === \"session\" || type === \"sso\" || type === \"logout\";\n case \"session\":\n return type === \"room\" || type === \"rooms\" || type === \"settings\" || type === \"create-room\";\n case \"rooms\":\n // downside of the approach: both of these will control which tile is selected\n return type === \"room\" || type === \"empty-grid-tile\";\n case \"room\":\n return type === \"lightbox\" || type === \"right-panel\";\n case \"right-panel\":\n return type === \"details\"|| type === \"members\" || type === \"member\";\n default:\n return false;\n }\n}\n\nexport function removeRoomFromPath(path: Path<SegmentType>, roomId: string): Path<SegmentType> | undefined {\n let newPath: Path<SegmentType> | undefined = path;\n const rooms = newPath.get(\"rooms\");\n let roomIdGridIndex = -1;\n // first delete from rooms segment\n if (rooms) {\n roomIdGridIndex = rooms.value.indexOf(roomId);\n if (roomIdGridIndex !== -1) {\n const idsWithoutRoom = rooms.value.slice();\n idsWithoutRoom[roomIdGridIndex] = \"\";\n newPath = newPath.replace(new Segment(\"rooms\", idsWithoutRoom));\n }\n }\n const room = newPath!.get(\"room\");\n // then from room (which occurs with or without rooms)\n if (room && room.value === roomId) {\n if (roomIdGridIndex !== -1) {\n newPath = newPath!.with(new Segment(\"empty-grid-tile\", roomIdGridIndex));\n } else {\n newPath = newPath!.until(\"session\");\n }\n }\n return newPath;\n}\n\nfunction roomsSegmentWithRoom(rooms: Segment<SegmentType, \"rooms\">, roomId: string, path: Path<SegmentType>): Segment<SegmentType, \"rooms\"> {\n if(!rooms.value.includes(roomId)) {\n const emptyGridTile = path.get(\"empty-grid-tile\");\n const oldRoom = path.get(\"room\");\n let index = 0;\n if (emptyGridTile) {\n index = emptyGridTile.value;\n } else if (oldRoom) {\n index = rooms.value.indexOf(oldRoom.value);\n }\n const roomIds = rooms.value.slice();\n roomIds[index] = roomId;\n return new Segment(\"rooms\", roomIds);\n } else {\n return rooms;\n }\n}\n\nfunction pushRightPanelSegment<T extends keyof SegmentType>(array: Segment<SegmentType>[], segment: T, ...value: OptionalValue<SegmentType[T]>): void {\n array.push(new Segment(\"right-panel\"));\n array.push(new Segment(segment, ...value));\n}\n\nexport function addPanelIfNeeded<T extends SegmentType>(navigation: Navigation<T>, path: Path<T>): Path<T> {\n const segments = navigation.path.segments;\n const i = segments.findIndex(segment => segment.type === \"right-panel\");\n let _path = path;\n if (i !== -1) {\n _path = path.until(\"room\");\n _path = _path.with(segments[i])!;\n _path = _path.with(segments[i + 1])!;\n }\n return _path;\n}\n\nexport function parseUrlPath(urlPath: string, currentNavPath: Path<SegmentType>, defaultSessionId?: string): Segment<SegmentType>[] {\n // substring(1) to take of initial /\n const parts = urlPath.substring(1).split(\"/\");\n const iterator = parts[Symbol.iterator]();\n const segments: Segment<SegmentType>[] = [];\n let next; \n while (!(next = iterator.next()).done) {\n const type = next.value;\n if (type === \"rooms\") {\n const roomsValue = iterator.next().value;\n if (roomsValue === undefined) { break; }\n const roomIds = roomsValue.split(\",\");\n segments.push(new Segment(type, roomIds));\n const selectedIndex = parseInt(iterator.next().value || \"0\", 10);\n const roomId = roomIds[selectedIndex];\n if (roomId) {\n segments.push(new Segment(\"room\", roomId));\n } else {\n segments.push(new Segment(\"empty-grid-tile\", selectedIndex));\n }\n } else if (type === \"open-room\") {\n const roomId = iterator.next().value;\n if (!roomId) { break; }\n const rooms = currentNavPath.get(\"rooms\");\n if (rooms) {\n segments.push(roomsSegmentWithRoom(rooms, roomId, currentNavPath));\n }\n segments.push(new Segment(\"room\", roomId));\n const openRoomPartIndex = parts.findIndex(part => part === \"open-room\");\n const hasOnlyRoomIdAfterPart = openRoomPartIndex >= parts.length - 2;\n if (hasOnlyRoomIdAfterPart) {\n // Copy right-panel segments from previous path only if there are no other parts after open-room\n // fixes memberlist -> member details closing/opening grid view\n const previousSegments = currentNavPath.segments;\n const i = previousSegments.findIndex(s => s.type === \"right-panel\");\n if (i !== -1) {\n segments.push(...previousSegments.slice(i));\n }\n }\n } else if (type === \"last-session\") {\n let sessionSegment = currentNavPath.get(\"session\");\n if (typeof sessionSegment?.value !== \"string\" && defaultSessionId) {\n sessionSegment = new Segment(\"session\", defaultSessionId);\n }\n if (sessionSegment) {\n segments.push(sessionSegment);\n }\n } else if (type === \"details\" || type === \"members\") {\n pushRightPanelSegment(segments, type);\n } else if (type === \"member\") {\n const userId = iterator.next().value;\n if (!userId) { break; }\n pushRightPanelSegment(segments, type, userId);\n } else if (type.includes(\"loginToken\")) {\n // Special case for SSO-login with query parameter loginToken=<token>\n const loginToken = type.split(\"=\").pop();\n segments.push(new Segment(\"sso\", loginToken));\n } else {\n // might be undefined, which will be turned into true by Segment \n const value = iterator.next().value;\n segments.push(new Segment(type, value));\n }\n }\n return segments;\n}\n\nexport function stringifyPath(path: Path<SegmentType>): string {\n let urlPath = \"\";\n let prevSegment: Segment<SegmentType> | undefined;\n for (const segment of path.segments) {\n switch (segment.type) {\n case \"rooms\":\n urlPath += `/rooms/${segment.value.join(\",\")}`;\n break;\n case \"empty-grid-tile\":\n urlPath += `/${segment.value}`;\n break;\n case \"room\":\n if (prevSegment?.type === \"rooms\") {\n const index = prevSegment.value.indexOf(segment.value);\n urlPath += `/${index}`;\n } else {\n urlPath += `/${segment.type}/${segment.value}`;\n }\n break;\n case \"right-panel\":\n case \"sso\":\n // Do not put these segments in URL\n continue;\n default:\n urlPath += `/${segment.type}`;\n if (segment.value && segment.value !== true) {\n urlPath += `/${segment.value}`;\n }\n }\n prevSegment = segment;\n }\n return urlPath;\n}\n\nexport function tests() {\n function createEmptyPath() {\n const nav: Navigation<SegmentType> = new Navigation(allowsChild);\n const path = nav.pathFrom([]);\n return path;\n }\n\n return {\n \"stringify grid url with focused empty tile\": assert => {\n const nav: Navigation<SegmentType> = new Navigation(allowsChild);\n const path = nav.pathFrom([\n new Segment(\"session\", 1),\n new Segment(\"rooms\", [\"a\", \"b\", \"c\"]),\n new Segment(\"empty-grid-tile\", 3)\n ]);\n const urlPath = stringifyPath(path);\n assert.equal(urlPath, \"/session/1/rooms/a,b,c/3\");\n },\n \"stringify grid url with focused room\": assert => {\n const nav: Navigation<SegmentType> = new Navigation(allowsChild);\n const path = nav.pathFrom([\n new Segment(\"session\", 1),\n new Segment(\"rooms\", [\"a\", \"b\", \"c\"]),\n new Segment(\"room\", \"b\")\n ]);\n const urlPath = stringifyPath(path);\n assert.equal(urlPath, \"/session/1/rooms/a,b,c/1\");\n },\n \"stringify url with right-panel and details segment\": assert => {\n const nav: Navigation<SegmentType> = new Navigation(allowsChild);\n const path = nav.pathFrom([\n new Segment(\"session\", 1),\n new Segment(\"rooms\", [\"a\", \"b\", \"c\"]),\n new Segment(\"room\", \"b\"),\n new Segment(\"right-panel\"),\n new Segment(\"details\")\n ]);\n const urlPath = stringifyPath(path);\n assert.equal(urlPath, \"/session/1/rooms/a,b,c/1/details\");\n },\n \"Parse loginToken query parameter into SSO segment\": assert => {\n const path = createEmptyPath();\n const segments = parseUrlPath(\"?loginToken=a1232aSD123\", path);\n assert.equal(segments.length, 1);\n assert.equal(segments[0].type, \"sso\");\n assert.equal(segments[0].value, \"a1232aSD123\");\n },\n \"parse grid url path with focused empty tile\": assert => {\n const path = createEmptyPath();\n const segments = parseUrlPath(\"/session/1/rooms/a,b,c/3\", path);\n assert.equal(segments.length, 3);\n assert.equal(segments[0].type, \"session\");\n assert.equal(segments[0].value, \"1\");\n assert.equal(segments[1].type, \"rooms\");\n assert.deepEqual(segments[1].value, [\"a\", \"b\", \"c\"]);\n assert.equal(segments[2].type, \"empty-grid-tile\");\n assert.equal(segments[2].value, 3);\n },\n \"parse grid url path with focused room\": assert => {\n const path = createEmptyPath();\n const segments = parseUrlPath(\"/session/1/rooms/a,b,c/1\", path);\n assert.equal(segments.length, 3);\n assert.equal(segments[0].type, \"session\");\n assert.equal(segments[0].value, \"1\");\n assert.equal(segments[1].type, \"rooms\");\n assert.deepEqual(segments[1].value, [\"a\", \"b\", \"c\"]);\n assert.equal(segments[2].type, \"room\");\n assert.equal(segments[2].value, \"b\");\n },\n \"parse empty grid url\": assert => {\n const path = createEmptyPath();\n const segments = parseUrlPath(\"/session/1/rooms/\", path);\n assert.equal(segments.length, 3);\n assert.equal(segments[0].type, \"session\");\n assert.equal(segments[0].value, \"1\");\n assert.equal(segments[1].type, \"rooms\");\n assert.deepEqual(segments[1].value, [\"\"]);\n assert.equal(segments[2].type, \"empty-grid-tile\");\n assert.equal(segments[2].value, 0);\n },\n \"parse empty grid url with focus\": assert => {\n const path = createEmptyPath();\n const segments = parseUrlPath(\"/session/1/rooms//1\", path);\n assert.equal(segments.length, 3);\n assert.equal(segments[0].type, \"session\");\n assert.equal(segments[0].value, \"1\");\n assert.equal(segments[1].type, \"rooms\");\n assert.deepEqual(segments[1].value, [\"\"]);\n assert.equal(segments[2].type, \"empty-grid-tile\");\n assert.equal(segments[2].value, 1);\n },\n \"parse open-room action replacing the current focused room\": assert => {\n const nav: Navigation<SegmentType> = new Navigation(allowsChild);\n const path = nav.pathFrom([\n new Segment(\"session\", 1),\n new Segment(\"rooms\", [\"a\", \"b\", \"c\"]),\n new Segment(\"room\", \"b\")\n ]);\n const segments = parseUrlPath(\"/session/1/open-room/d\", path);\n assert.equal(segments.length, 3);\n assert.equal(segments[0].type, \"session\");\n assert.equal(segments[0].value, \"1\");\n assert.equal(segments[1].type, \"rooms\");\n assert.deepEqual(segments[1].value, [\"a\", \"d\", \"c\"]);\n assert.equal(segments[2].type, \"room\");\n assert.equal(segments[2].value, \"d\");\n },\n \"parse open-room action changing focus to an existing room\": assert => {\n const nav: Navigation<SegmentType> = new Navigation(allowsChild);\n const path = nav.pathFrom([\n new Segment(\"session\", 1),\n new Segment(\"rooms\", [\"a\", \"b\", \"c\"]),\n new Segment(\"room\", \"b\")\n ]);\n const segments = parseUrlPath(\"/session/1/open-room/a\", path);\n assert.equal(segments.length, 3);\n assert.equal(segments[0].type, \"session\");\n assert.equal(segments[0].value, \"1\");\n assert.equal(segments[1].type, \"rooms\");\n assert.deepEqual(segments[1].value, [\"a\", \"b\", \"c\"]);\n assert.equal(segments[2].type, \"room\");\n assert.equal(segments[2].value, \"a\");\n },\n \"parse open-room action changing focus to an existing room with details open\": assert => {\n const nav: Navigation<SegmentType> = new Navigation(allowsChild);\n const path = nav.pathFrom([\n new Segment(\"session\", 1),\n new Segment(\"rooms\", [\"a\", \"b\", \"c\"]),\n new Segment(\"room\", \"b\"),\n new Segment(\"right-panel\", true),\n new Segment(\"details\", true)\n ]);\n const segments = parseUrlPath(\"/session/1/open-room/a\", path);\n assert.equal(segments.length, 5);\n assert.equal(segments[0].type, \"session\");\n assert.equal(segments[0].value, \"1\");\n assert.equal(segments[1].type, \"rooms\");\n assert.deepEqual(segments[1].value, [\"a\", \"b\", \"c\"]);\n assert.equal(segments[2].type, \"room\");\n assert.equal(segments[2].value, \"a\");\n assert.equal(segments[3].type, \"right-panel\");\n assert.equal(segments[3].value, true);\n assert.equal(segments[4].type, \"details\");\n assert.equal(segments[4].value, true);\n },\n \"open-room action should only copy over previous segments if there are no parts after open-room\": assert => {\n const nav: Navigation<SegmentType> = new Navigation(allowsChild);\n const path = nav.pathFrom([\n new Segment(\"session\", 1),\n new Segment(\"rooms\", [\"a\", \"b\", \"c\"]),\n new Segment(\"room\", \"b\"),\n new Segment(\"right-panel\", true),\n new Segment(\"members\", true)\n ]);\n const segments = parseUrlPath(\"/session/1/open-room/a/member/foo\", path);\n assert.equal(segments.length, 5);\n assert.equal(segments[0].type, \"session\");\n assert.equal(segments[0].value, \"1\");\n assert.equal(segments[1].type, \"rooms\");\n assert.deepEqual(segments[1].value, [\"a\", \"b\", \"c\"]);\n assert.equal(segments[2].type, \"room\");\n assert.equal(segments[2].value, \"a\");\n assert.equal(segments[3].type, \"right-panel\");\n assert.equal(segments[3].value, true);\n assert.equal(segments[4].type, \"member\");\n assert.equal(segments[4].value, \"foo\");\n },\n \"parse open-room action setting a room in an empty tile\": assert => {\n const nav: Navigation<SegmentType> = new Navigation(allowsChild);\n const path = nav.pathFrom([\n new Segment(\"session\", 1),\n new Segment(\"rooms\", [\"a\", \"b\", \"c\"]),\n new Segment(\"empty-grid-tile\", 4)\n ]);\n const segments = parseUrlPath(\"/session/1/open-room/d\", path);\n assert.equal(segments.length, 3);\n assert.equal(segments[0].type, \"session\");\n assert.equal(segments[0].value, \"1\");\n assert.equal(segments[1].type, \"rooms\");\n assert.deepEqual(segments[1].value, [\"a\", \"b\", \"c\", , \"d\"]); //eslint-disable-line no-sparse-arrays\n assert.equal(segments[2].type, \"room\");\n assert.equal(segments[2].value, \"d\");\n },\n \"parse session url path without id\": assert => {\n const path = createEmptyPath();\n const segments = parseUrlPath(\"/session\", path);\n assert.equal(segments.length, 1);\n assert.equal(segments[0].type, \"session\");\n assert.strictEqual(segments[0].value, true);\n },\n \"remove active room from grid path turns it into empty tile\": assert => {\n const nav: Navigation<SegmentType> = new Navigation(allowsChild);\n const path = nav.pathFrom([\n new Segment(\"session\", 1),\n new Segment(\"rooms\", [\"a\", \"b\", \"c\"]),\n new Segment(\"room\", \"b\")\n ]);\n const newPath = removeRoomFromPath(path, \"b\");\n assert.equal(newPath?.segments.length, 3);\n assert.equal(newPath?.segments[0].type, \"session\");\n assert.equal(newPath?.segments[0].value, 1);\n assert.equal(newPath?.segments[1].type, \"rooms\");\n assert.deepEqual(newPath?.segments[1].value, [\"a\", \"\", \"c\"]);\n assert.equal(newPath?.segments[2].type, \"empty-grid-tile\");\n assert.equal(newPath?.segments[2].value, 1);\n },\n \"remove inactive room from grid path\": assert => {\n const nav: Navigation<SegmentType> = new Navigation(allowsChild);\n const path = nav.pathFrom([\n new Segment(\"session\", 1),\n new Segment(\"rooms\", [\"a\", \"b\", \"c\"]),\n new Segment(\"room\", \"b\")\n ]);\n const newPath = removeRoomFromPath(path, \"a\");\n assert.equal(newPath?.segments.length, 3);\n assert.equal(newPath?.segments[0].type, \"session\");\n assert.equal(newPath?.segments[0].value, 1);\n assert.equal(newPath?.segments[1].type, \"rooms\");\n assert.deepEqual(newPath?.segments[1].value, [\"\", \"b\", \"c\"]);\n assert.equal(newPath?.segments[2].type, \"room\");\n assert.equal(newPath?.segments[2].value, \"b\");\n },\n \"remove inactive room from grid path with empty tile\": assert => {\n const nav: Navigation<SegmentType> = new Navigation(allowsChild);\n const path = nav.pathFrom([\n new Segment(\"session\", 1),\n new Segment(\"rooms\", [\"a\", \"b\", \"\"]),\n new Segment(\"empty-grid-tile\", 3)\n ]);\n const newPath = removeRoomFromPath(path, \"b\");\n assert.equal(newPath?.segments.length, 3);\n assert.equal(newPath?.segments[0].type, \"session\");\n assert.equal(newPath?.segments[0].value, 1);\n assert.equal(newPath?.segments[1].type, \"rooms\");\n assert.deepEqual(newPath?.segments[1].value, [\"a\", \"\", \"\"]);\n assert.equal(newPath?.segments[2].type, \"empty-grid-tile\");\n assert.equal(newPath?.segments[2].value, 3);\n },\n \"remove active room\": assert => {\n const nav: Navigation<SegmentType> = new Navigation(allowsChild);\n const path = nav.pathFrom([\n new Segment(\"session\", 1),\n new Segment(\"room\", \"b\")\n ]);\n const newPath = removeRoomFromPath(path, \"b\");\n assert.equal(newPath?.segments.length, 1);\n assert.equal(newPath?.segments[0].type, \"session\");\n assert.equal(newPath?.segments[0].value, 1);\n },\n \"remove inactive room doesn't do anything\": assert => {\n const nav: Navigation<SegmentType> = new Navigation(allowsChild);\n const path = nav.pathFrom([\n new Segment(\"session\", 1),\n new Segment(\"room\", \"b\")\n ]);\n const newPath = removeRoomFromPath(path, \"a\");\n assert.equal(newPath?.segments.length, 2);\n assert.equal(newPath?.segments[0].type, \"session\");\n assert.equal(newPath?.segments[0].value, 1);\n assert.equal(newPath?.segments[1].type, \"room\");\n assert.equal(newPath?.segments[1].value, \"b\");\n },\n \n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ViewModel} from \"../../ViewModel\";\nimport {RoomTileViewModel} from \"./RoomTileViewModel.js\";\nimport {InviteTileViewModel} from \"./InviteTileViewModel.js\";\nimport {RoomBeingCreatedTileViewModel} from \"./RoomBeingCreatedTileViewModel.js\";\nimport {RoomFilter} from \"./RoomFilter.js\";\nimport {ApplyMap} from \"../../../observable/map/ApplyMap.js\";\nimport {addPanelIfNeeded} from \"../../navigation/index\";\n\nexport class LeftPanelViewModel extends ViewModel {\n constructor(options) {\n super(options);\n const {session} = options;\n this._tileViewModelsMap = this._mapTileViewModels(session.roomsBeingCreated, session.invites, session.rooms);\n this._tileViewModelsFilterMap = new ApplyMap(this._tileViewModelsMap);\n this._tileViewModels = this._tileViewModelsFilterMap.sortValues((a, b) => a.compare(b));\n this._currentTileVM = null;\n this._setupNavigation();\n this._closeUrl = this.urlCreator.urlForSegment(\"session\");\n this._settingsUrl = this.urlCreator.urlForSegment(\"settings\");\n this._createRoomUrl = this.urlCreator.urlForSegment(\"create-room\");\n }\n\n _mapTileViewModels(roomsBeingCreated, invites, rooms) {\n // join is not commutative, invites will take precedence over rooms\n const allTiles = invites.join(roomsBeingCreated, rooms).mapValues((item, emitChange) => {\n let vm;\n if (item.isBeingCreated) {\n vm = new RoomBeingCreatedTileViewModel(this.childOptions({roomBeingCreated: item, emitChange}));\n } else if (item.isInvite) {\n vm = new InviteTileViewModel(this.childOptions({invite: item, emitChange}));\n } else {\n vm = new RoomTileViewModel(this.childOptions({room: item, emitChange}));\n }\n const isOpen = this.navigation.path.get(\"room\")?.value === item.id;\n if (isOpen) {\n vm.open();\n this._updateCurrentVM(vm);\n }\n return vm;\n });\n return allTiles;\n }\n\n _updateCurrentVM(vm) {\n // need to also update the current vm here as\n // we can't call `_open` from the ctor as the map\n // is only populated when the view subscribes.\n this._currentTileVM?.close();\n this._currentTileVM = vm;\n }\n\n get closeUrl() {\n return this._closeUrl;\n }\n\n get settingsUrl() {\n return this._settingsUrl;\n }\n\n get createRoomUrl() { return this._createRoomUrl; }\n\n _setupNavigation() {\n const roomObservable = this.navigation.observe(\"room\");\n this.track(roomObservable.subscribe(roomId => this._open(roomId)));\n\n const gridObservable = this.navigation.observe(\"rooms\");\n this.gridEnabled = !!gridObservable.get();\n this.track(gridObservable.subscribe(roomIds => {\n const changed = this.gridEnabled ^ !!roomIds;\n this.gridEnabled = !!roomIds;\n if (changed) {\n this.emitChange(\"gridEnabled\");\n }\n }));\n }\n\n _open(roomId) {\n this._currentTileVM?.close();\n this._currentTileVM = null;\n if (roomId) {\n this._currentTileVM = this._tileViewModelsMap.get(roomId);\n this._currentTileVM?.open();\n }\n }\n\n toggleGrid() {\n const room = this.navigation.path.get(\"room\");\n let path = this.navigation.path.until(\"session\");\n if (this.gridEnabled) {\n if (room) {\n path = path.with(room);\n path = addPanelIfNeeded(this.navigation, path);\n }\n } else {\n if (room) {\n path = path.with(this.navigation.segment(\"rooms\", [room.value]));\n path = path.with(room);\n path = addPanelIfNeeded(this.navigation, path);\n } else {\n path = path.with(this.navigation.segment(\"rooms\", []));\n path = path.with(this.navigation.segment(\"empty-grid-tile\", 0));\n }\n }\n this.navigation.applyPath(path);\n }\n\n get tileViewModels() {\n return this._tileViewModels;\n }\n\n clearFilter() {\n this._tileViewModelsFilterMap.setApply(null);\n this._tileViewModelsFilterMap.applyOnce((roomId, vm) => vm.hidden = false);\n }\n\n setFilter(query) {\n query = query.trim();\n if (query.length === 0) {\n this.clearFilter();\n return false;\n } else {\n const startFiltering = !this._tileViewModelsFilterMap.hasApply();\n const filter = new RoomFilter(query);\n this._tileViewModelsFilterMap.setApply((roomId, vm) => {\n vm.hidden = !filter.matches(vm);\n });\n return startFiltering;\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport class UpdateAction {\n constructor(remove, update, replace, updateParams) {\n this._remove = remove;\n this._update = update;\n this._replace = replace;\n this._updateParams = updateParams;\n }\n\n get shouldReplace() {\n return this._replace;\n }\n\n get shouldRemove() {\n return this._remove;\n }\n\n get shouldUpdate() {\n return this._update;\n }\n\n get updateParams() {\n return this._updateParams;\n }\n\n static Remove() {\n return new UpdateAction(true, false, false, null);\n }\n\n static Update(newParams) {\n return new UpdateAction(false, true, false, newParams);\n }\n\n static Nothing() {\n return new UpdateAction(false, false, false, null);\n }\n\n static Replace(params) {\n return new UpdateAction(false, false, true, params);\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseObservableList} from \"../../../../observable/list/BaseObservableList\";\nimport {sortedIndex} from \"../../../../utils/sortedIndex\";\n\n// maps 1..n entries to 0..1 tile. Entries are what is stored in the timeline, either an event or fragmentboundary\n// for now, tileClassForEntry should be stable in whether it returns a tile or not.\n// e.g. the decision to create a tile or not should be based on properties\n// not updated later on (e.g. event type)\n// also see big comment in onUpdate\nexport class TilesCollection extends BaseObservableList {\n constructor(entries, tileOptions) {\n super();\n this._entries = entries;\n this._tiles = null;\n this._entrySubscription = null;\n this._tileOptions = tileOptions;\n this._emitSpontanousUpdate = this._emitSpontanousUpdate.bind(this);\n }\n\n _createTile(entry) {\n const Tile = this._tileOptions.tileClassForEntry(entry);\n if (Tile) {\n return new Tile(entry, this._tileOptions);\n }\n }\n\n _emitSpontanousUpdate(tile, params) {\n const entry = tile.lowerEntry;\n const tileIdx = this._findTileIdx(entry);\n this.emitUpdate(tileIdx, tile, params);\n }\n\n onSubscribeFirst() {\n this._entrySubscription = this._entries.subscribe(this);\n this._populateTiles();\n }\n\n _populateTiles() {\n this._tiles = [];\n let currentTile = null;\n for (let entry of this._entries) {\n if (!currentTile || !currentTile.tryIncludeEntry(entry)) {\n currentTile = this._createTile(entry);\n if (currentTile) {\n this._tiles.push(currentTile);\n }\n }\n }\n let prevTile = null;\n for (let tile of this._tiles) {\n if (prevTile) {\n prevTile.updateNextSibling(tile);\n }\n tile.updatePreviousSibling(prevTile);\n prevTile = tile;\n }\n if (prevTile) {\n prevTile.updateNextSibling(null);\n }\n // now everything is wired up,\n // allow tiles to emit updates\n for (const tile of this._tiles) {\n tile.setUpdateEmit(this._emitSpontanousUpdate);\n }\n }\n\n _findTileIdx(entry) {\n return sortedIndex(this._tiles, entry, (entry, tile) => {\n // negate result because we're switching the order of the params\n return -tile.compareEntry(entry);\n });\n }\n\n _findTileAtIdx(entry, idx) {\n const tile = this._getTileAtIdx(idx);\n if (tile && tile.compareEntry(entry) === 0) {\n return tile;\n }\n }\n\n _getTileAtIdx(tileIdx) {\n if (tileIdx >= 0 && tileIdx < this._tiles.length) {\n return this._tiles[tileIdx];\n }\n return null;\n }\n\n onUnsubscribeLast() {\n this._entrySubscription = this._entrySubscription();\n for(let i = 0; i < this._tiles.length; i+= 1) {\n this._tiles[i].dispose();\n }\n this._tiles = null;\n }\n\n onReset() {\n // if TileViewModel were disposable, dispose here, or is that for views to do? views I suppose ...\n this._buildInitialTiles();\n this.emitReset();\n }\n\n onAdd(index, entry) {\n const tileIdx = this._findTileIdx(entry);\n const prevTile = this._getTileAtIdx(tileIdx - 1);\n if (prevTile && prevTile.tryIncludeEntry(entry)) {\n this.emitUpdate(tileIdx - 1, prevTile);\n return;\n }\n // not + 1 because this entry hasn't been added yet\n const nextTile = this._getTileAtIdx(tileIdx);\n if (nextTile && nextTile.tryIncludeEntry(entry)) {\n this.emitUpdate(tileIdx, nextTile);\n return;\n }\n\n const newTile = this._createTile(entry);\n if (newTile) {\n if (prevTile) {\n prevTile.updateNextSibling(newTile);\n // this emits an update while the add hasn't been emitted yet\n newTile.updatePreviousSibling(prevTile);\n }\n if (nextTile) {\n newTile.updateNextSibling(nextTile);\n nextTile.updatePreviousSibling(newTile);\n }\n this._tiles.splice(tileIdx, 0, newTile);\n this.emitAdd(tileIdx, newTile);\n // add event is emitted, now the tile\n // can emit updates\n newTile.setUpdateEmit(this._emitSpontanousUpdate);\n }\n // find position by sort key\n // ask siblings to be included? both? yes, twice: a (insert c here) b, ask a(c), if yes ask b(a), else ask b(c)? if yes then b(a)?\n }\n\n onUpdate(index, entry, params) {\n // if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it\n if (!this._tiles) {\n return;\n }\n const tileIdx = this._findTileIdx(entry);\n const tile = this._findTileAtIdx(entry, tileIdx);\n if (tile) {\n const action = tile.updateEntry(entry, params);\n if (action.shouldReplace) {\n const newTile = this._createTile(entry);\n if (newTile) {\n this._replaceTile(tileIdx, tile, newTile, action.updateParams);\n newTile.setUpdateEmit(this._emitSpontanousUpdate);\n } else {\n this._removeTile(tileIdx, tile);\n }\n }\n if (action.shouldRemove) {\n this._removeTile(tileIdx, tile);\n }\n if (action.shouldUpdate) {\n this.emitUpdate(tileIdx, tile, action.updateParams);\n }\n }\n // technically we should handle adding a tile here as well\n // in case before we didn't have a tile for it but now we do\n // but in reality we don't have this use case as the type and msgtype\n // doesn't change. Decryption maybe is the exception?\n\n\n // outcomes here can be\n // tiles should be removed (got redacted and we don't want it in the timeline)\n // tile should be added where there was none before ... ?\n // entry should get it's own tile now\n // merge with neighbours? ... hard to imagine use case for this ...\n }\n\n _replaceTile(tileIdx, existingTile, newTile, updateParams) {\n existingTile.dispose();\n const prevTile = this._getTileAtIdx(tileIdx - 1);\n const nextTile = this._getTileAtIdx(tileIdx + 1);\n this._tiles[tileIdx] = newTile;\n prevTile?.updateNextSibling(newTile);\n newTile.updatePreviousSibling(prevTile);\n newTile.updateNextSibling(nextTile);\n nextTile?.updatePreviousSibling(newTile);\n this.emitUpdate(tileIdx, newTile, updateParams);\n }\n\n _removeTile(tileIdx, tile) {\n const prevTile = this._getTileAtIdx(tileIdx - 1);\n const nextTile = this._getTileAtIdx(tileIdx + 1);\n // applying and emitting the remove should happen\n // atomically, as updateNext/PreviousSibling might\n // emit an update with the wrong index otherwise \n this._tiles.splice(tileIdx, 1);\n tile.dispose();\n this.emitRemove(tileIdx, tile);\n prevTile?.updateNextSibling(nextTile);\n nextTile?.updatePreviousSibling(prevTile);\n }\n\n // would also be called when unloading a part of the timeline\n onRemove(index, entry) {\n const tileIdx = this._findTileIdx(entry);\n const tile = this._findTileAtIdx(entry, tileIdx);\n if (tile) {\n const removeTile = tile.removeEntry(entry);\n if (removeTile) {\n this._removeTile(tileIdx, tile);\n } else {\n this.emitUpdate(tileIdx, tile);\n }\n }\n }\n\n onMove(/*fromIdx, toIdx, value*/) {\n // this ... cannot happen in the timeline?\n // perhaps we can use this event to support a local echo (in a different fragment)\n // to be moved to the key of the remote echo, so we don't loose state ... ?\n }\n\n [Symbol.iterator]() {\n return this._tiles.values();\n }\n\n get length() {\n return this._tiles.length;\n }\n\n getFirst() {\n return this._tiles[0];\n }\n\n getTileIndex(searchTile) {\n const idx = sortedIndex(this._tiles, searchTile, (searchTile, tile) => {\n return searchTile.compare(tile);\n });\n const foundTile = this._tiles[idx];\n if (foundTile?.compare(searchTile) === 0) {\n return idx;\n }\n return -1;\n }\n\n sliceIterator(start, end) {\n return this._tiles.slice(start, end)[Symbol.iterator]();\n }\n}\n\nimport {ObservableArray} from \"../../../../observable/list/ObservableArray\";\nimport {UpdateAction} from \"./UpdateAction.js\";\n\nexport function tests() {\n class TestTile {\n constructor(entry) {\n this.entry = entry;\n this.update = null;\n }\n setUpdateEmit(update) {\n this.update = update;\n }\n tryIncludeEntry() {\n return false;\n }\n compareEntry(b) {\n return this.entry.n - b.n;\n }\n removeEntry() {\n return true;\n }\n get upperEntry() {\n return this.entry;\n }\n\n get lowerEntry() {\n return this.entry;\n }\n updateNextSibling() {}\n updatePreviousSibling() {}\n updateEntry() {\n return UpdateAction.Nothing;\n }\n\n dispose() {}\n }\n\n return {\n \"don't emit update before add\": assert => {\n class UpdateOnSiblingTile extends TestTile {\n updateNextSibling() {\n // this happens with isContinuation\n this.update && this.update(this, \"next\");\n }\n updatePreviousSibling() {\n // this happens with isContinuation\n this.update && this.update(this, \"previous\");\n }\n }\n const entries = new ObservableArray([{n: 5}, {n: 10}]);\n const tileOptions = {\n tileClassForEntry: () => UpdateOnSiblingTile,\n };\n const tiles = new TilesCollection(entries, tileOptions);\n let receivedAdd = false;\n tiles.subscribe({\n onAdd(idx, tile) {\n assert(tile.entry.n, 7);\n receivedAdd = true;\n },\n onUpdate(idx, tile) {\n if (tile.entry.n === 7) {\n assert(!receivedAdd, \"receiving update before add\");\n }\n }\n });\n entries.insert(1, {n: 7});\n assert(receivedAdd);\n },\n \"emit update with correct index in updatePreviousSibling during remove\": assert => {\n class UpdateOnSiblingTile extends TestTile {\n updatePreviousSibling() {\n this.update?.(this, \"previous\");\n }\n }\n const entries = new ObservableArray([{n: 5}, {n: 10}, {n: 15}]);\n const tileOptions = {\n tileClassForEntry: () => UpdateOnSiblingTile,\n };\n const tiles = new TilesCollection(entries, tileOptions);\n const events = [];\n tiles.subscribe({\n onUpdate(idx, tile) {\n assert.equal(idx, 1);\n assert.equal(tile.entry.n, 15);\n events.push(\"update\");\n },\n onRemove(idx, tile) {\n assert.equal(idx, 1);\n assert.equal(tile.entry.n, 10);\n events.push(\"remove\");\n }\n });\n entries.remove(1);\n assert.deepEqual(events, [\"remove\", \"update\"]);\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/*\nneed better naming, but\nentry = event or gap from matrix layer\ntile = item on visual timeline like event, date separator?, group of joined events\n\n\nshall we put date separators as marker in EventViewItem or separate item? binary search will be complicated ...\n\n\npagination ...\n\non the timeline viewmodel (containing the TilesCollection?) we'll have a method to (un)load a tail or head of\nthe timeline (counted in tiles), which results to a range in sortKeys we want on the screen. We pass that range\nto the room timeline, which unload entries from memory.\nwhen loading, it just reads events from a sortkey backwards or forwards...\n*/\nimport {TilesCollection} from \"./TilesCollection.js\";\nimport {ViewModel} from \"../../../ViewModel\";\n\nexport class TimelineViewModel extends ViewModel {\n constructor(options) {\n super(options);\n const {timeline, tileOptions} = options;\n this._timeline = this.track(timeline);\n this._tiles = new TilesCollection(timeline.entries, tileOptions);\n this._startTile = null;\n this._endTile = null;\n this._topLoadingPromise = null;\n this._requestedStartTile = null;\n this._requestedEndTile = null;\n this._requestScheduled = false;\n this._showJumpDown = false;\n }\n\n /** if this.tiles is empty, call this with undefined for both startTile and endTile */\n setVisibleTileRange(startTile, endTile) {\n // don't clear these once done as they are used to check\n // for more tiles once loadAtTop finishes\n this._requestedStartTile = startTile;\n this._requestedEndTile = endTile;\n if (!this._requestScheduled) {\n Promise.resolve().then(() => {\n this._setVisibleTileRange(this._requestedStartTile, this._requestedEndTile);\n this._requestScheduled = false;\n });\n this._requestScheduled = true;\n }\n }\n\n _setVisibleTileRange(startTile, endTile) {\n let loadTop;\n if (startTile && endTile) {\n // old tiles could have been removed from tilescollection once we support unloading\n this._startTile = startTile;\n this._endTile = endTile;\n const startIndex = this._tiles.getTileIndex(this._startTile);\n const endIndex = this._tiles.getTileIndex(this._endTile);\n for (const tile of this._tiles.sliceIterator(startIndex, endIndex + 1)) {\n tile.notifyVisible();\n }\n loadTop = startIndex < 10;\n this._setShowJumpDown(endIndex < (this._tiles.length - 1));\n } else {\n // tiles collection is empty, load more at top\n loadTop = true;\n this._setShowJumpDown(false);\n }\n\n if (loadTop && !this._topLoadingPromise) {\n this._topLoadingPromise = this._timeline.loadAtTop(10).then(hasReachedEnd => {\n this._topLoadingPromise = null;\n if (!hasReachedEnd) {\n // check if more items need to be loaded by recursing\n // use the requested start / end tile,\n // so we don't end up overwriting a newly requested visible range here\n this.setVisibleTileRange(this._requestedStartTile, this._requestedEndTile);\n }\n });\n }\n }\n\n get tiles() {\n return this._tiles;\n }\n\n _setShowJumpDown(show) {\n if (this._showJumpDown !== show) {\n this._showJumpDown = show;\n this.emitChange(\"showJumpDown\");\n }\n }\n\n get showJumpDown() {\n return this._showJumpDown;\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ViewModel} from \"../../ViewModel\";\n\nexport class ComposerViewModel extends ViewModel {\n constructor(roomVM) {\n super(roomVM.options);\n this._roomVM = roomVM;\n this._isEmpty = true;\n this._replyVM = null;\n }\n\n setReplyingTo(entry) {\n const changed = new Boolean(entry) !== new Boolean(this._replyVM) || !this._replyVM?.id.equals(entry.asEventKey());\n if (changed) {\n this._replyVM = this.disposeTracked(this._replyVM);\n if (entry) {\n this._replyVM = this.track(this._roomVM._createTile(entry));\n this._replyVM.notifyVisible();\n }\n this.emitChange(\"replyViewModel\");\n this.emit(\"focus\");\n }\n }\n\n clearReplyingTo() {\n this.setReplyingTo(null);\n }\n\n get replyViewModel() {\n return this._replyVM;\n }\n\n get isEncrypted() {\n return this._roomVM.isEncrypted;\n }\n\n async sendMessage(message) {\n const success = await this._roomVM._sendMessage(message, this._replyVM);\n if (success) {\n this._isEmpty = true;\n this.emitChange(\"canSend\");\n this.clearReplyingTo();\n }\n return success;\n }\n\n sendPicture() {\n this._roomVM._pickAndSendPicture();\n }\n\n sendFile() {\n this._roomVM._pickAndSendFile();\n }\n\n sendVideo() {\n this._roomVM._pickAndSendVideo();\n }\n\n get canSend() {\n return !this._isEmpty;\n }\n\n async setInput(text) {\n const wasEmpty = this._isEmpty;\n this._isEmpty = text.length === 0;\n if (wasEmpty && !this._isEmpty) {\n this._roomVM._room.ensureMessageKeyIsShared();\n }\n if (wasEmpty !== this._isEmpty) {\n this.emitChange(\"canSend\");\n }\n }\n\n get kind() {\n return \"composer\";\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport function imageToInfo(image) {\n return {\n w: image.width,\n h: image.height,\n mimetype: image.blob.mimeType,\n size: image.blob.size\n };\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {UpdateAction} from \"../UpdateAction.js\";\nimport {ViewModel} from \"../../../../ViewModel\";\nimport {SendStatus} from \"../../../../../matrix/room/sending/PendingEvent.js\";\n\nexport class SimpleTile extends ViewModel {\n constructor(entry, options) {\n super(options);\n this._entry = entry;\n this._emitUpdate = undefined;\n }\n // view model props for all subclasses\n // hmmm, could also do instanceof ... ?\n get shape() {\n return null;\n // \"gap\" | \"message\" | \"image\" | ... ?\n }\n\n // don't show display name / avatar\n // probably only for BaseMessageTiles of some sort?\n get isContinuation() {\n return false;\n }\n\n get hasDateSeparator() {\n return false;\n }\n\n get id() {\n return this._entry.asEventKey();\n }\n\n get eventId() {\n return this._entry.id;\n }\n\n get isPending() {\n return this._entry.isPending;\n }\n\n get isUnsent() {\n return this._entry.isPending && this._entry.pendingEvent.status !== SendStatus.Sent;\n }\n\n get canAbortSending() {\n return this._entry.isPending &&\n !this._entry.pendingEvent.hasStartedSending;\n }\n\n abortSending() {\n this._entry.pendingEvent?.abort();\n }\n\n // TilesCollection contract below\n setUpdateEmit(emitUpdate) {\n this._emitUpdate = emitUpdate;\n }\n\n /** overrides the emitChange in ViewModel to also emit the update over the tiles collection */\n emitChange(changedProps) {\n if (this._emitUpdate) {\n // it can happen that after some network call\n // we switched away from the room and the response\n // comes in, triggering an emitChange in a tile that\n // has been disposed already (and hence the change\n // callback has been cleared by dispose) We should just ignore this.\n this._emitUpdate(this, changedProps);\n }\n super.emitChange(changedProps);\n }\n\n get upperEntry() {\n return this._entry;\n }\n\n get lowerEntry() {\n return this._entry;\n }\n\n compare(tile) {\n return this.upperEntry.compare(tile.upperEntry);\n }\n\n compareEntry(entry) {\n return this._entry.compare(entry);\n }\n\n // update received for already included (falls within sort keys) entry\n updateEntry(entry, param) {\n const renderedAsRedacted = this.shape === \"redacted\";\n if (!entry.isGap && entry.isRedacted !== renderedAsRedacted) {\n // recreate the tile if the entry becomes redacted\n return UpdateAction.Replace(\"shape\");\n } else {\n this._entry = entry;\n return UpdateAction.Update(param);\n }\n }\n\n // return whether the tile should be removed\n // as SimpleTile only has one entry, the tile should be removed\n removeEntry(/*entry*/) {\n return true;\n }\n\n // SimpleTile can only contain 1 entry\n tryIncludeEntry() {\n return false;\n }\n // let item know it has a new sibling\n updatePreviousSibling(/*prev*/) {\n\n }\n\n // let item know it has a new sibling\n updateNextSibling(/*next*/) {\n \n }\n\n notifyVisible() {}\n\n dispose() {\n this.setUpdateEmit(null);\n super.dispose();\n }\n // TilesCollection contract above\n\n get _room() {\n return this._roomVM.room;\n }\n\n get _roomVM() {\n return this._options.roomVM;\n }\n\n get _timeline() {\n return this._options.timeline;\n }\n\n get _powerLevels() {\n return this._timeline.powerLevels;\n }\n\n get _ownMember() {\n return this._options.timeline.me;\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {SimpleTile} from \"./SimpleTile.js\";\nimport {UpdateAction} from \"../UpdateAction.js\";\n\nexport class GapTile extends SimpleTile {\n constructor(entry, options) {\n super(entry, options);\n this._loading = false;\n this._error = null;\n this._isAtTop = true;\n this._siblingChanged = false;\n }\n\n async fill() {\n if (!this._loading && !this._entry.edgeReached) {\n this._loading = true;\n this.emitChange(\"isLoading\");\n try {\n await this._room.fillGap(this._entry, 10);\n } catch (err) {\n console.error(`room.fillGap(): ${err.message}:\\n${err.stack}`);\n this._error = err;\n this.emitChange(\"error\");\n // rethrow so caller of this method\n // knows not to keep calling this for now\n throw err;\n } finally {\n this._loading = false;\n this.emitChange(\"isLoading\");\n }\n return true;\n }\n return false;\n }\n\n async notifyVisible() {\n // we do (up to 10) backfills while no new tiles have been added to the timeline\n // because notifyVisible won't be called again until something gets added to the timeline\n let depth = 0;\n let canFillMore;\n this._siblingChanged = false;\n do {\n canFillMore = await this.fill();\n depth = depth + 1;\n } while (depth < 10 && !this._siblingChanged && canFillMore && !this.isDisposed);\n }\n\n get isAtTop() {\n return this._isAtTop;\n }\n\n updatePreviousSibling(prev) {\n super.updatePreviousSibling(prev);\n const isAtTop = !prev;\n if (this._isAtTop !== isAtTop) {\n this._isAtTop = isAtTop;\n this.emitChange(\"isAtTop\");\n }\n this._siblingChanged = true;\n }\n\n updateNextSibling() {\n // if the sibling of the gap changed while calling room.fill(),\n // we intepret this as at least one new tile has been added to\n // the timeline. See notifyVisible why this is important.\n this._siblingChanged = true;\n }\n\n updateEntry(entry, params) {\n super.updateEntry(entry, params);\n if (!entry.isGap) {\n return UpdateAction.Remove();\n } else {\n return UpdateAction.Nothing();\n }\n }\n\n get shape() {\n return \"gap\";\n }\n\n get isLoading() {\n return this._loading;\n }\n\n get error() {\n if (this._error) {\n const dir = this._entry.prev_batch ? \"previous\" : \"next\";\n return `Could not load ${dir} messages: ${this._error.message}`;\n }\n return null;\n }\n}\n\nimport {FragmentBoundaryEntry} from \"../../../../../matrix/room/timeline/entries/FragmentBoundaryEntry.js\";\nexport function tests() {\n return {\n \"uses updated token to fill\": async assert => {\n let currentToken = 5;\n const fragment = {\n id: 0,\n previousToken: currentToken,\n roomId: \"!abc\"\n };\n const room = {\n async fillGap(entry) {\n assert.equal(entry.token, currentToken);\n currentToken += 1;\n const newEntry = entry.withUpdatedFragment(Object.assign({}, fragment, {previousToken: currentToken}));\n tile.updateEntry(newEntry);\n }\n };\n const tile = new GapTile(new FragmentBoundaryEntry(fragment, true), {roomVM: {room}});\n await tile.fill();\n await tile.fill();\n await tile.fill();\n assert.equal(currentToken, 8);\n }\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\nimport {ObservableMap} from \"../../../../observable/map/ObservableMap\";\n\nexport class ReactionsViewModel {\n constructor(parentTile) {\n this._parentTile = parentTile;\n this._map = new ObservableMap();\n this._reactions = this._map.sortValues((a, b) => a._compare(b));\n }\n\n /** @package */\n update(annotations, pendingAnnotations) {\n if (annotations) {\n for (const key in annotations) {\n if (annotations.hasOwnProperty(key)) {\n const annotation = annotations[key];\n const reaction = this._map.get(key);\n if (reaction) {\n if (reaction._tryUpdate(annotation)) {\n this._map.update(key);\n }\n } else {\n this._map.add(key, new ReactionViewModel(key, annotation, null, this._parentTile));\n }\n }\n }\n }\n if (pendingAnnotations) {\n for (const [key, annotation] of pendingAnnotations.entries()) {\n const reaction = this._map.get(key);\n if (reaction) {\n reaction._tryUpdatePending(annotation);\n this._map.update(key);\n } else {\n this._map.add(key, new ReactionViewModel(key, null, annotation, this._parentTile));\n }\n }\n }\n for (const existingKey of this._map.keys()) {\n const hasPending = pendingAnnotations?.has(existingKey);\n const hasRemote = annotations?.hasOwnProperty(existingKey);\n if (!hasRemote && !hasPending) {\n this._map.remove(existingKey);\n } else if (!hasRemote) {\n if (this._map.get(existingKey)._tryUpdate(null)) {\n this._map.update(existingKey);\n }\n } else if (!hasPending) {\n if (this._map.get(existingKey)._tryUpdatePending(null)) {\n this._map.update(existingKey);\n }\n }\n }\n }\n\n get reactions() {\n return this._reactions;\n }\n\n getReaction(key) {\n return this._map.get(key);\n }\n}\n\nclass ReactionViewModel {\n constructor(key, annotation, pending, parentTile) {\n this._key = key;\n this._annotation = annotation;\n this._pending = pending;\n this._parentTile = parentTile;\n this._isToggling = false;\n }\n\n _tryUpdate(annotation) {\n const oneSetAndOtherNot = !!this._annotation !== !!annotation;\n const bothSet = this._annotation && annotation;\n const areDifferent = bothSet && (\n annotation.me !== this._annotation.me ||\n annotation.count !== this._annotation.count ||\n annotation.firstTimestamp !== this._annotation.firstTimestamp\n );\n if (oneSetAndOtherNot || areDifferent) {\n this._annotation = annotation;\n return true;\n }\n return false;\n }\n\n _tryUpdatePending(pending) {\n if (!pending && !this._pending) {\n return false;\n }\n this._pending = pending;\n return true;\n }\n\n get key() {\n return this._key;\n }\n\n get count() {\n return (this._pending?.count || 0) + (this._annotation?.count || 0);\n }\n\n get isPending() {\n return this._pending !== null;\n }\n\n /** @returns {boolean} true if the user has a (pending) reaction\n * already for this key, or they have a pending redaction for\n * the reaction, false if there is nothing pending and\n * the user has not reacted yet. */\n get isActive() {\n return this._annotation?.me || this.isPending;\n }\n\n get firstTimestamp() {\n let ts = Number.MAX_SAFE_INTEGER;\n if (this._annotation) {\n ts = Math.min(ts, this._annotation.firstTimestamp);\n }\n if (this._pending) {\n ts = Math.min(ts, this._pending.firstTimestamp);\n }\n return ts;\n }\n\n _compare(other) {\n // the comparator is also used to test for equality by sortValues, if the comparison returns 0\n // given that the firstTimestamp isn't set anymore when the last reaction is removed,\n // the remove event wouldn't be able to find the correct index anymore. So special case equality.\n if (other === this) {\n return 0;\n }\n if (this.count !== other.count) {\n return other.count - this.count;\n } else {\n const cmp = this.firstTimestamp - other.firstTimestamp;\n if (cmp === 0) {\n return this.key < other.key ? -1 : 1;\n }\n return cmp;\n }\n }\n\n async toggle(log = null) {\n if (this._isToggling) {\n console.log(\"busy toggling reaction already\");\n return;\n }\n this._isToggling = true;\n try {\n await this._parentTile.toggleReaction(this.key, log);\n } finally {\n this._isToggling = false;\n }\n }\n}\n\n// matrix classes uses in the integration test below\nimport {User} from \"../../../../matrix/User.js\";\nimport {SendQueue} from \"../../../../matrix/room/sending/SendQueue.js\";\nimport {Timeline} from \"../../../../matrix/room/timeline/Timeline.js\";\nimport {EventEntry} from \"../../../../matrix/room/timeline/entries/EventEntry.js\";\nimport {RelationWriter} from \"../../../../matrix/room/timeline/persistence/RelationWriter.js\";\nimport {FragmentIdComparer} from \"../../../../matrix/room/timeline/FragmentIdComparer.js\";\nimport {createAnnotation} from \"../../../../matrix/room/timeline/relations.js\";\n// mocks\nimport {Clock as MockClock} from \"../../../../mocks/Clock.js\";\nimport {createMockStorage} from \"../../../../mocks/Storage\";\nimport {ListObserver} from \"../../../../mocks/ListObserver.js\";\nimport {createEvent, withTextBody, withContent} from \"../../../../mocks/event.js\";\nimport {NullLogItem, NullLogger} from \"../../../../logging/NullLogger\";\nimport {HomeServer as MockHomeServer} from \"../../../../mocks/HomeServer.js\";\n// other imports\nimport {BaseMessageTile} from \"./tiles/BaseMessageTile.js\";\nimport {MappedList} from \"../../../../observable/list/MappedList\";\nimport {ObservableValue} from \"../../../../observable/ObservableValue\";\nimport {PowerLevels} from \"../../../../matrix/room/PowerLevels.js\";\n\nexport function tests() {\n const fragmentIdComparer = new FragmentIdComparer([]);\n const roomId = \"$abc\";\n const alice = \"@alice:hs.tld\";\n const bob = \"@bob:hs.tld\";\n const logger = new NullLogger();\n\n function findInIterarable(it, predicate) {\n let i = 0;\n for (const item of it) {\n if (predicate(item, i)) {\n return item;\n }\n i += 1;\n }\n throw new Error(\"not found\");\n }\n\n function mapMessageEntriesToBaseMessageTile(timeline, queue) {\n const room = {\n id: roomId,\n sendEvent(eventType, content, attachments, log) {\n return queue.enqueueEvent(eventType, content, attachments, log);\n },\n sendRedaction(eventIdOrTxnId, reason, log) {\n return queue.enqueueRedaction(eventIdOrTxnId, reason, log);\n }\n };\n const tiles = new MappedList(timeline.entries, entry => {\n if (entry.eventType === \"m.room.message\") {\n return new BaseMessageTile(entry, {roomVM: {room}, timeline, platform: {logger}});\n }\n return null;\n }, (tile, params, entry) => tile?.updateEntry(entry, params, function () {}));\n return tiles;\n }\n\n return {\n // these are more an integration test than unit tests,\n // but fully test the local echo when toggling and\n // the correct send queue modifications happen\n \"toggling reaction with own remote reaction\": async assert => {\n // 1. put message and reaction in storage\n const messageEvent = withTextBody(\"Dogs > Cats\", createEvent(\"m.room.message\", \"!abc\", bob));\n const myReactionEvent = withContent(createAnnotation(messageEvent.event_id, \"🐶\"), createEvent(\"m.reaction\", \"!def\", alice));\n myReactionEvent.origin_server_ts = 5;\n const myReactionEntry = new EventEntry({event: myReactionEvent, roomId}, fragmentIdComparer);\n const relationWriter = new RelationWriter({roomId, ownUserId: alice, fragmentIdComparer});\n const storage = await createMockStorage();\n const txn = await storage.readWriteTxn([\n storage.storeNames.timelineEvents,\n storage.storeNames.timelineRelations,\n storage.storeNames.timelineFragments\n ]);\n txn.timelineFragments.add({id: 1, roomId});\n txn.timelineEvents.tryInsert({fragmentId: 1, eventIndex: 2, event: messageEvent, roomId}, new NullLogItem());\n txn.timelineEvents.tryInsert({fragmentId: 1, eventIndex: 3, event: myReactionEvent, roomId}, new NullLogItem());\n await relationWriter.writeRelation(myReactionEntry, txn, new NullLogItem());\n await txn.complete();\n // 2. setup queue & timeline\n const queue = new SendQueue({roomId, storage, hsApi: new MockHomeServer().api});\n const powerLevelsObservable = new ObservableValue(new PowerLevels({ ownUserId: alice, membership: \"join\" }));\n const timeline = new Timeline({\n roomId,\n storage,\n fragmentIdComparer,\n clock: new MockClock(),\n pendingEvents: queue.pendingEvents,\n powerLevelsObservable\n });\n // 3. load the timeline, which will load the message with the reaction\n await timeline.load(new User(alice), \"join\", new NullLogItem());\n const tiles = mapMessageEntriesToBaseMessageTile(timeline, queue);\n // 4. subscribe to the queue to observe, and the tiles (so we can safely iterate)\n const queueObserver = new ListObserver();\n queue.pendingEvents.subscribe(queueObserver);\n tiles.subscribe(new ListObserver());\n const messageTile = findInIterarable(tiles, e => !!e); // the other entries are mapped to null\n const reactionVM = messageTile.reactions.getReaction(\"🐶\");\n // 5. test toggling\n // make sure the preexisting reaction is counted\n assert.equal(reactionVM.count, 1);\n // 5.1. unset reaction, should redact the pre-existing reaction\n await reactionVM.toggle();\n {\n assert.equal(reactionVM.count, 0);\n const {value: redaction, type} = await queueObserver.next();\n assert.equal(\"add\", type);\n assert.equal(redaction.eventType, \"m.room.redaction\");\n assert.equal(redaction.relatedEventId, myReactionEntry.id);\n // SendQueue puts redaction in sending status, as it is first in the queue\n assert.equal(\"update\", (await queueObserver.next()).type);\n }\n // 5.2. set reaction, should send a new reaction as the redaction is already sending\n await reactionVM.toggle();\n let reactionIndex;\n {\n assert.equal(reactionVM.count, 1);\n const {value: reaction, type, index} = await queueObserver.next();\n assert.equal(\"add\", type);\n assert.equal(reaction.eventType, \"m.reaction\");\n assert.equal(reaction.relatedEventId, messageEvent.event_id);\n reactionIndex = index;\n }\n // 5.3. unset reaction, should abort the previous pending reaction as it hasn't started sending yet\n await reactionVM.toggle();\n {\n assert.equal(reactionVM.count, 0);\n const {index, type} = await queueObserver.next();\n assert.equal(\"remove\", type);\n assert.equal(reactionIndex, index);\n }\n },\n \"toggling reaction without own remote reaction\": async assert => {\n // 1. put message in storage\n const messageEvent = withTextBody(\"Dogs > Cats\", createEvent(\"m.room.message\", \"!abc\", bob));\n const storage = await createMockStorage();\n\n const txn = await storage.readWriteTxn([\n storage.storeNames.timelineEvents,\n storage.storeNames.timelineFragments\n ]);\n txn.timelineFragments.add({id: 1, roomId});\n txn.timelineEvents.tryInsert({fragmentId: 1, eventIndex: 2, event: messageEvent, roomId}, new NullLogItem());\n await txn.complete();\n // 2. setup queue & timeline\n const queue = new SendQueue({roomId, storage, hsApi: new MockHomeServer().api});\n const powerLevelsObservable = new ObservableValue(new PowerLevels({ ownUserId: alice, membership: \"join\" }));\n const timeline = new Timeline({roomId, storage, fragmentIdComparer,\n clock: new MockClock(), pendingEvents: queue.pendingEvents, powerLevelsObservable});\n\n // 3. load the timeline, which will load the message with the reaction\n await timeline.load(new User(alice), \"join\", new NullLogItem());\n const tiles = mapMessageEntriesToBaseMessageTile(timeline, queue);\n // 4. subscribe to the queue to observe, and the tiles (so we can safely iterate)\n const queueObserver = new ListObserver();\n queue.pendingEvents.subscribe(queueObserver);\n tiles.subscribe(new ListObserver());\n const messageTile = findInIterarable(tiles, e => !!e); // the other entries are mapped to null\n // 5. test toggling\n assert.equal(messageTile.reactions, null);\n // 5.1. set reaction, should send a new reaction as there is none yet\n await messageTile.react(\"🐶\");\n // now there should be a reactions view model\n const reactionVM = messageTile.reactions.getReaction(\"🐶\");\n let reactionTxnId;\n {\n assert.equal(reactionVM.count, 1);\n const {value: reaction, type} = await queueObserver.next();\n assert.equal(\"add\", type);\n assert.equal(reaction.eventType, \"m.reaction\");\n assert.equal(reaction.relatedEventId, messageEvent.event_id);\n // SendQueue puts reaction in sending status, as it is first in the queue\n assert.equal(\"update\", (await queueObserver.next()).type);\n reactionTxnId = reaction.txnId;\n }\n // 5.2. unset reaction, should redact the previous pending reaction as it has started sending already\n let redactionIndex;\n await reactionVM.toggle();\n {\n assert.equal(reactionVM.count, 0);\n const {value: redaction, type, index} = await queueObserver.next();\n assert.equal(\"add\", type);\n assert.equal(redaction.eventType, \"m.room.redaction\");\n assert.equal(redaction.relatedTxnId, reactionTxnId);\n redactionIndex = index;\n }\n // 5.3. set reaction, should abort the previous pending redaction as it hasn't started sending yet\n await reactionVM.toggle();\n {\n assert.equal(reactionVM.count, 1);\n const {index, type} = await queueObserver.next();\n assert.equal(\"remove\", type);\n assert.equal(redactionIndex, index);\n redactionIndex = index;\n }\n },\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {SimpleTile} from \"./SimpleTile.js\";\nimport {ReactionsViewModel} from \"../ReactionsViewModel.js\";\nimport {getIdentifierColorNumber, avatarInitials, getAvatarHttpUrl} from \"../../../../avatar\";\n\nexport class BaseMessageTile extends SimpleTile {\n constructor(entry, options) {\n super(entry, options);\n this._date = this._entry.timestamp ? new Date(this._entry.timestamp) : null;\n this._isContinuation = false;\n this._reactions = null;\n this._replyTile = null;\n if (this._entry.annotations || this._entry.pendingAnnotations) {\n this._updateReactions();\n }\n this._updateReplyTileIfNeeded(undefined);\n }\n\n notifyVisible() {\n super.notifyVisible();\n this._replyTile?.notifyVisible();\n }\n\n\n get _mediaRepository() {\n return this._room.mediaRepository;\n }\n\n get permaLink() {\n return `https://matrix.to/#/${encodeURIComponent(this._room.id)}/${encodeURIComponent(this._entry.id)}`;\n }\n\n get senderProfileLink() {\n return `https://matrix.to/#/${encodeURIComponent(this.sender)}`;\n }\n\n get displayName() {\n return this._entry.displayName || this.sender;\n }\n\n get sender() {\n return this._entry.sender;\n }\n\n get memberPanelLink() {\n return `${this.urlCreator.urlUntilSegment(\"room\")}/member/${this.sender}`;\n }\n\n // Avatar view model contract\n get avatarColorNumber() {\n return getIdentifierColorNumber(this._entry.sender);\n }\n\n avatarUrl(size) {\n return getAvatarHttpUrl(this._entry.avatarUrl, size, this.platform, this._mediaRepository);\n }\n\n get avatarLetter() {\n return avatarInitials(this.sender);\n }\n\n get avatarTitle() {\n return this.displayName;\n }\n\n get date() {\n return this._date && this._date.toLocaleDateString({}, {month: \"numeric\", day: \"numeric\"});\n }\n\n get time() {\n return this._date && this._date.toLocaleTimeString({}, {hour: \"numeric\", minute: \"2-digit\"});\n }\n\n get isOwn() {\n return this._entry.sender === this._ownMember.userId;\n }\n\n get isContinuation() {\n return this._isContinuation;\n }\n\n get isUnverified() {\n return this._entry.isUnverified;\n }\n\n get isReply() {\n return this._entry.isReply;\n }\n\n _getContent() {\n return this._entry.content;\n }\n\n updatePreviousSibling(prev) {\n super.updatePreviousSibling(prev);\n let isContinuation = false;\n if (prev && prev instanceof BaseMessageTile && prev.sender === this.sender) {\n // timestamp is null for pending events\n const myTimestamp = this._entry.timestamp;\n const otherTimestamp = prev._entry.timestamp;\n // other message was sent less than 5min ago\n isContinuation = (myTimestamp - otherTimestamp) < (5 * 60 * 1000);\n }\n if (isContinuation !== this._isContinuation) {\n this._isContinuation = isContinuation;\n this.emitChange(\"isContinuation\");\n }\n }\n\n updateEntry(entry, param) {\n const action = super.updateEntry(entry, param);\n if (action.shouldUpdate) {\n this._updateReactions();\n }\n this._updateReplyTileIfNeeded(param);\n return action;\n }\n\n _updateReplyTileIfNeeded(param) {\n const replyEntry = this._entry.contextEntry;\n if (replyEntry) {\n // this is an update to contextEntry used for replyPreview\n const action = this._replyTile?.updateEntry(replyEntry, param);\n if (action?.shouldReplace || !this._replyTile) {\n this.disposeTracked(this._replyTile);\n const tileClassForEntry = this._options.tileClassForEntry;\n const ReplyTile = tileClassForEntry(replyEntry);\n if (ReplyTile) {\n this._replyTile = new ReplyTile(replyEntry, this._options);\n }\n }\n if(action?.shouldUpdate) {\n this._replyTile?.emitChange();\n }\n }\n }\n\n startReply() {\n this._roomVM.startReply(this._entry);\n }\n\n reply(msgtype, body, log = null) {\n return this._room.sendEvent(\"m.room.message\", this._entry.reply(msgtype, body), null, log);\n }\n\n redact(reason, log) {\n return this._room.sendRedaction(this._entry.id, reason, log);\n }\n\n get canRedact() {\n return this._powerLevels.canRedactFromSender(this._entry.sender);\n }\n\n get reactions() {\n if (this.shape !== \"redacted\") {\n return this._reactions;\n }\n return null;\n }\n\n get canReact() {\n return this._powerLevels.canSendType(\"m.reaction\");\n }\n\n react(key, log = null) {\n return this.logger.wrapOrRun(log, \"react\", async log => {\n if (!this.canReact) {\n log.set(\"powerlevel_lacking\", true);\n return;\n }\n if (this._entry.haveAnnotation(key)) {\n log.set(\"already_reacted\", true);\n return;\n }\n const redaction = this._entry.pendingAnnotations?.get(key)?.redactionEntry;\n if (redaction && !redaction.pendingEvent.hasStartedSending) {\n log.set(\"abort_redaction\", true);\n await redaction.pendingEvent.abort();\n } else {\n await this._room.sendEvent(\"m.reaction\", this._entry.annotate(key), null, log);\n }\n });\n }\n\n redactReaction(key, log = null) {\n return this.logger.wrapOrRun(log, \"redactReaction\", async log => {\n if (!this._powerLevels.canRedactFromSender(this._ownMember.userId)) {\n log.set(\"powerlevel_lacking\", true);\n return;\n }\n if (!this._entry.haveAnnotation(key)) {\n log.set(\"not_yet_reacted\", true);\n return;\n }\n let entry = this._entry.pendingAnnotations?.get(key)?.annotationEntry;\n if (!entry) {\n entry = await this._timeline.getOwnAnnotationEntry(this._entry.id, key);\n }\n if (entry) {\n await this._room.sendRedaction(entry.id, null, log);\n } else {\n log.set(\"no_reaction\", true);\n }\n });\n }\n\n toggleReaction(key, log = null) {\n return this.logger.wrapOrRun(log, \"toggleReaction\", async log => {\n if (this._entry.haveAnnotation(key)) {\n await this.redactReaction(key, log);\n } else {\n await this.react(key, log);\n }\n });\n }\n\n _updateReactions() {\n const {annotations, pendingAnnotations} = this._entry;\n if (!annotations && !pendingAnnotations) {\n if (this._reactions) {\n this._reactions = null;\n }\n } else {\n if (!this._reactions) {\n this._reactions = new ReactionsViewModel(this);\n }\n this._reactions.update(annotations, pendingAnnotations);\n }\n }\n\n get replyTile() {\n if (!this._entry.contextEventId) {\n return null;\n }\n return this._replyTile;\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/*\nThe regex is split into component strings;\nmeaning that any escapes (\\) must also\nbe escaped.\n*/\nconst scheme = \"(?:https|http|ftp):\\\\/\\\\/\";\nconst noSpaceNorPunctuation = \"[^\\\\s.,?!)]\";\nconst hostCharacter = \"[a-zA-Z0-9:.\\\\[\\\\]-]\";\n\n/*\nUsing non-consuming group here to combine two criteria for the last character.\nSee point 1 below.\n*/\nconst host = `${hostCharacter}*(?=${hostCharacter})${noSpaceNorPunctuation}`;\n\n/*\nUse sub groups so we accept just / or #; but if anything comes after it,\nit should not end with punctuation or space.\n*/\nconst pathOrFragment = `(?:[\\\\/#](?:[^\\\\s]*${noSpaceNorPunctuation})?)`;\n\n/*\nThings to keep in mind:\n1. URL must not contain non-ascii characters in host but may contain\n them in path or fragment components.\n https://matrix.org/<smiley> - valid\n https://matrix.org<smiley> - invalid\n2. Do not treat punctuation at the end as a part of the URL (.,?!)\n3. Path/fragment is optional.\n*/\nconst urlRegex = `${scheme}${host}${pathOrFragment}?`;\n\nexport const regex = new RegExp(urlRegex, \"gi\");\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { regex } from \"./regex.js\";\n\n/**\n * Splits text into links and non-links.\n * For each such separated token, callback is called\n * with the token and a boolean passed as argument.\n * The boolean indicates whether the token is a link or not.\n * @param {string} text Text to split\n * @param {function(string, boolean)} callback A function to call with split tokens\n */\nexport function linkify(text, callback) {\n const matches = text.matchAll(regex);\n let curr = 0;\n for (let match of matches) {\n const precedingText = text.slice(curr, match.index);\n callback(precedingText, false);\n callback(match[0], true);\n const len = match[0].length;\n curr = match.index + len;\n }\n const remainingText = text.slice(curr);\n callback(remainingText, false);\n}\n\nexport function tests() {\n\n class MockCallback {\n mockCallback(text, isLink) {\n if (!text.length) {\n return;\n }\n if (!this.result) {\n this.result = [];\n }\n const type = isLink ? \"link\" : \"text\";\n this.result.push({ type: type, text: text });\n }\n }\n\n function test(assert, input, output) {\n const m = new MockCallback;\n linkify(input, m.mockCallback.bind(m));\n assert.deepEqual(output, m.result);\n }\n\n function testLink(assert, link, expectFail = false) {\n const input = link;\n const output = expectFail ? [{ type: \"text\", text: input }] :\n [{ type: \"link\", text: input }];\n test(assert, input, output);\n }\n\n return {\n \"Link with host\": assert => {\n testLink(assert, \"https://matrix.org\");\n },\n\n \"Link with host & path\": assert => {\n testLink(assert, \"https://matrix.org/docs/develop\");\n },\n\n \"Link with host & fragment\": assert => {\n testLink(assert, \"https://matrix.org#test\");\n },\n\n \"Link with host & query\": assert => {\n testLink(assert, \"https://matrix.org/?foo=bar\");\n },\n\n \"Complex link\": assert => {\n const link = \"https://www.foobar.com/url?sa=t&rct=j&q=&esrc=s&source\" +\n \"=web&cd=&cad=rja&uact=8&ved=2ahUKEwjyu7DJ-LHwAhUQyzgGHc\" +\n \"OKA70QFjAAegQIBBAD&url=https%3A%2F%2Fmatrix.org%2Fdocs%\" +\n \"2Fprojects%2Fclient%2Felement%2F&usg=AOvVaw0xpENrPHv_R-\" +\n \"ERkyacR2Bd\";\n testLink(assert, link);\n },\n\n \"Localhost link\": assert => {\n testLink(assert, \"http://localhost\");\n testLink(assert, \"http://localhost:3000\");\n },\n\n \"IPV4 link\": assert => {\n testLink(assert, \"https://192.0.0.1\");\n testLink(assert, \"https://250.123.67.23:5924\");\n },\n\n \"IPV6 link\": assert => {\n testLink(assert, \"http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]\");\n testLink(assert, \"http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:7000\");\n },\n\n \"Missing scheme must not linkify\": assert => {\n testLink(assert, \"matrix.org/foo/bar\", true);\n },\n\n \"Punctuation at end of link must not linkify\": assert => {\n const link = \"https://foo.bar/?nenjil=lal810\";\n const end = \".,? \";\n for (const char of end) {\n const out = [{ type: \"link\", text: link }, { type: \"text\", text: char }];\n test(assert, link + char, out);\n }\n },\n\n \"Link doesn't adopt closing parenthesis\": assert => {\n const link = \"(https://matrix.org)\";\n const out = [{ type: \"text\", text: \"(\" }, { type: \"link\", text: \"https://matrix.org\" }, { type: \"text\", text: \")\" }];\n test(assert, link, out);\n },\n\n \"Unicode in hostname must not linkify\": assert => {\n const link = \"https://foo.bar\\uD83D\\uDE03.com\";\n const out = [{ type: \"link\", text: \"https://foo.bar\" },\n { type: \"text\", text: \"\\uD83D\\uDE03.com\" }];\n test(assert, link, out);\n },\n\n \"Link with unicode only after / must linkify\": assert => {\n testLink(assert, \"https://foo.bar.com/\\uD83D\\uDE03\");\n },\n\n \"Link with unicode after fragment without path must linkify\": assert => {\n testLink(assert, \"https://foo.bar.com#\\uD83D\\uDE03\");\n },\n\n \"Link ends with <\": assert => {\n const link = \"https://matrix.org<\";\n const out = [{ type: \"link\", text: \"https://matrix.org\" }, { type: \"text\", text: \"<\" }];\n test(assert, link, out);\n }\n };\n}\n","import { linkify } from \"./linkify/linkify.js\";\nimport { getIdentifierColorNumber, avatarInitials } from \"../../../avatar\";\n\n/**\n * Parse text into parts such as newline, links and text.\n * @param {string} body A string to parse into parts\n * @returns {MessageBody} Parsed result\n */\nexport function parsePlainBody(body) {\n const parts = [];\n const lines = body.split(\"\\n\");\n\n // create callback outside of loop\n const linkifyCallback = (text, isLink) => {\n if (isLink) {\n parts.push(new LinkPart(text, [new TextPart(text)]));\n } else {\n parts.push(new TextPart(text));\n }\n };\n\n for (let i = 0; i < lines.length; i += 1) {\n const line = lines[i];\n if (line.length) {\n linkify(line, linkifyCallback);\n }\n const isLastLine = i >= (lines.length - 1);\n if (!isLastLine) {\n parts.push(new NewLinePart());\n }\n }\n\n return new MessageBody(body, parts);\n}\n\nexport function stringAsBody(body) {\n return new MessageBody(body, [new TextPart(body)]);\n}\n\nexport class HeaderBlock {\n constructor(level, inlines) {\n this.level = level;\n this.inlines = inlines;\n }\n\n get type() { return \"header\"; }\n}\n\nexport class CodeBlock {\n constructor(language, text) {\n this.language = language;\n this.text = text;\n }\n\n get type() { return \"codeblock\"; }\n}\n\nexport class ListBlock {\n constructor(startOffset, items) {\n this.items = items;\n this.startOffset = startOffset;\n }\n\n get type() { return \"list\"; }\n}\n\nexport class TableBlock {\n constructor(head, body) {\n this.head = head;\n this.body = body;\n }\n\n get type() { return \"table\"; }\n}\n\nexport class RulePart {\n get type() { return \"rule\"; }\n}\n\nexport class NewLinePart {\n get type() { return \"newline\"; }\n}\n\nexport class FormatPart {\n constructor(format, children) {\n this.format = format.toLowerCase();\n this.children = children;\n }\n\n get type() { return \"format\"; }\n}\n\nexport class ImagePart {\n constructor(src, width, height, alt, title) {\n this.src = src;\n this.width = width;\n this.height = height;\n this.alt = alt;\n this.title = title;\n }\n\n get type() { return \"image\"; }\n}\n\nexport class PillPart {\n constructor(id, href, children) {\n this.id = id;\n this.href = href;\n this.children = children;\n }\n\n get type() { return \"pill\"; }\n\n get avatarColorNumber() {\n return getIdentifierColorNumber(this.id);\n }\n\n get avatarInitials() {\n return avatarInitials(this.id);\n }\n}\n\nexport class LinkPart {\n constructor(url, inlines) {\n this.url = url;\n this.inlines = inlines;\n }\n\n get type() { return \"link\"; }\n}\n\nexport class TextPart {\n constructor(text) {\n this.text = text;\n }\n\n get type() { return \"text\"; }\n}\n\nfunction isBlockquote(part){\n return part.type === \"format\" && part.format === \"blockquote\";\n}\n\nexport class MessageBody {\n constructor(sourceString, parts) {\n this.sourceString = sourceString;\n this.parts = parts;\n }\n\n insertEmote(string) {\n // We want to skip quotes introduced by replies when emoting.\n // We assume that such quotes are not TextParts, because replies\n // must have a formatted body.\n let i = 0;\n for (; i < this.parts.length && isBlockquote(this.parts[i]); i++);\n this.parts.splice(i, 0, new TextPart(string));\n }\n}\n\nexport function tests() {\n\n function test(assert, input, output) {\n assert.deepEqual(parsePlainBody(input), new MessageBody(input, output));\n }\n\n return {\n // Tests for text\n \"Text only\": assert => {\n const input = \"This is a sentence\";\n const output = [new TextPart(input)];\n test(assert, input, output);\n },\n\n \"Text with newline\": assert => {\n const input = \"This is a sentence.\\nThis is another sentence.\";\n const output = [\n new TextPart(\"This is a sentence.\"),\n new NewLinePart(),\n new TextPart(\"This is another sentence.\")\n ];\n test(assert, input, output);\n },\n\n \"Text with newline & trailing newline\": assert => {\n const input = \"This is a sentence.\\nThis is another sentence.\\n\";\n const output = [\n new TextPart(\"This is a sentence.\"),\n new NewLinePart(),\n new TextPart(\"This is another sentence.\"),\n new NewLinePart()\n ];\n test(assert, input, output);\n }\n };\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseMessageTile} from \"./BaseMessageTile.js\";\nimport {stringAsBody} from \"../MessageBody.js\";\nimport {createEnum} from \"../../../../../utils/enum\";\n\nexport const BodyFormat = createEnum(\"Plain\", \"Html\");\n\nexport class BaseTextTile extends BaseMessageTile {\n constructor(entry, options) {\n super(entry, options);\n this._messageBody = null;\n this._format = null\n }\n\n get shape() {\n return \"message\";\n }\n\n _parseBody(body) {\n return stringAsBody(body);\n }\n\n _getBodyFormat() {\n return BodyFormat.Plain;\n }\n\n get body() {\n const body = this._getBody();\n const format = this._getBodyFormat();\n // body is a string, so we can check for difference by just\n // doing an equality check\n // Even if the body hasn't changed, but the format has, we need\n // to re-fill our cache.\n if (!this._messageBody || this._messageBody.sourceString !== body || this._format !== format) {\n // body with markup is an array of parts,\n // so we should not recreate it for the same body string,\n // or else the equality check in the binding will always fail.\n // So cache it here.\n this._messageBody = this._parseBody(body, format);\n this._format = format;\n }\n return this._messageBody;\n }\n\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { MessageBody, HeaderBlock, TableBlock, ListBlock, CodeBlock, PillPart, FormatPart, NewLinePart, RulePart, TextPart, LinkPart, ImagePart } from \"./MessageBody.js\"\nimport { linkify } from \"./linkify/linkify.js\";\n\n/* At the time of writing (Jul 1 2021), Matrix Spec recommends\n * allowing the following HTML tags:\n * font, del, h1, h2, h3, h4, h5, h6, blockquote, p, a, ul, ol, sup, sub, li, b, i, u,\n * strong, em, strike, code, hr, br, div, table, thead, tbody, tr, th, td, caption, pre, span, img\n */\n\n/**\n * Nodes that don't have any properties to them other than their tag.\n * While <a> has `href`, and <img> has `src`, these have... themselves.\n */\nconst basicInline = [\"EM\", \"STRONG\", \"CODE\", \"DEL\", \"SPAN\" ];\nconst basicBlock = [\"DIV\", \"BLOCKQUOTE\"];\nconst safeSchemas = [\"https\", \"http\", \"ftp\", \"mailto\", \"magnet\"].map(name => `${name}://`);\nconst baseUrl = 'https://matrix.to';\nconst linkPrefix = `${baseUrl}/#/`;\n\nclass Deserializer {\n constructor(result, mediaRepository) {\n this.result = result;\n this.mediaRepository = mediaRepository;\n }\n\n parsePillLink(link) {\n if (!link.startsWith(linkPrefix)) {\n return null;\n }\n const contents = link.substring(linkPrefix.length);\n if (contents[0] === '@') {\n return contents;\n }\n return null;\n }\n\n parseLink(node, children) {\n const href = this.result.getAttributeValue(node, \"href\");\n const lcUrl = href?.toLowerCase();\n // urls should be absolute and with a safe schema, as listed in the spec\n if (!lcUrl || !safeSchemas.some(schema => lcUrl.startsWith(schema))) {\n return new FormatPart(\"span\", children);\n }\n const pillId = this.parsePillLink(href);\n if (pillId) {\n return new PillPart(pillId, href, children);\n }\n return new LinkPart(href, children);\n }\n\n parseList(node) {\n const result = this.result;\n let start = null;\n if (result.getNodeElementName(node) === \"OL\") {\n // Will return 1 for, say, '1A', which may not be intended?\n start = parseInt(result.getAttributeValue(node, \"start\")) || 1;\n }\n const items = [];\n for (const child of result.getChildNodes(node)) {\n if (result.getNodeElementName(child) !== \"LI\") {\n continue;\n }\n const item = this.parseAnyNodes(result.getChildNodes(child));\n items.push(item);\n }\n return new ListBlock(start, items);\n }\n\n _ensureElement(node, tag) {\n return node &&\n this.result.isElementNode(node) &&\n this.result.getNodeElementName(node) === tag;\n }\n\n parseCodeBlock(node) {\n const result = this.result;\n let codeNode;\n for (const child of result.getChildNodes(node)) {\n codeNode = child;\n break;\n }\n let language = null;\n if (!this._ensureElement(codeNode, \"CODE\")) {\n return new CodeBlock(language, this.result.getNodeText(node));\n }\n const cl = result.getAttributeValue(codeNode, \"class\") || \"\"\n for (const clname of cl.split(\" \")) {\n if (clname.startsWith(\"language-\") && !clname.startsWith(\"language-_\")) {\n language = clname.substring(9) // \"language-\".length\n break;\n }\n }\n return new CodeBlock(language, this.result.getNodeText(codeNode));\n }\n\n parseImage(node) {\n const result = this.result;\n const src = result.getAttributeValue(node, \"src\") || \"\";\n const url = this.mediaRepository.mxcUrl(src);\n // We just ignore non-mxc `src` attributes.\n if (!url) {\n return null;\n }\n const width = parseInt(result.getAttributeValue(node, \"width\")) || null;\n const height = parseInt(result.getAttributeValue(node, \"height\")) || null;\n const alt = result.getAttributeValue(node, \"alt\");\n const title = result.getAttributeValue(node, \"title\");\n return new ImagePart(url, width, height, alt, title);\n }\n\n parseTableRow(row, tag) {\n const cells = [];\n for (const node of this.result.getChildNodes(row)) {\n if(!this._ensureElement(node, tag)) {\n continue;\n }\n const children = this.result.getChildNodes(node);\n const inlines = this.parseInlineNodes(children);\n cells.push(inlines);\n }\n return cells;\n }\n\n parseTableHead(head) {\n let headRow = null;\n for (const node of this.result.getChildNodes(head)) {\n headRow = node;\n break;\n }\n if (this._ensureElement(headRow, \"TR\")) {\n return this.parseTableRow(headRow, \"TH\");\n }\n return null;\n }\n\n parseTableBody(body) {\n const rows = [];\n for (const node of this.result.getChildNodes(body)) {\n if(!this._ensureElement(node, \"TR\")) {\n continue;\n }\n rows.push(this.parseTableRow(node, \"TD\"));\n }\n return rows;\n }\n\n parseTable(node) {\n // We are only assuming iterable, so convert to arrary for indexing.\n const children = Array.from(this.result.getChildNodes(node));\n let head, body;\n if (this._ensureElement(children[0], \"THEAD\") && this._ensureElement(children[1], \"TBODY\")) {\n head = this.parseTableHead(children[0]);\n body = this.parseTableBody(children[1]);\n } else if (this._ensureElement(children[0], \"TBODY\")) {\n head = null;\n body = this.parseTableBody(children[0]);\n }\n return new TableBlock(head, body);\n }\n\n /** Once a node is known to be an element,\n * attempt to interpret it as an inline element.\n *\n * @returns the inline message part, or null if the element\n * is not inline or not allowed.\n */\n parseInlineElement(node) {\n const result = this.result;\n const tag = result.getNodeElementName(node);\n const children = result.getChildNodes(node);\n switch (tag) {\n case \"A\": {\n const inlines = this.parseInlineNodes(children);\n return this.parseLink(node, inlines);\n }\n case \"BR\":\n return new NewLinePart();\n default: {\n if (!basicInline.includes(tag)) {\n return null;\n }\n const inlines = this.parseInlineNodes(children);\n return new FormatPart(tag, inlines);\n }\n }\n }\n\n /** Attempt to interpret a node as inline.\n *\n * @returns the inline message part, or null if the\n * element is not inline or not allowed.\n */\n parseInlineNode(node) {\n if (this.result.isElementNode(node)) {\n return this.parseInlineElement(node);\n }\n return null;\n }\n\n /** Once a node is known to be an element, attempt\n * to interpret it as a block element.\n *\n * @returns the block message part, or null of the\n * element is not a block or not allowed.\n */\n parseBlockElement(node) {\n const result = this.result;\n const tag = result.getNodeElementName(node);\n const children = result.getChildNodes(node);\n switch (tag) {\n case \"H1\":\n case \"H2\":\n case \"H3\":\n case \"H4\":\n case \"H5\":\n case \"H6\": {\n const inlines = this.parseInlineNodes(children);\n return new HeaderBlock(parseInt(tag[1]), inlines)\n }\n case \"UL\":\n case \"OL\":\n return this.parseList(node);\n case \"PRE\":\n return this.parseCodeBlock(node);\n case \"HR\":\n return new RulePart();\n case \"IMG\":\n return this.parseImage(node);\n case \"P\": {\n const inlines = this.parseInlineNodes(children);\n return new FormatPart(tag, inlines);\n }\n case \"TABLE\":\n return this.parseTable(node);\n default: {\n if (!basicBlock.includes(tag)) {\n return null;\n }\n const blocks = this.parseAnyNodes(children);\n return new FormatPart(tag, blocks);\n }\n }\n }\n\n /** Attempt to parse a node as a block.\n *\n * @return the block message part, or null if the node\n * is not a block element.\n */\n parseBlockNode(node) {\n if (this.result.isElementNode(node)) {\n return this.parseBlockElement(node);\n }\n return null;\n }\n\n _parseTextParts(node, into) {\n if(!this.result.isTextNode(node)) {\n return false;\n }\n\n // XXX pretty much identical to `MessageBody`'s.\n const linkifyCallback = (text, isLink) => {\n if (isLink) {\n into.push(new LinkPart(text, [new TextPart(text)]));\n } else {\n into.push(new TextPart(text));\n }\n };\n linkify(this.result.getNodeText(node), linkifyCallback);\n return true;\n }\n\n _isAllowedNode(node) {\n return !this._ensureElement(node, \"MX-REPLY\");\n }\n\n _parseInlineNodes(nodes, into) {\n for (const htmlNode of nodes) {\n if (this._parseTextParts(htmlNode, into)) {\n // This was a text node, and we already\n // dumped its parts into our list.\n continue;\n }\n const node = this.parseInlineNode(htmlNode);\n if (node) {\n into.push(node);\n continue;\n }\n // Node is either block or unrecognized. In\n // both cases, just move on to its children.\n if (this._isAllowedNode(htmlNode)) {\n this._parseInlineNodes(this.result.getChildNodes(htmlNode), into);\n }\n }\n }\n\n parseInlineNodes(nodes) {\n const into = [];\n this._parseInlineNodes(nodes, into);\n return into;\n }\n\n // XXX very similar to `_parseInlineNodes`.\n _parseAnyNodes(nodes, into) {\n for (const htmlNode of nodes) {\n if (this._parseTextParts(htmlNode, into)) {\n // This was a text node, and we already\n // dumped its parts into our list.\n continue;\n }\n const node = this.parseInlineNode(htmlNode) || this.parseBlockNode(htmlNode);\n if (node) {\n into.push(node);\n continue;\n }\n // Node is unrecognized. Just move on to its children.\n if (this._isAllowedNode(htmlNode)) {\n this._parseAnyNodes(this.result.getChildNodes(htmlNode), into);\n }\n }\n }\n\n parseAnyNodes(nodes) {\n const into = [];\n this._parseAnyNodes(nodes, into);\n return into;\n }\n}\n\nexport function parseHTMLBody(platform, mediaRepository, html) {\n const parseResult = platform.parseHTML(html);\n const deserializer = new Deserializer(parseResult, mediaRepository);\n const parts = deserializer.parseAnyNodes(parseResult.rootNodes);\n return new MessageBody(html, parts);\n}\n\n\nexport async function tests() {\n // don't import node-html-parser until it's safe to assume we're actually in a unit test,\n // as this is a devDependency\n const nodeHtmlParser = await import(\"node-html-parser\");\n const {parse} = nodeHtmlParser.default;\n\n class HTMLParseResult {\n constructor(bodyNode) {\n this._bodyNode = bodyNode;\n }\n\n get rootNodes() {\n return this._bodyNode.childNodes;\n }\n\n getChildNodes(node) {\n return node.childNodes;\n }\n\n getAttributeNames(node) {\n return node.getAttributeNames();\n }\n\n getAttributeValue(node, attr) {\n return node.getAttribute(attr);\n }\n\n isTextNode(node) {\n return !node.tagName;\n }\n\n getNodeText(node) {\n return node.text;\n }\n\n isElementNode(node) {\n return !!node.tagName;\n }\n\n getNodeElementName(node) {\n return node.tagName;\n }\n }\n\n const platform = {\n parseHTML: (html) => new HTMLParseResult(parse(html))\n };\n\n function test(assert, input, output) {\n assert.deepEqual(parseHTMLBody(platform, null, input), new MessageBody(input, output));\n }\n\n return {\n \"Text only\": assert => {\n const input = \"This is a sentence\";\n const output = [new TextPart(input)];\n test(assert, input, output);\n },\n \"Text with inline code format\": assert => {\n const input = \"Here's <em>some</em> <code>code</code>!\";\n const output = [\n new TextPart(\"Here's \"),\n new FormatPart(\"em\", [new TextPart(\"some\")]),\n new TextPart(\" \"),\n new FormatPart(\"code\", [new TextPart(\"code\")]),\n new TextPart(\"!\")\n ];\n test(assert, input, output);\n },\n \"Text with ordered list with no attributes\": assert => {\n const input = \"<ol><li>Lorem</li><li>Ipsum</li></ol>\";\n const output = [\n new ListBlock(1, [\n [ new TextPart(\"Lorem\") ],\n [ new TextPart(\"Ipsum\") ]\n ])\n ];\n test(assert, input, output);\n },\n \"Text with ordered list starting at 3\": assert => {\n const input = '<ol start=\"3\"><li>Lorem</li><li>Ipsum</li></ol>';\n const output = [\n new ListBlock(3, [\n [ new TextPart(\"Lorem\") ],\n [ new TextPart(\"Ipsum\") ]\n ])\n ];\n test(assert, input, output);\n },\n \"Text with unordered list\": assert => {\n const input = '<ul start=\"3\"><li>Lorem</li><li>Ipsum</li></ul>';\n const output = [\n new ListBlock(null, [\n [ new TextPart(\"Lorem\") ],\n [ new TextPart(\"Ipsum\") ]\n ])\n ];\n test(assert, input, output);\n },\n \"Auto-closed tags\": assert => {\n const input = '<p>hello<p>world</p></p>';\n const output = [\n new FormatPart(\"p\", [new TextPart(\"hello\")]),\n new FormatPart(\"p\", [new TextPart(\"world\")])\n ];\n test(assert, input, output);\n },\n \"Block elements ignored inside inline elements\": assert => {\n const input = '<span><p><code>Hello</code></p></span>';\n const output = [\n new FormatPart(\"span\", [new FormatPart(\"code\", [new TextPart(\"Hello\")])])\n ];\n test(assert, input, output);\n },\n \"Unknown tags are ignored, but their children are kept\": assert => {\n const input = '<span><dfn><code>Hello</code></dfn><footer><em>World</em></footer></span>';\n const output = [\n new FormatPart(\"span\", [\n new FormatPart(\"code\", [new TextPart(\"Hello\")]),\n new FormatPart(\"em\", [new TextPart(\"World\")])\n ])\n ];\n test(assert, input, output);\n },\n \"Unknown and invalid attributes are stripped\": assert => {\n const input = '<em onmouseover=alert(\"Bad code!\")>Hello</em>';\n const output = [\n new FormatPart(\"em\", [new TextPart(\"Hello\")])\n ];\n test(assert, input, output);\n },\n \"Text with code block but no <code> tag\": assert => {\n const code = 'main :: IO ()\\nmain = putStrLn \"Hello\"'\n const input = `<pre>${code}</pre>`;\n const output = [\n new CodeBlock(null, code)\n ];\n test(assert, input, output);\n },\n \"Text with code block and 'unsupported' tag\": assert => {\n const code = '<em>Hello, world</em>'\n const input = `<pre>${code}</pre>`;\n const output = [\n new CodeBlock(null, code)\n ];\n test(assert, input, output);\n },\n \"Reply fallback is always stripped\": assert => {\n const input = 'Hello, <em><mx-reply>World</mx-reply></em>!';\n const output = [\n new TextPart('Hello, '),\n new FormatPart(\"em\", []),\n new TextPart('!'),\n ];\n assert.deepEqual(parseHTMLBody(platform, null, input), new MessageBody(input, output));\n }\n /* Doesnt work: HTML library doesn't handle <pre><code> properly.\n \"Text with code block\": assert => {\n const code = 'main :: IO ()\\nmain = putStrLn \"Hello\"'\n const input = `<pre><code>${code}</code></pre>`;\n const output = [\n new CodeBlock(null, code)\n ];\n test(assert, input, output);\n }\n */\n };\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseTextTile, BodyFormat} from \"./BaseTextTile.js\";\nimport {parsePlainBody} from \"../MessageBody.js\";\nimport {parseHTMLBody} from \"../deserialize.js\";\n\nexport class TextTile extends BaseTextTile {\n _getContentString(key) {\n return this._getContent()?.[key] || \"\";\n }\n\n _getPlainBody() {\n return this._getContentString(\"body\");\n }\n\n _getFormattedBody() {\n return this._getContentString(\"formatted_body\");\n }\n\n _getBody() {\n if (this._getBodyFormat() === BodyFormat.Html) {\n return this._getFormattedBody();\n } else {\n return this._getPlainBody();\n }\n }\n\n _getBodyFormat() {\n if (this._getContent()?.format === \"org.matrix.custom.html\") {\n return BodyFormat.Html;\n } else {\n return BodyFormat.Plain;\n }\n }\n\n _parseBody(body, format) {\n let messageBody;\n if (format === BodyFormat.Html) {\n messageBody = parseHTMLBody(this.platform, this._mediaRepository, body);\n } else {\n messageBody = parsePlainBody(body);\n }\n if (this._getContent()?.msgtype === \"m.emote\") {\n messageBody.insertEmote(`* ${this.displayName} `);\n }\n return messageBody;\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseMessageTile} from \"./BaseMessageTile.js\";\n\nexport class RedactedTile extends BaseMessageTile {\n get shape() {\n return \"redacted\";\n }\n\n get description() {\n const {redactionReason} = this._entry;\n if (this.isRedacting) {\n if (redactionReason) {\n return this.i18n`This message is being deleted (${redactionReason})…`;\n } else {\n return this.i18n`This message is being deleted…`;\n }\n } else {\n if (redactionReason) {\n return this.i18n`This message has been deleted (${redactionReason}).`;\n } else {\n return this.i18n`This message has been deleted.`;\n }\n }\n }\n\n get isRedacting() {\n return this._entry.isRedacting;\n }\n \n /** override parent property to disable redacting, even if still pending */\n get canRedact() {\n return false;\n }\n\n abortPendingRedaction() {\n return this._entry.abortPendingRedaction();\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseMessageTile} from \"./BaseMessageTile.js\";\nimport {SendStatus} from \"../../../../../matrix/room/sending/PendingEvent.js\";\nconst MAX_HEIGHT = 300;\nconst MAX_WIDTH = 400;\n\nexport class BaseMediaTile extends BaseMessageTile {\n constructor(entry, options) {\n super(entry, options);\n this._decryptedThumbnail = null;\n this._decryptedFile = null;\n this._isVisible = false;\n this._error = null;\n this._downloading = false;\n this._downloadError = null;\n }\n\n async downloadMedia() {\n if (this._downloading || this.isPending) {\n return;\n }\n const content = this._getContent();\n const filename = content.body;\n this._downloading = true;\n this.emitChange(\"status\");\n let blob;\n try {\n blob = await this._mediaRepository.downloadAttachment(content);\n this.platform.saveFileAs(blob, filename);\n } catch (err) {\n this._downloadError = err;\n } finally {\n blob?.dispose();\n this._downloading = false;\n }\n this.emitChange(\"status\");\n }\n\n get isUploading() {\n return this.isPending && this._entry.pendingEvent.status === SendStatus.UploadingAttachments;\n }\n\n get uploadPercentage() {\n const {pendingEvent} = this._entry;\n return pendingEvent && Math.round((pendingEvent.attachmentsSentBytes / pendingEvent.attachmentsTotalBytes) * 100);\n }\n\n get status() {\n const {pendingEvent} = this._entry;\n switch (pendingEvent?.status) {\n case SendStatus.Waiting:\n return this.i18n`Waiting…`;\n case SendStatus.EncryptingAttachments:\n case SendStatus.Encrypting:\n return this.i18n`Encrypting…`;\n case SendStatus.UploadingAttachments:\n return this.i18n`Uploading…`;\n case SendStatus.Sending:\n return this.i18n`Sending…`;\n case SendStatus.Error:\n return this.i18n`Error: ${pendingEvent.error.message}`;\n default:\n if (this._downloadError) {\n return `Download failed`;\n }\n if (this._downloading) {\n return this.i18n`Downloading…`;\n }\n return \"\";\n }\n }\n\n get thumbnailUrl() {\n if (!this._isVisible) {\n return \"\";\n }\n if (this._decryptedThumbnail) {\n return this._decryptedThumbnail.url;\n } else {\n const thumbnailMxc = this._getContent().info?.thumbnail_url;\n if (thumbnailMxc) {\n return this._mediaRepository.mxcUrlThumbnail(thumbnailMxc, this.width, this.height, \"scale\");\n }\n }\n if (this._entry.isPending) {\n const attachment = this._entry.pendingEvent.getAttachment(\"info.thumbnail_url\");\n return attachment && attachment.localPreview.url;\n }\n if (this._isMainResourceImage()) {\n if (this._decryptedFile) {\n return this._decryptedFile.url;\n } else {\n const mxcUrl = this._getContent()?.url;\n if (typeof mxcUrl === \"string\") {\n return this._mediaRepository.mxcUrlThumbnail(mxcUrl, this.width, this.height, \"scale\");\n }\n }\n }\n return \"\";\n }\n\n notifyVisible() {\n super.notifyVisible();\n this._isVisible = true;\n this.emitChange(\"thumbnailUrl\");\n if (!this.isPending) {\n this._tryLoadEncryptedThumbnail();\n }\n }\n\n get width() {\n const info = this._getContent()?.info;\n return Math.round(info?.w * this._scaleFactor());\n }\n\n get height() {\n const info = this._getContent()?.info;\n return Math.round(info?.h * this._scaleFactor());\n }\n\n get mimeType() {\n const info = this._getContent()?.info;\n return info?.mimetype;\n }\n\n get label() {\n return this._getContent().body;\n }\n\n get error() {\n if (this._error) {\n return `Could not load media: ${this._error.message}`;\n }\n return null;\n }\n\n setViewError(err) {\n this._error = err;\n this.emitChange(\"error\");\n }\n\n async _loadEncryptedFile(file) {\n const blob = await this._mediaRepository.downloadEncryptedFile(file, true);\n if (this.isDisposed) {\n blob.dispose();\n return;\n }\n return this.track(blob);\n }\n\n async _tryLoadEncryptedThumbnail() {\n try {\n const thumbnailFile = this._getContent().info?.thumbnail_file;\n const file = this._getContent().file;\n if (thumbnailFile) {\n this._decryptedThumbnail = await this._loadEncryptedFile(thumbnailFile);\n this.emitChange(\"thumbnailUrl\");\n } else if (file && this._isMainResourceImage()) { // is the main resource an image? then try that for a thumbnail\n this._decryptedFile = await this._loadEncryptedFile(file);\n this.emitChange(\"thumbnailUrl\");\n }\n } catch (err) {\n this._error = err;\n this.emitChange(\"error\");\n }\n }\n\n _scaleFactor() {\n const info = this._getContent()?.info;\n const scaleHeightFactor = MAX_HEIGHT / info?.h;\n const scaleWidthFactor = MAX_WIDTH / info?.w;\n // take the smallest scale factor, to respect all constraints\n // we should not upscale images, so limit scale factor to 1 upwards\n return Math.min(scaleWidthFactor, scaleHeightFactor, 1);\n }\n\n _isMainResourceImage() {\n return true; // overwritten in VideoTile\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseMediaTile} from \"./BaseMediaTile.js\";\n\nexport class ImageTile extends BaseMediaTile {\n constructor(entry, options) {\n super(entry, options);\n this._lightboxUrl = this.urlCreator.urlForSegments([\n // ensure the right room is active if in grid view\n this.navigation.segment(\"room\", this._room.id),\n this.navigation.segment(\"lightbox\", this._entry.id)\n ]);\n }\n\n get lightboxUrl() {\n if (!this.isPending) {\n return this._lightboxUrl;\n }\n return \"\";\n }\n\n get shape() {\n return \"image\";\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseMediaTile} from \"./BaseMediaTile.js\";\n\nexport class VideoTile extends BaseMediaTile {\n async loadVideo() {\n const file = this._getContent().file;\n if (file && !this._decryptedFile) {\n this._decryptedFile = await this._loadEncryptedFile(file);\n this.emitChange(\"videoUrl\");\n }\n }\n\n get videoUrl() {\n if (this._decryptedFile) {\n return this._decryptedFile.url;\n }\n const mxcUrl = this._getContent()?.url;\n if (typeof mxcUrl === \"string\") {\n return this._mediaRepository.mxcUrl(mxcUrl);\n }\n return \"\";\n }\n\n get shape() {\n return \"video\";\n }\n\n _isMainResourceImage() {\n return false;\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n\nexport function formatSize(size: number, decimals: number = 2): string {\n if (Number.isSafeInteger(size)) {\n const base = Math.min(3, Math.floor(Math.log(size) / Math.log(1024)));\n const formattedSize = Math.round(size / Math.pow(1024, base)).toFixed(decimals);\n switch (base) {\n case 0: return `${formattedSize} bytes`;\n case 1: return `${formattedSize} KB`;\n case 2: return `${formattedSize} MB`;\n case 3: return `${formattedSize} GB`;\n }\n }\n return \"\";\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseMessageTile} from \"./BaseMessageTile.js\";\nimport {formatSize} from \"../../../../../utils/formatSize\";\nimport {SendStatus} from \"../../../../../matrix/room/sending/PendingEvent.js\";\n\nexport class FileTile extends BaseMessageTile {\n constructor(entry, options) {\n super(entry, options);\n this._downloadError = null;\n this._downloading = false;\n }\n\n async download() {\n if (this._downloading || this.isPending) {\n return;\n }\n const content = this._getContent();\n const filename = content.body;\n this._downloading = true;\n this.emitChange(\"label\");\n let blob;\n try {\n blob = await this._mediaRepository.downloadAttachment(content);\n this.platform.saveFileAs(blob, filename);\n } catch (err) {\n this._downloadError = err;\n } finally {\n blob?.dispose();\n this._downloading = false;\n }\n this.emitChange(\"label\");\n }\n\n get label() {\n if (this._downloadError) {\n return `Could not download file: ${this._downloadError.message}`;\n }\n const content = this._getContent();\n const filename = content.body;\n\n if (this._entry.isPending) {\n const {pendingEvent} = this._entry;\n switch (pendingEvent?.status) {\n case SendStatus.Waiting:\n return this.i18n`Waiting to send ${filename}…`;\n case SendStatus.EncryptingAttachments:\n case SendStatus.Encrypting:\n return this.i18n`Encrypting ${filename}…`;\n case SendStatus.UploadingAttachments:{\n const percent = Math.round((pendingEvent.attachmentsSentBytes / pendingEvent.attachmentsTotalBytes) * 100);\n return this.i18n`Uploading ${filename}: ${percent}%`;\n }\n case SendStatus.Sending:\n case SendStatus.Sent:\n return this.i18n`Sending ${filename}…`;\n case SendStatus.Error:\n return this.i18n`Error: could not send ${filename}: ${pendingEvent.error.message}`;\n default:\n return `Unknown send status for ${filename}`;\n }\n } else {\n const size = formatSize(this._getContent().info?.size);\n if (this._downloading) {\n return this.i18n`Downloading ${filename} (${size})…`;\n } else {\n return this.i18n`Download ${filename} (${size})`;\n } \n }\n }\n\n get shape() {\n return \"file\";\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseMessageTile} from \"./BaseMessageTile.js\";\n\nexport class LocationTile extends BaseMessageTile {\n get shape() {\n return \"location\";\n }\n\n get mapsLink() {\n try {\n const url = new URL(this._getContent().geo_uri);\n if (url.protocol !== \"geo:\") {\n return \"\";\n }\n const [locationStr, ...namedParams] = url.pathname.split(\";\");\n const [latStr, longStr] = locationStr.split(\",\");\n const lat = parseFloat(latStr);\n const long = parseFloat(longStr);\n let uncertainty;\n for (const namedParam of namedParams) {\n const [name, value] = namedParam.split(\"=\");\n if (name === \"u\") {\n uncertainty = parseFloat(value);\n }\n }\n if (this.platform.isIOS) {\n return `http://maps.apple.com/?ll=${lat},${long}`;\n } else {\n let uri = `geo:${lat},${long}`;\n if (uncertainty) {\n uri = uri + `;u=${uncertainty}`;\n }\n return uri;\n }\n } catch {\n return \"\";\n }\n }\n\n get label() {\n return this.i18n`${this.displayName} sent their location`;\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {SimpleTile} from \"./SimpleTile.js\";\n\nexport class RoomNameTile extends SimpleTile {\n \n get shape() {\n return \"announcement\";\n }\n\n get announcement() {\n const content = this._entry.content;\n return `${this._entry.displayName || this._entry.sender} named the room \"${content?.name}\"`\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {SimpleTile} from \"./SimpleTile.js\";\n\nexport class RoomMemberTile extends SimpleTile {\n\n get shape() {\n return \"announcement\";\n }\n\n get announcement() {\n const {sender, content, prevContent, stateKey} = this._entry;\n const senderName = this._entry.displayName || sender;\n const targetName = sender === stateKey ? senderName : (this._entry.content?.displayname || stateKey);\n const membership = content && content.membership;\n const prevMembership = prevContent && prevContent.membership;\n\n if (prevMembership === \"join\" && membership === \"join\") {\n if (content.avatar_url !== prevContent.avatar_url) {\n return `${senderName} changed their avatar`; \n } else if (content.displayname !== prevContent.displayname) {\n if (!content.displayname) {\n return `${stateKey} removed their name (${prevContent.displayname})`;\n }\n return `${prevContent.displayname ?? stateKey} changed their name to ${content.displayname}`; \n }\n } else if (membership === \"join\") {\n return `${targetName} joined the room`;\n } else if (membership === \"invite\") {\n return `${targetName} was invited to the room by ${senderName}`;\n } else if (prevMembership === \"invite\") {\n if (membership === \"join\") {\n return `${targetName} accepted the invitation to join the room`;\n } else if (membership === \"leave\") {\n return `${targetName} declined the invitation to join the room`;\n }\n } else if (membership === \"leave\") {\n if (stateKey === sender) {\n return `${targetName} left the room`;\n } else {\n const reason = content.reason;\n return `${targetName} was kicked from the room by ${senderName}${reason ? `: ${reason}` : \"\"}`;\n }\n } else if (membership === \"ban\") {\n return `${targetName} was banned from the room by ${senderName}`;\n }\n \n return `${sender} membership changed to ${content.membership}`;\n }\n}\n\nexport function tests() {\n return {\n \"user removes display name\": (assert) => {\n const tile = new RoomMemberTile(\n {\n prevContent: {displayname: \"foo\", membership: \"join\"},\n content: {membership: \"join\"},\n stateKey: \"foo@bar.com\",\n },\n {}\n );\n assert.strictEqual(tile.announcement, \"foo@bar.com removed their name (foo)\");\n },\n \"user without display name sets a new display name\": (assert) => {\n const tile = new RoomMemberTile(\n {\n prevContent: {membership: \"join\"},\n content: {displayname: \"foo\", membership: \"join\" },\n stateKey: \"foo@bar.com\",\n },\n {}\n );\n assert.strictEqual(tile.announcement, \"foo@bar.com changed their name to foo\");\n },\n };\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseTextTile} from \"./BaseTextTile.js\";\nimport {UpdateAction} from \"../UpdateAction.js\";\n\nexport class EncryptedEventTile extends BaseTextTile {\n updateEntry(entry, params) {\n const parentResult = super.updateEntry(entry, params);\n // event got decrypted, recreate the tile and replace this one with it\n if (entry.eventType !== \"m.room.encrypted\") {\n // the \"shape\" parameter trigger tile recreation in TimelineView\n return UpdateAction.Replace(\"shape\");\n } else {\n return parentResult;\n }\n }\n\n get shape() {\n return \"message-status\"\n }\n\n _getBody() {\n const decryptionError = this._entry.decryptionError;\n const code = decryptionError?.code;\n let string;\n if (code === \"MEGOLM_NO_SESSION\") {\n string = this.i18n`The sender hasn't sent us the key for this message yet.`;\n } else {\n string = decryptionError?.message || this.i18n`Could not decrypt message because of unknown reason.`;\n }\n return string;\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {SimpleTile} from \"./SimpleTile.js\";\n\nexport class EncryptionEnabledTile extends SimpleTile {\n get shape() {\n return \"announcement\";\n }\n\n get announcement() {\n const senderName = this._entry.displayName || this._entry.sender;\n return this.i18n`${senderName} has enabled end-to-end encryption`;\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseMessageTile} from \"./BaseMessageTile.js\";\n\nexport class MissingAttachmentTile extends BaseMessageTile {\n get shape() {\n return \"missing-attachment\"\n }\n\n get label() {\n const name = this._getContent().body;\n const msgtype = this._getContent().msgtype;\n if (msgtype === \"m.image\") {\n return this.i18n`The image ${name} wasn't fully sent previously and could not be recovered.`;\n } else {\n return this.i18n`The file ${name} wasn't fully sent previously and could not be recovered.`;\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {GapTile} from \"./GapTile.js\";\nimport {TextTile} from \"./TextTile.js\";\nimport {RedactedTile} from \"./RedactedTile.js\";\nimport {ImageTile} from \"./ImageTile.js\";\nimport {VideoTile} from \"./VideoTile.js\";\nimport {FileTile} from \"./FileTile.js\";\nimport {LocationTile} from \"./LocationTile.js\";\nimport {RoomNameTile} from \"./RoomNameTile.js\";\nimport {RoomMemberTile} from \"./RoomMemberTile.js\";\nimport {EncryptedEventTile} from \"./EncryptedEventTile.js\";\nimport {EncryptionEnabledTile} from \"./EncryptionEnabledTile.js\";\nimport {MissingAttachmentTile} from \"./MissingAttachmentTile.js\";\n\nimport type {SimpleTile} from \"./SimpleTile.js\";\nimport type {Room} from \"../../../../../matrix/room/Room\";\nimport type {Timeline} from \"../../../../../matrix/room/timeline/Timeline\";\nimport type {FragmentBoundaryEntry} from \"../../../../../matrix/room/timeline/entries/FragmentBoundaryEntry\";\nimport type {EventEntry} from \"../../../../../matrix/room/timeline/entries/EventEntry\";\nimport type {PendingEventEntry} from \"../../../../../matrix/room/timeline/entries/PendingEventEntry\";\nimport type {Options as ViewModelOptions} from \"../../../../ViewModel\";\n\nexport type TimelineEntry = FragmentBoundaryEntry | EventEntry | PendingEventEntry;\nexport type TileClassForEntryFn = (entry: TimelineEntry) => TileConstructor | undefined;\nexport type Options = ViewModelOptions & {\n room: Room,\n timeline: Timeline\n tileClassForEntry: TileClassForEntryFn;\n};\nexport type TileConstructor = new (entry: TimelineEntry, options: Options) => SimpleTile;\n\nexport function tileClassForEntry(entry: TimelineEntry): TileConstructor | undefined {\n if (entry.isGap) {\n return GapTile;\n } else if (entry.isPending && entry.pendingEvent.isMissingAttachments) {\n return MissingAttachmentTile;\n } else if (entry.eventType) {\n switch (entry.eventType) {\n case \"m.room.message\": {\n if (entry.isRedacted) {\n return RedactedTile;\n }\n const content = entry.content;\n const msgtype = content && content.msgtype;\n switch (msgtype) {\n case \"m.text\":\n case \"m.notice\":\n case \"m.emote\":\n return TextTile;\n case \"m.image\":\n return ImageTile;\n case \"m.video\":\n return VideoTile;\n case \"m.file\":\n return FileTile;\n case \"m.location\":\n return LocationTile;\n default:\n // unknown msgtype not rendered\n return undefined;\n }\n }\n case \"m.room.name\":\n return RoomNameTile;\n case \"m.room.member\":\n return RoomMemberTile;\n case \"m.room.encrypted\":\n if (entry.isRedacted) {\n return RedactedTile;\n }\n return EncryptedEventTile;\n case \"m.room.encryption\":\n return EncryptionEnabledTile;\n default:\n // unknown type not rendered\n return undefined;\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TimelineViewModel} from \"./timeline/TimelineViewModel.js\";\nimport {ComposerViewModel} from \"./ComposerViewModel.js\"\nimport {avatarInitials, getIdentifierColorNumber, getAvatarHttpUrl} from \"../../avatar\";\nimport {ViewModel} from \"../../ViewModel\";\nimport {imageToInfo} from \"../common.js\";\n// TODO: remove fallback so default isn't included in bundle for SDK users that have their custom tileClassForEntry\n// this is a breaking SDK change though to make this option mandatory\nimport {tileClassForEntry as defaultTileClassForEntry} from \"./timeline/tiles/index\";\nimport {RoomStatus} from \"../../../matrix/room/common\";\n\nexport class RoomViewModel extends ViewModel {\n constructor(options) {\n super(options);\n const {room, tileClassForEntry} = options;\n this._room = room;\n this._timelineVM = null;\n this._tileClassForEntry = tileClassForEntry ?? defaultTileClassForEntry;\n this._tileOptions = undefined;\n this._onRoomChange = this._onRoomChange.bind(this);\n this._timelineError = null;\n this._sendError = null;\n this._composerVM = null;\n if (room.isArchived) {\n this._composerVM = this.track(new ArchivedViewModel(this.childOptions({archivedRoom: room})));\n } else {\n this._recreateComposerOnPowerLevelChange();\n }\n this._clearUnreadTimout = null;\n this._closeUrl = this.urlCreator.urlUntilSegment(\"session\");\n }\n\n async load() {\n this._room.on(\"change\", this._onRoomChange);\n try {\n const timeline = await this._room.openTimeline();\n this._tileOptions = this.childOptions({\n roomVM: this,\n timeline,\n tileClassForEntry: this._tileClassForEntry,\n });\n this._timelineVM = this.track(new TimelineViewModel(this.childOptions({\n tileOptions: this._tileOptions,\n timeline,\n })));\n this.emitChange(\"timelineViewModel\");\n } catch (err) {\n console.error(`room.openTimeline(): ${err.message}:\\n${err.stack}`);\n this._timelineError = err;\n this.emitChange(\"error\");\n }\n this._clearUnreadAfterDelay();\n }\n\n async _recreateComposerOnPowerLevelChange() {\n const powerLevelObservable = await this._room.observePowerLevels();\n const canSendMessage = () => powerLevelObservable.get().canSendType(\"m.room.message\");\n let oldCanSendMessage = canSendMessage();\n const recreateComposer = newCanSendMessage => {\n this._composerVM = this.disposeTracked(this._composerVM);\n if (newCanSendMessage) {\n this._composerVM = this.track(new ComposerViewModel(this));\n }\n else {\n this._composerVM = this.track(new LowerPowerLevelViewModel(this.childOptions()));\n }\n this.emitChange(\"powerLevelObservable\")\n };\n this.track(powerLevelObservable.subscribe(() => {\n const newCanSendMessage = canSendMessage();\n if (oldCanSendMessage !== newCanSendMessage) {\n recreateComposer(newCanSendMessage);\n oldCanSendMessage = newCanSendMessage;\n }\n }));\n recreateComposer(oldCanSendMessage);\n }\n\n async _clearUnreadAfterDelay() {\n if (this._room.isArchived || this._clearUnreadTimout) {\n return;\n }\n this._clearUnreadTimout = this.clock.createTimeout(2000);\n try {\n await this._clearUnreadTimout.elapsed();\n await this._room.clearUnread();\n this._clearUnreadTimout = null;\n } catch (err) {\n if (err.name !== \"AbortError\") {\n throw err;\n }\n }\n }\n\n focus() {\n this._clearUnreadAfterDelay();\n }\n\n dispose() {\n super.dispose();\n this._room.off(\"change\", this._onRoomChange);\n if (this._room.isArchived) {\n this._room.release();\n }\n if (this._clearUnreadTimout) {\n this._clearUnreadTimout.abort();\n this._clearUnreadTimout = null;\n }\n }\n\n // room doesn't tell us yet which fields changed,\n // so emit all fields originating from summary\n _onRoomChange() {\n // propagate the update to the child view models so it's bindings can update based on room changes\n this._composerVM.emitChange();\n this.emitChange();\n }\n\n get kind() { return \"room\"; }\n get closeUrl() { return this._closeUrl; }\n get name() { return this._room.name || this.i18n`Empty Room`; }\n get id() { return this._room.id; }\n get timelineViewModel() { return this._timelineVM; }\n get isEncrypted() { return this._room.isEncrypted; }\n\n get error() {\n if (this._timelineError) {\n return `Something went wrong loading the timeline: ${this._timelineError.message}`;\n }\n if (this._sendError) {\n return `Something went wrong sending your message: ${this._sendError.message}`;\n }\n return \"\";\n }\n\n get avatarLetter() {\n return avatarInitials(this.name);\n }\n\n get avatarColorNumber() {\n return getIdentifierColorNumber(this._room.avatarColorId)\n }\n\n avatarUrl(size) {\n return getAvatarHttpUrl(this._room.avatarUrl, size, this.platform, this._room.mediaRepository);\n }\n\n get avatarTitle() {\n return this.name;\n }\n\n get canLeave() {\n return this._room.isJoined;\n }\n\n leaveRoom() {\n this._room.leave();\n }\n\n get canForget() {\n return this._room.isArchived;\n }\n\n forgetRoom() {\n this._room.forget();\n }\n\n get canRejoin() {\n return this._room.isArchived;\n }\n\n rejoinRoom() {\n this._room.join();\n }\n\n _createTile(entry) {\n if (this._tileOptions) {\n const Tile = this._tileOptions.tileClassForEntry(entry);\n if (Tile) {\n return new Tile(entry, this._tileOptions);\n }\n }\n }\n \n async _processCommandJoin(roomName) {\n try {\n const roomId = await this._options.client.session.joinRoom(roomName);\n const roomStatusObserver = await this._options.client.session.observeRoomStatus(roomId);\n await roomStatusObserver.waitFor(status => status === RoomStatus.Joined);\n this.navigation.push(\"room\", roomId);\n } catch (err) {\n let exc;\n if ((err.statusCode ?? err.status) === 400) {\n exc = new Error(`/join : '${roomName}' was not legal room ID or room alias`);\n } else if ((err.statusCode ?? err.status) === 404 || (err.statusCode ?? err.status) === 502 || err.message == \"Internal Server Error\") {\n exc = new Error(`/join : room '${roomName}' not found`);\n } else if ((err.statusCode ?? err.status) === 403) {\n exc = new Error(`/join : you're not invited to join '${roomName}'`);\n } else {\n exc = err;\n }\n this._sendError = exc;\n this._timelineError = null;\n this.emitChange(\"error\");\n }\n } \n\n async _processCommand (message) {\n let msgtype;\n const [commandName, ...args] = message.substring(1).split(\" \");\n switch (commandName) {\n case \"me\":\n message = args.join(\" \");\n msgtype = \"m.emote\";\n break;\n case \"join\":\n if (args.length === 1) {\n const roomName = args[0];\n await this._processCommandJoin(roomName);\n } else {\n this._sendError = new Error(\"join syntax: /join <room-id>\");\n this._timelineError = null;\n this.emitChange(\"error\");\n }\n break;\n case \"shrug\":\n message = \"¯\\\\_(ツ)_/¯ \" + args.join(\" \");\n msgtype = \"m.text\";\n break;\n case \"tableflip\":\n message = \"(╯°□°)╯︵ ┻━┻ \" + args.join(\" \");\n msgtype = \"m.text\";\n break;\n case \"unflip\":\n message = \"┬──┬ ( ゜-゜ノ) \" + args.join(\" \");\n msgtype = \"m.text\";\n break;\n case \"lenny\":\n message = \"( ͡° ͜ʖ ͡°) \" + args.join(\" \");\n msgtype = \"m.text\";\n break;\n default:\n this._sendError = new Error(`no command name \"${commandName}\". To send the message instead of executing, please type \"/${message}\"`);\n this._timelineError = null;\n this.emitChange(\"error\");\n message = undefined;\n }\n return {type: msgtype, message: message};\n }\n \n async _sendMessage(message, replyingTo) {\n if (!this._room.isArchived && message) {\n let messinfo = {type : \"m.text\", message : message};\n if (message.startsWith(\"//\")) {\n messinfo.message = message.substring(1).trim();\n } else if (message.startsWith(\"/\")) {\n messinfo = await this._processCommand(message);\n }\n try {\n const msgtype = messinfo.type;\n const message = messinfo.message;\n if (msgtype && message) {\n if (replyingTo) {\n await replyingTo.reply(msgtype, message);\n } else {\n await this._room.sendEvent(\"m.room.message\", {msgtype, body: message});\n }\n }\n } catch (err) {\n console.error(`room.sendMessage(): ${err.message}:\\n${err.stack}`);\n this._sendError = err;\n this._timelineError = null;\n this.emitChange(\"error\");\n return false;\n }\n return true;\n }\n return false;\n }\n\n async _pickAndSendFile() {\n try {\n const file = await this.platform.openFile();\n if (!file) {\n return;\n }\n return this._sendFile(file);\n } catch (err) {\n console.error(err);\n }\n }\n\n async _sendFile(file) {\n const content = {\n body: file.name,\n msgtype: \"m.file\"\n };\n await this._room.sendEvent(\"m.room.message\", content, {\n \"url\": this._room.createAttachment(file.blob, file.name)\n });\n }\n\n async _pickAndSendVideo() {\n try {\n if (!this.platform.hasReadPixelPermission()) {\n alert(\"Please allow canvas image data access, so we can scale your images down.\");\n return;\n }\n const file = await this.platform.openFile(\"video/*\");\n if (!file) {\n return;\n }\n if (!file.blob.mimeType.startsWith(\"video/\")) {\n return this._sendFile(file);\n }\n let video;\n try {\n video = await this.platform.loadVideo(file.blob);\n } catch (err) {\n // TODO: extract platform dependent code from view model\n if (err instanceof window.MediaError && err.code === 4) {\n throw new Error(`this browser does not support videos of type ${file?.blob.mimeType}.`);\n } else {\n throw err;\n }\n }\n const content = {\n body: file.name,\n msgtype: \"m.video\",\n info: videoToInfo(video)\n };\n const attachments = {\n \"url\": this._room.createAttachment(video.blob, file.name),\n };\n\n const limit = await this.platform.settingsStorage.getInt(\"sentImageSizeLimit\");\n const maxDimension = limit || Math.min(video.maxDimension, 800);\n const thumbnail = await video.scale(maxDimension);\n content.info.thumbnail_info = imageToInfo(thumbnail);\n attachments[\"info.thumbnail_url\"] = \n this._room.createAttachment(thumbnail.blob, file.name);\n await this._room.sendEvent(\"m.room.message\", content, attachments);\n } catch (err) {\n this._sendError = err;\n this.emitChange(\"error\");\n console.error(err.stack);\n }\n }\n\n async _pickAndSendPicture() {\n try {\n if (!this.platform.hasReadPixelPermission()) {\n alert(\"Please allow canvas image data access, so we can scale your images down.\");\n return;\n }\n const file = await this.platform.openFile(\"image/*\");\n if (!file) {\n return;\n }\n if (!file.blob.mimeType.startsWith(\"image/\")) {\n return this._sendFile(file);\n }\n let image = await this.platform.loadImage(file.blob);\n const limit = await this.platform.settingsStorage.getInt(\"sentImageSizeLimit\");\n if (limit && image.maxDimension > limit) {\n const scaledImage = await image.scale(limit);\n image.dispose();\n image = scaledImage;\n }\n const content = {\n body: file.name,\n msgtype: \"m.image\",\n info: imageToInfo(image)\n };\n const attachments = {\n \"url\": this._room.createAttachment(image.blob, file.name),\n };\n if (image.maxDimension > 600) {\n const thumbnail = await image.scale(400);\n content.info.thumbnail_info = imageToInfo(thumbnail);\n attachments[\"info.thumbnail_url\"] = \n this._room.createAttachment(thumbnail.blob, file.name);\n }\n await this._room.sendEvent(\"m.room.message\", content, attachments);\n } catch (err) {\n this._sendError = err;\n this.emitChange(\"error\");\n console.error(err.stack);\n }\n }\n\n get room() {\n return this._room;\n }\n\n get composerViewModel() {\n return this._composerVM;\n }\n\n openDetailsPanel() {\n let path = this.navigation.path.until(\"room\");\n path = path.with(this.navigation.segment(\"right-panel\", true));\n path = path.with(this.navigation.segment(\"details\", true));\n this.navigation.applyPath(path);\n }\n\n startReply(entry) {\n if (!this._room.isArchived) {\n this._composerVM.setReplyingTo(entry);\n }\n }\n \n dismissError() {\n this._sendError = null;\n this.emitChange(\"error\");\n }\n}\n\nfunction videoToInfo(video) {\n const info = imageToInfo(video);\n info.duration = video.duration;\n return info;\n}\n\nclass ArchivedViewModel extends ViewModel {\n constructor(options) {\n super(options);\n this._archivedRoom = options.archivedRoom;\n }\n\n get description() {\n if (this._archivedRoom.isKicked) {\n if (this._archivedRoom.kickReason) {\n return this.i18n`You were kicked from the room by ${this._archivedRoom.kickedBy.name} because: ${this._archivedRoom.kickReason}`;\n } else {\n return this.i18n`You were kicked from the room by ${this._archivedRoom.kickedBy.name}.`;\n }\n } else if (this._archivedRoom.isBanned) {\n if (this._archivedRoom.kickReason) {\n return this.i18n`You were banned from the room by ${this._archivedRoom.kickedBy.name} because: ${this._archivedRoom.kickReason}`;\n } else {\n return this.i18n`You were banned from the room by ${this._archivedRoom.kickedBy.name}.`;\n }\n } else {\n return this.i18n`You left this room`;\n }\n }\n\n get kind() {\n return \"disabled\";\n }\n}\n\nclass LowerPowerLevelViewModel extends ViewModel {\n get description() {\n return this.i18n`You do not have the powerlevel necessary to send messages`;\n }\n\n get kind() {\n return \"disabled\";\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ViewModel} from \"../../ViewModel\";\n\nexport class UnknownRoomViewModel extends ViewModel {\n constructor(options) {\n super(options);\n const {roomIdOrAlias, session} = options;\n this._session = session;\n this.roomIdOrAlias = roomIdOrAlias;\n this._error = null;\n this._busy = false;\n }\n\n get error() {\n return this._error?.message;\n }\n\n async join() {\n this._busy = true;\n this.emitChange(\"busy\");\n try {\n const roomId = await this._session.joinRoom(this.roomIdOrAlias);\n // navigate to roomId if we were at the alias\n // so we're subscribed to the right room status\n // and we'll switch to the room view model once\n // the join is synced\n this.navigation.push(\"room\", roomId);\n // keep busy on true while waiting for the join to sync\n } catch (err) {\n this._error = err;\n this._busy = false;\n this.emitChange(\"error\");\n }\n }\n\n get busy() {\n return this._busy;\n }\n\n get kind() {\n return \"unknown\";\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020, 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {avatarInitials, getIdentifierColorNumber, getAvatarHttpUrl} from \"../../avatar\";\nimport {ViewModel} from \"../../ViewModel\";\n\nexport class InviteViewModel extends ViewModel {\n constructor(options) {\n super(options);\n const {invite, mediaRepository} = options;\n this._invite = invite;\n this._mediaRepository = mediaRepository;\n this._onInviteChange = this._onInviteChange.bind(this);\n this._error = null;\n this._closeUrl = this.urlCreator.urlUntilSegment(\"session\");\n this._invite.on(\"change\", this._onInviteChange);\n this._inviter = null;\n if (this._invite.inviter) {\n this._inviter = new RoomMemberViewModel(this._invite.inviter, mediaRepository, this.platform);\n }\n this._roomDescription = this._createRoomDescription();\n }\n\n get kind() { return \"invite\"; }\n get closeUrl() { return this._closeUrl; }\n get name() { return this._invite.name; }\n get id() { return this._invite.id; }\n get isEncrypted() { return this._invite.isEncrypted; }\n get isDirectMessage() { return this._invite.isDirectMessage; }\n get inviter() { return this._inviter; }\n get busy() { return this._invite.accepting || this._invite.rejecting; }\n\n get error() {\n if (this._error) {\n return `Something went wrong: ${this._error.message}`;\n }\n return \"\";\n }\n\n get avatarLetter() {\n return avatarInitials(this.name);\n }\n\n get avatarColorNumber() {\n return getIdentifierColorNumber(this._invite.avatarColorId)\n }\n\n avatarUrl(size) {\n return getAvatarHttpUrl(this._invite.avatarUrl, size, this.platform, this._mediaRepository);\n }\n\n _createRoomDescription() {\n const parts = [];\n if (this._invite.isPublic) {\n parts.push(\"Public room\");\n } else {\n parts.push(\"Private room\");\n }\n\n if (this._invite.canonicalAlias) {\n parts.push(this._invite.canonicalAlias);\n }\n return parts.join(\" • \")\n }\n\n get roomDescription() {\n return this._roomDescription;\n }\n\n get avatarTitle() {\n return this.name;\n }\n\n focus() {}\n\n async accept() {\n try {\n await this._invite.accept();\n } catch (err) {\n this._error = err;\n this.emitChange(\"error\");\n }\n }\n\n async reject() {\n try {\n await this._invite.reject();\n } catch (err) {\n this._error = err;\n this.emitChange(\"error\");\n }\n }\n\n _onInviteChange() {\n this.emitChange();\n }\n\n dispose() {\n super.dispose();\n this._invite.off(\"change\", this._onInviteChange);\n }\n}\n\nclass RoomMemberViewModel {\n constructor(member, mediaRepository, platform) {\n this._member = member;\n this._mediaRepository = mediaRepository;\n this._platform = platform;\n }\n\n get id() {\n return this._member.userId;\n }\n\n get name() {\n return this._member.name;\n }\n\n get avatarLetter() {\n return avatarInitials(this.name);\n }\n\n get avatarColorNumber() {\n return getIdentifierColorNumber(this._member.userId);\n }\n\n avatarUrl(size) {\n return getAvatarHttpUrl(this._member.avatarUrl, size, this._platform, this._mediaRepository);\n }\n\n get avatarTitle() {\n return this.name;\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020, 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {avatarInitials, getIdentifierColorNumber, getAvatarHttpUrl} from \"../../avatar\";\nimport {ViewModel} from \"../../ViewModel\";\n\nexport class RoomBeingCreatedViewModel extends ViewModel {\n constructor(options) {\n super(options);\n const {roomBeingCreated, mediaRepository} = options;\n this._roomBeingCreated = roomBeingCreated;\n this._mediaRepository = mediaRepository;\n this._onRoomChange = this._onRoomChange.bind(this);\n this._closeUrl = this.urlCreator.urlUntilSegment(\"session\");\n this._roomBeingCreated.on(\"change\", this._onRoomChange);\n }\n\n get kind() { return \"roomBeingCreated\"; }\n get closeUrl() { return this._closeUrl; }\n get name() { return this._roomBeingCreated.name; }\n get id() { return this._roomBeingCreated.id; }\n get isEncrypted() { return this._roomBeingCreated.isEncrypted; }\n get error() {\n const {error} = this._roomBeingCreated;\n if (error) {\n if (error.name === \"ConnectionError\") {\n return this.i18n`You seem to be offline`;\n } else {\n return error.message;\n }\n }\n return \"\";\n }\n get avatarLetter() { return avatarInitials(this.name); }\n get avatarColorNumber() { return getIdentifierColorNumber(this._roomBeingCreated.avatarColorId); }\n get avatarTitle() { return this.name; }\n\n avatarUrl(size) {\n // allow blob url which doesn't need mxc => http resolution\n return this._roomBeingCreated.avatarBlobUrl ??\n getAvatarHttpUrl(this._roomBeingCreated.avatarUrl, size, this.platform, this._mediaRepository);\n }\n\n focus() {}\n\n _onRoomChange() {\n this.emitChange();\n }\n\n cancel() {\n this._roomBeingCreated.cancel();\n // navigate away from the room\n this.navigation.applyPath(this.navigation.path.until(\"session\"));\n }\n\n dispose() {\n super.dispose();\n this._roomBeingCreated.off(\"change\", this._onRoomChange);\n }\n}\n\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ViewModel} from \"../../ViewModel\";\n\nexport class LightboxViewModel extends ViewModel {\n constructor(options) {\n super(options);\n this._eventId = options.eventId;\n this._unencryptedImageUrl = null;\n this._decryptedImage = null;\n this._closeUrl = this.urlCreator.urlUntilSegment(\"room\");\n this._eventEntry = null;\n this._date = null;\n this._subscribeToEvent(options.room, options.eventId);\n }\n\n _subscribeToEvent(room, eventId) {\n const eventObservable = room.observeEvent(eventId);\n this.track(eventObservable.subscribe(eventEntry => {\n this._loadEvent(room, eventEntry);\n }));\n this._loadEvent(room, eventObservable.get());\n }\n\n async _loadEvent(room, eventEntry) {\n if (!eventEntry) {\n return;\n }\n const {mediaRepository} = room;\n this._eventEntry = eventEntry;\n const {content} = this._eventEntry;\n this._date = this._eventEntry.timestamp ? new Date(this._eventEntry.timestamp) : null;\n if (content.url) {\n this._unencryptedImageUrl = mediaRepository.mxcUrl(content.url);\n this.emitChange(\"imageUrl\");\n } else if (content.file) {\n this._decryptedImage = this.track(await mediaRepository.downloadEncryptedFile(content.file));\n this.emitChange(\"imageUrl\");\n }\n }\n\n get imageWidth() {\n return this._eventEntry?.content?.info?.w;\n }\n\n get imageHeight() {\n return this._eventEntry?.content?.info?.h;\n }\n\n get name() {\n return this._eventEntry?.content?.body;\n }\n\n get sender() {\n return this._eventEntry?.displayName;\n }\n\n get imageUrl() {\n if (this._decryptedImage) {\n return this._decryptedImage.url;\n } else if (this._unencryptedImageUrl) {\n return this._unencryptedImageUrl;\n } else {\n return \"\";\n }\n }\n\n get date() {\n return this._date && this._date.toLocaleDateString({}, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });\n }\n\n get time() {\n return this._date && this._date.toLocaleTimeString({}, {hour: \"numeric\", minute: \"2-digit\"});\n }\n\n get closeUrl() {\n return this._closeUrl;\n }\n\n close() {\n this.platform.history.pushUrl(this.closeUrl);\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ViewModel} from \"../ViewModel\";\nimport {createEnum} from \"../../utils/enum\";\nimport {ConnectionStatus} from \"../../matrix/net/Reconnector\";\nimport {SyncStatus} from \"../../matrix/Sync.js\";\n\nconst SessionStatus = createEnum(\n \"Disconnected\",\n \"Connecting\",\n \"FirstSync\",\n \"Sending\",\n \"Syncing\",\n \"SyncError\"\n);\n\nexport class SessionStatusViewModel extends ViewModel {\n constructor(options) {\n super(options);\n const {sync, reconnector, session} = options;\n this._sync = sync;\n this._reconnector = reconnector;\n this._status = this._calculateState(reconnector.connectionStatus.get(), sync.status.get());\n this._session = session;\n this._setupKeyBackupUrl = this.urlCreator.urlForSegment(\"settings\");\n this._dismissSecretStorage = false;\n }\n\n start() {\n const update = () => this._updateStatus();\n this.track(this._sync.status.subscribe(update));\n this.track(this._reconnector.connectionStatus.subscribe(update));\n this.track(this._session.needsKeyBackup.subscribe(() => {\n this.emitChange();\n }));\n }\n\n get setupKeyBackupUrl () {\n return this._setupKeyBackupUrl;\n }\n\n get isShown() {\n return (this._session.needsKeyBackup.get() && !this._dismissSecretStorage) || this._status !== SessionStatus.Syncing;\n }\n\n get statusLabel() {\n switch (this._status) {\n case SessionStatus.Disconnected:{\n const retryIn = Math.round(this._reconnector.retryIn / 1000);\n return this.i18n`Disconnected, trying to reconnect in ${retryIn}s…`;\n }\n case SessionStatus.Connecting:\n return this.i18n`Trying to reconnect now…`;\n case SessionStatus.FirstSync:\n return this.i18n`Catching up with your conversations…`;\n case SessionStatus.SyncError:\n return this.i18n`Sync failed because of ${this._sync.error}`;\n }\n if (this._session.needsKeyBackup.get()) {\n return this.i18n`Set up session backup to decrypt older messages.`;\n }\n return \"\";\n }\n\n get isWaiting() {\n switch (this._status) {\n case SessionStatus.Connecting:\n case SessionStatus.FirstSync:\n return true;\n default:\n return false;\n }\n }\n\n _updateStatus() {\n const newStatus = this._calculateState(\n this._reconnector.connectionStatus.get(),\n this._sync.status.get()\n );\n if (newStatus !== this._status) {\n if (newStatus === SessionStatus.Disconnected) {\n this._retryTimer = this.track(this.clock.createInterval(() => {\n this.emitChange(\"statusLabel\");\n }, 1000));\n } else {\n this._retryTimer = this.disposeTracked(this._retryTimer);\n }\n this._status = newStatus;\n this.emitChange();\n }\n }\n\n _calculateState(connectionStatus, syncStatus) {\n if (connectionStatus !== ConnectionStatus.Online) {\n switch (connectionStatus) {\n case ConnectionStatus.Reconnecting:\n return SessionStatus.Connecting;\n case ConnectionStatus.Waiting:\n return SessionStatus.Disconnected;\n }\n } else if (syncStatus !== SyncStatus.Syncing) {\n switch (syncStatus) {\n // InitialSync should be awaited in the SessionLoadViewModel,\n // but include it here anyway\n case SyncStatus.InitialSync:\n case SyncStatus.CatchupSync:\n return SessionStatus.FirstSync;\n case SyncStatus.Stopped:\n return SessionStatus.SyncError;\n }\n } /* else if (session.pendingMessageCount) {\n return SessionStatus.Sending;\n } */ else {\n return SessionStatus.Syncing;\n }\n }\n\n get isConnectNowShown() {\n return this._status === SessionStatus.Disconnected;\n }\n\n get isSecretStorageShown() {\n // TODO: we need a model here where we can have multiple messages queued up and their buttons don't bleed into each other.\n return this._status === SessionStatus.Syncing && this._session.needsKeyBackup.get() && !this._dismissSecretStorage;\n }\n\n get canDismiss() {\n return this.isSecretStorageShown;\n }\n\n dismiss() {\n if (this.isSecretStorageShown) {\n this._dismissSecretStorage = true;\n this.emitChange();\n }\n }\n\n connectNow() {\n if (this.isConnectNowShown) {\n this._reconnector.tryNow();\n }\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ViewModel} from \"../ViewModel\";\nimport {addPanelIfNeeded} from \"../navigation/index\";\n\nfunction dedupeSparse(roomIds) {\n return roomIds.map((id, idx) => {\n if (roomIds.slice(0, idx).includes(id)) {\n return undefined;\n } else {\n return id;\n }\n });\n}\n\nexport class RoomGridViewModel extends ViewModel {\n constructor(options) {\n super(options);\n\n this._width = options.width;\n this._height = options.height;\n this._createRoomViewModelObservable = options.createRoomViewModelObservable;\n this._selectedIndex = 0;\n this._viewModelsObservables = [];\n this._setupNavigation();\n }\n\n _setupNavigation() {\n const focusTileIndex = this.navigation.observe(\"empty-grid-tile\");\n this.track(focusTileIndex.subscribe(index => {\n if (typeof index === \"number\") {\n this._setFocusIndex(index);\n }\n }));\n if (typeof focusTileIndex.get() === \"number\") {\n this._selectedIndex = focusTileIndex.get();\n }\n\n const focusedRoom = this.navigation.observe(\"room\");\n this.track(focusedRoom.subscribe(roomId => {\n if (roomId) {\n // as the room will be in the \"rooms\" observable\n // (monitored by the parent vmo) as well,\n // we only change the focus here and trust\n // setRoomIds to have created the vmo already\n this._setFocusRoom(roomId);\n }\n }));\n // initial focus for a room is set by initializeRoomIdsAndTransferVM\n }\n\n roomViewModelAt(i) {\n return this._viewModelsObservables[i]?.get();\n }\n\n get focusIndex() {\n return this._selectedIndex;\n }\n\n get width() {\n return this._width;\n }\n\n get height() {\n return this._height;\n }\n\n _switchToRoom(roomId) {\n let path = this.navigation.path.until(\"rooms\");\n path = path.with(this.navigation.segment(\"room\", roomId));\n path = addPanelIfNeeded(this.navigation, path);\n this.navigation.applyPath(path);\n }\n\n focusTile(index) {\n if (index === this._selectedIndex) {\n return;\n }\n const vmo = this._viewModelsObservables[index];\n if (vmo) {\n this._switchToRoom(vmo.id);\n } else {\n this.navigation.push(\"empty-grid-tile\", index);\n }\n }\n\n /** called from SessionViewModel */\n initializeRoomIdsAndTransferVM(roomIds, existingRoomVM) {\n roomIds = dedupeSparse(roomIds);\n let transfered = false;\n if (existingRoomVM) {\n const index = roomIds.indexOf(existingRoomVM.id);\n if (index !== -1) {\n this._viewModelsObservables[index] = this.track(existingRoomVM);\n existingRoomVM.subscribe(viewModel => this._refreshRoomViewModel(viewModel));\n transfered = true;\n }\n }\n this.setRoomIds(roomIds);\n // now all view models exist, set the focus to the selected room\n const focusedRoom = this.navigation.path.get(\"room\");\n if (focusedRoom) {\n const index = this._viewModelsObservables.findIndex(vmo => vmo && vmo.id === focusedRoom.value);\n if (index !== -1) {\n this._selectedIndex = index;\n }\n }\n return transfered;\n }\n\n /** called from SessionViewModel */\n setRoomIds(roomIds) {\n roomIds = dedupeSparse(roomIds);\n let changed = false;\n const len = this._height * this._width;\n for (let i = 0; i < len; i += 1) {\n const newId = roomIds[i];\n const vmo = this._viewModelsObservables[i];\n // did anything change?\n if ((!vmo && newId) || (vmo && vmo.id !== newId)) {\n if (vmo) {\n this._viewModelsObservables[i] = this.disposeTracked(vmo);\n }\n if (newId) {\n const vmo = this._createRoomViewModelObservable(newId);\n this._viewModelsObservables[i] = this.track(vmo);\n vmo.subscribe(viewModel => this._refreshRoomViewModel(viewModel));\n vmo.initialize();\n }\n changed = true;\n }\n }\n if (changed) {\n this.emitChange();\n }\n return changed;\n }\n\n _refreshRoomViewModel(viewModel) {\n this.emitChange();\n viewModel?.focus();\n }\n\n /** called from SessionViewModel */\n releaseRoomViewModel(roomId) {\n const index = this._viewModelsObservables.findIndex(vmo => vmo && vmo.id === roomId);\n if (index !== -1) {\n const vmo = this._viewModelsObservables[index];\n this.untrack(vmo);\n vmo.unsubscribeAll();\n this._viewModelsObservables[index] = null;\n return vmo;\n }\n }\n\n _setFocusIndex(idx) {\n if (idx === this._selectedIndex || idx >= (this._width * this._height)) {\n return;\n }\n this._selectedIndex = idx;\n const vmo = this._viewModelsObservables[this._selectedIndex];\n vmo?.get()?.focus();\n this.emitChange(\"focusIndex\");\n }\n\n _setFocusRoom(roomId) {\n const index = this._viewModelsObservables.findIndex(vmo => vmo?.id === roomId);\n if (index >= 0) {\n this._setFocusIndex(index);\n }\n }\n}\n\nimport {createNavigation} from \"../navigation/index\";\nimport {ObservableValue} from \"../../observable/ObservableValue\";\n\nexport function tests() { \n class RoomVMMock {\n constructor(id) {\n this.id = id;\n this.disposed = false;\n this.focused = false;\n }\n dispose() {\n this.disposed = true;\n }\n focus() {\n this.focused = true;\n }\n }\n\n class RoomViewModelObservableMock extends ObservableValue {\n async initialize() {}\n dispose() { this.get()?.dispose(); }\n get id() { return this.get()?.id; }\n }\n\n function createNavigationForRoom(rooms, room) {\n const navigation = createNavigation();\n navigation.applyPath(navigation.pathFrom([\n navigation.segment(\"session\", \"1\"),\n navigation.segment(\"rooms\", rooms),\n navigation.segment(\"room\", room),\n ]));\n return navigation;\n }\n\n function createNavigationForEmptyTile(rooms, idx) {\n const navigation = createNavigation();\n navigation.applyPath(navigation.pathFrom([\n navigation.segment(\"session\", \"1\"),\n navigation.segment(\"rooms\", rooms),\n navigation.segment(\"empty-grid-tile\", idx),\n ]));\n return navigation;\n }\n\n return {\n \"initialize with duplicate set of rooms\": assert => {\n const navigation = createNavigationForRoom([\"c\", \"a\", \"b\", undefined, \"a\"], \"a\");\n const gridVM = new RoomGridViewModel({\n createRoomViewModelObservable: id => new RoomViewModelObservableMock(new RoomVMMock(id)),\n navigation,\n width: 3,\n height: 2,\n });\n gridVM.initializeRoomIdsAndTransferVM(navigation.path.get(\"rooms\").value);\n assert.equal(gridVM.focusIndex, 1);\n assert.equal(gridVM.roomViewModelAt(0).id, \"c\");\n assert.equal(gridVM.roomViewModelAt(1).id, \"a\");\n assert.equal(gridVM.roomViewModelAt(2).id, \"b\");\n assert.equal(gridVM.roomViewModelAt(3), undefined);\n assert.equal(gridVM.roomViewModelAt(4), undefined);\n assert.equal(gridVM.roomViewModelAt(5), undefined);\n },\n \"transfer room view model\": assert => {\n const navigation = createNavigationForRoom([\"a\"], \"a\");\n const gridVM = new RoomGridViewModel({\n createRoomViewModelObservable: () => assert.fail(\"no vms should be created\"),\n navigation,\n width: 3,\n height: 2,\n });\n const existingRoomVM = new RoomViewModelObservableMock(new RoomVMMock(\"a\"));\n const transfered = gridVM.initializeRoomIdsAndTransferVM(navigation.path.get(\"rooms\").value, existingRoomVM);\n assert.equal(transfered, true);\n assert.equal(gridVM.focusIndex, 0);\n assert.equal(gridVM.roomViewModelAt(0).id, \"a\");\n },\n \"reject transfer for non-matching room view model\": assert => {\n const navigation = createNavigationForRoom([\"a\"], \"a\");\n const gridVM = new RoomGridViewModel({\n createRoomViewModelObservable: id => new RoomViewModelObservableMock(new RoomVMMock(id)),\n navigation,\n width: 3,\n height: 2,\n });\n const existingRoomVM = new RoomViewModelObservableMock(new RoomVMMock(\"f\"));\n const transfered = gridVM.initializeRoomIdsAndTransferVM(navigation.path.get(\"rooms\").value, existingRoomVM);\n assert.equal(transfered, false);\n assert.equal(gridVM.focusIndex, 0);\n assert.equal(gridVM.roomViewModelAt(0).id, \"a\");\n },\n \"created & released room view model is not disposed\": assert => {\n const navigation = createNavigationForRoom([\"a\"], \"a\");\n const gridVM = new RoomGridViewModel({\n createRoomViewModelObservable: id => new RoomViewModelObservableMock(new RoomVMMock(id)),\n navigation,\n width: 3,\n height: 2,\n });\n const transfered = gridVM.initializeRoomIdsAndTransferVM(navigation.path.get(\"rooms\").value);\n assert.equal(transfered, false);\n const releasedVM = gridVM.releaseRoomViewModel(\"a\");\n gridVM.dispose();\n assert.equal(releasedVM.get().disposed, false);\n },\n \"transfered & released room view model is not disposed\": assert => {\n const navigation = createNavigationForRoom([undefined, \"a\"], \"a\");\n const gridVM = new RoomGridViewModel({\n createRoomViewModelObservable: () => assert.fail(\"no vms should be created\"),\n navigation,\n width: 3,\n height: 2,\n });\n const existingRoomVM = new RoomViewModelObservableMock(new RoomVMMock(\"a\"));\n const transfered = gridVM.initializeRoomIdsAndTransferVM(navigation.path.get(\"rooms\").value, existingRoomVM);\n assert.equal(transfered, true);\n const releasedVM = gridVM.releaseRoomViewModel(\"a\");\n gridVM.dispose();\n assert.equal(releasedVM.get().disposed, false);\n },\n \"try release non-existing room view model is\": assert => {\n const navigation = createNavigationForEmptyTile([undefined, \"b\"], 3);\n const gridVM = new RoomGridViewModel({\n createRoomViewModelObservable: id => new RoomViewModelObservableMock(new RoomVMMock(id)),\n navigation,\n width: 3,\n height: 2,\n });\n gridVM.initializeRoomIdsAndTransferVM(navigation.path.get(\"rooms\").value);\n const releasedVM = gridVM.releaseRoomViewModel(\"c\");\n assert(!releasedVM);\n },\n \"initial focus is set to empty tile\": assert => {\n const navigation = createNavigationForEmptyTile([\"a\"], 1);\n const gridVM = new RoomGridViewModel({\n createRoomViewModelObservable: id => new RoomViewModelObservableMock(new RoomVMMock(id)),\n navigation,\n width: 3,\n height: 2,\n });\n gridVM.initializeRoomIdsAndTransferVM(navigation.path.get(\"rooms\").value);\n assert.equal(gridVM.focusIndex, 1);\n assert.equal(gridVM.roomViewModelAt(0).id, \"a\");\n },\n \"change room ids after creation\": assert => {\n const navigation = createNavigationForRoom([\"a\", \"b\"], \"a\");\n const gridVM = new RoomGridViewModel({\n createRoomViewModelObservable: id => new RoomViewModelObservableMock(new RoomVMMock(id)),\n navigation,\n width: 3,\n height: 2,\n });\n navigation.observe(\"rooms\").subscribe(roomIds => {\n gridVM.setRoomIds(roomIds);\n });\n gridVM.initializeRoomIdsAndTransferVM(navigation.path.get(\"rooms\").value);\n const oldA = gridVM.roomViewModelAt(0);\n const oldB = gridVM.roomViewModelAt(1);\n assert.equal(oldA.id, \"a\");\n assert.equal(oldB.id, \"b\");\n navigation.applyPath(navigation.path\n .with(navigation.segment(\"rooms\", [\"b\", \"c\", \"b\"]))\n .with(navigation.segment(\"room\", \"c\"))\n );\n assert.equal(oldA.disposed, true);\n assert.equal(oldB.disposed, true);\n assert.equal(gridVM.focusIndex, 1);\n assert.equal(gridVM.roomViewModelAt(0).id, \"b\");\n assert.equal(gridVM.roomViewModelAt(0).disposed, false);\n assert.equal(gridVM.roomViewModelAt(1).id, \"c\");\n assert.equal(gridVM.roomViewModelAt(1).focused, true);\n assert.equal(gridVM.roomViewModelAt(2), undefined);\n }\n };\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ViewModel} from \"../../ViewModel\";\nimport {KeyType} from \"../../../matrix/ssss/index\";\nimport {createEnum} from \"../../../utils/enum\";\n\nexport const Status = createEnum(\"Enabled\", \"SetupKey\", \"SetupPhrase\", \"Pending\", \"NewVersionAvailable\"); \nexport const BackupWriteStatus = createEnum(\"Writing\", \"Stopped\", \"Done\", \"Pending\"); \n\nexport class KeyBackupViewModel extends ViewModel {\n constructor(options) {\n super(options);\n this._session = options.session;\n this._error = null;\n this._isBusy = false;\n this._dehydratedDeviceId = undefined;\n this._status = undefined;\n this._backupOperation = this._session.keyBackup.flatMap(keyBackup => keyBackup.operationInProgress);\n this._progress = this._backupOperation.flatMap(op => op.progress);\n this.track(this._backupOperation.subscribe(() => {\n // see if needsNewKey might be set\n this._reevaluateStatus();\n this.emitChange(\"isBackingUp\");\n }));\n this.track(this._progress.subscribe(() => this.emitChange(\"backupPercentage\")));\n this._reevaluateStatus();\n this.track(this._session.keyBackup.subscribe(() => {\n if (this._reevaluateStatus()) {\n this.emitChange(\"status\");\n }\n }));\n }\n\n _reevaluateStatus() {\n if (this._isBusy) {\n return false;\n }\n let status;\n const keyBackup = this._session.keyBackup.get();\n if (keyBackup) {\n status = keyBackup.needsNewKey ? Status.NewVersionAvailable : Status.Enabled;\n } else if (keyBackup === null) {\n status = this.showPhraseSetup() ? Status.SetupPhrase : Status.SetupKey;\n } else {\n status = Status.Pending;\n }\n const changed = status !== this._status;\n this._status = status;\n return changed;\n }\n\n get decryptAction() {\n return this.i18n`Set up`;\n }\n\n get purpose() {\n return this.i18n`set up key backup`;\n }\n\n offerDehydratedDeviceSetup() {\n return true;\n }\n\n get dehydratedDeviceId() {\n return this._dehydratedDeviceId;\n }\n \n get isBusy() {\n return this._isBusy;\n }\n\n get backupVersion() {\n return this._session.keyBackup.get()?.version;\n }\n\n get backupWriteStatus() {\n const keyBackup = this._session.keyBackup.get();\n if (!keyBackup) {\n return BackupWriteStatus.Pending;\n } else if (keyBackup.hasStopped) {\n return BackupWriteStatus.Stopped;\n }\n const operation = keyBackup.operationInProgress.get();\n if (operation) {\n return BackupWriteStatus.Writing;\n } else if (keyBackup.hasBackedUpAllKeys) {\n return BackupWriteStatus.Done;\n } else {\n return BackupWriteStatus.Pending;\n }\n }\n\n get backupError() {\n return this._session.keyBackup.get()?.error?.message;\n }\n\n get status() {\n return this._status;\n }\n\n get error() {\n return this._error?.message;\n }\n\n showPhraseSetup() {\n if (this._status === Status.SetupKey) {\n this._status = Status.SetupPhrase;\n this.emitChange(\"status\");\n }\n }\n\n showKeySetup() {\n if (this._status === Status.SetupPhrase) {\n this._status = Status.SetupKey;\n this.emitChange(\"status\");\n }\n }\n\n async _enterCredentials(keyType, credential, setupDehydratedDevice) {\n if (credential) {\n try {\n this._isBusy = true;\n this.emitChange(\"isBusy\");\n const key = await this._session.enableSecretStorage(keyType, credential);\n if (setupDehydratedDevice) {\n this._dehydratedDeviceId = await this._session.setupDehydratedDevice(key);\n }\n } catch (err) {\n console.error(err);\n this._error = err;\n this.emitChange(\"error\");\n } finally {\n this._isBusy = false;\n this._reevaluateStatus();\n this.emitChange(\"\");\n }\n }\n }\n\n enterSecurityPhrase(passphrase, setupDehydratedDevice) {\n this._enterCredentials(KeyType.Passphrase, passphrase, setupDehydratedDevice);\n }\n\n enterSecurityKey(securityKey, setupDehydratedDevice) {\n this._enterCredentials(KeyType.RecoveryKey, securityKey, setupDehydratedDevice);\n }\n\n async disable() {\n try {\n this._isBusy = true;\n this.emitChange(\"isBusy\");\n await this._session.disableSecretStorage();\n } catch (err) {\n console.error(err);\n this._error = err;\n this.emitChange(\"error\");\n } finally {\n this._isBusy = false;\n this._reevaluateStatus();\n this.emitChange(\"\");\n }\n }\n\n get isBackingUp() {\n return !!this._backupOperation.get();\n }\n\n get backupPercentage() {\n const progress = this._progress.get();\n if (progress) {\n return Math.round((progress.finished / progress.total) * 100);\n }\n return 0;\n }\n\n get backupInProgressLabel() {\n const progress = this._progress.get();\n if (progress) {\n return this.i18n`${progress.finished} of ${progress.total}`;\n }\n return this.i18n`…`;\n }\n\n cancelBackup() {\n this._backupOperation.get()?.abort();\n }\n\n startBackup() {\n this._session.keyBackup.get()?.flush();\n }\n}\n\n","/*\nCopyright 2022 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport type {BlobHandle} from \"../platform/web/dom/BlobHandle\";\nimport type {RequestFunction} from \"../platform/types/types\";\n\n// see https://github.com/matrix-org/rageshake#readme\ntype RageshakeData = {\n // A textual description of the problem. Included in the details.log.gz file.\n text: string | undefined;\n // Application user-agent. Included in the details.log.gz file.\n userAgent: string;\n // Identifier for the application (eg 'riot-web'). Should correspond to a mapping configured in the configuration file for github issue reporting to work.\n app: string;\n // Application version. Included in the details.log.gz file.\n version: string;\n // Label to attach to the github issue, and include in the details file.\n label: string | undefined;\n};\n\nexport async function submitLogsToRageshakeServer(data: RageshakeData, logsBlob: BlobHandle, submitUrl: string, request: RequestFunction): Promise<void> {\n const formData = new Map<string, string | {name: string, blob: BlobHandle}>();\n if (data.text) {\n formData.set(\"text\", data.text);\n }\n formData.set(\"user_agent\", data.userAgent);\n formData.set(\"app\", data.app);\n formData.set(\"version\", data.version);\n if (data.label) {\n formData.set(\"label\", data.label);\n }\n formData.set(\"file\", {name: \"logs.json\", blob: logsBlob});\n const headers: Map<string, string> = new Map();\n headers.set(\"Accept\", \"application/json\");\n const result = request(submitUrl, {\n method: \"POST\",\n body: formData,\n headers\n });\n let response;\n try {\n response = await result.response();\n } catch (err) {\n throw new Error(`Could not submit logs to ${submitUrl}, got error ${err.message}`);\n }\n const {status, body} = response;\n if (status < 200 || status >= 300) {\n throw new Error(`Could not submit logs to ${submitUrl}, got status code ${status} with body ${body}`);\n }\n // we don't bother with reading report_url from the body as the rageshake server doesn't always return it\n // and would have to have CORS setup properly for us to be able to read it.\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ViewModel} from \"../../ViewModel\";\nimport {KeyBackupViewModel} from \"./KeyBackupViewModel.js\";\nimport {submitLogsToRageshakeServer} from \"../../../domain/rageshake\";\n\nclass PushNotificationStatus {\n constructor() {\n this.supported = null;\n this.enabled = false;\n this.updating = false;\n this.enabledOnServer = null;\n this.serverError = null;\n }\n}\n\nfunction formatKey(key) {\n const partLength = 4;\n const partCount = Math.ceil(key.length / partLength);\n let formattedKey = \"\";\n for (let i = 0; i < partCount; i += 1) {\n formattedKey += (formattedKey.length ? \" \" : \"\") + key.slice(i * partLength, (i + 1) * partLength);\n }\n return formattedKey;\n}\n\nexport class SettingsViewModel extends ViewModel {\n constructor(options) {\n super(options);\n this._updateService = options.updateService;\n const {client} = options;\n this._client = client;\n this._keyBackupViewModel = this.track(new KeyBackupViewModel(this.childOptions({session: this._session})));\n this._closeUrl = this.urlCreator.urlUntilSegment(\"session\");\n this._estimate = null;\n this.sentImageSizeLimit = null;\n this.minSentImageSizeLimit = 400;\n this.maxSentImageSizeLimit = 4000;\n this.pushNotifications = new PushNotificationStatus();\n this._activeTheme = undefined;\n this._logsFeedbackMessage = undefined;\n }\n\n get _session() {\n return this._client.session;\n }\n\n async logout() {\n this.navigation.push(\"logout\", this._client.sessionId);\n }\n\n setSentImageSizeLimit(size) {\n if (size > this.maxSentImageSizeLimit || size < this.minSentImageSizeLimit) {\n this.sentImageSizeLimit = null;\n this.platform.settingsStorage.remove(\"sentImageSizeLimit\");\n } else {\n this.sentImageSizeLimit = Math.round(size);\n this.platform.settingsStorage.setInt(\"sentImageSizeLimit\", size);\n }\n this.emitChange(\"sentImageSizeLimit\");\n }\n\n async load() {\n this._estimate = await this.platform.estimateStorageUsage();\n this.sentImageSizeLimit = await this.platform.settingsStorage.getInt(\"sentImageSizeLimit\");\n this.pushNotifications.supported = await this.platform.notificationService.supportsPush();\n this.pushNotifications.enabled = await this._session.arePushNotificationsEnabled();\n if (!import.meta.env.DEV) {\n this._activeTheme = await this.platform.themeLoader.getActiveTheme();\n }\n this.emitChange(\"\");\n }\n\n get closeUrl() {\n return this._closeUrl;\n }\n\n get fingerprintKey() {\n const key = this._session.fingerprintKey;\n if (!key) {\n return null;\n }\n return formatKey(key);\n }\n\n get deviceId() {\n return this._session.deviceId;\n }\n\n get userId() {\n return this._session.userId;\n }\n\n get version() {\n const {updateService} = this.platform; \n if (updateService) {\n return `${updateService.version} (${updateService.buildHash})`;\n }\n return this.i18n`development version`;\n }\n\n checkForUpdate() {\n this.platform.updateService?.checkForUpdate();\n }\n\n get showUpdateButton() {\n return !!this.platform.updateService;\n }\n\n get keyBackupViewModel() {\n return this._keyBackupViewModel;\n }\n\n get storageQuota() {\n return this._formatBytes(this._estimate?.quota);\n }\n\n get storageUsage() {\n return this._formatBytes(this._estimate?.usage);\n }\n\n get themeMapping() {\n return this.platform.themeLoader.themeMapping;\n }\n\n get activeTheme() {\n return this._activeTheme;\n }\n\n _formatBytes(n) {\n if (typeof n === \"number\") {\n return Math.round(n / (1024 * 1024)).toFixed(1) + \" MB\";\n } else {\n return this.i18n`unknown`;\n }\n }\n\n async exportLogs() {\n const logExport = await this.logger.export();\n this.platform.saveFileAs(logExport.asBlob(), `hydrogen-logs-${this.platform.clock.now()}.json`);\n }\n\n get canSendLogsToServer() {\n return !!this.platform.config.bugReportEndpointUrl;\n }\n\n get logsServer() {\n const {bugReportEndpointUrl} = this.platform.config;\n try {\n if (bugReportEndpointUrl) {\n return new URL(bugReportEndpointUrl).hostname;\n }\n } catch (e) {}\n return \"\";\n }\n\n async sendLogsToServer() {\n const {bugReportEndpointUrl} = this.platform.config;\n if (bugReportEndpointUrl) {\n this._logsFeedbackMessage = this.i18n`Sending logs…`;\n this.emitChange();\n try {\n const logExport = await this.logger.export();\n await submitLogsToRageshakeServer(\n {\n app: \"hydrogen\",\n userAgent: this.platform.description,\n version: DEFINE_VERSION,\n text: `Submit logs from settings for user ${this._session.userId} on device ${this._session.deviceId}`,\n },\n logExport.asBlob(),\n bugReportEndpointUrl,\n this.platform.request\n );\n this._logsFeedbackMessage = this.i18n`Logs sent succesfully!`;\n this.emitChange();\n } catch (err) {\n this._logsFeedbackMessage = err.message;\n this.emitChange();\n }\n }\n }\n\n get logsFeedbackMessage() {\n return this._logsFeedbackMessage;\n }\n\n async togglePushNotifications() {\n this.pushNotifications.updating = true;\n this.pushNotifications.enabledOnServer = null;\n this.pushNotifications.serverError = null;\n this.emitChange(\"pushNotifications.updating\");\n try {\n if (await this._session.enablePushNotifications(!this.pushNotifications.enabled)) {\n this.pushNotifications.enabled = !this.pushNotifications.enabled;\n if (this.pushNotifications.enabled) {\n this.platform.notificationService.showNotification(this.i18n`Push notifications are now enabled`);\n }\n }\n } finally {\n this.pushNotifications.updating = false;\n this.emitChange(\"pushNotifications.updating\");\n }\n }\n\n async checkPushEnabledOnServer() {\n this.pushNotifications.enabledOnServer = null;\n this.pushNotifications.serverError = null;\n try {\n this.pushNotifications.enabledOnServer = await this._session.checkPusherEnabledOnHomeserver();\n this.emitChange(\"pushNotifications.enabledOnServer\");\n } catch (err) {\n this.pushNotifications.serverError = err;\n this.emitChange(\"pushNotifications.serverError\");\n }\n }\n\n changeThemeOption(themeName, themeVariant) {\n this.platform.themeLoader.setTheme(themeName, themeVariant);\n // emit so that radio-buttons become displayed/hidden\n this.emitChange(\"themeOption\");\n }\n}\n\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ViewModel} from \"../ViewModel\";\nimport {imageToInfo} from \"./common.js\";\nimport {RoomType} from \"../../matrix/room/common\";\n\nexport class CreateRoomViewModel extends ViewModel {\n constructor(options) {\n super(options);\n const {session} = options;\n this._session = session;\n this._name = undefined;\n this._topic = undefined;\n this._roomAlias = undefined;\n this._isPublic = false;\n this._isEncrypted = true;\n this._isAdvancedShown = false;\n this._isFederationDisabled = false;\n this._avatarScaledBlob = undefined;\n this._avatarFileName = undefined;\n this._avatarInfo = undefined;\n }\n\n get isPublic() { return this._isPublic; }\n get isEncrypted() { return this._isEncrypted; }\n get canCreate() { return !!this._name; }\n avatarUrl() { return this._avatarScaledBlob.url; }\n get avatarTitle() { return this._name; }\n get avatarLetter() { return \"\"; }\n get avatarColorNumber() { return 0; }\n get hasAvatar() { return !!this._avatarScaledBlob; }\n get isFederationDisabled() { return this._isFederationDisabled; }\n get isAdvancedShown() { return this._isAdvancedShown; }\n\n setName(name) {\n this._name = name;\n this.emitChange(\"canCreate\");\n }\n\n setRoomAlias(roomAlias) {\n this._roomAlias = roomAlias;\n }\n\n setTopic(topic) {\n this._topic = topic;\n }\n\n setPublic(isPublic) {\n this._isPublic = isPublic;\n this.emitChange(\"isPublic\");\n }\n\n setEncrypted(isEncrypted) {\n this._isEncrypted = isEncrypted;\n this.emitChange(\"isEncrypted\");\n }\n\n setFederationDisabled(disable) {\n this._isFederationDisabled = disable;\n this.emitChange(\"isFederationDisabled\");\n }\n\n toggleAdvancedShown() {\n this._isAdvancedShown = !this._isAdvancedShown;\n this.emitChange(\"isAdvancedShown\");\n }\n\n create() {\n let avatar;\n if (this._avatarScaledBlob) {\n avatar = {\n info: this._avatarInfo,\n name: this._avatarFileName,\n blob: this._avatarScaledBlob\n }\n }\n const roomBeingCreated = this._session.createRoom({\n type: this.isPublic ? RoomType.Public : RoomType.Private,\n name: this._name ?? undefined,\n topic: this._topic ?? undefined,\n isEncrypted: !this.isPublic && this._isEncrypted,\n isFederationDisabled: this._isFederationDisabled,\n alias: this.isPublic ? ensureAliasIsLocalPart(this._roomAlias) : undefined,\n avatar,\n });\n this.navigation.push(\"room\", roomBeingCreated.id);\n }\n\n async selectAvatar() {\n if (!this.platform.hasReadPixelPermission()) {\n alert(\"Please allow canvas image data access, so we can scale your images down.\");\n return;\n }\n if (this._avatarScaledBlob) {\n this._avatarScaledBlob.dispose();\n }\n this._avatarScaledBlob = undefined;\n this._avatarFileName = undefined;\n this._avatarInfo = undefined;\n\n const file = await this.platform.openFile(\"image/*\");\n if (!file || !file.blob.mimeType.startsWith(\"image/\")) {\n // allow to clear the avatar by not selecting an image\n this.emitChange(\"hasAvatar\");\n return;\n }\n let image = await this.platform.loadImage(file.blob);\n const limit = 800;\n if (image.maxDimension > limit) {\n const scaledImage = await image.scale(limit);\n image.dispose();\n image = scaledImage;\n }\n this._avatarScaledBlob = image.blob;\n this._avatarInfo = imageToInfo(image);\n this._avatarFileName = file.name;\n this.emitChange(\"hasAvatar\");\n }\n}\n\nfunction ensureAliasIsLocalPart(roomAliasLocalPart) {\n if (roomAliasLocalPart.startsWith(\"#\")) {\n roomAliasLocalPart = roomAliasLocalPart.substr(1);\n }\n const colonIdx = roomAliasLocalPart.indexOf(\":\");\n if (colonIdx !== -1) {\n roomAliasLocalPart = roomAliasLocalPart.substr(0, colonIdx);\n }\n return roomAliasLocalPart;\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ObservableValue} from \"../../observable/ObservableValue\";\nimport {RoomStatus} from \"../../matrix/room/common\";\n\n/**\nDepending on the status of a room (invited, joined, archived, or none),\nwe want to show a different view with a different view model\nwhen showing a room. Furthermore, this logic is needed both in the \nsingle room view and in the grid view. So this logic is extracted here,\nand this observable updates with the right view model as the status for\na room changes.\n\nTo not have to track the subscription manually in the SessionViewModel and\nthe RoomGridViewModel, all subscriptions are removed in the dispose method.\nOnly when transferring a RoomViewModelObservable between the SessionViewModel\nand RoomGridViewModel, unsubscribeAll should be called prior to doing\nthe transfer, so either parent view model don't keep getting updates for\nthe now transferred child view model.\n\nThis is also why there is an explicit initialize method, see comment there.\n*/\nexport class RoomViewModelObservable extends ObservableValue {\n constructor(sessionViewModel, roomIdOrLocalId) {\n super(null);\n this._statusSubscription = null;\n this._sessionViewModel = sessionViewModel;\n this.id = roomIdOrLocalId;\n }\n\n /**\n Separate initialize method rather than doing this onSubscribeFirst because \n we don't want to run this again when transferring this value between\n SessionViewModel and RoomGridViewModel, as onUnsubscribeLast and onSubscribeFirst\n are called in that case.\n */\n async initialize() {\n const {session} = this._sessionViewModel._client;\n const statusObservable = await session.observeRoomStatus(this.id);\n this.set(await this._statusToViewModel(statusObservable.get()));\n this._statusSubscription = statusObservable.subscribe(async status => {\n // first dispose existing VM, if any\n this.get()?.dispose();\n this.set(await this._statusToViewModel(status));\n });\n }\n\n async _statusToViewModel(status) {\n if (status & RoomStatus.Replaced) {\n if (status & RoomStatus.BeingCreated) {\n const {session} = this._sessionViewModel._client;\n const roomBeingCreated = session.roomsBeingCreated.get(this.id);\n this._sessionViewModel.notifyRoomReplaced(roomBeingCreated.id, roomBeingCreated.roomId);\n } else {\n throw new Error(\"Don't know how to replace a room with this status: \" + (status ^ RoomStatus.Replaced));\n }\n } else if (status & RoomStatus.BeingCreated) {\n return this._sessionViewModel._createRoomBeingCreatedViewModel(this.id);\n } else if (status & RoomStatus.Invited) {\n return this._sessionViewModel._createInviteViewModel(this.id);\n } else if (status & RoomStatus.Joined) {\n return this._sessionViewModel._createRoomViewModelInstance(this.id);\n } else if (status & RoomStatus.Archived) {\n return await this._sessionViewModel._createArchivedRoomViewModel(this.id);\n } else {\n return this._sessionViewModel._createUnknownRoomViewModel(this.id);\n }\n }\n\n dispose() {\n if (this._statusSubscription) {\n this._statusSubscription = this._statusSubscription();\n }\n this.unsubscribeAll();\n this.get()?.dispose();\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ViewModel} from \"../../ViewModel\";\nimport {avatarInitials, getIdentifierColorNumber, getAvatarHttpUrl} from \"../../avatar\";\n\nexport class RoomDetailsViewModel extends ViewModel {\n constructor(options) {\n super(options);\n this._room = options.room;\n this._onRoomChange = this._onRoomChange.bind(this);\n this._room.on(\"change\", this._onRoomChange);\n }\n\n get type() {\n return \"room-details\";\n }\n\n get shouldShowBackButton() {\n return false;\n }\n\n get previousSegmentName() {\n return false;\n }\n\n get roomId() {\n return this._room.id;\n }\n\n get canonicalAlias() {\n return this._room.canonicalAlias;\n }\n\n get name() {\n return this._room.name;\n }\n\n get isEncrypted() {\n return !!this._room.isEncrypted;\n }\n\n get memberCount() {\n return this._room.joinedMemberCount;\n }\n\n get avatarLetter() {\n return avatarInitials(this.name);\n }\n\n get avatarColorNumber() {\n return getIdentifierColorNumber(this._room.avatarColorId)\n }\n\n avatarUrl(size) {\n return getAvatarHttpUrl(this._room.avatarUrl, size, this.platform, this._room.mediaRepository);\n }\n\n get avatarTitle() {\n return this.name;\n }\n\n _onRoomChange() {\n this.emitChange();\n }\n\n dispose() {\n super.dispose();\n this._room.off(\"change\", this._onRoomChange);\n }\n\n openPanel(segment) {\n let path = this.navigation.path.until(\"room\");\n path = path.with(this.navigation.segment(\"right-panel\", true));\n path = path.with(this.navigation.segment(segment, true));\n this.navigation.applyPath(path);\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ViewModel} from \"../../ViewModel\";\nimport {avatarInitials, getIdentifierColorNumber, getAvatarHttpUrl} from \"../../avatar\";\n\nexport class MemberTileViewModel extends ViewModel {\n constructor(options) {\n super(options);\n this._member = this._options.member;\n this._mediaRepository = options.mediaRepository\n this._previousName = null;\n this._nameChanged = true;\n }\n\n get name() {\n return `${this._member.name}${this._disambiguationPart}`;\n }\n\n get _disambiguationPart() {\n return this._disambiguate ? ` (${this.userId})` : \"\";\n }\n\n get userId() {\n return this._member.userId;\n }\n\n get previousName() {\n return this._previousName;\n }\n\n get nameChanged() {\n return this._nameChanged;\n }\n\n get detailsUrl() {\n const roomId = this.navigation.path.get(\"room\").value;\n return `${this.urlCreator.openRoomActionUrl(roomId)}/member/${this._member.userId}`;\n }\n\n _updatePreviousName(newName) {\n const currentName = this._member.name;\n if (currentName !== newName) {\n this._previousName = currentName;\n this._nameChanged = true;\n } else {\n this._nameChanged = false;\n }\n }\n\n setDisambiguation(status) {\n this._disambiguate = status;\n this.emitChange();\n }\n\n updateFrom(newMember) {\n this._updatePreviousName(newMember.name);\n this._member = newMember;\n }\n\n get avatarLetter() {\n return avatarInitials(this.name);\n }\n\n get avatarColorNumber() {\n return getIdentifierColorNumber(this.userId)\n }\n\n avatarUrl(size) {\n return getAvatarHttpUrl(this._member.avatarUrl, size, this.platform, this._mediaRepository);\n }\n\n get avatarTitle() {\n return this.name;\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {PowerLevels} from \"../../../../matrix/room/PowerLevels.js\";\n\nexport function createMemberComparator(powerLevels) {\n const collator = new Intl.Collator();\n const removeCharacter = string => string.charAt(0) === \"@\"? string.slice(1) : string;\n\n return function comparator(member, otherMember) {\n const p1 = powerLevels.getUserLevel(member.userId);\n const p2 = powerLevels.getUserLevel(otherMember.userId);\n if (p1 !== p2) { return p2 - p1; }\n const name = removeCharacter(member.name);\n const otherName = removeCharacter(otherMember.name);\n return collator.compare(name, otherName);\n };\n}\n\nexport function tests() {\n\n function createComparatorWithPowerLevel(map) {\n let users = {};\n for (const prop in map) {\n Object.assign(users, {[prop]: map[prop]});\n }\n const powerLevelEvent = {\n content: {users, users_default: 0}\n };\n return createMemberComparator(new PowerLevels({powerLevelEvent}));\n }\n\n return {\n \"power_level(member1) > power_level(member2) returns value <= 0\": assert => {\n const fn = createComparatorWithPowerLevel({\"@alice:hs.tld\": 50});\n const member1 = {userId: \"@alice:hs.tld\", name: \"alice\"};\n const member2 = {userId: \"@bob:hs.tld\", name: \"bob\"};\n assert.strictEqual(fn(member1, member2) <= 0, true);\n },\n\n \"power_level(member1) < power_level(member2) returns value > 0\": assert => {\n const fn = createComparatorWithPowerLevel({\"@alice:hs.tld\": 50});\n const member1 = {userId: \"@bob:hs.tld\", name: \"bob\"};\n const member2 = {userId: \"@alice:hs.tld\", name: \"alice\"};\n assert.strictEqual(fn(member1, member2) > 0, true);\n },\n\n \"alphabetic compare on name\": assert => {\n const fn = createComparatorWithPowerLevel();\n const member1 = {userId: \"@bob:hs.tld\", name: \"bob\"};\n const member2 = {userId: \"@alice:hs.tld\", name: \"alice\"};\n assert.strictEqual(fn(member1, member2) > 0, true);\n assert.strictEqual(fn(member2, member1) <= 0, true);\n },\n\n \"alphabetic compare with case (alice comes before Bob)\": assert => {\n const fn = createComparatorWithPowerLevel();\n const member1 = {userId: \"@bob:hs.tld\", name: \"Bob\"};\n const member2 = {userId: \"@alice:hs.tld\", name: \"alice\"};\n assert.strictEqual(fn(member1, member2) > 0, true);\n assert.strictEqual(fn(member2, member1) <= 0, true);\n },\n\n \"equal powerlevel and same names returns 0\": assert => {\n const fn = createComparatorWithPowerLevel({\"@bobby:hs.tld\": 50, \"@bob:hs.tld\": 50});\n const member1 = {userId: \"@bob:hs.tld\", name: \"bob\"};\n const member2 = {userId: \"@bobby:hs.tld\", name: \"bob\"};\n assert.strictEqual(fn(member1, member2), 0);\n assert.strictEqual(fn(member2, member1), 0);\n },\n\n \"(both_negative_powerlevel) power_level(member1) < power_level(member2) returns value > 0\": assert => {\n const fn = createComparatorWithPowerLevel({\"@alice:hs.tld\": -100, \"@bob:hs.tld\": -50});\n const member1 = {userId: \"@alice:hs.tld\", name: \"alice\"};\n const member2 = {userId: \"@bob:hs.tld\", name: \"bob\"};\n assert.strictEqual(fn(member1, member2) > 0, true);\n },\n\n \"(both_negative_powerlevel) power_level(member1) > power_level(member2) returns value <= 0\": assert => {\n const fn = createComparatorWithPowerLevel({\"@alice:hs.tld\": -50, \"@bob:hs.tld\": -100});\n const member1 = {userId: \"@alice:hs.tld\", name: \"alice\"};\n const member2 = {userId: \"@bob:hs.tld\", name: \"bob\"};\n assert.strictEqual(fn(member1, member2) <= 0, true);\n },\n\n \"(one_negative_powerlevel) power_level(member1) > power_level(member2) returns value <= 0\": assert => {\n const fn = createComparatorWithPowerLevel({\"@alice:hs.tld\": 50, \"@bob:hs.tld\": -100});\n const member1 = {userId: \"@alice:hs.tld\", name: \"alice\"};\n const member2 = {userId: \"@bob:hs.tld\", name: \"bob\"};\n assert.strictEqual(fn(member1, member2) <= 0, true);\n },\n\n \"(one_negative_powerlevel) power_level(member1) < power_level(member2) returns value > 0\": assert => {\n const fn = createComparatorWithPowerLevel({\"@alice:hs.tld\": -100, \"@bob:hs.tld\": 50});\n const member1 = {userId: \"@alice:hs.tld\", name: \"alice\"};\n const member2 = {userId: \"@bob:hs.tld\", name: \"bob\"};\n assert.strictEqual(fn(member1, member2) > 0, true);\n },\n };\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport class Disambiguator {\n constructor() {\n this._map = new Map();\n }\n\n _unDisambiguate(vm, array) {\n const idx = array.indexOf(vm);\n if (idx !== -1) {\n const [removed] = array.splice(idx, 1);\n removed.setDisambiguation(false);\n }\n }\n\n _handlePreviousName(vm) {\n const previousName = vm.previousName;\n if (typeof previousName !== \"string\") { return; }\n const value = this._map.get(previousName);\n if (Array.isArray(value)) {\n this._unDisambiguate(vm, value);\n if (value.length === 1) {\n const vm = value[0];\n vm.setDisambiguation(false);\n this._map.set(previousName, vm);\n }\n } else {\n this._map.delete(previousName);\n }\n }\n\n _updateMap(vm) {\n const name = vm.name;\n const value = this._map.get(name);\n if (value) {\n if (Array.isArray(value)) {\n if (value.findIndex(member => member.userId === vm.userId) !== -1) { return; }\n value.push(vm);\n return value;\n } else if(vm.userId !== value.userId) {\n const array = [value, vm]\n this._map.set(name, array);\n return array;\n }\n } else {\n this._map.set(name, vm);\n }\n }\n\n disambiguate(vm) {\n if (!vm.nameChanged) { return; }\n this._handlePreviousName(vm);\n const value = this._updateMap(vm);\n value?.forEach((vm) => vm.setDisambiguation(true));\n }\n}\n\nexport function tests(){\n\n class MockViewModel {\n constructor(name, userId) {\n this.name = name;\n this.disambiguate = false;\n this.userId = userId;\n this.nameChanged = true;\n }\n \n updateName(newName) {\n if (this.name !== newName) {\n this.previousName = this.name;\n this.nameChanged = true;\n }\n else {\n this.nameChanged = false;\n }\n this.name = newName;\n }\n\n setDisambiguation(status) {\n this.disambiguate = status;\n }\n }\n\n function createVmAndDisambiguator(nameList) {\n const d = new Disambiguator();\n const array = nameList.map(([name, id]) => new MockViewModel(name, id));\n return [...array, d];\n }\n\n return {\n \"Unique names\": assert => {\n const [vm1, vm2, d] = createVmAndDisambiguator([[\"foo\", \"a\"], [\"bar\", \"b\"]]);\n d.disambiguate(vm1);\n d.disambiguate(vm2);\n assert.strictEqual(vm1.disambiguate, false);\n assert.strictEqual(vm2.disambiguate, false);\n },\n\n \"Same names are disambiguated\": assert => {\n const [vm1, vm2, vm3, d] = createVmAndDisambiguator([[\"foo\", \"a\"], [\"foo\", \"b\"], [\"foo\", \"c\"]]);\n d.disambiguate(vm1);\n d.disambiguate(vm2);\n d.disambiguate(vm3);\n assert.strictEqual(vm1.disambiguate, true);\n assert.strictEqual(vm2.disambiguate, true);\n assert.strictEqual(vm3.disambiguate, true);\n },\n\n \"Name updates disambiguate\": assert => {\n const [vm1, vm2, vm3, d] = createVmAndDisambiguator([[\"foo\", \"a\"], [\"bar\", \"b\"], [\"jar\", \"c\"]]);\n d.disambiguate(vm1);\n d.disambiguate(vm2);\n d.disambiguate(vm3);\n \n vm2.updateName(\"foo\");\n d.disambiguate(vm2);\n assert.strictEqual(vm1.disambiguate, true);\n assert.strictEqual(vm2.disambiguate, true);\n\n vm1.updateName(\"bar\");\n d.disambiguate(vm1);\n assert.strictEqual(vm1.disambiguate, false);\n assert.strictEqual(vm2.disambiguate, false);\n\n vm3.updateName(\"foo\");\n d.disambiguate(vm3);\n vm1.updateName(\"foo\");\n d.disambiguate(vm1);\n assert.strictEqual(vm1.disambiguate, true);\n assert.strictEqual(vm2.disambiguate, true);\n assert.strictEqual(vm3.disambiguate, true);\n\n vm2.updateName(\"bar\");\n d.disambiguate(vm2);\n assert.strictEqual(vm1.disambiguate, true);\n assert.strictEqual(vm2.disambiguate, false);\n assert.strictEqual(vm3.disambiguate, true);\n },\n\n \"Multiple disambiguate events\": assert => {\n const [vm1, d] = createVmAndDisambiguator([[\"foo\", \"a\"]]);\n d.disambiguate(vm1);\n vm1.updateName(vm1.name);\n d.disambiguate(vm1);\n assert.strictEqual(vm1.disambiguate, false);\n },\n\n \"Empty names must un-disambiguate\": assert => {\n const [vm1, vm2, d] = createVmAndDisambiguator([[\"\", \"a\"], [\"\", \"b\"]]);\n d.disambiguate(vm1);\n d.disambiguate(vm2);\n vm1.updateName(\"foo\");\n d.disambiguate(vm1);\n assert.strictEqual(vm1.disambiguate, false);\n assert.strictEqual(vm2.disambiguate, false);\n }\n };\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ViewModel} from \"../../ViewModel\";\nimport {MemberTileViewModel} from \"./MemberTileViewModel.js\";\nimport {createMemberComparator} from \"./members/comparator.js\";\nimport {Disambiguator} from \"./members/disambiguator.js\";\n\nexport class MemberListViewModel extends ViewModel {\n constructor(options) {\n super(options);\n const list = options.members;\n\n const powerLevelsObservable = options.powerLevelsObservable;\n this.track(powerLevelsObservable.subscribe(() => { /*resort based on new power levels here*/ }));\n\n const powerLevels = powerLevelsObservable.get();\n this.memberTileViewModels = this._mapTileViewModels(list.members.filterValues(member => member.membership === \"join\"))\n .sortValues(createMemberComparator(powerLevels));\n this.nameDisambiguator = new Disambiguator();\n this.mediaRepository = options.mediaRepository;\n }\n\n get type() { return \"member-list\"; }\n\n get shouldShowBackButton() { return true; }\n\n get previousSegmentName() { return \"details\"; }\n\n _mapTileViewModels(members) {\n const mapper = (member, emitChange) => {\n const mediaRepository = this.mediaRepository;\n const vm = new MemberTileViewModel(this.childOptions({member, emitChange, mediaRepository}));\n this.nameDisambiguator.disambiguate(vm);\n return vm;\n }\n const updater = (vm, params, newMember) => {\n vm.updateFrom(newMember);\n this.nameDisambiguator.disambiguate(vm);\n };\n return members.mapValues(mapper, updater);\n }\n\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ViewModel} from \"../../ViewModel\";\nimport {RoomType} from \"../../../matrix/room/common\";\nimport {avatarInitials, getIdentifierColorNumber, getAvatarHttpUrl} from \"../../avatar\";\n\nexport class MemberDetailsViewModel extends ViewModel {\n constructor(options) {\n super(options);\n this._observableMember = options.observableMember;\n this._mediaRepository = options.mediaRepository;\n this._member = this._observableMember.get();\n this._isEncrypted = options.isEncrypted;\n this._powerLevelsObservable = options.powerLevelsObservable;\n this._session = options.session;\n this.track(this._powerLevelsObservable.subscribe(() => this._onPowerLevelsChange()));\n this.track(this._observableMember.subscribe( () => this._onMemberChange()));\n }\n\n get name() { return this._member.name; }\n get userId() { return this._member.userId; }\n\n get type() { return \"member-details\"; }\n get shouldShowBackButton() { return true; }\n get previousSegmentName() { return \"members\"; }\n \n get role() {\n if (this.powerLevel >= 100) { return this.i18n`Admin`; }\n else if (this.powerLevel >= 50) { return this.i18n`Moderator`; }\n else if (this.powerLevel === 0) { return this.i18n`Default`; }\n else { return this.i18n`Custom (${this.powerLevel})`; }\n }\n\n _onMemberChange() {\n this._member = this._observableMember.get();\n this.emitChange(\"member\");\n }\n\n _onPowerLevelsChange() {\n this.emitChange(\"role\");\n }\n\n get avatarLetter() {\n return avatarInitials(this.name);\n }\n\n get avatarColorNumber() {\n return getIdentifierColorNumber(this.userId)\n }\n\n avatarUrl(size) {\n return getAvatarHttpUrl(this._member.avatarUrl, size, this.platform, this._mediaRepository);\n }\n\n get avatarTitle() {\n return this.name;\n }\n\n get isEncrypted() {\n return this._isEncrypted;\n }\n\n get powerLevel() {\n return this._powerLevelsObservable.get()?.getUserLevel(this._member.userId);\n }\n\n get linkToUser() {\n return `https://matrix.to/#/${encodeURIComponent(this._member.userId)}`;\n }\n\n async openDirectMessage() {\n const room = this._session.findDirectMessageForUserId(this.userId);\n let roomId = room?.id;\n if (!roomId) {\n const roomBeingCreated = await this._session.createRoom({\n type: RoomType.DirectMessage,\n invites: [this.userId]\n });\n roomId = roomBeingCreated.id;\n }\n this.navigation.push(\"room\", roomId);\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ViewModel} from \"../../ViewModel\";\nimport {RoomDetailsViewModel} from \"./RoomDetailsViewModel.js\";\nimport {MemberListViewModel} from \"./MemberListViewModel.js\";\nimport {MemberDetailsViewModel} from \"./MemberDetailsViewModel.js\";\n\nexport class RightPanelViewModel extends ViewModel {\n constructor(options) {\n super(options);\n this._room = options.room;\n this._session = options.session;\n this._members = null;\n this._setupNavigation();\n }\n\n get activeViewModel() { return this._activeViewModel; }\n\n async _getMemberListArguments() {\n if (!this._members) {\n this._members = await this._room.loadMemberList();\n this.track(() => this._members.release());\n }\n const room = this._room;\n const powerLevelsObservable = await this._room.observePowerLevels();\n return {members: this._members, powerLevelsObservable, mediaRepository: room.mediaRepository};\n }\n\n async _getMemberDetailsArguments() {\n const segment = this.navigation.path.get(\"member\"); \n const userId = segment.value;\n const observableMember = await this._room.observeMember(userId);\n if (!observableMember) {\n return false;\n }\n const isEncrypted = this._room.isEncrypted;\n const powerLevelsObservable = await this._room.observePowerLevels();\n return {\n observableMember,\n isEncrypted,\n powerLevelsObservable,\n mediaRepository: this._room.mediaRepository,\n session: this._session\n };\n }\n\n _setupNavigation() {\n this._hookUpdaterToSegment(\"details\", RoomDetailsViewModel, () => { return {room: this._room}; });\n this._hookUpdaterToSegment(\"members\", MemberListViewModel, () => this._getMemberListArguments());\n this._hookUpdaterToSegment(\"member\", MemberDetailsViewModel, () => this._getMemberDetailsArguments(),\n () => {\n // If we fail to create the member details panel, fallback to memberlist\n const url = `${this.urlCreator.urlUntilSegment(\"room\")}/members`;\n this.urlCreator.pushUrl(url);\n }\n );\n }\n\n _hookUpdaterToSegment(segment, viewmodel, argCreator, failCallback) {\n const observable = this.navigation.observe(segment);\n const updater = this._setupUpdater(segment, viewmodel, argCreator, failCallback);\n this.track(observable.subscribe(updater));\n }\n\n _setupUpdater(segment, viewmodel, argCreator, failCallback) {\n const updater = async (skipDispose = false) => {\n if (!skipDispose) {\n this._activeViewModel = this.disposeTracked(this._activeViewModel);\n }\n const enable = !!this.navigation.path.get(segment)?.value;\n if (enable) {\n const args = await argCreator();\n if (!args && failCallback) {\n failCallback();\n return;\n }\n this._activeViewModel = this.track(new viewmodel(this.childOptions(args)));\n }\n this.emitChange(\"activeViewModel\");\n };\n updater(true);\n return updater;\n }\n\n closePanel() {\n const path = this.navigation.path.until(\"room\");\n this.navigation.applyPath(path);\n }\n\n showPreviousPanel() {\n const segmentName = this.activeViewModel.previousSegmentName;\n if (segmentName) {\n let path = this.navigation.path.until(\"room\");\n path = path.with(this.navigation.segment(\"right-panel\", true));\n path = path.with(this.navigation.segment(segmentName, true));\n this.navigation.applyPath(path);\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {LeftPanelViewModel} from \"./leftpanel/LeftPanelViewModel.js\";\nimport {RoomViewModel} from \"./room/RoomViewModel.js\";\nimport {UnknownRoomViewModel} from \"./room/UnknownRoomViewModel.js\";\nimport {InviteViewModel} from \"./room/InviteViewModel.js\";\nimport {RoomBeingCreatedViewModel} from \"./room/RoomBeingCreatedViewModel.js\";\nimport {LightboxViewModel} from \"./room/LightboxViewModel.js\";\nimport {SessionStatusViewModel} from \"./SessionStatusViewModel.js\";\nimport {RoomGridViewModel} from \"./RoomGridViewModel.js\";\nimport {SettingsViewModel} from \"./settings/SettingsViewModel.js\";\nimport {CreateRoomViewModel} from \"./CreateRoomViewModel.js\";\nimport {ViewModel} from \"../ViewModel\";\nimport {RoomViewModelObservable} from \"./RoomViewModelObservable.js\";\nimport {RightPanelViewModel} from \"./rightpanel/RightPanelViewModel.js\";\n\nexport class SessionViewModel extends ViewModel {\n constructor(options) {\n super(options);\n const {client} = options;\n this._client = this.track(client);\n this._sessionStatusViewModel = this.track(new SessionStatusViewModel(this.childOptions({\n sync: client.sync,\n reconnector: client.reconnector,\n session: client.session,\n })));\n this._leftPanelViewModel = this.track(new LeftPanelViewModel(this.childOptions({session: this._client.session})));\n this._settingsViewModel = null;\n this._roomViewModelObservable = null;\n this._gridViewModel = null;\n this._createRoomViewModel = null;\n this._setupNavigation();\n }\n\n _setupNavigation() {\n const gridRooms = this.navigation.observe(\"rooms\");\n // this gives us a set of room ids in the grid\n this.track(gridRooms.subscribe(roomIds => {\n this._updateGrid(roomIds);\n }));\n if (gridRooms.get()) {\n this._updateGrid(gridRooms.get());\n }\n\n const currentRoomId = this.navigation.observe(\"room\");\n // this gives us the active room\n this.track(currentRoomId.subscribe(roomId => {\n if (!this._gridViewModel) {\n this._updateRoom(roomId);\n }\n this._updateRightPanel();\n }));\n if (!this._gridViewModel) {\n this._updateRoom(currentRoomId.get());\n }\n\n const settings = this.navigation.observe(\"settings\");\n this.track(settings.subscribe(settingsOpen => {\n this._updateSettings(settingsOpen);\n }));\n this._updateSettings(settings.get());\n\n const createRoom = this.navigation.observe(\"create-room\");\n this.track(createRoom.subscribe(createRoomOpen => {\n this._updateCreateRoom(createRoomOpen);\n }));\n this._updateCreateRoom(createRoom.get());\n\n const lightbox = this.navigation.observe(\"lightbox\");\n this.track(lightbox.subscribe(eventId => {\n this._updateLightbox(eventId);\n }));\n this._updateLightbox(lightbox.get());\n\n\n const rightpanel = this.navigation.observe(\"right-panel\");\n this.track(rightpanel.subscribe(() => this._updateRightPanel()));\n this._updateRightPanel();\n }\n\n get id() {\n return this._client.sessionId;\n }\n\n start() {\n this._sessionStatusViewModel.start();\n }\n\n get activeMiddleViewModel() {\n return this._roomViewModelObservable?.get() || this._gridViewModel || this._settingsViewModel || this._createRoomViewModel;\n }\n\n get roomGridViewModel() {\n return this._gridViewModel;\n }\n\n get leftPanelViewModel() {\n return this._leftPanelViewModel;\n }\n\n get sessionStatusViewModel() {\n return this._sessionStatusViewModel;\n }\n\n get settingsViewModel() {\n return this._settingsViewModel;\n }\n\n get currentRoomViewModel() {\n return this._roomViewModelObservable?.get();\n }\n\n get rightPanelViewModel() {\n return this._rightPanelViewModel;\n }\n\n get createRoomViewModel() {\n return this._createRoomViewModel;\n }\n\n _updateGrid(roomIds) {\n const changed = !(this._gridViewModel && roomIds);\n const currentRoomId = this.navigation.path.get(\"room\");\n if (roomIds) {\n if (!this._gridViewModel) {\n this._gridViewModel = this.track(new RoomGridViewModel(this.childOptions({\n width: 3,\n height: 2,\n createRoomViewModelObservable: roomId => new RoomViewModelObservable(this, roomId),\n })));\n // try to transfer the current room view model, so we don't have to reload the timeline\n this._roomViewModelObservable?.unsubscribeAll();\n if (this._gridViewModel.initializeRoomIdsAndTransferVM(roomIds, this._roomViewModelObservable)) {\n this._roomViewModelObservable = this.untrack(this._roomViewModelObservable);\n } else if (this._roomViewModelObservable) {\n this._roomViewModelObservable = this.disposeTracked(this._roomViewModelObservable);\n }\n } else {\n this._gridViewModel.setRoomIds(roomIds);\n }\n } else if (this._gridViewModel && !roomIds) {\n // closing grid, try to show focused room in grid\n if (currentRoomId) {\n const vmo = this._gridViewModel.releaseRoomViewModel(currentRoomId.value);\n if (vmo) {\n this._roomViewModelObservable = this.track(vmo);\n this._roomViewModelObservable.subscribe(() => {\n this.emitChange(\"activeMiddleViewModel\");\n });\n }\n }\n this._gridViewModel = this.disposeTracked(this._gridViewModel);\n }\n if (changed) {\n this.emitChange(\"activeMiddleViewModel\");\n }\n }\n\n _createRoomViewModelInstance(roomId) {\n const room = this._client.session.rooms.get(roomId);\n if (room) {\n const roomVM = new RoomViewModel(this.childOptions({room}));\n roomVM.load();\n return roomVM;\n }\n return null;\n }\n\n _createUnknownRoomViewModel(roomIdOrAlias) {\n return new UnknownRoomViewModel(this.childOptions({\n roomIdOrAlias,\n session: this._client.session,\n }));\n }\n\n async _createArchivedRoomViewModel(roomId) {\n const room = await this._client.session.loadArchivedRoom(roomId);\n if (room) {\n const roomVM = new RoomViewModel(this.childOptions({room}));\n roomVM.load();\n return roomVM;\n }\n return null;\n }\n\n _createInviteViewModel(roomId) {\n const invite = this._client.session.invites.get(roomId);\n if (invite) {\n return new InviteViewModel(this.childOptions({\n invite,\n mediaRepository: this._client.session.mediaRepository,\n }));\n }\n return null;\n }\n\n _createRoomBeingCreatedViewModel(localId) {\n const roomBeingCreated = this._client.session.roomsBeingCreated.get(localId);\n if (roomBeingCreated) {\n return new RoomBeingCreatedViewModel(this.childOptions({\n roomBeingCreated,\n mediaRepository: this._client.session.mediaRepository,\n }));\n }\n return null;\n }\n\n _updateRoom(roomId) {\n // opening a room and already open?\n if (this._roomViewModelObservable?.id === roomId) {\n return;\n }\n // close if needed\n if (this._roomViewModelObservable) {\n this._roomViewModelObservable = this.disposeTracked(this._roomViewModelObservable);\n }\n if (!roomId) {\n // if clearing the activeMiddleViewModel rather than changing to a different one,\n // emit so the view picks it up and show the placeholder\n this.emitChange(\"activeMiddleViewModel\");\n return;\n }\n const vmo = new RoomViewModelObservable(this, roomId);\n this._roomViewModelObservable = this.track(vmo);\n // subscription is unsubscribed in RoomViewModelObservable.dispose, and thus handled by track\n this._roomViewModelObservable.subscribe(() => {\n this.emitChange(\"activeMiddleViewModel\");\n });\n vmo.initialize();\n }\n\n _updateSettings(settingsOpen) {\n if (this._settingsViewModel) {\n this._settingsViewModel = this.disposeTracked(this._settingsViewModel);\n }\n if (settingsOpen) {\n this._settingsViewModel = this.track(new SettingsViewModel(this.childOptions({\n client: this._client,\n })));\n this._settingsViewModel.load();\n }\n this.emitChange(\"activeMiddleViewModel\");\n }\n\n _updateCreateRoom(createRoomOpen) {\n if (this._createRoomViewModel) {\n this._createRoomViewModel = this.disposeTracked(this._createRoomViewModel);\n }\n if (createRoomOpen) {\n this._createRoomViewModel = this.track(new CreateRoomViewModel(this.childOptions({session: this._client.session})));\n }\n this.emitChange(\"activeMiddleViewModel\");\n }\n\n _updateLightbox(eventId) {\n if (this._lightboxViewModel) {\n this._lightboxViewModel = this.disposeTracked(this._lightboxViewModel);\n }\n if (eventId) {\n const room = this._roomFromNavigation();\n this._lightboxViewModel = this.track(new LightboxViewModel(this.childOptions({eventId, room})));\n }\n this.emitChange(\"lightboxViewModel\");\n }\n\n get lightboxViewModel() {\n return this._lightboxViewModel;\n }\n\n _roomFromNavigation() {\n const roomId = this.navigation.path.get(\"room\")?.value;\n const room = this._client.session.rooms.get(roomId);\n return room;\n }\n\n _updateRightPanel() {\n this._rightPanelViewModel = this.disposeTracked(this._rightPanelViewModel);\n const enable = !!this.navigation.path.get(\"right-panel\")?.value;\n if (enable) {\n const room = this._roomFromNavigation();\n this._rightPanelViewModel = this.track(new RightPanelViewModel(this.childOptions({room, session: this._client.session})));\n }\n this.emitChange(\"rightPanelViewModel\");\n }\n\n notifyRoomReplaced(oldId, newId) {\n this.navigation.push(\"room\", newId);\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ViewModel} from \"./ViewModel\";\nimport {KeyType} from \"../matrix/ssss/index\";\nimport {Status} from \"./session/settings/KeyBackupViewModel.js\";\n\nexport class AccountSetupViewModel extends ViewModel {\n constructor(options) {\n super(options);\n this._accountSetup = options.accountSetup;\n this._dehydratedDevice = undefined;\n this._decryptDehydratedDeviceViewModel = undefined;\n if (this._accountSetup.encryptedDehydratedDevice) {\n this._decryptDehydratedDeviceViewModel = new DecryptDehydratedDeviceViewModel(this, dehydratedDevice => {\n this._dehydratedDevice = dehydratedDevice;\n this._decryptDehydratedDeviceViewModel = undefined;\n this.emitChange(\"deviceDecrypted\");\n });\n }\n }\n\n get decryptDehydratedDeviceViewModel() {\n return this._decryptDehydratedDeviceViewModel;\n }\n\n get deviceDecrypted() {\n return !!this._dehydratedDevice;\n }\n\n get dehydratedDeviceId() {\n return this._accountSetup.encryptedDehydratedDevice.deviceId;\n }\n\n finish() {\n this._accountSetup.finish(this._dehydratedDevice);\n }\n}\n\n// this vm adopts the same shape as KeyBackupViewModel so the same view can be reused.\nclass DecryptDehydratedDeviceViewModel extends ViewModel {\n constructor(accountSetupViewModel, decryptedCallback) {\n super(accountSetupViewModel.options);\n this._accountSetupViewModel = accountSetupViewModel;\n this._isBusy = false;\n this._status = Status.SetupKey;\n this._error = undefined;\n this._decryptedCallback = decryptedCallback;\n }\n\n get decryptAction() {\n return this.i18n`Restore`;\n }\n\n get purpose() {\n return this.i18n`claim your dehydrated device`;\n }\n\n get offerDehydratedDeviceSetup() {\n return false;\n }\n\n get dehydratedDeviceId() {\n return this._accountSetupViewModel._dehydratedDevice?.deviceId;\n }\n \n get isBusy() {\n return this._isBusy;\n }\n\n get backupVersion() { return 0; }\n\n get status() {\n return this._status;\n }\n\n get error() {\n return this._error?.message;\n }\n\n showPhraseSetup() {\n if (this._status === Status.SetupKey) {\n this._status = Status.SetupPhrase;\n this.emitChange(\"status\");\n }\n }\n\n showKeySetup() {\n if (this._status === Status.SetupPhrase) {\n this._status = Status.SetupKey;\n this.emitChange(\"status\");\n }\n }\n\n async _enterCredentials(keyType, credential) {\n if (credential) {\n try {\n this._isBusy = true;\n this.emitChange(\"isBusy\");\n const {encryptedDehydratedDevice} = this._accountSetupViewModel._accountSetup;\n const dehydratedDevice = await encryptedDehydratedDevice.decrypt(keyType, credential);\n this._decryptedCallback(dehydratedDevice);\n } catch (err) {\n console.error(err);\n this._error = err;\n this.emitChange(\"error\");\n } finally {\n this._isBusy = false;\n this.emitChange(\"\");\n }\n }\n }\n\n enterSecurityPhrase(passphrase) {\n this._enterCredentials(KeyType.Passphrase, passphrase);\n }\n\n enterSecurityKey(securityKey) {\n this._enterCredentials(KeyType.RecoveryKey, securityKey);\n }\n\n disable() {}\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {AccountSetupViewModel} from \"./AccountSetupViewModel.js\";\nimport {LoadStatus} from \"../matrix/Client.js\";\nimport {SyncStatus} from \"../matrix/Sync.js\";\nimport {ViewModel} from \"./ViewModel\";\n\nexport class SessionLoadViewModel extends ViewModel {\n constructor(options) {\n super(options);\n const {client, ready, homeserver, deleteSessionOnCancel} = options;\n this._client = client;\n this._ready = ready;\n this._homeserver = homeserver;\n this._deleteSessionOnCancel = deleteSessionOnCancel;\n this._loading = false;\n this._error = null;\n this.backUrl = this.urlCreator.urlForSegment(\"session\", true);\n this._accountSetupViewModel = undefined;\n\n }\n\n async start() {\n if (this._loading) {\n return;\n }\n try {\n this._loading = true;\n this.emitChange(\"loading\");\n this._waitHandle = this._client.loadStatus.waitFor(s => {\n if (s === LoadStatus.AccountSetup) {\n this._accountSetupViewModel = new AccountSetupViewModel(this.childOptions({accountSetup: this._client.accountSetup}));\n } else {\n this._accountSetupViewModel = undefined;\n }\n this.emitChange(\"loadLabel\");\n // wait for initial sync, but not catchup sync\n const isCatchupSync = s === LoadStatus.FirstSync &&\n this._client.sync.status.get() === SyncStatus.CatchupSync;\n return isCatchupSync ||\n s === LoadStatus.LoginFailed ||\n s === LoadStatus.Error ||\n s === LoadStatus.Ready;\n });\n try {\n await this._waitHandle.promise;\n } catch (err) {\n return; // aborted by goBack\n }\n // TODO: should we deal with no connection during initial sync \n // and we're retrying as well here?\n // e.g. show in the label what is going on wrt connectionstatus\n // much like we will once you are in the app. Probably a good idea\n\n // did it finish or get stuck at LoginFailed or Error?\n const loadStatus = this._client.loadStatus.get();\n const loadError = this._client.loadError;\n if (loadStatus === LoadStatus.FirstSync || loadStatus === LoadStatus.Ready) {\n const client = this._client;\n // session container is ready,\n // don't dispose it anymore when \n // we get disposed\n this._client = null;\n this._ready(client);\n }\n if (loadError) {\n console.error(\"session load error\", loadError);\n }\n } catch (err) {\n this._error = err;\n console.error(\"error thrown during session load\", err.stack);\n } finally {\n this._loading = false;\n // loadLabel in case of client.loadError also gets updated through this\n this.emitChange(\"loading\");\n }\n }\n\n\n dispose() {\n if (this._client) {\n this._client.dispose();\n this._client = null;\n }\n if (this._waitHandle) {\n // rejects with AbortError\n this._waitHandle.dispose();\n this._waitHandle = null;\n }\n }\n\n // to show a spinner or not\n get loading() {\n const client = this._client;\n if (client && client.loadStatus.get() === LoadStatus.AccountSetup) {\n return false;\n }\n return this._loading;\n }\n\n get loadLabel() {\n const client = this._client;\n const error = this._getError();\n if (error || (client && client.loadStatus.get() === LoadStatus.Error)) {\n return `Something went wrong: ${error && error.message}.`;\n }\n\n // Statuses related to login are handled by respective login view models\n if (client) {\n switch (client.loadStatus.get()) {\n case LoadStatus.QueryAccount:\n return `Querying account encryption setup…`;\n case LoadStatus.AccountSetup:\n return \"\"; // we'll show a header ing AccountSetupView\n case LoadStatus.SessionSetup:\n return `Setting up your encryption keys…`;\n case LoadStatus.Loading:\n return `Loading your conversations…`;\n case LoadStatus.FirstSync:\n return `Getting your conversations from the server…`;\n default:\n return this._client.loadStatus.get();\n }\n }\n\n return `Preparing…`;\n }\n\n _getError() {\n return this._error || this._client?.loadError; \n }\n\n get hasError() {\n return !!this._getError();\n }\n\n async exportLogs() {\n const logExport = await this.logger.export();\n this.platform.saveFileAs(logExport.asBlob(), `hydrogen-logs-${this.platform.clock.now()}.json`);\n }\n\n async logout() {\n await this._client.logout();\n this.navigation.push(\"session\", true);\n }\n\n get accountSetupViewModel() {\n return this._accountSetupViewModel;\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ViewModel} from \"../ViewModel\";\nimport {LoginFailure} from \"../../matrix/Client.js\";\n\nexport class PasswordLoginViewModel extends ViewModel {\n constructor(options) {\n super(options);\n const {loginOptions, attemptLogin} = options;\n this._loginOptions = loginOptions;\n this._attemptLogin = attemptLogin;\n this._isBusy = false;\n this._errorMessage = \"\";\n }\n\n get isBusy() { return this._isBusy; }\n get errorMessage() { return this._errorMessage; }\n\n setBusy(status) {\n this._isBusy = status;\n this.emitChange(\"isBusy\");\n }\n\n _showError(message) {\n this._errorMessage = message;\n this.emitChange(\"errorMessage\");\n }\n\n async login(username, password) {\n this._errorMessage = \"\";\n this.emitChange(\"errorMessage\");\n const status = await this._attemptLogin(this._loginOptions.password(username, password));\n let error = \"\";\n switch (status) {\n case LoginFailure.Credentials:\n error = this.i18n`Your username and/or password don't seem to be correct.`;\n break;\n case LoginFailure.Connection:\n error = this.i18n`Can't connect to ${this._loginOptions.homeserver}.`;\n break;\n case LoginFailure.Unknown:\n error = this.i18n`Something went wrong while checking your login and password.`;\n break;\n }\n if (error) {\n this._showError(error);\n }\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ViewModel} from \"../ViewModel\";\n\nexport class StartSSOLoginViewModel extends ViewModel{\n constructor(options) {\n super(options);\n this._sso = options.loginOptions.sso;\n this._isBusy = false;\n }\n \n get isBusy() { return this._isBusy; }\n \n setBusy(status) {\n this._isBusy = status;\n this.emitChange(\"isBusy\");\n }\n\n async startSSOLogin() {\n await this.platform.settingsStorage.setString(\"sso_ongoing_login_homeserver\", this._sso.homeserver);\n const link = this._sso.createSSORedirectURL(this.urlCreator.createSSOCallbackURL());\n this.platform.openUrl(link);\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ViewModel} from \"../ViewModel\";\nimport {LoginFailure} from \"../../matrix/Client.js\";\n\nexport class CompleteSSOLoginViewModel extends ViewModel {\n constructor(options) {\n super(options);\n const {\n loginToken,\n client,\n attemptLogin,\n } = options;\n this._loginToken = loginToken;\n this._client = client;\n this._attemptLogin = attemptLogin;\n this._errorMessage = \"\";\n this.performSSOLoginCompletion();\n }\n\n get errorMessage() { return this._errorMessage; }\n\n _showError(message) {\n this._errorMessage = message;\n this.emitChange(\"errorMessage\");\n }\n\n async performSSOLoginCompletion() {\n if (!this._loginToken) {\n return;\n }\n const homeserver = await this.platform.settingsStorage.getString(\"sso_ongoing_login_homeserver\");\n let loginOptions;\n try {\n loginOptions = await this._client.queryLogin(homeserver).result;\n }\n catch (err) {\n this._showError(err.message);\n return;\n }\n if (!loginOptions.token) {\n this.navigation.push(\"session\");\n return;\n }\n const status = await this._attemptLogin(loginOptions.token(this._loginToken));\n let error = \"\";\n switch (status) {\n case LoginFailure.Credentials:\n error = this.i18n`Your login token is invalid.`;\n break;\n case LoginFailure.Connection:\n error = this.i18n`Can't connect to ${homeserver}.`;\n break;\n case LoginFailure.Unknown:\n error = this.i18n`Something went wrong while checking your login token.`;\n break;\n }\n if (error) {\n this._showError(error);\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {Client} from \"../../matrix/Client.js\";\nimport {Options as BaseOptions, ViewModel} from \"../ViewModel\";\nimport {PasswordLoginViewModel} from \"./PasswordLoginViewModel.js\";\nimport {StartSSOLoginViewModel} from \"./StartSSOLoginViewModel.js\";\nimport {CompleteSSOLoginViewModel} from \"./CompleteSSOLoginViewModel.js\";\nimport {LoadStatus} from \"../../matrix/Client.js\";\nimport {SessionLoadViewModel} from \"../SessionLoadViewModel.js\";\nimport {SegmentType} from \"../navigation/index\";\n\nimport type {PasswordLoginMethod, SSOLoginHelper, TokenLoginMethod, ILoginMethod} from \"../../matrix/login\";\n\ntype Options = {\n defaultHomeserver: string;\n ready: ReadyFn;\n loginToken?: string;\n} & BaseOptions;\n\nexport class LoginViewModel extends ViewModel<SegmentType, Options> {\n private _ready: ReadyFn;\n private _loginToken?: string;\n private _client: Client;\n private _loginOptions?: LoginOptions;\n private _passwordLoginViewModel?: PasswordLoginViewModel;\n private _startSSOLoginViewModel?: StartSSOLoginViewModel;\n private _completeSSOLoginViewModel?: CompleteSSOLoginViewModel;\n private _loadViewModel?: SessionLoadViewModel;\n private _loadViewModelSubscription?: () => void;\n private _homeserver: string;\n private _queriedHomeserver?: string;\n private _abortHomeserverQueryTimeout?: () => void;\n private _abortQueryOperation?: () => void;\n\n private _hideHomeserver: boolean = false;\n private _isBusy: boolean = false;\n private _errorMessage: string = \"\";\n\n constructor(options: Readonly<Options>) {\n super(options);\n const {ready, defaultHomeserver, loginToken} = options;\n this._ready = ready;\n this._loginToken = loginToken;\n this._client = new Client(this.platform);\n this._homeserver = defaultHomeserver;\n this._initViewModels();\n }\n\n get passwordLoginViewModel(): PasswordLoginViewModel {\n return this._passwordLoginViewModel;\n }\n\n get startSSOLoginViewModel(): StartSSOLoginViewModel {\n return this._startSSOLoginViewModel;\n }\n\n get completeSSOLoginViewModel(): CompleteSSOLoginViewModel {\n return this._completeSSOLoginViewModel;\n }\n\n get homeserver(): string {\n return this._homeserver;\n }\n\n get resolvedHomeserver(): string | undefined {\n return this._loginOptions?.homeserver;\n }\n\n get errorMessage(): string {\n return this._errorMessage;\n }\n\n get showHomeserver(): boolean {\n return !this._hideHomeserver;\n }\n\n get loadViewModel(): SessionLoadViewModel {\n return this._loadViewModel;\n }\n\n get isBusy(): boolean {\n return this._isBusy;\n }\n\n get isFetchingLoginOptions(): boolean {\n return !!this._abortQueryOperation;\n }\n\n goBack(): void {\n this.navigation.push(\"session\");\n }\n\n private _initViewModels(): void {\n if (this._loginToken) {\n this._hideHomeserver = true;\n this._completeSSOLoginViewModel = this.track(new CompleteSSOLoginViewModel(\n this.childOptions(\n {\n client: this._client,\n attemptLogin: (loginMethod: TokenLoginMethod) => this.attemptLogin(loginMethod),\n loginToken: this._loginToken\n })));\n this.emitChange(\"completeSSOLoginViewModel\");\n }\n else {\n void this.queryHomeserver();\n }\n }\n\n private _showPasswordLogin(): void {\n this._passwordLoginViewModel = this.track(new PasswordLoginViewModel(\n this.childOptions({\n loginOptions: this._loginOptions,\n attemptLogin: (loginMethod: PasswordLoginMethod) => this.attemptLogin(loginMethod)\n })));\n this.emitChange(\"passwordLoginViewModel\");\n }\n\n private _showSSOLogin(): void {\n this._startSSOLoginViewModel = this.track(\n new StartSSOLoginViewModel(this.childOptions({loginOptions: this._loginOptions}))\n );\n this.emitChange(\"startSSOLoginViewModel\");\n }\n\n private _showError(message: string): void {\n this._errorMessage = message;\n this.emitChange(\"errorMessage\");\n }\n\n private _setBusy(status: boolean): void {\n this._isBusy = status;\n this._passwordLoginViewModel?.setBusy(status);\n this._startSSOLoginViewModel?.setBusy(status);\n this.emitChange(\"isBusy\");\n }\n\n async attemptLogin(loginMethod: ILoginMethod): Promise<null> {\n this._setBusy(true);\n void this._client.startWithLogin(loginMethod, {inspectAccountSetup: true});\n const loadStatus = this._client.loadStatus;\n const handle = loadStatus.waitFor((status: LoadStatus) => status !== LoadStatus.Login);\n await handle.promise;\n this._setBusy(false);\n const status = loadStatus.get();\n if (status === LoadStatus.LoginFailed) {\n return this._client.loginFailure;\n }\n this._hideHomeserver = true;\n this.emitChange(\"hideHomeserver\");\n this._disposeViewModels();\n void this._createLoadViewModel();\n return null;\n }\n\n private _createLoadViewModel(): void {\n this._loadViewModelSubscription = this.disposeTracked(this._loadViewModelSubscription);\n this._loadViewModel = this.disposeTracked(this._loadViewModel);\n this._loadViewModel = this.track(\n new SessionLoadViewModel(\n this.childOptions({\n ready: (client) => {\n // make sure we don't delete the session in dispose when navigating away\n this._client = null;\n this._ready(client);\n },\n client: this._client,\n homeserver: this._homeserver\n })\n )\n );\n void this._loadViewModel.start();\n this.emitChange(\"loadViewModel\");\n this._loadViewModelSubscription = this.track(\n this._loadViewModel.disposableOn(\"change\", () => {\n if (!this._loadViewModel.loading) {\n this._loadViewModelSubscription = this.disposeTracked(this._loadViewModelSubscription);\n }\n this._setBusy(false);\n })\n );\n }\n\n private _disposeViewModels(): void {\n this._startSSOLoginViewModel = this.disposeTracked(this._startSSOLoginViewModel);\n this._passwordLoginViewModel = this.disposeTracked(this._passwordLoginViewModel);\n this._completeSSOLoginViewModel = this.disposeTracked(this._completeSSOLoginViewModel);\n this.emitChange(\"disposeViewModels\");\n }\n\n async setHomeserver(newHomeserver: string): Promise<void> {\n this._homeserver = newHomeserver;\n // clear everything set by queryHomeserver\n this._loginOptions = undefined;\n this._queriedHomeserver = undefined;\n this._showError(\"\");\n this._disposeViewModels();\n this._abortQueryOperation = this.disposeTracked(this._abortQueryOperation);\n this.emitChange(\"loginViewModels\"); // multiple fields changing\n // also clear the timeout if it is still running\n this.disposeTracked(this._abortHomeserverQueryTimeout);\n const timeout = this.clock.createTimeout(1000);\n this._abortHomeserverQueryTimeout = this.track(() => timeout.abort());\n try {\n await timeout.elapsed();\n } catch (err) {\n if (err.name === \"AbortError\") {\n return; // still typing, don't query\n } else {\n throw err;\n }\n }\n this._abortHomeserverQueryTimeout = this.disposeTracked(this._abortHomeserverQueryTimeout);\n void this.queryHomeserver();\n }\n\n async queryHomeserver(): Promise<void> {\n // don't repeat a query we've just done\n if (this._homeserver === this._queriedHomeserver || this._homeserver === \"\") {\n return;\n }\n this._queriedHomeserver = this._homeserver;\n // given that setHomeserver already clears everything set here,\n // and that is the only way to change the homeserver,\n // we don't need to reset things again here.\n // However, clear things set by setHomeserver:\n // if query is called before the typing timeout hits (e.g. field lost focus),\n // cancel the timeout so we don't query again.\n this._abortHomeserverQueryTimeout = this.disposeTracked(this._abortHomeserverQueryTimeout);\n // cancel ongoing query operation, if any\n this._abortQueryOperation = this.disposeTracked(this._abortQueryOperation);\n try {\n const queryOperation = this._client.queryLogin(this._homeserver);\n this._abortQueryOperation = this.track(() => queryOperation.abort());\n this.emitChange(\"isFetchingLoginOptions\");\n this._loginOptions = await queryOperation.result;\n this.emitChange(\"resolvedHomeserver\");\n }\n catch (e) {\n if (e.name === \"AbortError\") {\n return; //aborted, bail out\n } else {\n this._loginOptions = undefined;\n }\n } finally {\n this._abortQueryOperation = this.disposeTracked(this._abortQueryOperation);\n this.emitChange(\"isFetchingLoginOptions\");\n }\n if (this._loginOptions) {\n if (this._loginOptions.sso) { this._showSSOLogin(); }\n if (this._loginOptions.password) { this._showPasswordLogin(); }\n if (!this._loginOptions.sso && !this._loginOptions.password) {\n this._showError(\"This homeserver supports neither SSO nor password based login flows\");\n }\n }\n else {\n this._showError(`Could not query login methods supported by ${this.homeserver}`);\n }\n }\n\n dispose(): void {\n super.dispose();\n if (this._client) {\n // if we move away before we're done with initial sync\n // delete the session\n void this._client.deleteSession();\n }\n }\n}\n\ntype ReadyFn = (client: Client) => void;\n\n// TODO: move to Client.js when its converted to typescript.\ntype LoginOptions = {\n homeserver: string;\n password?: (username: string, password: string) => PasswordLoginMethod;\n sso?: SSOLoginHelper;\n token?: (loginToken: string) => TokenLoginMethod;\n};\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {Options as BaseOptions, ViewModel} from \"./ViewModel\";\nimport {Client} from \"../matrix/Client.js\";\nimport {SegmentType} from \"./navigation/index\";\n\ntype Options = { sessionId: string; } & BaseOptions;\n\nexport class LogoutViewModel extends ViewModel<SegmentType, Options> {\n private _sessionId: string;\n private _busy: boolean;\n private _showConfirm: boolean;\n private _error?: Error;\n\n constructor(options: Options) {\n super(options);\n this._sessionId = options.sessionId;\n this._busy = false;\n this._showConfirm = true;\n this._error = undefined;\n }\n\n get showConfirm(): boolean {\n return this._showConfirm;\n }\n\n get busy(): boolean {\n return this._busy;\n }\n\n get cancelUrl(): string | undefined {\n return this.urlCreator.urlForSegment(\"session\", true);\n }\n\n async logout(): Promise<void> {\n this._busy = true;\n this._showConfirm = false;\n this.emitChange(\"busy\");\n try {\n const client = new Client(this.platform);\n await client.startLogout(this._sessionId);\n this.navigation.push(\"session\", true);\n } catch (err) {\n this._error = err;\n this._busy = false;\n this.emitChange(\"busy\");\n }\n }\n\n get status(): string {\n if (this._error) {\n return this.i18n`Could not log out of device: ${this._error.message}`;\n } else {\n return this.i18n`Logging out… Please don't close the app.`;\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {SortedArray} from \"../observable/index.js\";\nimport {ViewModel} from \"./ViewModel\";\nimport {avatarInitials, getIdentifierColorNumber} from \"./avatar\";\n\nclass SessionItemViewModel extends ViewModel {\n constructor(options, pickerVM) {\n super(options);\n this._pickerVM = pickerVM;\n this._sessionInfo = options.sessionInfo;\n this._isDeleting = false;\n this._isClearing = false;\n this._error = null;\n this._exportDataUrl = null;\n }\n\n get error() {\n return this._error && this._error.message;\n }\n\n get id() {\n return this._sessionInfo.id;\n }\n\n get openUrl() {\n return this.urlCreator.urlForSegment(\"session\", this.id);\n }\n\n get label() {\n const {userId, comment} = this._sessionInfo;\n if (comment) {\n return `${userId} (${comment})`;\n } else {\n return userId;\n }\n }\n\n get sessionInfo() {\n return this._sessionInfo;\n }\n\n get exportDataUrl() {\n return this._exportDataUrl;\n }\n\n get avatarColorNumber() {\n return getIdentifierColorNumber(this._sessionInfo.userId);\n }\n\n get avatarInitials() {\n return avatarInitials(this._sessionInfo.userId);\n }\n}\n\n\nexport class SessionPickerViewModel extends ViewModel {\n constructor(options) {\n super(options);\n this._sessions = new SortedArray((s1, s2) => s1.id.localeCompare(s2.id));\n this._loadViewModel = null;\n this._error = null;\n }\n\n // this loads all the sessions\n async load() {\n const sessions = await this.platform.sessionInfoStorage.getAll();\n this._sessions.setManyUnsorted(sessions.map(s => {\n return new SessionItemViewModel(this.childOptions({sessionInfo: s}), this);\n }));\n }\n\n // for the loading of 1 picked session\n get loadViewModel() {\n return this._loadViewModel;\n }\n\n get sessions() {\n return this._sessions;\n }\n\n get cancelUrl() {\n return this.urlCreator.urlForSegment(\"login\");\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {Client} from \"../matrix/Client.js\";\nimport {SessionViewModel} from \"./session/SessionViewModel.js\";\nimport {SessionLoadViewModel} from \"./SessionLoadViewModel.js\";\nimport {LoginViewModel} from \"./login/LoginViewModel\";\nimport {LogoutViewModel} from \"./LogoutViewModel\";\nimport {SessionPickerViewModel} from \"./SessionPickerViewModel.js\";\nimport {ViewModel} from \"./ViewModel\";\n\nexport class RootViewModel extends ViewModel {\n constructor(options) {\n super(options);\n this._error = null;\n this._sessionPickerViewModel = null;\n this._sessionLoadViewModel = null;\n this._loginViewModel = null;\n this._logoutViewModel = null;\n this._sessionViewModel = null;\n this._pendingClient = null;\n }\n\n async load() {\n this.track(this.navigation.observe(\"login\").subscribe(() => this._applyNavigation()));\n this.track(this.navigation.observe(\"session\").subscribe(() => this._applyNavigation()));\n this.track(this.navigation.observe(\"sso\").subscribe(() => this._applyNavigation()));\n this._applyNavigation(true);\n }\n\n async _applyNavigation(shouldRestoreLastUrl) {\n const isLogin = this.navigation.path.get(\"login\");\n const logoutSessionId = this.navigation.path.get(\"logout\")?.value;\n const sessionId = this.navigation.path.get(\"session\")?.value;\n const loginToken = this.navigation.path.get(\"sso\")?.value;\n if (isLogin) {\n if (this.activeSection !== \"login\") {\n this._showLogin();\n }\n } else if (logoutSessionId) {\n if (this.activeSection !== \"logout\") {\n this._showLogout(logoutSessionId);\n }\n } else if (sessionId === true) {\n if (this.activeSection !== \"picker\") {\n this._showPicker();\n }\n } else if (sessionId) {\n if (!this._sessionViewModel || this._sessionViewModel.id !== sessionId) {\n // see _showLogin for where _pendingClient comes from\n if (this._pendingClient && this._pendingClient.sessionId === sessionId) {\n const client = this._pendingClient;\n this._pendingClient = null;\n this._showSession(client);\n } else {\n // this should never happen, but we want to be sure not to leak it\n if (this._pendingClient) {\n this._pendingClient.dispose();\n this._pendingClient = null;\n }\n this._showSessionLoader(sessionId);\n }\n }\n } else if (loginToken) {\n this.urlCreator.normalizeUrl();\n if (this.activeSection !== \"login\") {\n this._showLogin(loginToken);\n }\n }\n else {\n try {\n if (!(shouldRestoreLastUrl && this.urlCreator.tryRestoreLastUrl())) {\n const sessionInfos = await this.platform.sessionInfoStorage.getAll();\n if (sessionInfos.length === 0) {\n this.navigation.push(\"login\");\n } else if (sessionInfos.length === 1) {\n this.navigation.push(\"session\", sessionInfos[0].id);\n } else {\n this.navigation.push(\"session\");\n }\n }\n } catch (err) {\n this._setSection(() => this._error = err);\n }\n }\n }\n\n async _showPicker() {\n this._setSection(() => {\n this._sessionPickerViewModel = new SessionPickerViewModel(this.childOptions());\n });\n try {\n await this._sessionPickerViewModel.load();\n } catch (err) {\n this._setSection(() => this._error = err);\n }\n }\n\n _showLogin(loginToken) {\n this._setSection(() => {\n this._loginViewModel = new LoginViewModel(this.childOptions({\n defaultHomeserver: this.platform.config[\"defaultHomeServer\"],\n ready: client => {\n // we don't want to load the session container again,\n // but we also want the change of screen to go through the navigation\n // so we store the session container in a temporary variable that will be\n // consumed by _applyNavigation, triggered by the navigation change\n //\n // Also, we should not call _setSection before the navigation is in the correct state,\n // as url creation (e.g. in RoomTileViewModel)\n // won't be using the correct navigation base path.\n this._pendingClient = client;\n this.navigation.push(\"session\", client.sessionId);\n },\n loginToken\n }));\n });\n }\n\n _showLogout(sessionId) {\n this._setSection(() => {\n this._logoutViewModel = new LogoutViewModel(this.childOptions({sessionId}));\n });\n }\n\n _showSession(client) {\n this._setSection(() => {\n this._sessionViewModel = new SessionViewModel(this.childOptions({client}));\n this._sessionViewModel.start();\n });\n }\n\n _showSessionLoader(sessionId) {\n const client = new Client(this.platform);\n client.startWithExistingSession(sessionId);\n this._setSection(() => {\n this._sessionLoadViewModel = new SessionLoadViewModel(this.childOptions({\n client,\n ready: client => this._showSession(client)\n }));\n this._sessionLoadViewModel.start();\n });\n }\n\n get activeSection() {\n if (this._error) {\n return \"error\";\n } else if (this._sessionViewModel) {\n return \"session\";\n } else if (this._loginViewModel) {\n return \"login\";\n } else if (this._logoutViewModel) {\n return \"logout\";\n } else if (this._sessionPickerViewModel) {\n return \"picker\";\n } else if (this._sessionLoadViewModel) {\n return \"loading\";\n } else {\n return \"redirecting\";\n }\n }\n\n _setSection(setter) {\n // clear all members the activeSection depends on\n this._error = null;\n this._sessionPickerViewModel = this.disposeTracked(this._sessionPickerViewModel);\n this._sessionLoadViewModel = this.disposeTracked(this._sessionLoadViewModel);\n this._loginViewModel = this.disposeTracked(this._loginViewModel);\n this._logoutViewModel = this.disposeTracked(this._logoutViewModel);\n this._sessionViewModel = this.disposeTracked(this._sessionViewModel);\n // now set it again\n setter();\n this._sessionPickerViewModel && this.track(this._sessionPickerViewModel);\n this._sessionLoadViewModel && this.track(this._sessionLoadViewModel);\n this._loginViewModel && this.track(this._loginViewModel);\n this._logoutViewModel && this.track(this._logoutViewModel);\n this._sessionViewModel && this.track(this._sessionViewModel);\n this.emitChange(\"activeSection\");\n }\n\n get error() { return this._error; }\n get sessionViewModel() { return this._sessionViewModel; }\n get loginViewModel() { return this._loginViewModel; }\n get logoutViewModel() { return this._logoutViewModel; }\n get sessionPickerViewModel() { return this._sessionPickerViewModel; }\n get sessionLoadViewModel() { return this._sessionLoadViewModel; }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// import {RecordRequester, ReplayRequester} from \"./matrix/net/request/replay\";\nimport {RootViewModel} from \"../../domain/RootViewModel.js\";\nimport {createNavigation, createRouter} from \"../../domain/navigation/index\";\n// Don't use a default export here, as we use multiple entries during legacy build,\n// which does not support default exports,\n// see https://github.com/rollup/plugins/tree/master/packages/multi-entry\nexport async function main(platform) {\n try {\n // to replay:\n // const fetchLog = await (await fetch(\"/fetchlogs/constrainterror.json\")).json();\n // const replay = new ReplayRequester(fetchLog, {delay: false});\n // const request = replay.request;\n\n // to record:\n // const recorder = new RecordRequester(createFetchRequest(clock.createTimeout));\n // const request = recorder.request;\n // window.getBrawlFetchLog = () => recorder.log();\n await platform.init();\n const navigation = createNavigation();\n platform.setNavigation(navigation);\n const urlRouter = createRouter({navigation, history: platform.history});\n urlRouter.attach();\n const vm = new RootViewModel({\n platform,\n // the only public interface of the router is to create urls,\n // so we call it that in the view models\n urlCreator: urlRouter,\n navigation,\n });\n await vm.load();\n platform.createAndMountRootView(vm);\n } catch(err) {\n console.error(`${err.message}:\\n${err.stack}`);\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ConnectionError} from \"../matrix/error.js\";\nimport type {Timeout} from \"../platform/web/dom/Clock.js\"\nimport type {IAbortable} from \"./AbortableOperation\";\n\ntype TimeoutCreator = (ms: number) => Timeout;\n\nexport function abortOnTimeout(createTimeout: TimeoutCreator, timeoutAmount: number, requestResult: IAbortable, responsePromise: Promise<Response>) {\n const timeout = createTimeout(timeoutAmount);\n // abort request if timeout finishes first\n let timedOut = false;\n timeout.elapsed().then(\n () => {\n timedOut = true;\n requestResult.abort();\n },\n () => {} // ignore AbortError when timeout is aborted\n );\n // abort timeout if request finishes first\n return responsePromise.then(\n response => {\n timeout.abort();\n return response;\n },\n err => {\n timeout.abort();\n // map error to TimeoutError\n if (err.name === \"AbortError\" && timedOut) {\n throw new ConnectionError(`Request timed out after ${timeoutAmount}ms`, true);\n } else {\n throw err;\n }\n }\n );\n}\n\n// because impunity only takes one entrypoint currently,\n// these tests aren't run by yarn test as that does not\n// include platform specific code,\n// and this file is only included by platform specific code,\n// see how to run in package.json and replace src/main.js with this file.\nimport {Clock as MockClock} from \"../mocks/Clock.js\";\nimport {Request as MockRequest} from \"../mocks/Request.js\";\nimport {AbortError} from \"../matrix/error.js\";\nexport function tests() {\n return {\n \"ConnectionError on timeout\": async assert => {\n const clock = new MockClock();\n const request = new MockRequest();\n const promise = abortOnTimeout(clock.createTimeout, 10000, request, request.response());\n clock.elapse(10000);\n await assert.rejects(promise, ConnectionError);\n assert(request.aborted);\n },\n \"Abort is canceled once response resolves\": async assert => {\n const clock = new MockClock();\n const request = new MockRequest();\n const promise = abortOnTimeout(clock.createTimeout, 10000, request, request.response());\n request.resolve(5);\n clock.elapse(10000);\n assert(!request.aborted);\n assert.equal(await promise, 5);\n },\n \"AbortError from request is not mapped to ConnectionError\": async assert => {\n const clock = new MockClock();\n const request = new MockRequest();\n const promise = abortOnTimeout(clock.createTimeout, 10000, request, request.response());\n request.reject(new AbortError());\n assert(!request.aborted);\n assert.rejects(promise, AbortError);\n }\n }\n\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport function addCacheBuster(urlStr, random = Math.random) {\n // XHR doesn't have a good way to disable cache,\n // so add a random query param\n // see https://davidtranscend.com/blog/prevent-ie11-cache-ajax-requests/\n if (urlStr.includes(\"?\")) {\n urlStr = urlStr + \"&\";\n } else {\n urlStr = urlStr + \"?\";\n }\n return urlStr + `_cacheBuster=${Math.ceil(random() * Number.MAX_SAFE_INTEGER)}`;\n}\n\nexport function mapAsFormData(map) {\n const formData = new FormData();\n for (const [name, value] of map) {\n // Special case {name: string, blob: BlobHandle} to set a filename.\n // This is the format returned by platform.openFile\n if (value.blob?.nativeBlob && value.name) {\n formData.set(name, value.blob.nativeBlob, value.name);\n } else {\n formData.set(name, value);\n }\n }\n return formData;\n}\n\nexport function tests() {\n return {\n \"add cache buster\": assert => {\n const random = () => 0.5;\n assert.equal(addCacheBuster(\"http://foo\", random), \"http://foo?_cacheBuster=4503599627370496\");\n assert.equal(addCacheBuster(\"http://foo?bar=baz\", random), \"http://foo?bar=baz&_cacheBuster=4503599627370496\");\n }\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {\n AbortError,\n ConnectionError\n} from \"../../../../matrix/error.js\";\nimport {addCacheBuster, mapAsFormData} from \"./common.js\";\n\nclass RequestResult {\n constructor(promise, xhr) {\n this._promise = promise;\n this._xhr = xhr;\n }\n\n abort() {\n this._xhr.abort();\n }\n\n response() {\n return this._promise;\n }\n}\n\nfunction createXhr(url, {method, headers, timeout, format, uploadProgress}) {\n const xhr = new XMLHttpRequest();\n\n if (uploadProgress) {\n xhr.upload.addEventListener(\"progress\", evt => uploadProgress(evt.loaded));\n }\n\n xhr.open(method, url);\n \n if (format === \"buffer\") {\n // important to call this after calling open\n xhr.responseType = \"arraybuffer\";\n }\n if (headers) {\n for(const [name, value] of headers.entries()) {\n try {\n xhr.setRequestHeader(name, value);\n } catch (err) {\n console.info(`Could not set ${name} header: ${err.message}`);\n }\n }\n }\n if (timeout) {\n xhr.timeout = timeout;\n }\n\n return xhr;\n}\n\nfunction xhrAsPromise(xhr, method, url) {\n return new Promise((resolve, reject) => {\n xhr.addEventListener(\"load\", () => resolve(xhr));\n xhr.addEventListener(\"abort\", () => reject(new AbortError()));\n xhr.addEventListener(\"error\", () => reject(new ConnectionError(`Error ${method} ${url}`)));\n xhr.addEventListener(\"timeout\", () => reject(new ConnectionError(`Timeout ${method} ${url}`, true)));\n });\n}\n\nexport function xhrRequest(url, options) {\n let {cache, format, body, method} = options;\n if (!cache) {\n url = addCacheBuster(url);\n }\n const xhr = createXhr(url, options);\n const promise = xhrAsPromise(xhr, method, url).then(xhr => {\n const {status} = xhr;\n let body = null;\n if (format === \"buffer\") {\n body = xhr.response;\n } else if (xhr.getResponseHeader(\"Content-Type\") === \"application/json\") {\n body = JSON.parse(xhr.responseText);\n }\n return {status, body};\n });\n\n // if a BlobHandle, take native blob\n if (body?.nativeBlob) {\n body = body.nativeBlob;\n }\n if (body instanceof Map) {\n body = mapAsFormData(body);\n }\n xhr.send(body || null);\n\n return new RequestResult(promise, xhr);\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {\n AbortError,\n ConnectionError\n} from \"../../../../matrix/error.js\";\nimport {abortOnTimeout} from \"../../../../utils/timeout\";\nimport {addCacheBuster, mapAsFormData} from \"./common.js\";\nimport {xhrRequest} from \"./xhr.js\";\n\nclass RequestResult {\n constructor(promise, controller) {\n if (!controller) {\n const abortPromise = new Promise((_, reject) => {\n this._controller = {\n abort() {\n const err = new Error(\"fetch request aborted\");\n err.name = \"AbortError\";\n reject(err);\n }\n };\n });\n this.promise = Promise.race([promise, abortPromise]);\n } else {\n this.promise = promise;\n this._controller = controller;\n }\n }\n\n abort() {\n this._controller.abort();\n }\n\n response() {\n return this.promise;\n }\n}\n\nexport function createFetchRequest(createTimeout, serviceWorkerHandler) {\n return function fetchRequest(url, requestOptions) {\n if (serviceWorkerHandler?.haltRequests) {\n // prevent any requests while waiting\n // for the new service worker to get activated.\n // Once this happens, the page will be reloaded\n // by the serviceWorkerHandler so this is fine.\n return new RequestResult(new Promise(() => {}), {});\n }\n // fetch doesn't do upload progress yet, delegate to xhr\n if (requestOptions?.uploadProgress) {\n return xhrRequest(url, requestOptions);\n }\n let {method, headers, body, timeout, format, cache = false} = requestOptions;\n const controller = typeof AbortController === \"function\" ? new AbortController() : null;\n // if a BlobHandle, take native blob\n if (body?.nativeBlob) {\n body = body.nativeBlob;\n }\n if (body instanceof Map) {\n body = mapAsFormData(body);\n }\n let options = {method, body};\n if (controller) {\n options = Object.assign(options, {\n signal: controller.signal\n });\n }\n if (!cache) {\n url = addCacheBuster(url);\n }\n options = Object.assign(options, {\n mode: \"cors\",\n credentials: \"omit\",\n referrer: \"no-referrer\",\n // ideally we'd turn off cache here, but Safari interprets\n // `Access-Control-Allow-Headers` strictly (only when fetch is\n // intercepted by a service worker strangely enough), in that\n // it gives a CORS error if Cache-Control is not present\n // in the list of allowed headers (which it isn't commonly, at least not on matrix.org).\n // With no-store or no-cache here, it does set `Cache-Control`\n // so we don't do that, and prevent caching with `addCacheBuster`.\n // We also hope the server responds with `Cache-Control: no-store` so\n // we don't fill the http cache with api responses.\n // \n // cache: \"no-store\",\n cache: \"default\",\n });\n if (headers) {\n const fetchHeaders = new Headers();\n for(const [name, value] of headers.entries()) {\n fetchHeaders.append(name, value);\n }\n options.headers = fetchHeaders;\n }\n const promise = fetch(url, options).then(async response => {\n const {status} = response;\n let body;\n try {\n if (format === \"json\") {\n body = await response.json();\n } else if (format === \"buffer\") {\n body = await response.arrayBuffer();\n }\n else if (format === \"text\") {\n body = await response.text();\n }\n } catch (err) {\n // some error pages return html instead of json, ignore error\n if (!(err.name === \"SyntaxError\" && status >= 400)) {\n throw err;\n }\n }\n return {status, body};\n }, err => {\n if (err.name === \"AbortError\") {\n // map DOMException with name AbortError to our own AbortError type\n // as we don't want DOMExceptions in the protocol layer.\n throw new AbortError();\n } else if (err instanceof TypeError) {\n // Network errors are reported as TypeErrors, see\n // https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Checking_that_the_fetch_was_successful\n // this can either mean user is offline, server is offline, or a CORS error (server misconfiguration).\n // \n // One could check navigator.onLine to rule out the first\n // but the 2 latter ones are indistinguishable from javascript.\n throw new ConnectionError(`${method} ${url}: ${err.message}`);\n }\n throw err;\n });\n const result = new RequestResult(promise, controller);\n\n if (timeout) {\n result.promise = abortOnTimeout(createTimeout, timeout, result, result.promise);\n }\n\n return result;\n } \n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\ninterface ISessionInfo {\n id: string;\n deviceId: string;\n userId: string;\n homeserver: string;\n homeServer: string; // deprecate this over time\n accessToken: string;\n lastUsed: number;\n}\n\n// todo: this should probably be in platform/types?\ninterface ISessionInfoStorage {\n getAll(): Promise<ISessionInfo[]>;\n updateLastUsed(id: string, timestamp: number): Promise<void>;\n get(id: string): Promise<ISessionInfo | undefined>;\n add(sessionInfo: ISessionInfo): Promise<void>;\n delete(sessionId: string): Promise<void>;\n}\n\nexport class SessionInfoStorage implements ISessionInfoStorage {\n private readonly _name: string;\n\n constructor(name: string) {\n this._name = name;\n }\n\n getAll(): Promise<ISessionInfo[]> {\n const sessionsJson = localStorage.getItem(this._name);\n if (sessionsJson) {\n const sessions = JSON.parse(sessionsJson);\n if (Array.isArray(sessions)) {\n return Promise.resolve(sessions);\n }\n }\n return Promise.resolve([]);\n }\n\n async updateLastUsed(id: string, timestamp: number): Promise<void> {\n const sessions = await this.getAll();\n if (sessions) {\n const session = sessions.find(session => session.id === id);\n if (session) {\n session.lastUsed = timestamp;\n localStorage.setItem(this._name, JSON.stringify(sessions));\n }\n }\n }\n\n async get(id: string): Promise<ISessionInfo | undefined> {\n const sessions = await this.getAll();\n if (sessions) {\n return sessions.find(session => session.id === id);\n }\n }\n\n async add(sessionInfo: ISessionInfo): Promise<void> {\n const sessions = await this.getAll();\n sessions.push(sessionInfo);\n localStorage.setItem(this._name, JSON.stringify(sessions));\n }\n\n async delete(sessionId: string): Promise<void> {\n let sessions = await this.getAll();\n sessions = sessions.filter(s => s.id !== sessionId);\n localStorage.setItem(this._name, JSON.stringify(sessions));\n }\n \n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport class SettingsStorage {\n constructor(prefix) {\n this._prefix = prefix;\n }\n\n async setInt(key, value) {\n this._set(key, value);\n }\n\n async getInt(key, defaultValue = 0) {\n const value = window.localStorage.getItem(`${this._prefix}${key}`);\n if (typeof value === \"string\") {\n return parseInt(value, 10);\n }\n return defaultValue;\n }\n\n async setBool(key, value) {\n this._set(key, value);\n }\n\n async getBool(key, defaultValue = false) {\n const value = window.localStorage.getItem(`${this._prefix}${key}`);\n if (typeof value === \"string\") {\n return value === \"true\";\n }\n return defaultValue;\n }\n\n async setString(key, value) {\n this._set(key, value);\n }\n\n async getString(key) {\n return window.localStorage.getItem(`${this._prefix}${key}`);\n }\n\n async remove(key) {\n window.localStorage.removeItem(`${this._prefix}${key}`);\n }\n\n async _set(key, value) {\n window.localStorage.setItem(`${this._prefix}${key}`, value);\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\nexport class UTF8 {\n constructor() {\n this._encoder = null;\n this._decoder = null;\n }\n\n encode(str) {\n if (!this._encoder) {\n this._encoder = new TextEncoder();\n }\n return this._encoder.encode(str);\n }\n\n decode(buffer) {\n if (!this._decoder) {\n this._decoder = new TextDecoder();\n }\n return this._decoder.decode(buffer);\n }\n}\n","/*\n * base64-arraybuffer\n * https://github.com/niklasvh/base64-arraybuffer\n *\n * Copyright (c) 2012 Niklas von Hertzen\n * Licensed under the MIT license.\n */\n(function(){\n \"use strict\";\n\n var chars = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\n // Use a lookup table to find the index.\n var lookup = new Uint8Array(256);\n for (var i = 0; i < chars.length; i++) {\n lookup[chars.charCodeAt(i)] = i;\n }\n\n exports.encode = function(arraybuffer) {\n var bytes = new Uint8Array(arraybuffer),\n i, len = bytes.length, base64 = \"\";\n\n for (i = 0; i < len; i+=3) {\n base64 += chars[bytes[i] >> 2];\n base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)];\n base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)];\n base64 += chars[bytes[i + 2] & 63];\n }\n\n if ((len % 3) === 2) {\n base64 = base64.substring(0, base64.length - 1) + \"=\";\n } else if (len % 3 === 1) {\n base64 = base64.substring(0, base64.length - 2) + \"==\";\n }\n\n return base64;\n };\n\n exports.decode = function(base64) {\n var bufferLength = base64.length * 0.75,\n len = base64.length, i, p = 0,\n encoded1, encoded2, encoded3, encoded4;\n\n if (base64[base64.length - 1] === \"=\") {\n bufferLength--;\n if (base64[base64.length - 2] === \"=\") {\n bufferLength--;\n }\n }\n\n var arraybuffer = new ArrayBuffer(bufferLength),\n bytes = new Uint8Array(arraybuffer);\n\n for (i = 0; i < len; i+=4) {\n encoded1 = lookup[base64.charCodeAt(i)];\n encoded2 = lookup[base64.charCodeAt(i+1)];\n encoded3 = lookup[base64.charCodeAt(i+2)];\n encoded4 = lookup[base64.charCodeAt(i+3)];\n\n bytes[p++] = (encoded1 << 2) | (encoded2 >> 4);\n bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);\n bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);\n }\n\n return arraybuffer;\n };\n})();\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport base64 from \"base64-arraybuffer\";\n\nexport class Base64 {\n encodeUnpadded(buffer) {\n const str = base64.encode(buffer);\n const paddingIdx = str.indexOf(\"=\");\n if (paddingIdx !== -1) {\n return str.substr(0, paddingIdx);\n } else {\n return str;\n }\n }\n\n encode(buffer) {\n return base64.encode(buffer);\n }\n\n decode(str) {\n return base64.decode(str);\n }\n}\n","var Buffer = {\n isBuffer: function(array) {return array instanceof Uint8Array;},\n from: function(arrayBuffer) {return arrayBuffer;},\n allocUnsafe: function(size) {return Buffer.alloc(size);},\n alloc: function(size) {return new Uint8Array(size);}\n};\nexport default Buffer;\n","'use strict'\n// base-x encoding / decoding\n// Copyright (c) 2018 base-x contributors\n// Copyright (c) 2014-2018 The Bitcoin Core developers (base58.cpp)\n// Distributed under the MIT software license, see the accompanying\n// file LICENSE or http://www.opensource.org/licenses/mit-license.php.\n// @ts-ignore\nvar _Buffer = require('safe-buffer').Buffer\nfunction base (ALPHABET) {\n if (ALPHABET.length >= 255) { throw new TypeError('Alphabet too long') }\n var BASE_MAP = new Uint8Array(256)\n for (var j = 0; j < BASE_MAP.length; j++) {\n BASE_MAP[j] = 255\n }\n for (var i = 0; i < ALPHABET.length; i++) {\n var x = ALPHABET.charAt(i)\n var xc = x.charCodeAt(0)\n if (BASE_MAP[xc] !== 255) { throw new TypeError(x + ' is ambiguous') }\n BASE_MAP[xc] = i\n }\n var BASE = ALPHABET.length\n var LEADER = ALPHABET.charAt(0)\n var FACTOR = Math.log(BASE) / Math.log(256) // log(BASE) / log(256), rounded up\n var iFACTOR = Math.log(256) / Math.log(BASE) // log(256) / log(BASE), rounded up\n function encode (source) {\n if (Array.isArray(source) || source instanceof Uint8Array) { source = _Buffer.from(source) }\n if (!_Buffer.isBuffer(source)) { throw new TypeError('Expected Buffer') }\n if (source.length === 0) { return '' }\n // Skip & count leading zeroes.\n var zeroes = 0\n var length = 0\n var pbegin = 0\n var pend = source.length\n while (pbegin !== pend && source[pbegin] === 0) {\n pbegin++\n zeroes++\n }\n // Allocate enough space in big-endian base58 representation.\n var size = ((pend - pbegin) * iFACTOR + 1) >>> 0\n var b58 = new Uint8Array(size)\n // Process the bytes.\n while (pbegin !== pend) {\n var carry = source[pbegin]\n // Apply \"b58 = b58 * 256 + ch\".\n var i = 0\n for (var it1 = size - 1; (carry !== 0 || i < length) && (it1 !== -1); it1--, i++) {\n carry += (256 * b58[it1]) >>> 0\n b58[it1] = (carry % BASE) >>> 0\n carry = (carry / BASE) >>> 0\n }\n if (carry !== 0) { throw new Error('Non-zero carry') }\n length = i\n pbegin++\n }\n // Skip leading zeroes in base58 result.\n var it2 = size - length\n while (it2 !== size && b58[it2] === 0) {\n it2++\n }\n // Translate the result into a string.\n var str = LEADER.repeat(zeroes)\n for (; it2 < size; ++it2) { str += ALPHABET.charAt(b58[it2]) }\n return str\n }\n function decodeUnsafe (source) {\n if (typeof source !== 'string') { throw new TypeError('Expected String') }\n if (source.length === 0) { return _Buffer.alloc(0) }\n var psz = 0\n // Skip leading spaces.\n if (source[psz] === ' ') { return }\n // Skip and count leading '1's.\n var zeroes = 0\n var length = 0\n while (source[psz] === LEADER) {\n zeroes++\n psz++\n }\n // Allocate enough space in big-endian base256 representation.\n var size = (((source.length - psz) * FACTOR) + 1) >>> 0 // log(58) / log(256), rounded up.\n var b256 = new Uint8Array(size)\n // Process the characters.\n while (source[psz]) {\n // Decode character\n var carry = BASE_MAP[source.charCodeAt(psz)]\n // Invalid character\n if (carry === 255) { return }\n var i = 0\n for (var it3 = size - 1; (carry !== 0 || i < length) && (it3 !== -1); it3--, i++) {\n carry += (BASE * b256[it3]) >>> 0\n b256[it3] = (carry % 256) >>> 0\n carry = (carry / 256) >>> 0\n }\n if (carry !== 0) { throw new Error('Non-zero carry') }\n length = i\n psz++\n }\n // Skip trailing spaces.\n if (source[psz] === ' ') { return }\n // Skip leading zeroes in b256.\n var it4 = size - length\n while (it4 !== size && b256[it4] === 0) {\n it4++\n }\n var vch = _Buffer.allocUnsafe(zeroes + (size - it4))\n vch.fill(0x00, 0, zeroes)\n var j = zeroes\n while (it4 !== size) {\n vch[j++] = b256[it4++]\n }\n return vch\n }\n function decode (string) {\n var buffer = decodeUnsafe(string)\n if (buffer) { return buffer }\n throw new Error('Non-base' + BASE + ' character')\n }\n return {\n encode: encode,\n decodeUnsafe: decodeUnsafe,\n decode: decode\n }\n}\nmodule.exports = base\n","var basex = require('base-x')\nvar ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'\n\nmodule.exports = basex(ALPHABET)\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport bs58 from \"bs58\";\n\nexport class Base58 {\n encode(buffer) {\n return bs58.encode(buffer);\n }\n\n decode(str) {\n return bs58.decode(str);\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {UTF8} from \"../dom/UTF8.js\";\nimport {Base64} from \"./Base64.js\";\nimport {Base58} from \"./Base58.js\";\n\nexport class Encoding {\n constructor() {\n this.utf8 = new UTF8();\n this.base64 = new Base64();\n this.base58 = new Base58();\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport class OlmWorker {\n constructor(workerPool) {\n this._workerPool = workerPool;\n }\n\n megolmDecrypt(session, ciphertext) {\n const sessionKey = session.export_session(session.first_known_index());\n return this._workerPool.send({type: \"megolm_decrypt\", ciphertext, sessionKey});\n }\n\n async createAccountAndOTKs(account, otkAmount) {\n // IE11 does not support getRandomValues in a worker, so we have to generate the values upfront.\n let randomValues;\n if (window.msCrypto) {\n randomValues = [\n window.msCrypto.getRandomValues(new Uint8Array(64)),\n window.msCrypto.getRandomValues(new Uint8Array(otkAmount * 32)),\n ];\n }\n const pickle = await this._workerPool.send({type: \"olm_create_account_otks\", randomValues, otkAmount}).response();\n account.unpickle(\"\", pickle);\n }\n\n async createOutboundOlmSession(account, newSession, theirIdentityKey, theirOneTimeKey) {\n const accountPickle = account.pickle(\"\");\n let randomValues;\n if (window.msCrypto) {\n randomValues = [\n window.msCrypto.getRandomValues(new Uint8Array(64)),\n ];\n }\n const sessionPickle = await this._workerPool.send({type: \"olm_create_outbound\", accountPickle, theirIdentityKey, theirOneTimeKey, randomValues}).response();\n newSession.unpickle(\"\", sessionPickle);\n }\n\n dispose() {\n this._workerPool.dispose();\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {\n openDatabase,\n txnAsPromise,\n reqAsPromise,\n iterateCursor,\n fetchResults,\n} from \"../matrix/storage/idb/utils\";\nimport {BaseLogger} from \"./BaseLogger\";\nimport type {Interval} from \"../platform/web/dom/Clock\";\nimport type {Platform} from \"../platform/web/Platform.js\";\nimport type {BlobHandle} from \"../platform/web/dom/BlobHandle.js\";\nimport type {ILogItem, ILogExport, ISerializedItem} from \"./types\";\nimport type {LogFilter} from \"./LogFilter\";\n\ntype QueuedItem = {\n json: string;\n id?: number;\n}\n\nexport class IDBLogger extends BaseLogger {\n private readonly _name: string;\n private readonly _limit: number;\n private readonly _flushInterval: Interval;\n private _queuedItems: QueuedItem[];\n\n constructor(options: {name: string, flushInterval?: number, limit?: number, platform: Platform, serializedTransformer?: (item: ISerializedItem) => ISerializedItem}) {\n super(options);\n const {name, flushInterval = 60 * 1000, limit = 3000} = options;\n this._name = name;\n this._limit = limit;\n this._queuedItems = this._loadQueuedItems();\n // TODO: also listen for unload just in case sync keeps on running after pagehide is fired?\n window.addEventListener(\"pagehide\", this, false);\n this._flushInterval = this._platform.clock.createInterval(() => this._tryFlush(), flushInterval);\n }\n\n // TODO: move dispose to ILogger, listen to pagehide elsewhere and call dispose from there, which calls _finishAllAndFlush\n dispose(): void {\n window.removeEventListener(\"pagehide\", this, false);\n this._flushInterval.dispose();\n }\n\n handleEvent(evt: Event): void {\n if (evt.type === \"pagehide\") {\n this._finishAllAndFlush();\n }\n }\n\n async _tryFlush(): Promise<void> {\n const db = await this._openDB();\n try {\n const txn = db.transaction([\"logs\"], \"readwrite\");\n const logs = txn.objectStore(\"logs\");\n const amount = this._queuedItems.length;\n for(const i of this._queuedItems) {\n logs.add(i);\n }\n const itemCount = await reqAsPromise(logs.count());\n if (itemCount > this._limit) {\n // delete an extra 10% so we don't need to delete every time we flush\n let deleteAmount = (itemCount - this._limit) + Math.round(0.1 * this._limit);\n await iterateCursor(logs.openCursor(), (_, __, cursor) => {\n cursor.delete();\n deleteAmount -= 1;\n return {done: deleteAmount === 0};\n });\n }\n await txnAsPromise(txn);\n this._queuedItems.splice(0, amount);\n } catch (err) {\n console.error(\"Could not flush logs\", err);\n } finally {\n try {\n db.close();\n } catch (e) {}\n }\n }\n\n _finishAllAndFlush(): void {\n this._finishOpenItems();\n this.log({l: \"pagehide, closing logs\", t: \"navigation\"});\n this._persistQueuedItems(this._queuedItems);\n }\n\n _loadQueuedItems(): QueuedItem[] {\n const key = `${this._name}_queuedItems`;\n try {\n const json = window.localStorage.getItem(key);\n if (json) {\n window.localStorage.removeItem(key);\n return JSON.parse(json);\n }\n } catch (err) {\n console.error(\"Could not load queued log items\", err);\n }\n return [];\n }\n\n _openDB(): Promise<IDBDatabase> {\n return openDatabase(this._name, db => db.createObjectStore(\"logs\", {keyPath: \"id\", autoIncrement: true}), 1);\n }\n \n _persistItem(logItem: ILogItem, filter: LogFilter, forced: boolean): void {\n const serializedItem = logItem.serialize(filter, undefined, forced);\n if (serializedItem) {\n const transformedSerializedItem = this._serializedTransformer(serializedItem);\n this._queuedItems.push({\n json: JSON.stringify(transformedSerializedItem)\n });\n }\n }\n\n _persistQueuedItems(items: QueuedItem[]): void {\n try {\n window.localStorage.setItem(`${this._name}_queuedItems`, JSON.stringify(items));\n } catch (e) {\n console.error(\"Could not persist queued log items in localStorage, they will likely be lost\", e);\n }\n }\n\n async export(): Promise<ILogExport> {\n const db = await this._openDB();\n try {\n const txn = db.transaction([\"logs\"], \"readonly\");\n const logs = txn.objectStore(\"logs\");\n const storedItems: QueuedItem[] = await fetchResults(logs.openCursor(), () => false);\n const allItems = storedItems.concat(this._queuedItems);\n return new IDBLogExport(allItems, this, this._platform);\n } finally {\n try {\n db.close();\n } catch (e) {}\n }\n }\n\n async _removeItems(items: QueuedItem[]): Promise<void> {\n const db = await this._openDB();\n try {\n const txn = db.transaction([\"logs\"], \"readwrite\");\n const logs = txn.objectStore(\"logs\");\n for (const item of items) {\n if (typeof item.id === \"number\") {\n logs.delete(item.id);\n } else {\n // assume the (non-persisted) object in each array will be the same\n const queuedIdx = this._queuedItems.indexOf(item);\n if (queuedIdx === -1) {\n this._queuedItems.splice(queuedIdx, 1);\n }\n }\n }\n await txnAsPromise(txn);\n } finally {\n try {\n db.close();\n } catch (e) {}\n }\n }\n}\n\nclass IDBLogExport implements ILogExport {\n private readonly _items: QueuedItem[];\n private readonly _logger: IDBLogger;\n private readonly _platform: Platform;\n\n constructor(items: QueuedItem[], logger: IDBLogger, platform: Platform) {\n this._items = items;\n this._logger = logger;\n this._platform = platform;\n }\n \n get count(): number {\n return this._items.length;\n }\n\n /**\n * @return {Promise}\n */\n removeFromStore(): Promise<void> {\n return this._logger._removeItems(this._items);\n }\n\n asBlob(): BlobHandle {\n const log = {\n formatVersion: 1,\n appVersion: this._platform.updateService?.version,\n items: this._items.map(i => JSON.parse(i.json))\n };\n const json = JSON.stringify(log);\n const buffer: Uint8Array = this._platform.encoding.utf8.encode(json);\n const blob: BlobHandle = this._platform.createBlob(buffer, \"application/json\");\n return blob;\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2021 Daniel Fedorin <danila.fedorin@gmail.com>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// DOM helper functions\n\nimport {ViewNode} from \"./types\";\n\nexport type ClassNames<T> = { [className: string]: boolean | ((value: T) => boolean) }\nexport type BasicAttributes<T> = { [attribute: string]: ClassNames<T> | boolean | string }\nexport type Child = string | Text | ViewNode;\n\nexport function isChildren(children: object | Child | Child[]): children is Child | Child[] {\n // children should be an not-object (that's the attributes), or a domnode, or an array\n return typeof children !== \"object\" || \"nodeType\" in children || Array.isArray(children);\n}\n\nexport function classNames<T>(obj: ClassNames<T>, value: T): string {\n return Object.entries(obj).reduce((cn, [name, enabled]) => {\n if (typeof enabled === \"function\") {\n enabled = enabled(value);\n }\n if (enabled) {\n return cn + (cn.length ? \" \" : \"\") + name;\n } else {\n return cn;\n }\n }, \"\");\n}\n\nexport function setAttribute(el: Element, name: string, value: string | boolean): void {\n if (name === \"className\") {\n name = \"class\";\n }\n if (value === false) {\n el.removeAttribute(name);\n } else {\n if (value === true) {\n value = name;\n }\n el.setAttribute(name, value);\n }\n}\n\nexport function el(elementName: string, attributes?: BasicAttributes<never> | Child | Child[], children?: Child | Child[]): Element {\n return elNS(HTML_NS, elementName, attributes, children);\n}\n\nexport function elNS(ns: string, elementName: string, attributes?: BasicAttributes<never> | Child | Child[], children?: Child | Child[]): Element {\n if (attributes && isChildren(attributes)) {\n children = attributes;\n attributes = undefined;\n }\n\n const e = document.createElementNS(ns, elementName);\n\n if (attributes) {\n for (let [name, value] of Object.entries(attributes)) {\n if (typeof value === \"object\") {\n // Only className should ever be an object; be careful\n // here anyway and ignore object-valued non-className attributes.\n value = (value !== null && name === \"className\") ? classNames(value, undefined) : false;\n }\n setAttribute(e, name, value);\n }\n }\n\n if (children) {\n if (!Array.isArray(children)) {\n children = [children];\n }\n for (let c of children) {\n if (typeof c === \"string\") {\n c = text(c);\n }\n e.appendChild(c);\n }\n }\n return e;\n}\n\nexport function text(str: string): Text {\n return document.createTextNode(str);\n}\n\nexport const HTML_NS: string = \"http://www.w3.org/1999/xhtml\";\nexport const SVG_NS: string = \"http://www.w3.org/2000/svg\";\n\nexport const TAG_NAMES = {\n [HTML_NS]: [\n \"br\", \"a\", \"ol\", \"ul\", \"li\", \"div\", \"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\",\n \"p\", \"strong\", \"em\", \"span\", \"img\", \"section\", \"main\", \"article\", \"aside\", \"del\", \"blockquote\",\n \"table\", \"thead\", \"tbody\", \"tr\", \"th\", \"td\", \"hr\",\n \"pre\", \"code\", \"button\", \"time\", \"input\", \"textarea\", \"select\", \"option\", \"label\", \"form\",\n \"progress\", \"output\", \"video\"],\n [SVG_NS]: [\"svg\", \"g\", \"path\", \"circle\", \"ellipse\", \"rect\", \"use\"]\n} as const;\n\nexport const tag: { [tagName in typeof TAG_NAMES[string][number]]: (attributes?: BasicAttributes<never> | Child | Child[], children?: Child | Child[]) => Element } = {} as any;\n\nfor (const [ns, tags] of Object.entries(TAG_NAMES)) {\n for (const tagName of tags) {\n tag[tagName] = function(attributes, children) {\n return elNS(ns, tagName, attributes, children);\n }\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {IView, IMountArgs, ViewNode} from \"./types\";\nimport {tag} from \"./html\";\n\nexport function mountView(view: IView, mountArgs?: IMountArgs): ViewNode {\n let node;\n try {\n node = view.mount(mountArgs);\n } catch (err) {\n node = errorToDOM(err);\n }\n return node;\n}\n\nexport function errorToDOM(error: Error): Element {\n const stack = new Error().stack;\n let callee: string | null = null;\n if (stack) {\n callee = stack.split(\"\\n\")[1];\n }\n return tag.div([\n tag.h2(\"Something went wrong…\"),\n tag.h3(error.message),\n tag.p(`This occurred while running ${callee}.`),\n tag.pre(error.stack),\n ]);\n}\n\nexport function insertAt(parentNode: Element, idx: number, childNode: Node): void {\n const isLast = idx === parentNode.childElementCount;\n if (isLast) {\n parentNode.appendChild(childNode);\n } else {\n const nextDomNode = parentNode.children[idx];\n parentNode.insertBefore(childNode, nextDomNode);\n }\n}\n\nexport function removeChildren(parentNode: Element): void {\n parentNode.innerHTML = '';\n}\n\nexport function disableTargetCallback(callback: (evt: Event) => Promise<void>): (evt: Event) => Promise<void> {\n return async (evt: Event) => {\n (evt.target as HTMLElement)?.setAttribute(\"disabled\", \"disabled\");\n await callback(evt);\n (evt.target as HTMLElement)?.removeAttribute(\"disabled\");\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {el} from \"./html\";\nimport {mountView, insertAt} from \"./utils\";\nimport {SubscriptionHandle} from \"../../../../observable/BaseObservable\";\nimport {BaseObservableList as ObservableList, IListObserver} from \"../../../../observable/list/BaseObservableList\";\nimport {IView, IMountArgs} from \"./types\";\n\nexport interface IOptions<T, V> {\n list: ObservableList<T>,\n onItemClick?: (childView: V, evt: UIEvent) => void,\n className?: string,\n tagName?: string,\n parentProvidesUpdates?: boolean\n}\n\nexport class ListView<T, V extends IView> implements IView, IListObserver<T> {\n\n private _onItemClick?: (childView: V, evt: UIEvent) => void;\n private _className?: string;\n private _tagName: string;\n private _root?: Element;\n protected _subscription?: SubscriptionHandle;\n protected _childCreator: (value: T) => V;\n protected _mountArgs: IMountArgs;\n protected _list: ObservableList<T>;\n protected _childInstances?: V[];\n\n constructor(\n {list, onItemClick, className, tagName = \"ul\", parentProvidesUpdates = true}: IOptions<T, V>, \n childCreator: (value: T) => V\n ) {\n this._onItemClick = onItemClick;\n this._list = list;\n this._className = className;\n this._tagName = tagName;\n this._root = undefined;\n this._subscription = undefined;\n this._childCreator = childCreator;\n this._childInstances = undefined;\n this._mountArgs = {parentProvidesUpdates};\n }\n\n root(): Element | undefined {\n // won't be undefined when called between mount and unmount\n return this._root;\n }\n\n update(attributes: IOptions<T, V>) {\n if (attributes.list) {\n if (this._subscription) {\n this._unloadList();\n while (this._root!.lastChild) {\n this._root!.lastChild.remove();\n }\n }\n this._list = attributes.list;\n this.loadList();\n }\n }\n\n mount(): Element {\n const attr: {[name: string]: any} = {};\n if (this._className) {\n attr.className = this._className;\n }\n const root = this._root = el(this._tagName, attr);\n this.loadList();\n if (this._onItemClick) {\n root.addEventListener(\"click\", this);\n }\n return root;\n }\n\n handleEvent(evt: Event) {\n if (evt.type === \"click\") {\n this._handleClick(evt as UIEvent);\n }\n }\n\n unmount(): void {\n if (this._list) {\n this._unloadList();\n }\n }\n\n private _handleClick(event: UIEvent) {\n if (event.target === this._root || !this._onItemClick) {\n return;\n }\n let childNode = event.target as Element;\n while (childNode.parentNode !== this._root) {\n childNode = childNode.parentNode as Element;\n }\n const index = Array.prototype.indexOf.call(this._root!.childNodes, childNode);\n const childView = this._childInstances![index];\n if (childView) {\n this._onItemClick(childView, event);\n }\n }\n\n private _unloadList() {\n this._subscription = this._subscription!();\n for (let child of this._childInstances!) {\n child.unmount();\n }\n this._childInstances = undefined;\n }\n\n protected loadList() {\n if (!this._list) {\n return;\n }\n this._subscription = this._list.subscribe(this);\n this._childInstances = [];\n const fragment = document.createDocumentFragment();\n for (let item of this._list) {\n const child = this._childCreator(item);\n this._childInstances!.push(child);\n fragment.appendChild(mountView(child, this._mountArgs));\n }\n this._root!.appendChild(fragment);\n }\n\n onReset() {\n for (const child of this._childInstances!) {\n child.root()!.remove();\n child.unmount();\n }\n this._childInstances!.length = 0;\n }\n\n onAdd(idx: number, value: T) {\n this.addChild(idx, value);\n }\n\n onRemove(idx: number, value: T) {\n this.removeChild(idx);\n }\n\n onMove(fromIdx: number, toIdx: number, value: T) {\n this.moveChild(fromIdx, toIdx);\n }\n\n onUpdate(i: number, value: T, params: any) {\n this.updateChild(i, value, params);\n }\n\n protected addChild(childIdx: number, value: T) {\n const child = this._childCreator(value);\n this._childInstances!.splice(childIdx, 0, child);\n insertAt(this._root!, childIdx, mountView(child, this._mountArgs));\n }\n\n protected removeChild(childIdx: number) {\n const [child] = this._childInstances!.splice(childIdx, 1);\n child.root()!.remove();\n child.unmount();\n }\n\n protected moveChild(fromChildIdx: number, toChildIdx: number) {\n const [child] = this._childInstances!.splice(fromChildIdx, 1);\n this._childInstances!.splice(toChildIdx, 0, child);\n child.root()!.remove();\n insertAt(this._root!, toChildIdx, child.root()! as Element);\n }\n\n protected updateChild(childIdx: number, value: T, params: any) {\n if (this._childInstances) {\n const instance = this._childInstances![childIdx];\n instance && instance.update(value, params);\n }\n }\n\n // TODO: is this the list or view index? \n protected recreateItem(index: number, value: T) {\n if (this._childInstances) {\n const child = this._childCreator(value);\n if (!child) {\n this.onRemove(index, value);\n } else {\n const [oldChild] = this._childInstances!.splice(index, 1, child);\n this._root!.replaceChild(child.mount(this._mountArgs), oldChild.root()!);\n oldChild.unmount();\n }\n }\n }\n\n public getChildInstanceByIndex(idx: number): V | undefined {\n return this._childInstances?.[idx];\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\nCopyright 2021 Daniel Fedorin <danila.fedorin@gmail.com>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {IMountArgs, ViewNode, IView} from \"./types\";\n\nexport interface IObservableValue {\n on?(event: \"change\", handler: (props?: string[]) => void): void;\n off?(event: \"change\", handler: (props?: string[]) => void): void;\n}\n\nexport abstract class BaseUpdateView<T extends IObservableValue> implements IView {\n protected _value: T\n protected _boundUpdateFromValue: ((props?: string[]) => void) | null\n\n abstract mount(args?: IMountArgs): ViewNode;\n abstract root(): ViewNode | undefined;\n abstract update(...any);\n\n constructor(value :T) {\n this._value = value;\n // TODO: can avoid this if we adopt the handleEvent pattern in our EventListener\n this._boundUpdateFromValue = null;\n }\n\n subscribeOnMount(options?: IMountArgs): void {\n const parentProvidesUpdates = options && options.parentProvidesUpdates;\n if (!parentProvidesUpdates) {\n this._subscribe();\n }\n }\n\n unmount(): void {\n this._unsubscribe();\n }\n\n get value(): T {\n return this._value;\n }\n\n _updateFromValue(changedProps?: string[]) {\n this.update(this._value, changedProps);\n }\n\n _subscribe(): void {\n if (typeof this._value?.on === \"function\") {\n this._boundUpdateFromValue = this._updateFromValue.bind(this) as (props?: string[]) => void;\n this._value.on(\"change\", this._boundUpdateFromValue);\n }\n }\n\n _unsubscribe(): void {\n if (this._boundUpdateFromValue) {\n if (typeof this._value.off === \"function\") {\n this._value.off(\"change\", this._boundUpdateFromValue);\n }\n this._boundUpdateFromValue = null;\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2021 Daniel Fedorin <danila.fedorin@gmail.com>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { setAttribute, text, isChildren, classNames, TAG_NAMES, HTML_NS, ClassNames, Child as NonBoundChild} from \"./html\";\nimport {mountView} from \"./utils\";\nimport {BaseUpdateView, IObservableValue} from \"./BaseUpdateView\";\nimport {IMountArgs, ViewNode, IView} from \"./types\";\n\nfunction objHasFns(obj: ClassNames<unknown>): obj is { [className: string]: boolean } {\n for(const value of Object.values(obj)) {\n if (typeof value === \"function\") {\n return true;\n }\n }\n return false;\n}\n\nexport type RenderFn<T> = (t: Builder<T>, vm: T) => ViewNode;\ntype TextBinding<T> = (T) => string | number | boolean | undefined | null;\ntype Child<T> = NonBoundChild | TextBinding<T>;\ntype Children<T> = Child<T> | Child<T>[];\ntype EventHandler = ((event: Event) => void);\ntype AttributeStaticValue = string | boolean;\ntype AttributeBinding<T> = (value: T) => AttributeStaticValue;\nexport type AttrValue<T> = AttributeStaticValue | AttributeBinding<T> | EventHandler | ClassNames<T>;\nexport type Attributes<T> = { [attribute: string]: AttrValue<T> };\ntype ElementFn<T> = (attributes?: Attributes<T> | Children<T>, children?: Children<T>) => Element;\nexport type Builder<T> = TemplateBuilder<T> & { [tagName in typeof TAG_NAMES[string][number]]: ElementFn<T> };\n\n/**\n Bindable template. Renders once, and allows bindings for given nodes. If you need\n to change the structure on a condition, use a subtemplate (if)\n\n supports\n - event handlers (attribute fn value with name that starts with on)\n - one way binding of attributes (other attribute fn value)\n - one way binding of text values (child fn value)\n - refs to get dom nodes\n - className binding returning object with className => enabled map\n - add subviews inside the template\n*/\n// TODO: should we rename this to BoundView or something? As opposed to StaticView ...\nexport abstract class TemplateView<T extends IObservableValue> extends BaseUpdateView<T> {\n private _eventListeners?: { node: Element, name: string, fn: EventHandler, useCapture: boolean }[] = undefined;\n private _bindings?: (() => void)[] = undefined;\n private _root?: ViewNode = undefined;\n // public because used by TemplateBuilder\n _subViews?: IView[] = undefined;\n\n _attach(): void {\n if (this._eventListeners) {\n for (let {node, name, fn, useCapture} of this._eventListeners) {\n node.addEventListener(name, fn, useCapture);\n }\n }\n }\n\n _detach(): void {\n if (this._eventListeners) {\n for (let {node, name, fn, useCapture} of this._eventListeners) {\n node.removeEventListener(name, fn, useCapture);\n }\n }\n }\n\n abstract render(t: Builder<T>, value: T): ViewNode;\n\n mount(options?: IMountArgs): ViewNode {\n const builder = new TemplateBuilder(this) as Builder<T>;\n try {\n this._root = this.render(builder, this._value);\n } finally {\n builder.close();\n }\n // takes care of update being called when needed\n this.subscribeOnMount(options);\n this._attach();\n return this._root!;\n }\n\n unmount(): void {\n this._detach();\n super.unmount();\n if (this._subViews) {\n for (const v of this._subViews) {\n v.unmount();\n }\n }\n }\n\n root(): ViewNode | undefined {\n return this._root;\n }\n\n update(value: T, props?: string[]): void {\n this._value = value;\n if (this._bindings) {\n for (const binding of this._bindings) {\n binding();\n }\n }\n }\n\n _addEventListener(node: Element, name: string, fn: (event: Event) => void, useCapture: boolean = false): void {\n if (!this._eventListeners) {\n this._eventListeners = [];\n }\n this._eventListeners.push({node, name, fn, useCapture});\n }\n\n _addBinding(bindingFn: () => void): void {\n if (!this._bindings) {\n this._bindings = [];\n }\n this._bindings.push(bindingFn);\n }\n\n addSubView(view: IView): void {\n if (!this._subViews) {\n this._subViews = [];\n }\n this._subViews.push(view);\n }\n\n removeSubView(view: IView): void {\n if (!this._subViews) { return; }\n const idx = this._subViews.indexOf(view);\n if (idx !== -1) {\n this._subViews.splice(idx, 1);\n }\n }\n\n updateSubViews(value: IObservableValue, props: string[]) {\n if (this._subViews) {\n for (const v of this._subViews) {\n v.update(value, props);\n }\n }\n }\n}\n\n// what is passed to render\nexport class TemplateBuilder<T extends IObservableValue> {\n private _templateView: TemplateView<T>;\n private _closed: boolean = false;\n\n constructor(templateView: TemplateView<T>) {\n this._templateView = templateView;\n }\n\n close(): void {\n this._closed = true;\n }\n\n _addBinding(fn: () => void): void {\n if (this._closed) {\n console.trace(\"Adding a binding after render will likely cause memory leaks\");\n }\n this._templateView._addBinding(fn);\n }\n\n get _value(): T {\n return this._templateView.value;\n }\n\n addEventListener(node: Element, name: string, fn: (event: Event) => void, useCapture: boolean = false): void {\n this._templateView._addEventListener(node, name, fn, useCapture);\n }\n\n _addAttributeBinding(node: Element, name: string, fn: (value: T) => boolean | string): void {\n let prevValue: string | boolean | undefined = undefined;\n const binding = () => {\n const newValue = fn(this._value);\n if (prevValue !== newValue) {\n prevValue = newValue;\n setAttribute(node, name, newValue);\n }\n };\n this._addBinding(binding);\n binding();\n }\n\n _addClassNamesBinding(node: Element, obj: ClassNames<T>): void {\n this._addAttributeBinding(node, \"className\", value => classNames(obj, value));\n }\n\n _addTextBinding(fn: (value: T) => ReturnType<TextBinding<T>>): Text {\n const initialValue = fn(this._value)+\"\";\n const node = text(initialValue);\n let prevValue = initialValue;\n const binding = () => {\n const newValue = fn(this._value)+\"\";\n if (prevValue !== newValue) {\n prevValue = newValue;\n node.textContent = newValue;\n }\n };\n\n this._addBinding(binding);\n return node;\n }\n\n _isEventHandler(key: string, value: AttrValue<T>): value is (event: Event) => void {\n // This isn't actually safe, but it's incorrect to feed event handlers to\n // non-on* attributes.\n return key.startsWith(\"on\") && key.length > 2 && typeof value === \"function\";\n }\n\n _setNodeAttributes(node: Element, attributes: Attributes<T>): void {\n for(let [key, value] of Object.entries(attributes)) {\n // binding for className as object of className => enabled\n if (typeof value === \"object\") {\n if (key !== \"className\" || value === null) {\n // Ignore non-className objects.\n continue;\n }\n if (objHasFns(value)) {\n this._addClassNamesBinding(node, value);\n } else {\n setAttribute(node, key, classNames(value, this._value));\n }\n } else if (this._isEventHandler(key, value)) {\n const eventName = key.substr(2, 1).toLowerCase() + key.substr(3);\n const handler = value;\n this._templateView._addEventListener(node, eventName, handler);\n } else if (typeof value === \"function\") {\n this._addAttributeBinding(node, key, value);\n } else {\n setAttribute(node, key, value);\n }\n }\n }\n\n _setNodeChildren(node: Element, children: Children<T>): void{\n if (!Array.isArray(children)) {\n children = [children];\n }\n for (let child of children) {\n if (typeof child === \"function\") {\n child = this._addTextBinding(child);\n } else if (typeof child === \"string\") {\n // not a DOM node, turn into text\n child = text(child);\n }\n node.appendChild(child);\n }\n }\n \n _addReplaceNodeBinding<R>(fn: (value: T) => R, renderNode: (old: ViewNode | null) => ViewNode): ViewNode {\n let prevValue = fn(this._value);\n let node = renderNode(null);\n\n const binding = () => {\n const newValue = fn(this._value);\n if (prevValue !== newValue) {\n prevValue = newValue;\n const newNode = renderNode(node);\n if (node.parentNode) {\n node.parentNode.replaceChild(newNode, node);\n }\n node = newNode;\n }\n };\n this._addBinding(binding);\n return node;\n }\n\n el(name: string, attributes?: Attributes<T> | Children<T>, children?: Children<T>): ViewNode {\n return this.elNS(HTML_NS, name, attributes, children);\n }\n\n elNS(ns: string, name: string, attributesOrChildren?: Attributes<T> | Children<T>, children?: Children<T>): ViewNode {\n let attributes: Attributes<T> | undefined;\n if (attributesOrChildren) {\n if (isChildren(attributesOrChildren)) {\n children = attributesOrChildren as Children<T>;\n } else {\n attributes = attributesOrChildren as Attributes<T>;\n }\n }\n\n const node = document.createElementNS(ns, name);\n \n if (attributes) {\n this._setNodeAttributes(node, attributes);\n }\n if (children) {\n this._setNodeChildren(node, children);\n }\n\n return node;\n }\n\n // this inserts a view, and is not a view factory for `if`, so returns the root element to insert in the template\n // you should not call t.view() and not use the result (e.g. attach the result to the template DOM tree).\n view(view: IView, mountOptions?: IMountArgs): ViewNode {\n this._templateView.addSubView(view);\n return mountView(view, mountOptions);\n }\n\n // map a value to a view, every time the value changes\n mapView<R>(mapFn: (value: T) => R, viewCreator: (mapped: R) => IView | null): ViewNode {\n return this._addReplaceNodeBinding(mapFn, (prevNode) => {\n if (prevNode && prevNode.nodeType !== Node.COMMENT_NODE) {\n const subViews = this._templateView._subViews;\n if (subViews) {\n const viewIdx = subViews.findIndex(v => v.root() === prevNode);\n if (viewIdx !== -1) {\n const [view] = subViews.splice(viewIdx, 1);\n view.unmount();\n }\n }\n }\n const view = viewCreator(mapFn(this._value));\n if (view) {\n return this.view(view);\n } else {\n return document.createComment(\"node binding placeholder\");\n }\n });\n }\n\n // Special case of mapView for a TemplateView.\n // Always creates a TemplateView, if this is optional depending\n // on mappedValue, use `if` or `mapView`\n map<R>(mapFn: (value: T) => R, renderFn: (mapped: R, t: Builder<T>, vm: T) => ViewNode): ViewNode {\n return this.mapView(mapFn, mappedValue => {\n return new InlineTemplateView(this._value, (t, vm) => {\n const rootNode = renderFn(mappedValue, t, vm);\n if (!rootNode) {\n // TODO: this will confuse mapView which assumes that\n // a comment node means there is no view to clean up\n return document.createComment(\"map placeholder\");\n }\n return rootNode;\n });\n });\n }\n\n ifView(predicate: (value: T) => boolean, viewCreator: (value: T) => IView): ViewNode {\n return this.mapView(\n value => !!predicate(value),\n enabled => enabled ? viewCreator(this._value) : null\n );\n }\n\n // creates a conditional subtemplate\n // use mapView if you need to map to a different view class\n if(predicate: (value: T) => boolean, renderFn: (t: Builder<T>, vm: T) => ViewNode) {\n return this.ifView(predicate, vm => new InlineTemplateView(vm, renderFn));\n }\n\n /** You probably are looking for something else, like map or mapView.\n This is an escape hatch that allows you to do manual DOM manipulations\n as a reaction to a binding change.\n This should only be used if the side-effect won't add any bindings,\n event handlers, ...\n You should not call the TemplateBuilder (e.g. `t.xxx()`) at all from the side effect,\n instead use tags from html.ts to help you construct any DOM you need. */\n mapSideEffect<R>(mapFn: (value: T) => R, sideEffect: (newV: R, oldV: R | undefined) => void) {\n let prevValue = mapFn(this._value);\n const binding = () => {\n const newValue = mapFn(this._value);\n if (prevValue !== newValue) {\n sideEffect(newValue, prevValue);\n prevValue = newValue;\n }\n };\n this._addBinding(binding);\n sideEffect(prevValue, undefined);\n }\n}\n\n\nfor (const [ns, tags] of Object.entries(TAG_NAMES)) {\n for (const tag of tags) {\n TemplateBuilder.prototype[tag] = function(attributes, children) {\n return this.elNS(ns, tag, attributes, children);\n };\n }\n}\n\nexport class InlineTemplateView<T> extends TemplateView<T> {\n private _render: RenderFn<T>;\n\n constructor(value: T, render: RenderFn<T>) {\n super(value);\n this._render = render;\n }\n\n override render(t: Builder<T>, value: T): ViewNode {\n return this._render(t, value);\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {tag, text, classNames, setAttribute} from \"./general/html\";\n/**\n * @param {Object} vm view model with {avatarUrl, avatarColorNumber, avatarTitle, avatarLetter}\n * @param {Number} size\n * @return {Element}\n */\nexport function renderStaticAvatar(vm, size, extraClasses = undefined) {\n const hasAvatar = !!vm.avatarUrl(size);\n let avatarClasses = classNames({\n avatar: true,\n [`size-${size}`]: true,\n [`usercolor${vm.avatarColorNumber}`]: !hasAvatar\n });\n if (extraClasses) {\n avatarClasses += ` ${extraClasses}`;\n }\n const avatarContent = hasAvatar ? renderImg(vm, size) : text(vm.avatarLetter);\n const avatar = tag.div({className: avatarClasses, \"data-testid\": \"avatar\"}, [avatarContent]);\n if (hasAvatar) {\n setAttribute(avatar, \"data-avatar-letter\", vm.avatarLetter);\n setAttribute(avatar, \"data-avatar-color\", vm.avatarColorNumber);\n }\n return avatar;\n}\n\nexport function renderImg(vm, size) {\n const sizeStr = size.toString();\n return tag.img({src: vm.avatarUrl(size), width: sizeStr, height: sizeStr, title: vm.avatarTitle});\n}\n\nfunction isAvatarEvent(e) {\n const element = e.target;\n const parent = element.parentElement;\n return element.tagName === \"IMG\" && parent.classList.contains(\"avatar\");\n}\n\nexport function handleAvatarError(e) {\n if (!isAvatarEvent(e)) { return; }\n const parent = e.target.parentElement;\n const avatarColorNumber = parent.getAttribute(\"data-avatar-color\");\n parent.classList.add(`usercolor${avatarColorNumber}`);\n const avatarLetter = parent.getAttribute(\"data-avatar-letter\");\n parent.textContent = avatarLetter;\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseUpdateView} from \"./general/BaseUpdateView\";\nimport {renderStaticAvatar, renderImg} from \"./avatar\";\n\n/*\noptimization to not use a sub view when changing between img and text\nbecause there can be many many instances of this view\n*/\n\nexport class AvatarView extends BaseUpdateView {\n /**\n * @param {ViewModel} value view model with {avatarUrl, avatarColorNumber, avatarTitle, avatarLetter}\n * @param {Number} size\n */\n constructor(value, size) {\n super(value);\n this._root = null;\n this._avatarUrl = null;\n this._avatarTitle = null;\n this._avatarLetter = null;\n this._size = size;\n }\n\n _avatarUrlChanged() {\n if (this.value.avatarUrl(this._size) !== this._avatarUrl) {\n this._avatarUrl = this.value.avatarUrl(this._size);\n return true;\n }\n return false;\n }\n\n _avatarTitleChanged() {\n if (this.value.avatarTitle !== this._avatarTitle) {\n this._avatarTitle = this.value.avatarTitle;\n return true;\n }\n return false;\n }\n\n _avatarLetterChanged() {\n if (this.value.avatarLetter !== this._avatarLetter) {\n this._avatarLetter = this.value.avatarLetter;\n return true;\n }\n return false;\n }\n\n mount(options) {\n this._avatarUrlChanged();\n this._avatarLetterChanged();\n this._avatarTitleChanged();\n this._root = renderStaticAvatar(this.value, this._size);\n // takes care of update being called when needed\n this.subscribeOnMount(options);\n return this._root;\n }\n\n root() {\n return this._root;\n }\n\n update(vm) {\n // important to always call _...changed for every prop \n if (this._avatarUrlChanged()) {\n // avatarColorNumber won't change, it's based on room/user id\n const bgColorClass = `usercolor${vm.avatarColorNumber}`;\n if (vm.avatarUrl(this._size)) {\n this._root.replaceChild(renderImg(vm, this._size), this._root.firstChild);\n this._root.classList.remove(bgColorClass);\n } else {\n this._root.textContent = vm.avatarLetter;\n this._root.classList.add(bgColorClass);\n }\n }\n const hasAvatar = !!vm.avatarUrl(this._size);\n if (this._avatarTitleChanged() && hasAvatar) {\n const element = this._root.firstChild;\n if (element.tagName === \"IMG\") {\n element.setAttribute(\"title\", vm.avatarTitle);\n }\n }\n if (this._avatarLetterChanged() && !hasAvatar) {\n this._root.textContent = vm.avatarLetter;\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nlet container;\n\nexport function spinner(t, extraClasses = undefined) {\n if (container === undefined) {\n container = document.querySelector(\".hydrogen\");\n }\n const classes = Object.assign({\"spinner\": true}, extraClasses);\n if (container?.classList.contains(\"legacy\")) {\n return t.div({className: classes}, [\n t.div(),\n t.div(),\n t.div(),\n t.div(),\n ]);\n } else {\n return t.svg({className: classes, viewBox:\"0 0 100 100\"}, \n t.circle({cx:\"50%\", cy:\"50%\", r:\"45%\", pathLength:\"100\"})\n );\n }\n}\n\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../../general/TemplateView\";\nimport {AvatarView} from \"../../AvatarView.js\";\nimport {spinner} from \"../../common.js\";\n\nexport class RoomTileView extends TemplateView {\n render(t, vm) {\n const classes = {\n \"active\": vm => vm.isOpen,\n \"hidden\": vm => vm.hidden\n };\n return t.li({\"className\": classes}, [\n t.a({href: vm.url}, [\n t.view(new AvatarView(vm, 32), {parentProvidesUpdates: true}),\n t.div({className: \"description\"}, [\n t.div({className: {\"name\": true, unread: vm => vm.isUnread}}, vm => vm.name),\n t.map(vm => vm.busy, busy => {\n if (busy) {\n return spinner(t);\n } else {\n return t.div({\n className: {\n badge: true,\n highlighted: vm => vm.isHighlighted,\n hidden: vm => !vm.badgeCount\n }\n }, vm => vm.badgeCount);\n }\n })\n ])\n ])\n ]);\n }\n\n update(value, props) {\n super.update(value);\n // update the AvatarView as we told it to not subscribe itself with parentProvidesUpdates\n this.updateSubViews(value, props);\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ListView} from \"../../general/ListView\";\nimport {TemplateView} from \"../../general/TemplateView\";\nimport {RoomTileView} from \"./RoomTileView.js\";\n\nclass FilterField extends TemplateView {\n render(t, options) {\n const clear = () => {\n filterInput.value = \"\";\n filterInput.blur();\n clearButton.blur();\n options.clear();\n };\n const filterInput = t.input({\n type: \"text\",\n placeholder: options?.label,\n \"aria-label\": options?.label,\n autocomplete: options?.autocomplete,\n enterkeyhint: 'search',\n name: options?.name,\n onInput: event => options.set(event.target.value),\n onKeydown: event => {\n if (event.key === \"Escape\" || event.key === \"Esc\") {\n clear();\n }\n },\n onFocus: () => filterInput.select()\n });\n const clearButton = t.button({\n onClick: clear,\n title: options.i18n`Clear`,\n \"aria-label\": options.i18n`Clear`\n });\n return t.div({className: \"FilterField\"}, [filterInput, clearButton]);\n }\n}\n\nexport class LeftPanelView extends TemplateView {\n render(t, vm) {\n const gridButtonLabel = vm => {\n return vm.gridEnabled ?\n vm.i18n`Show single room` :\n vm.i18n`Enable grid layout`;\n };\n const roomList = t.view(new ListView(\n {\n className: \"RoomList\",\n list: vm.tileViewModels,\n },\n tileVM => new RoomTileView(tileVM)\n ));\n const utilitiesRow = t.div({className: \"utilities\"}, [\n t.a({className: \"button-utility close-session\", href: vm.closeUrl, \"aria-label\": vm.i18n`Back to account list`, title: vm.i18n`Back to account list`}),\n t.view(new FilterField({\n i18n: vm.i18n,\n label: vm.i18n`Filter rooms…`,\n name: \"room-filter\",\n autocomplete: true,\n set: query => {\n // scroll up if we just started filtering\n if (vm.setFilter(query)) {\n roomList.scrollTop = 0;\n }\n },\n clear: () => vm.clearFilter()\n })),\n t.button({\n onClick: () => vm.toggleGrid(),\n className: {\n \"button-utility\": true,\n grid: true,\n on: vm => vm.gridEnabled\n },\n title: gridButtonLabel,\n \"aria-label\": gridButtonLabel\n }),\n t.a({className: \"button-utility settings\", href: vm.settingsUrl, \"aria-label\": vm.i18n`Settings`, title: vm.i18n`Settings`}),\n t.a({className: \"button-utility create\", href: vm.createRoomUrl, \"aria-label\": vm.i18n`Create room`, title: vm.i18n`Create room`}),\n ]);\n\n return t.div({className: \"LeftPanel\"}, [\n utilitiesRow,\n roomList\n ]);\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {tag} from \"./html\";\n\nexport class Popup {\n constructor(view, closeCallback = null) {\n this._view = view;\n this._target = null;\n this._arrangement = null;\n this._scroller = null;\n this._fakeRoot = null;\n this._trackingTemplateView = null;\n this._closeCallback = closeCallback;\n }\n\n _getPopupContainer() {\n const appContainer = this._target.closest(\".hydrogen\");\n let popupContainer = appContainer.querySelector(\".popupContainer\");\n if (!popupContainer) {\n popupContainer = tag.div({className: \"popupContainer\"});\n appContainer.appendChild(popupContainer);\n }\n return popupContainer;\n }\n\n trackInTemplateView(templateView) {\n this._trackingTemplateView = templateView;\n this._trackingTemplateView.addSubView(this);\n }\n\n showRelativeTo(target, verticalPadding = 0) {\n this._target = target;\n this._verticalPadding = verticalPadding;\n this._scroller = findScrollParent(this._target);\n this._view.mount();\n this._getPopupContainer().appendChild(this._popup);\n this._position();\n if (this._scroller) {\n document.body.addEventListener(\"scroll\", this, true);\n }\n setTimeout(() => {\n document.body.addEventListener(\"click\", this, false);\n }, 10);\n }\n\n get isOpen() {\n return !!this._view;\n }\n\n close() {\n if (this._view) {\n this._view.unmount();\n this._trackingTemplateView.removeSubView(this);\n if (this._scroller) {\n document.body.removeEventListener(\"scroll\", this, true);\n }\n document.body.removeEventListener(\"click\", this, false);\n this._popup.remove();\n this._view = null;\n if (this._closeCallback) {\n this._closeCallback();\n }\n }\n }\n\n get _popup() {\n return this._view.root();\n }\n\n handleEvent(evt) {\n if (evt.type === \"scroll\") {\n if(!this._position()) {\n this.close();\n }\n } else if (evt.type === \"click\") {\n this._onClick(evt);\n }\n }\n\n _onClick() {\n this.close();\n }\n\n _position() {\n const targetPosition = this._target.getBoundingClientRect();\n const popupWidth = this._popup.clientWidth;\n const popupHeight = this._popup.clientHeight;\n const viewport = (this._scroller ? this._scroller : document.documentElement).getBoundingClientRect();\n\n if (\n targetPosition.top > viewport.bottom ||\n targetPosition.left > viewport.right ||\n targetPosition.bottom < viewport.top ||\n targetPosition.right < viewport.left\n ) {\n return false;\n }\n if (viewport.bottom >= targetPosition.bottom + popupHeight) {\n // show below\n this._popup.style.top = `${targetPosition.bottom + this._verticalPadding}px`;\n } else if (viewport.top <= targetPosition.top - popupHeight) {\n // show top\n this._popup.style.top = `${targetPosition.top - popupHeight - this._verticalPadding}px`;\n } else {\n return false;\n }\n if (viewport.right >= targetPosition.right + popupWidth) {\n // show right\n this._popup.style.left = `${targetPosition.left}px`;\n } else if (viewport.left <= targetPosition.left - popupWidth) {\n // show left\n this._popup.style.left = `${targetPosition.right - popupWidth}px`;\n } else {\n return false;\n }\n return true;\n }\n\n /* fake IView api, so it can be tracked by a template view as a subview */\n root() {\n return this._fakeRoot;\n }\n\n mount() {\n this._fakeRoot = document.createComment(\"popup\");\n return this._fakeRoot;\n }\n\n unmount() {\n this.close();\n }\n\n update() {}\n}\n\nfunction findScrollParent(el) {\n let parent = el;\n do {\n parent = parent.parentElement;\n if (parent.scrollHeight > parent.clientHeight) {\n // double check that overflow would allow a scrollbar\n // because some elements, like a button with negative margin to increate the click target\n // can cause the scrollHeight to be larger than the clientHeight in the parent\n // see button.link class\n const style = window.getComputedStyle(parent);\n const overflowY = style.getPropertyValue(\"overflow-y\");\n if (overflowY === \"auto\" || overflowY === \"scroll\") {\n return parent;\n }\n }\n } while (parent !== document.body);\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"./TemplateView\";\n\nexport class Menu extends TemplateView {\n static option(label, callback) {\n return new MenuOption(label, callback);\n }\n\n constructor(options) {\n super();\n this._options = options;\n }\n\n render(t) {\n return t.ul({className: \"menu\", role: \"menu\"}, this._options.map(o => o.toDOM(t)));\n }\n}\n\nclass MenuOption {\n constructor(label, callback) {\n this.label = label;\n this.callback = callback;\n this.icon = null;\n this.destructive = false;\n }\n\n setIcon(className) {\n this.icon = className;\n return this;\n }\n\n setDestructive() {\n this.destructive = true;\n return this;\n }\n\n toDOM(t) {\n const className = {\n destructive: this.destructive,\n };\n if (this.icon) {\n className.icon = true;\n className[this.icon] = true;\n }\n return t.li({\n className,\n }, t.button({className:\"menu-item\", onClick: this.callback}, this.label));\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ListView} from \"../../general/ListView\";\nimport type {IView} from \"../../general/types\";\nimport {TemplateView, Builder} from \"../../general/TemplateView\";\nimport {IObservableValue} from \"../../general/BaseUpdateView\";\nimport {MissingAttachmentView} from \"./timeline/MissingAttachmentView.js\";\nimport {AnnouncementView} from \"./timeline/AnnouncementView.js\";\nimport {RedactedView} from \"./timeline/RedactedView.js\";\nimport {SimpleTile} from \"../../../../../domain/session/room/timeline/tiles/SimpleTile.js\";\nimport {BaseObservableList as ObservableList} from \"../../../../../observable/list/BaseObservableList\";\n\nexport interface TileView extends IView {\n readonly value: SimpleTile;\n onClick(event: UIEvent);\n} \nexport type TileViewConstructor = new (\n tile: SimpleTile,\n viewClassForTile: ViewClassForEntryFn,\n renderFlags?: { reply?: boolean, interactive?: boolean }\n) => TileView;\nexport type ViewClassForEntryFn = (tile: SimpleTile) => TileViewConstructor;\n\n//import {TimelineViewModel} from \"../../../../../domain/session/room/timeline/TimelineViewModel.js\";\nexport interface TimelineViewModel extends IObservableValue {\n showJumpDown: boolean;\n tiles: ObservableList<SimpleTile>;\n setVisibleTileRange(start?: SimpleTile, end?: SimpleTile);\n}\n\nfunction bottom(node: HTMLElement): number {\n return node.offsetTop + node.clientHeight;\n}\n\nfunction findFirstNodeIndexAtOrBelow(tiles: HTMLElement, top: number, startIndex: number = (tiles.children.length - 1)): number {\n for (var i = startIndex; i >= 0; i--) {\n const node = tiles.children[i] as HTMLElement;\n if (node.offsetTop < top) {\n return i;\n }\n }\n // return first item if nothing matched before\n return 0;\n}\n\nexport class TimelineView extends TemplateView<TimelineViewModel> {\n\n private anchoredNode?: HTMLElement;\n private anchoredBottom: number = 0;\n private stickToBottom: boolean = true;\n private tilesView?: TilesListView;\n private resizeObserver?: ResizeObserver;\n\n constructor(vm: TimelineViewModel, private readonly viewClassForTile: ViewClassForEntryFn) {\n super(vm);\n }\n\n render(t: Builder<TimelineViewModel>, vm: TimelineViewModel) {\n // assume this view will be mounted in the parent DOM straight away\n requestAnimationFrame(() => {\n // do initial scroll positioning\n this.restoreScrollPosition();\n });\n this.tilesView = new TilesListView(vm.tiles, () => this.restoreScrollPosition(), this.viewClassForTile);\n const root = t.div({className: \"Timeline\"}, [\n t.div({\n className: \"Timeline_scroller bottom-aligned-scroll\",\n onScroll: () => this.onScroll()\n }, t.view(this.tilesView)),\n t.button({\n className: {\n \"Timeline_jumpDown\": true,\n hidden: vm => !vm.showJumpDown\n },\n title: \"Jump down\",\n onClick: () => this.jumpDown()\n })\n ]);\n\n if (typeof ResizeObserver === \"function\") {\n this.resizeObserver = new ResizeObserver(() => {\n this.restoreScrollPosition();\n });\n this.resizeObserver.observe(root);\n }\n\n return root;\n }\n\n private get scrollNode(): HTMLElement {\n return (this.root()! as HTMLElement).firstElementChild! as HTMLElement;\n }\n\n private get tilesNode(): HTMLElement {\n return this.tilesView!.root()! as HTMLElement;\n }\n\n private jumpDown() {\n const {scrollNode} = this;\n this.stickToBottom = true;\n scrollNode.scrollTop = scrollNode.scrollHeight;\n }\n\n public unmount() {\n super.unmount();\n if (this.resizeObserver) {\n this.resizeObserver.unobserve(this.root()! as Element);\n this.resizeObserver = undefined;\n }\n }\n\n private restoreScrollPosition() {\n const {scrollNode, tilesNode} = this;\n\n const missingTilesHeight = scrollNode.clientHeight - tilesNode.clientHeight;\n if (missingTilesHeight > 0) {\n tilesNode.style.setProperty(\"margin-top\", `${missingTilesHeight}px`);\n // we don't have enough tiles to fill the viewport, so set all as visible\n const len = this.value.tiles.length;\n this.updateVisibleRange(0, len - 1);\n } else {\n tilesNode.style.removeProperty(\"margin-top\");\n if (this.stickToBottom) {\n scrollNode.scrollTop = scrollNode.scrollHeight;\n } else if (this.anchoredNode) {\n const newAnchoredBottom = bottom(this.anchoredNode!);\n if (newAnchoredBottom !== this.anchoredBottom) {\n const bottomDiff = newAnchoredBottom - this.anchoredBottom;\n // scrollBy tends to create less scroll jumps than reassigning scrollTop as it does\n // not depend on reading scrollTop, which might be out of date as some platforms\n // run scrolling off the main thread.\n if (typeof scrollNode.scrollBy === \"function\") {\n scrollNode.scrollBy(0, bottomDiff);\n } else {\n scrollNode.scrollTop = scrollNode.scrollTop + bottomDiff;\n }\n this.anchoredBottom = newAnchoredBottom;\n }\n }\n // TODO: should we be updating the visible range here as well as the range might have changed even though\n // we restored the bottom tile\n }\n }\n\n private onScroll(): void {\n const {scrollNode, tilesNode} = this;\n const {scrollHeight, scrollTop, clientHeight} = scrollNode;\n\n let bottomNodeIndex;\n this.stickToBottom = Math.abs(scrollHeight - (scrollTop + clientHeight)) < 1;\n if (this.stickToBottom) {\n const len = this.value.tiles.length;\n bottomNodeIndex = len - 1;\n } else {\n const viewportBottom = scrollTop + clientHeight;\n const anchoredNodeIndex = findFirstNodeIndexAtOrBelow(tilesNode, viewportBottom);\n this.anchoredNode = tilesNode.childNodes[anchoredNodeIndex] as HTMLElement;\n this.anchoredBottom = bottom(this.anchoredNode!);\n bottomNodeIndex = anchoredNodeIndex;\n }\n let topNodeIndex = findFirstNodeIndexAtOrBelow(tilesNode, scrollTop, bottomNodeIndex);\n this.updateVisibleRange(topNodeIndex, bottomNodeIndex);\n }\n\n private updateVisibleRange(startIndex: number, endIndex: number) {\n // can be undefined, meaning the tiles collection is still empty\n const firstVisibleChild = this.tilesView!.getChildInstanceByIndex(startIndex);\n const lastVisibleChild = this.tilesView!.getChildInstanceByIndex(endIndex);\n this.value.setVisibleTileRange(firstVisibleChild?.value, lastVisibleChild?.value);\n }\n}\n\nclass TilesListView extends ListView<SimpleTile, TileView> {\n\n private onChanged: () => void;\n\n constructor(tiles: ObservableList<SimpleTile>, onChanged: () => void, private readonly viewClassForTile: ViewClassForEntryFn) {\n super({\n list: tiles,\n onItemClick: (tileView, evt) => tileView.onClick(evt),\n }, tile => {\n const TileView = viewClassForTile(tile);\n return new TileView(tile, viewClassForTile);\n });\n this.onChanged = onChanged;\n }\n\n onReset() {\n super.onReset();\n this.onChanged();\n }\n\n onUpdate(index: number, value: SimpleTile, param: any) {\n if (param === \"shape\") {\n const ExpectedClass = this.viewClassForTile(value);\n const child = this.getChildInstanceByIndex(index);\n if (!ExpectedClass || !(child instanceof ExpectedClass)) {\n // shape was updated, so we need to recreate the tile view,\n // the shape parameter is set in EncryptedEventTile.updateEntry\n // (and perhaps elsewhere by the time you read this)\n super.recreateItem(index, value);\n return;\n }\n }\n super.onUpdate(index, value, param);\n this.onChanged();\n }\n\n onAdd(idx: number, value: SimpleTile) {\n super.onAdd(idx, value);\n this.onChanged();\n }\n\n onRemove(idx: number, value: SimpleTile) {\n super.onRemove(idx, value);\n this.onChanged();\n }\n\n onMove(fromIdx: number, toIdx: number, value: SimpleTile) {\n super.onMove(fromIdx, toIdx, value);\n this.onChanged();\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../../general/TemplateView\";\nimport {spinner} from \"../../common.js\";\n\nexport class TimelineLoadingView extends TemplateView {\n render(t, vm) {\n return t.div({className: \"TimelineLoadingView\"}, [\n spinner(t),\n t.div(vm.isEncrypted ? vm.i18n`Loading encrypted messages…` : vm.i18n`Loading messages…`)\n ]);\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../../general/TemplateView\";\nimport {Popup} from \"../../general/Popup.js\";\nimport {Menu} from \"../../general/Menu.js\";\n\nexport class MessageComposer extends TemplateView {\n constructor(viewModel, viewClassForTile) {\n super(viewModel);\n this._viewClassForTile = viewClassForTile;\n this._input = null;\n this._attachmentPopup = null;\n this._focusInput = null;\n this._rafResizeHandle = undefined;\n }\n\n render(t, vm) {\n this._input = t.textarea({\n onKeydown: e => this._onKeyDown(e),\n onInput: () => {\n vm.setInput(this._input.value);\n if (this._input.value) {\n this._adjustHeight();\n } else {\n this._clearHeight();\n }\n },\n placeholder: vm => vm.isEncrypted ? \"Send an encrypted message…\" : \"Send a message…\",\n rows: \"1\"\n });\n this._focusInput = () => this._input.focus();\n this.value.on(\"focus\", this._focusInput);\n const replyPreview = t.map(vm => vm.replyViewModel, (rvm, t) => {\n const TileView = rvm && this._viewClassForTile(rvm);\n if (!TileView) { return null; }\n return t.div({\n className: \"MessageComposer_replyPreview\"\n }, [\n t.span({ className: \"replying\" }, \"Replying\"),\n t.button({\n className: \"cancel\",\n onClick: () => this._clearReplyingTo()\n }, \"Close\"),\n t.view(new TileView(rvm, this._viewClassForTile, { interactive: false }, \"div\"))\n ]);\n });\n const input = t.div({className: \"MessageComposer_input\"}, [\n this._input,\n t.button({\n className: \"sendFile\",\n title: vm.i18n`Pick attachment`,\n onClick: evt => this._toggleAttachmentMenu(evt),\n }, vm.i18n`Send file`),\n t.button({\n className: \"send\",\n title: vm.i18n`Send`,\n onClick: () => this._trySend(),\n }, vm.i18n`Send`),\n ]);\n return t.div({ className: {\n MessageComposer: true,\n MessageComposer_canSend: vm => vm.canSend\n } }, [replyPreview, input]);\n }\n\n unmount() {\n if (this._focusInput) {\n this.value.off(\"focus\", this._focusInput);\n }\n super.unmount();\n }\n\n _clearReplyingTo() {\n this.value.clearReplyingTo();\n }\n\n async _trySend() {\n this._input.focus();\n // we clear the composer while enqueuing\n // and restore it when that didn't work somehow\n // to prevent the user from sending the message\n // every time they hit enter while it's still enqueuing.\n const {value} = this._input;\n const restoreValue = () => {\n this._input.value = value;\n this._adjustHeight();\n };\n this._input.value = \"\";\n this._clearHeight();\n try {\n if (!await this.value.sendMessage(value)) {\n restoreValue();\n }\n } catch (err) {\n restoreValue();\n console.error(err);\n }\n }\n\n _onKeyDown(event) {\n if (event.key === \"Enter\" && !event.shiftKey) {\n // don't insert newline into composer\n event.preventDefault();\n this._trySend();\n }\n }\n\n _toggleAttachmentMenu(evt) {\n if (this._attachmentPopup && this._attachmentPopup.isOpen) {\n this._attachmentPopup.close();\n } else {\n const vm = this.value;\n this._attachmentPopup = new Popup(new Menu([\n Menu.option(vm.i18n`Send video`, () => vm.sendVideo()).setIcon(\"video\"),\n Menu.option(vm.i18n`Send picture`, () => vm.sendPicture()).setIcon(\"picture\"),\n Menu.option(vm.i18n`Send file`, () => vm.sendFile()).setIcon(\"file\"),\n ]));\n this._attachmentPopup.trackInTemplateView(this);\n this._attachmentPopup.showRelativeTo(evt.target, 12);\n }\n }\n\n _adjustHeight() {\n if (this._rafResizeHandle) {\n return;\n }\n this._rafResizeHandle = window.requestAnimationFrame(() => {\n const scrollHeight = this._input.scrollHeight;\n this._input.style.height = `${scrollHeight}px`;\n this._rafResizeHandle = undefined;\n });\n }\n\n _clearHeight() {\n this._input.style.removeProperty(\"height\");\n }\n\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../../general/TemplateView\";\n\nexport class DisabledComposerView extends TemplateView {\n render(t) {\n return t.div({className: \"DisabledComposerView\"}, t.h3(vm => vm.description));\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../../general/TemplateView\";\nimport {Popup} from \"../../general/Popup.js\";\nimport {Menu} from \"../../general/Menu.js\";\nimport {TimelineView} from \"./TimelineView\";\nimport {TimelineLoadingView} from \"./TimelineLoadingView.js\";\nimport {MessageComposer} from \"./MessageComposer.js\";\nimport {DisabledComposerView} from \"./DisabledComposerView.js\";\nimport {AvatarView} from \"../../AvatarView.js\";\n\nexport class RoomView extends TemplateView {\n constructor(vm, viewClassForTile) {\n super(vm);\n this._viewClassForTile = viewClassForTile;\n this._optionsPopup = null;\n }\n\n render(t, vm) {\n return t.main({className: \"RoomView middle\"}, [\n t.div({className: \"RoomHeader middle-header\"}, [\n t.a({className: \"button-utility close-middle\", href: vm.closeUrl, title: vm.i18n`Close room`}),\n t.view(new AvatarView(vm, 32)),\n t.div({className: \"room-description\"}, [\n t.h2(vm => vm.name),\n ]),\n t.button({\n className: \"button-utility room-options\",\n \"aria-label\":vm.i18n`Room options`,\n onClick: evt => this._toggleOptionsMenu(evt)\n })\n ]),\n t.div({className: \"RoomView_body\"}, [\n t.div({className: \"RoomView_error\"}, [\n t.if(vm => vm.error, t => t.div( \n [\n t.p({}, vm => vm.error),\n t.button({ className: \"RoomView_error_closerButton\", onClick: evt => vm.dismissError(evt) })\n ])\n )]),\n t.mapView(vm => vm.timelineViewModel, timelineViewModel => {\n return timelineViewModel ?\n new TimelineView(timelineViewModel, this._viewClassForTile) :\n new TimelineLoadingView(vm); // vm is just needed for i18n\n }),\n t.mapView(vm => vm.composerViewModel,\n composerViewModel => {\n switch (composerViewModel?.kind) {\n case \"composer\":\n return new MessageComposer(vm.composerViewModel, this._viewClassForTile);\n case \"disabled\":\n return new DisabledComposerView(vm.composerViewModel);\n }\n }),\n ])\n ]);\n }\n \n _toggleOptionsMenu(evt) {\n if (this._optionsPopup && this._optionsPopup.isOpen) {\n this._optionsPopup.close();\n } else {\n const vm = this.value;\n const options = [];\n options.push(Menu.option(vm.i18n`Room details`, () => vm.openDetailsPanel()))\n if (vm.canLeave) {\n options.push(Menu.option(vm.i18n`Leave room`, () => this._confirmToLeaveRoom()).setDestructive());\n }\n if (vm.canForget) {\n options.push(Menu.option(vm.i18n`Forget room`, () => vm.forgetRoom()).setDestructive());\n }\n if (vm.canRejoin) {\n options.push(Menu.option(vm.i18n`Rejoin room`, () => vm.rejoinRoom()));\n }\n if (!options.length) {\n return;\n }\n this._optionsPopup = new Popup(new Menu(options));\n this._optionsPopup.trackInTemplateView(this);\n this._optionsPopup.showRelativeTo(evt.target, 10);\n }\n }\n\n _confirmToLeaveRoom() {\n if (confirm(this.value.i18n`Are you sure you want to leave \"${this.value.name}\"?`)) {\n this.value.leaveRoom();\n }\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../../general/TemplateView\";\n\nexport class UnknownRoomView extends TemplateView {\n render(t, vm) {\n return t.main({className: \"UnknownRoomView middle\"}, t.div([\n t.h2([\n vm.i18n`You are currently not in ${vm.roomIdOrAlias}.`,\n t.br(),\n vm.i18n`Want to join it?`\n ]),\n t.button({\n className: \"button-action primary\",\n onClick: () => vm.join(),\n disabled: vm => vm.busy,\n }, vm.i18n`Join room`),\n t.if(vm => vm.error, t => t.p({className: \"error\"}, vm.error))\n ]));\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {tag} from \"../general/html\";\n\nexport class StaticView {\n constructor(value, render = undefined) {\n if (typeof value === \"function\" && !render) {\n render = value;\n value = null;\n }\n this._root = render ? render(tag, value) : this.render(tag, value);\n }\n\n mount() {\n return this._root;\n }\n\n root() {\n return this._root;\n }\n\n unmount() {}\n update() {}\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {StaticView} from \"./StaticView\";\nimport {spinner} from \"../common.js\";\n\nexport class LoadingView extends StaticView {\n constructor(label = \"Loading\") {\n super(label, (t, label) => {\n return t.div({ className: \"LoadingView\" }, [spinner(t), label]);\n });\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../../general/TemplateView\";\nimport {LoadingView} from \"../../general/LoadingView\";\nimport {AvatarView} from \"../../AvatarView\";\n\nexport class RoomBeingCreatedView extends TemplateView {\n render(t, vm) {\n return t.main({className: \"RoomView middle\"}, [\n t.div({className: \"RoomHeader middle-header\"}, [\n t.a({className: \"button-utility close-middle\", href: vm.closeUrl, title: vm.i18n`Close room`}),\n t.view(new AvatarView(vm, 32)),\n t.div({className: \"room-description\"}, [\n t.h2(vm => vm.name),\n ])\n ]),\n t.div({className: \"RoomView_body\"}, [\n t.mapView(vm => vm.error, error => {\n if (error) {\n return new ErrorView(vm);\n } else {\n return new LoadingView(vm.i18n`Setting up the room…`);\n }\n })\n ])\n ]);\n }\n}\n\nclass ErrorView extends TemplateView {\n render(t,vm) {\n return t.div({className: \"RoomBeingCreated_error centered-column\"}, [\n t.h3(vm.i18n`Could not create the room, something went wrong:`),\n t.div({className: \"RoomView_error form-group\"}, vm.error),\n t.div({className: \"button-row\"},\n t.button({\n className: \"button-action primary destructive\",\n onClick: () => vm.cancel()\n }, vm.i18n`Cancel`))\n ]);\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../../general/TemplateView\";\nimport {renderStaticAvatar} from \"../../avatar\";\n\nexport class InviteView extends TemplateView {\n render(t, vm) {\n let inviteNodes = [];\n if (vm.isDirectMessage) {\n inviteNodes.push(renderStaticAvatar(vm, 128, \"InviteView_dmAvatar\"));\n }\n let inviterNodes;\n if (vm.isDirectMessage) {\n inviterNodes = [t.strong(vm.name), ` (${vm.inviter?.id}) wants to chat with you.`];\n } else if (vm.inviter) {\n inviterNodes = [renderStaticAvatar(vm.inviter, 24), t.strong(vm.inviter.name), ` (${vm.inviter.id}) invited you.`];\n } else {\n inviterNodes = `You were invited to join.`;\n }\n inviteNodes.push(t.p({className: \"InviteView_inviter\"}, inviterNodes));\n if (!vm.isDirectMessage) {\n inviteNodes.push(t.div({className: \"InviteView_roomProfile\"}, [\n renderStaticAvatar(vm, 64, \"InviteView_roomAvatar\"),\n t.h3(vm.name),\n t.p({className: \"InviteView_roomDescription\"}, vm.roomDescription)\n ]));\n }\n\n return t.main({className: \"InviteView middle\"}, [\n t.div({className: \"RoomHeader middle-header\"}, [\n t.a({className: \"button-utility close-middle\", href: vm.closeUrl, title: vm.i18n`Close invite`}),\n renderStaticAvatar(vm, 32),\n t.div({className: \"room-description\"}, [\n t.h2(vm => vm.name),\n ]),\n ]),\n t.if(vm => vm.error, t => t.div({className: \"RoomView_error\"}, vm => vm.error)),\n t.div({className: \"InviteView_body\"}, [\n t.div({className: \"InviteView_invite\"}, [\n ...inviteNodes,\n t.div({className: \"InviteView_buttonRow\"},\n t.button({\n className: \"button-action primary\",\n disabled: vm => vm.busy,\n onClick: () => vm.accept()\n }, vm.i18n`Accept`)\n ),\n t.div({className: \"InviteView_buttonRow\"},\n t.button({\n className: \"button-action primary destructive\",\n disabled: vm => vm.busy,\n onClick: () => vm.reject()\n }, vm.i18n`Reject`)\n ),\n ])\n ])\n ]);\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../../general/TemplateView\";\nimport {spinner} from \"../../common.js\";\n\nexport class LightboxView extends TemplateView {\n render(t, vm) {\n const close = t.a({href: vm.closeUrl, title: vm.i18n`Close`, className: \"close\"});\n const image = t.div({\n role: \"img\",\n \"aria-label\": vm => vm.name,\n title: vm => vm.name,\n className: {\n picture: true,\n hidden: vm => !vm.imageUrl,\n },\n style: vm => `background-image: url('${vm.imageUrl}'); max-width: ${vm.imageWidth}px; max-height: ${vm.imageHeight}px;`\n });\n const loading = t.div({\n className: {\n loading: true,\n hidden: vm => !!vm.imageUrl\n }\n }, [\n spinner(t),\n t.div(vm.i18n`Loading image…`)\n ]);\n const details = t.div({\n className: \"details\"\n }, [t.strong(vm => vm.name), t.br(), \"uploaded by \", t.strong(vm => vm.sender), vm => ` at ${vm.time} on ${vm.date}.`]);\n const dialog = t.div({\n role: \"dialog\",\n className: \"lightbox\",\n onClick: evt => this.clickToClose(evt),\n onKeydown: evt => this.closeOnEscKey(evt)\n }, [image, loading, details, close]);\n trapFocus(t, dialog);\n return dialog;\n }\n\n clickToClose(evt) {\n if (evt.target === this.root()) {\n this.value.close();\n }\n }\n\n closeOnEscKey(evt) {\n if (evt.key === \"Escape\" || evt.key === \"Esc\") {\n this.value.close();\n }\n }\n}\n\nfunction trapFocus(t, element) {\n const elements = focusables(element);\n const first = elements[0];\n const last = elements[elements.length - 1];\n\n t.addEventListener(element, \"keydown\", evt => {\n if (evt.key === \"Tab\") {\n if (evt.shiftKey) {\n if (document.activeElement === first) {\n last.focus();\n evt.preventDefault();\n }\n } else {\n if (document.activeElement === last) {\n first.focus();\n evt.preventDefault();\n }\n }\n }\n }, true);\n Promise.resolve().then(() => {\n first.focus();\n });\n}\n\nfunction focusables(element) {\n return element.querySelectorAll('a[href], button, textarea, input, select');\n}\n\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../general/TemplateView\";\nimport {spinner} from \"../common.js\";\n\nexport class SessionStatusView extends TemplateView {\n render(t, vm) {\n return t.div({className: {\n \"SessionStatusView\": true,\n \"hidden\": vm => !vm.isShown,\n }}, [\n spinner(t, {hidden: vm => !vm.isWaiting}),\n t.p(vm => vm.statusLabel),\n t.if(vm => vm.isConnectNowShown, t => t.button({className: \"link\", onClick: () => vm.connectNow()}, \"Retry now\")),\n t.if(vm => vm.isSecretStorageShown, t => t.a({href: vm.setupKeyBackupUrl}, \"Go to settings\")),\n t.if(vm => vm.canDismiss, t => t.div({className: \"end\"}, t.button({className: \"dismiss\", onClick: () => vm.dismiss()}))),\n ]);\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {RoomView} from \"./room/RoomView.js\";\nimport {RoomBeingCreatedView} from \"./room/RoomBeingCreatedView.js\";\nimport {InviteView} from \"./room/InviteView.js\";\nimport {TemplateView} from \"../general/TemplateView\";\nimport {StaticView} from \"../general/StaticView.js\";\n\nexport class RoomGridView extends TemplateView {\n constructor(vm, viewClassForTile) {\n super(vm);\n this._viewClassForTile = viewClassForTile;\n }\n\n render(t, vm) {\n const children = [];\n for (let i = 0; i < (vm.height * vm.width); i+=1) {\n children.push(t.div({\n onClick: () => vm.focusTile(i),\n onFocusin: () => vm.focusTile(i),\n className: {\n \"container\": true,\n [`tile${i}`]: true,\n \"focused\": vm => vm.focusIndex === i\n },\n }, t.mapView(vm => vm.roomViewModelAt(i), roomVM => {\n if (roomVM) {\n if (roomVM.kind === \"roomBeingCreated\") {\n return new RoomBeingCreatedView(roomVM);\n } else if (roomVM.kind === \"invite\") {\n return new InviteView(roomVM);\n } else {\n return new RoomView(roomVM, this._viewClassForTile);\n }\n } else {\n return new StaticView(t => t.div({className: \"room-placeholder\"}, [\n t.h2({className: \"focused\"}, vm.i18n`Select a room on the left`),\n t.h2({className: \"unfocused\"}, vm.i18n`Click to select this tile`),\n ]));\n }\n })));\n }\n children.push(t.div({className: vm => `focus-ring tile${vm.focusIndex}`}));\n return t.div({className: \"RoomGridView middle layout3x2\"}, children);\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../../general/TemplateView\";\n\nexport class KeyBackupSettingsView extends TemplateView {\n render(t) {\n return t.div([\n t.map(vm => vm.status, (status, t, vm) => {\n switch (status) {\n case \"Enabled\": return renderEnabled(t, vm);\n case \"NewVersionAvailable\": return renderNewVersionAvailable(t, vm);\n case \"SetupKey\": return renderEnableFromKey(t, vm);\n case \"SetupPhrase\": return renderEnableFromPhrase(t, vm);\n case \"Pending\": return t.p(vm.i18n`Waiting to go online…`);\n }\n }),\n t.map(vm => vm.backupWriteStatus, (status, t, vm) => {\n switch (status) {\n case \"Writing\": {\n const progress = t.progress({\n min: 0,\n max: 100,\n value: vm => vm.backupPercentage,\n });\n return t.div([`Backup in progress `, progress, \" \", vm => vm.backupInProgressLabel]);\n }\n case \"Stopped\": {\n let label;\n const error = vm.backupError;\n if (error) {\n label = `Backup has stopped because of an error: ${vm.backupError}`;\n } else {\n label = `Backup has stopped`;\n }\n return t.p(label, \" \", t.button({onClick: () => vm.startBackup()}, `Backup now`));\n }\n case \"Done\":\n return t.p(`All keys are backed up.`);\n default:\n return null;\n }\n })\n ]);\n }\n}\n\nfunction renderEnabled(t, vm) {\n const items = [\n t.p([vm.i18n`Key backup is enabled, using backup version ${vm.backupVersion}. `, t.button({onClick: () => vm.disable()}, vm.i18n`Disable`)])\n ];\n if (vm.dehydratedDeviceId) {\n items.push(t.p(vm.i18n`A dehydrated device id was set up with id ${vm.dehydratedDeviceId} which you can use during your next login with your secret storage key.`));\n }\n return t.div(items);\n}\n\nfunction renderNewVersionAvailable(t, vm) {\n const items = [\n t.p([vm.i18n`A new backup version has been created from another device. Disable key backup and enable it again with the new key.`, t.button({onClick: () => vm.disable()}, vm.i18n`Disable`)])\n ];\n return t.div(items);\n}\n\nfunction renderEnableFromKey(t, vm) {\n const useASecurityPhrase = t.button({className: \"link\", onClick: () => vm.showPhraseSetup()}, vm.i18n`use a security phrase`);\n return t.div([\n t.p(vm.i18n`Enter your secret storage security key below to ${vm.purpose}, which will enable you to decrypt messages received before you logged into this session. The security key is a code of 12 groups of 4 characters separated by a space that Element created for you when setting up security.`),\n renderError(t),\n renderEnableFieldRow(t, vm, vm.i18n`Security key`, (key, setupDehydratedDevice) => vm.enterSecurityKey(key, setupDehydratedDevice)),\n t.p([vm.i18n`Alternatively, you can `, useASecurityPhrase, vm.i18n` if you have one.`]),\n ]);\n}\n\nfunction renderEnableFromPhrase(t, vm) {\n const useASecurityKey = t.button({className: \"link\", onClick: () => vm.showKeySetup()}, vm.i18n`use your security key`);\n return t.div([\n t.p(vm.i18n`Enter your secret storage security phrase below to ${vm.purpose}, which will enable you to decrypt messages received before you logged into this session. The security phrase is a freeform secret phrase you optionally chose when setting up security in Element. It is different from your password to login, unless you chose to set them to the same value.`),\n renderError(t),\n renderEnableFieldRow(t, vm, vm.i18n`Security phrase`, (phrase, setupDehydratedDevice) => vm.enterSecurityPhrase(phrase, setupDehydratedDevice)),\n t.p([vm.i18n`You can also `, useASecurityKey, vm.i18n`.`]),\n ]);\n}\n\nfunction renderEnableFieldRow(t, vm, label, callback) {\n let setupDehydrationCheck;\n const eventHandler = () => callback(input.value, setupDehydrationCheck?.checked || false);\n const input = t.input({type: \"password\", disabled: vm => vm.isBusy, placeholder: label});\n const children = [\n t.p([\n input,\n t.button({disabled: vm => vm.isBusy, onClick: eventHandler}, vm.decryptAction),\n ]),\n ];\n if (vm.offerDehydratedDeviceSetup) {\n setupDehydrationCheck = t.input({type: \"checkbox\", id:\"enable-dehydrated-device\"});\n const moreInfo = t.a({href: \"https://github.com/uhoreg/matrix-doc/blob/dehydration/proposals/2697-device-dehydration.md\", target: \"_blank\", rel: \"noopener\"}, \"more info\");\n children.push(t.p([\n setupDehydrationCheck,\n t.label({for: setupDehydrationCheck.id}, [vm.i18n`Back up my device as well (`, moreInfo, \")\"])\n ]));\n }\n return t.div({className: `row`}, [\n t.div({className: \"label\"}, label),\n t.div({className: \"content\"}, children),\n ]);\n}\n\nfunction renderError(t) {\n return t.if(vm => vm.error, (t, vm) => {\n return t.div([\n t.p({className: \"error\"}, vm => vm.i18n`Could not enable key backup: ${vm.error}.`),\n t.p(vm.i18n`Try double checking that you did not mix up your security key, security phrase and login password as explained above.`)\n ])\n });\n}\n\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../../general/TemplateView\";\nimport {disableTargetCallback} from \"../../general/utils\";\nimport {KeyBackupSettingsView} from \"./KeyBackupSettingsView.js\"\n\nexport class SettingsView extends TemplateView {\n render(t, vm) {\n let version = vm.version;\n if (vm.showUpdateButton) {\n version = t.span([\n vm.version,\n t.button({onClick: () => vm.checkForUpdate()}, vm.i18n`Check for updates`)\n ]);\n }\n\n const row = (t, label, content, extraClass = \"\") => {\n return t.div({className: `row ${extraClass}`}, [\n t.div({className: \"label\"}, label),\n t.div({className: \"content\"}, content),\n ]);\n };\n\n const settingNodes = [];\n\n settingNodes.push(\n t.h3(\"Session\"),\n row(t, vm.i18n`User ID`, vm.userId),\n row(t, vm.i18n`Session ID`, vm.deviceId, \"code\"),\n row(t, vm.i18n`Session key`, vm.fingerprintKey, \"code\"),\n row(t, \"\", t.button({\n onClick: () => vm.logout(),\n disabled: vm => vm.isLoggingOut\n }, vm.i18n`Log out`)),\n );\n settingNodes.push(\n t.h3(\"Key backup\"),\n t.view(new KeyBackupSettingsView(vm.keyBackupViewModel))\n );\n\n settingNodes.push(\n t.h3(\"Notifications\"),\n t.map(vm => vm.pushNotifications.supported, (supported, t) => {\n if (supported === null) {\n return t.p(vm.i18n`Loading…`);\n } else if (supported) {\n const label = vm => vm.pushNotifications.enabled ?\n vm.i18n`Push notifications are enabled`:\n vm.i18n`Push notifications are disabled`;\n const buttonLabel = vm => vm.pushNotifications.enabled ?\n vm.i18n`Disable`:\n vm.i18n`Enable`;\n return row(t, label, t.button({\n onClick: () => vm.togglePushNotifications(),\n disabled: vm => vm.pushNotifications.updating\n }, buttonLabel));\n } else {\n return t.p(vm.i18n`Push notifications are not supported on this browser`);\n }\n }),\n t.if(vm => vm.pushNotifications.supported && vm.pushNotifications.enabled, t => {\n return t.div([\n t.p([\n \"If you think push notifications are not being delivered, \",\n t.button({className: \"link\", onClick: () => vm.checkPushEnabledOnServer()}, \"check\"),\n \" if they got disabled on the server\"\n ]),\n t.map(vm => vm.pushNotifications.enabledOnServer, (enabled, t) => {\n if (enabled === true) {\n return t.p(\"Push notifications are still enabled on the server, so everything should be working. Sometimes notifications can get dropped if they can't be delivered within a given time.\");\n } else if (enabled === false) {\n return t.p(\"Push notifications have been disabled on the server, likely due to a bug. Please re-enable them by clicking Disable and then Enable again above.\");\n }\n }),\n t.map(vm => vm.pushNotifications.serverError, (err, t) => {\n if (err) {\n return t.p(\"Couldn't not check on server: \" + err.message);\n }\n })\n ]);\n })\n );\n \n settingNodes.push(\n t.h3(\"Preferences\"),\n row(t, vm.i18n`Scale down images when sending`, this._imageCompressionRange(t, vm)),\n t.if(vm => !import.meta.env.DEV && vm.activeTheme, (t, vm) => {\n return row(t, vm.i18n`Use the following theme`, this._themeOptions(t, vm));\n }),\n );\n const logButtons = [];\n if (vm.canSendLogsToServer) {\n logButtons.push(t.button({onClick: disableTargetCallback(() => vm.sendLogsToServer())}, `Submit logs to ${vm.logsServer}`));\n }\n logButtons.push(t.button({onClick: () => vm.exportLogs()}, \"Download logs\"));\n settingNodes.push(\n t.h3(\"Application\"),\n row(t, vm.i18n`Version`, version),\n row(t, vm.i18n`Storage usage`, vm => `${vm.storageUsage} / ${vm.storageQuota}`),\n row(t, vm.i18n`Debug logs`, logButtons),\n t.p({className: {hidden: vm => !vm.logsFeedbackMessage}}, vm => vm.logsFeedbackMessage),\n t.p([\"Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited, the usernames of other users and the names of files you send. They do not contain messages. For more information, review our \",\n t.a({href: \"https://element.io/privacy\", target: \"_blank\", rel: \"noopener\"}, \"privacy policy\"), \".\"]),\n );\n\n return t.main({className: \"Settings middle\"}, [\n t.div({className: \"middle-header\"}, [\n t.a({className: \"button-utility close-middle\", href: vm.closeUrl, title: vm.i18n`Close settings`}),\n t.h2(\"Settings\")\n ]),\n t.div({className: \"SettingsBody\"}, settingNodes)\n ]);\n }\n\n _imageCompressionRange(t, vm) {\n const step = 32;\n const min = Math.ceil(vm.minSentImageSizeLimit / step) * step;\n const max = (Math.floor(vm.maxSentImageSizeLimit / step) + 1) * step;\n const updateSetting = evt => vm.setSentImageSizeLimit(parseInt(evt.target.value, 10));\n return [t.input({\n type: \"range\",\n step,\n min,\n max,\n value: vm => vm.sentImageSizeLimit || max,\n onInput: updateSetting,\n onChange: updateSetting,\n }), \" \", t.output(vm => {\n return vm.sentImageSizeLimit ? \n vm.i18n`resize to ${vm.sentImageSizeLimit}px` :\n vm.i18n`no resizing`;\n })];\n }\n\n _themeOptions(t, vm) {\n const { themeName: activeThemeName, themeVariant: activeThemeVariant } = vm.activeTheme;\n const optionTags = [];\n // 1. render the dropdown containing the themes\n for (const name of Object.keys(vm.themeMapping)) {\n optionTags.push( t.option({ value: name, selected: name === activeThemeName} , name));\n }\n const select = t.select({\n onChange: (e) => {\n const themeName = e.target.value;\n if(!(\"id\" in vm.themeMapping[themeName])) {\n const colorScheme = darkRadioButton.checked ? \"dark\" : lightRadioButton.checked ? \"light\" : \"default\";\n // execute the radio-button callback so that the theme actually changes!\n // otherwise the theme would only change when another radio-button is selected.\n radioButtonCallback(colorScheme);\n return;\n }\n vm.changeThemeOption(themeName);\n }\n }, optionTags);\n // 2. render the radio-buttons used to choose variant\n const radioButtonCallback = (colorScheme) => {\n const selectedThemeName = select.options[select.selectedIndex].value;\n vm.changeThemeOption(selectedThemeName, colorScheme);\n };\n const isDarkSelected = activeThemeVariant === \"dark\";\n const isLightSelected = activeThemeVariant === \"light\";\n const darkRadioButton = t.input({ type: \"radio\", name: \"radio-chooser\", value: \"dark\", id: \"dark\", checked: isDarkSelected });\n const defaultRadioButton = t.input({ type: \"radio\", name: \"radio-chooser\", value: \"default\", id: \"default\", checked: !(isDarkSelected || isLightSelected) });\n const lightRadioButton = t.input({ type: \"radio\", name: \"radio-chooser\", value: \"light\", id: \"light\", checked: isLightSelected });\n const radioButtons = t.form({\n className: {\n hidden: () => {\n const themeName = select.options[select.selectedIndex].value;\n return \"id\" in vm.themeMapping[themeName];\n }\n },\n onChange: (e) => radioButtonCallback(e.target.value)\n },\n [\n defaultRadioButton,\n t.label({for: \"default\"}, \"Match system theme\"),\n darkRadioButton,\n t.label({for: \"dark\"}, \"dark\"),\n lightRadioButton,\n t.label({for: \"light\"}, \"light\"),\n ]);\n return t.div({ className: \"theme-chooser\" }, [select, radioButtons]);\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../general/TemplateView\";\nimport {AvatarView} from \"../AvatarView\";\nimport {StaticView} from \"../general/StaticView\";\n\nexport class CreateRoomView extends TemplateView {\n render(t, vm) {\n return t.main({className: \"middle\"}, \n t.div({className: \"CreateRoomView centered-column\"}, [\n t.h2(\"Create room\"),\n //t.div({className: \"RoomView_error\"}, vm => vm.error),\n t.form({className: \"CreateRoomView_detailsForm form\", onChange: evt => this.onFormChange(evt), onSubmit: evt => this.onSubmit(evt)}, [\n t.div({className: \"vertical-layout\"}, [\n t.button({type: \"button\", className: \"CreateRoomView_selectAvatar\", onClick: () => vm.selectAvatar()},\n t.mapView(vm => vm.hasAvatar, hasAvatar => {\n if (hasAvatar) {\n return new AvatarView(vm, 64);\n } else {\n return new StaticView(undefined, t => {\n return t.div({className: \"CreateRoomView_selectAvatarPlaceholder\"})\n });\n }\n })\n ),\n t.div({className: \"stretch form-row text\"}, [\n t.label({for: \"name\"}, vm.i18n`Room name`),\n t.input({\n onInput: evt => vm.setName(evt.target.value),\n type: \"text\", name: \"name\", id: \"name\",\n placeholder: vm.i18n`Enter a room name`\n }),\n ]),\n ]),\n t.div({className: \"form-row text\"}, [\n t.label({for: \"topic\"}, vm.i18n`Topic (optional)`),\n t.textarea({\n onInput: evt => vm.setTopic(evt.target.value),\n name: \"topic\", id: \"topic\",\n placeholder: vm.i18n`Topic`\n }),\n ]),\n t.div({className: \"form-group\"}, [\n t.div({className: \"form-row check\"}, [\n t.input({type: \"radio\", name: \"isPublic\", id: \"isPrivate\", value: \"false\", checked: !vm.isPublic}),\n t.label({for: \"isPrivate\"}, vm.i18n`Private room, only upon invitation.`)\n ]),\n t.div({className: \"form-row check\"}, [\n t.input({type: \"radio\", name: \"isPublic\", id: \"isPublic\", value: \"true\", checked: vm.isPublic}),\n t.label({for: \"isPublic\"}, vm.i18n`Public room, anyone can join`)\n ]),\n ]),\n t.div({className: {\"form-row check\": true, hidden: vm => vm.isPublic}}, [\n t.input({type: \"checkbox\", name: \"isEncrypted\", id: \"isEncrypted\", checked: vm.isEncrypted}),\n t.label({for: \"isEncrypted\"}, vm.i18n`Enable end-to-end encryption`)\n ]),\n t.div({className: {\"form-row text\": true, hidden: vm => !vm.isPublic}}, [\n t.label({for: \"roomAlias\"}, vm.i18n`Room alias`),\n t.input({\n onInput: evt => vm.setRoomAlias(evt.target.value),\n type: \"text\", name: \"roomAlias\", id: \"roomAlias\",\n placeholder: vm.i18n`Room alias (<alias>, or #<alias> or #<alias>:hs.tld`}),\n ]),\n t.div({className: \"form-group\"}, [\n t.div(t.button({className: \"link\", type: \"button\", onClick: () => vm.toggleAdvancedShown()},\n vm => vm.isAdvancedShown ? vm.i18n`Hide advanced settings` : vm.i18n`Show advanced settings`)),\n t.div({className: {\"form-row check\": true, hidden: vm => !vm.isAdvancedShown}}, [\n t.input({type: \"checkbox\", name: \"isFederationDisabled\", id: \"isFederationDisabled\", checked: vm.isFederationDisabled}),\n t.label({for: \"isFederationDisabled\"}, [\n vm.i18n`Disable federation`,\n t.p({className: \"form-row-description\"}, vm.i18n`Can't be changed later. This will prevent people on other homeservers from joining the room. This is typically used when only people from your own organisation (if applicable) should be allowed in the room, and is otherwise not needed.`)\n ]),\n ]),\n ]),\n t.div({className: \"button-row\"}, [\n t.button({\n className: \"button-action primary\",\n type: \"submit\",\n disabled: vm => !vm.canCreate\n }, vm.i18n`Create room`),\n ]),\n ])\n ])\n );\n }\n\n onFormChange(evt) {\n switch (evt.target.name) {\n case \"isEncrypted\":\n this.value.setEncrypted(evt.target.checked);\n break;\n case \"isPublic\":\n this.value.setPublic(evt.currentTarget.isPublic.value === \"true\");\n break;\n case \"isFederationDisabled\":\n this.value.setFederationDisabled(evt.target.checked);\n break;\n }\n }\n\n onSubmit(evt) {\n evt.preventDefault();\n this.value.create();\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../../general/TemplateView\";\nimport {classNames, tag} from \"../../general/html\";\nimport {AvatarView} from \"../../AvatarView.js\";\n\nexport class RoomDetailsView extends TemplateView {\n render(t, vm) {\n const encryptionString = () => vm.isEncrypted ? vm.i18n`On` : vm.i18n`Off`;\n return t.div({className: \"RoomDetailsView\"}, [\n t.div({className: \"RoomDetailsView_avatar\"},\n [\n t.view(new AvatarView(vm, 52)),\n t.mapView(vm => vm.isEncrypted, isEncrypted => new EncryptionIconView(isEncrypted))\n ]),\n t.div({className: \"RoomDetailsView_name\"}, [t.h2(vm => vm.name)]),\n this._createRoomAliasDisplay(vm),\n t.div({className: \"RoomDetailsView_rows\"},\n [\n this._createRightPanelButtonRow(t, vm.i18n`People`, { MemberCount: true }, vm => vm.memberCount,\n () => vm.openPanel(\"members\")),\n this._createRightPanelRow(t, vm.i18n`Encryption`, {EncryptionStatus: true}, encryptionString)\n ])\n ]);\n }\n\n _createRoomAliasDisplay(vm) {\n return vm.canonicalAlias ? tag.div({className: \"RoomDetailsView_id\"}, [vm.canonicalAlias]) :\n \"\";\n }\n\n _createRightPanelRow(t, label, labelClass, value) {\n const labelClassString = classNames({RoomDetailsView_label: true, ...labelClass});\n return t.div({className: \"RoomDetailsView_row\"}, [\n t.div({className: labelClassString}, [label]),\n t.div({className: \"RoomDetailsView_value\"}, value)\n ]);\n }\n\n _createRightPanelButtonRow(t, label, labelClass, value, onClick) {\n const labelClassString = classNames({RoomDetailsView_label: true, ...labelClass});\n return t.button({className: \"RoomDetailsView_row\", onClick}, [\n t.div({className: labelClassString}, [label]),\n t.div({className: \"RoomDetailsView_value\"}, value)\n ]);\n }\n\n}\n\nclass EncryptionIconView extends TemplateView {\n render(t, isEncrypted) {\n return t.div({className: \"EncryptionIconView\"},\n [t.div({className: isEncrypted ? \"EncryptionIconView_encrypted\" : \"EncryptionIconView_unencrypted\"})]);\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// start is included in the range,\n// end is excluded,\n// so [2, 2[ means an empty range\nexport class Range {\n constructor(\n public readonly start: number,\n public readonly end: number\n ) {}\n\n get length() {\n return this.end - this.start;\n }\n\n contains(range: Range): boolean {\n return range.start >= this.start && range.end <= this.end;\n }\n\n containsIndex(idx: number): boolean {\n return idx >= this.start && idx < this.end;\n }\n\n toLocalIndex(idx: number) {\n return idx - this.start;\n }\n\n intersects(range: Range): boolean {\n return range.start < this.end && this.start < range.end;\n }\n\n forEachInIterator<T>(it: Iterator<T>, callback: ((T, i: number) => void)) {\n let i = 0;\n for (i = 0; i < this.start; i += 1) {\n it.next();\n }\n for (i = 0; i < this.length; i += 1) {\n const result = it.next();\n if (result.done) {\n break;\n } else {\n callback(result.value, this.start + i);\n }\n }\n }\n\n [Symbol.iterator](): Iterator<number> {\n return new RangeIterator(this);\n }\n\n reverseIterable(): Iterable<number> {\n return new ReverseRangeIterator(this);\n }\n\n clampIndex(idx: number, end = this.end - 1) {\n return Math.min(Math.max(this.start, idx), end);\n }\n\n getIndexZone(idx): RangeZone {\n if (idx < this.start) {\n return RangeZone.Before;\n } else if (idx < this.end) {\n return RangeZone.Inside;\n } else {\n return RangeZone.After;\n }\n }\n}\n\nexport enum RangeZone {\n Before = 1,\n Inside,\n After\n}\n\nclass RangeIterator implements Iterator<number> {\n private idx: number;\n constructor(private readonly range: Range) {\n this.idx = range.start - 1;\n }\n\n next(): IteratorResult<number> {\n if (this.idx < (this.range.end - 1)) {\n this.idx += 1;\n return {value: this.idx, done: false};\n } else {\n return {value: undefined, done: true};\n }\n }\n}\n\nclass ReverseRangeIterator implements Iterable<number>, Iterator<number> {\n private idx: number;\n constructor(private readonly range: Range) {\n this.idx = range.end;\n }\n\n [Symbol.iterator]() {\n return this;\n }\n\n next(): IteratorResult<number> {\n if (this.idx > this.range.start) {\n this.idx -= 1;\n return {value: this.idx, done: false};\n } else {\n return {value: undefined, done: true};\n }\n }\n}\n\nexport function tests() {\n return {\n \"length\": assert => {\n const a = new Range(2, 5);\n assert.equal(a.length, 3);\n },\n \"iterator\": assert => {\n assert.deepEqual(Array.from(new Range(2, 5)), [2, 3, 4]);\n },\n \"reverseIterable\": assert => {\n assert.deepEqual(Array.from(new Range(2, 5).reverseIterable()), [4, 3, 2]);\n },\n \"containsIndex\": assert => {\n const a = new Range(2, 5);\n assert.equal(a.containsIndex(0), false);\n assert.equal(a.containsIndex(1), false);\n assert.equal(a.containsIndex(2), true);\n assert.equal(a.containsIndex(3), true);\n assert.equal(a.containsIndex(4), true);\n assert.equal(a.containsIndex(5), false);\n assert.equal(a.containsIndex(6), false);\n },\n \"intersects returns false for touching ranges\": assert => {\n const a = new Range(2, 5);\n const b = new Range(5, 10);\n assert.equal(a.intersects(b), false);\n assert.equal(b.intersects(a), false);\n },\n \"intersects returns false\": assert => {\n const a = new Range(2, 5);\n const b = new Range(50, 100);\n assert.equal(a.intersects(b), false);\n assert.equal(b.intersects(a), false);\n },\n \"intersects returns true for 1 overlapping item\": assert => {\n const a = new Range(2, 5);\n const b = new Range(4, 10);\n assert.equal(a.intersects(b), true);\n assert.equal(b.intersects(a), true);\n },\n \"contains beyond left edge\": assert => {\n const a = new Range(2, 5);\n const b = new Range(1, 3);\n assert.equal(a.contains(b), false);\n },\n \"contains at left edge\": assert => {\n const a = new Range(2, 5);\n const b = new Range(2, 3);\n assert.equal(a.contains(b), true);\n },\n \"contains between edges\": assert => {\n const a = new Range(2, 5);\n const b = new Range(3, 4);\n assert.equal(a.contains(b), true);\n },\n \"contains at right edge\": assert => {\n const a = new Range(2, 5);\n const b = new Range(3, 5);\n assert.equal(a.contains(b), true);\n },\n \"contains beyond right edge\": assert => {\n const a = new Range(2, 5);\n const b = new Range(4, 6);\n assert.equal(a.contains(b), false);\n },\n \"contains for non-intersecting ranges\": assert => {\n const a = new Range(2, 5);\n const b = new Range(5, 6);\n assert.equal(a.contains(b), false);\n },\n \"forEachInIterator with more values available\": assert => {\n const callbackValues: {v: string, i: number}[] = [];\n const values = [\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"];\n const it = values[Symbol.iterator]();\n new Range(2, 5).forEachInIterator(it, (v, i) => callbackValues.push({v, i}));\n assert.deepEqual(callbackValues, [\n {v: \"c\", i: 2},\n {v: \"d\", i: 3},\n {v: \"e\", i: 4},\n ]);\n },\n \"forEachInIterator with fewer values available\": assert => {\n const callbackValues: {v: string, i: number}[] = [];\n const values = [\"a\", \"b\", \"c\"];\n const it = values[Symbol.iterator]();\n new Range(2, 5).forEachInIterator(it, (v, i) => callbackValues.push({v, i}));\n assert.deepEqual(callbackValues, [\n {v: \"c\", i: 2},\n ]);\n },\n \"clampIndex\": assert => {\n assert.equal(new Range(2, 5).clampIndex(0), 2);\n assert.equal(new Range(2, 5).clampIndex(2), 2);\n assert.equal(new Range(2, 5).clampIndex(3), 3);\n assert.equal(new Range(2, 5).clampIndex(4), 4);\n assert.equal(new Range(2, 5).clampIndex(5), 4);\n assert.equal(new Range(2, 5).clampIndex(10), 4);\n }\n };\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {Range, RangeZone} from \"./Range\";\nimport {defaultObserverWith} from \"../../../../observable/list/BaseObservableList\";\n\nfunction skipOnIterator<T>(it: Iterator<T>, pos: number): boolean {\n let i = 0;\n while (i < pos) {\n i += 1;\n if(it.next().done) {\n return false;\n }\n }\n return true;\n}\n\nfunction getIteratorValueAtIdx<T>(it: Iterator<T>, idx: number): undefined | T {\n if (skipOnIterator(it, idx)) {\n const result = it.next();\n if (!result.done) {\n return result.value;\n }\n }\n return undefined;\n}\n\nexport enum ResultType {\n Move,\n Add,\n Remove,\n RemoveAndAdd,\n UpdateRange\n}\n\nexport interface MoveResult {\n type: ResultType.Move;\n fromIdx: number;\n toIdx: number\n}\n\ninterface AddResult<T> {\n type: ResultType.Add;\n newRange?: ListRange;\n /** the list index of an item to add */\n addIdx: number;\n /** the value to add at addIdx */\n value: T\n}\n\ninterface RemoveResult {\n type: ResultType.Remove;\n newRange?: ListRange;\n /** the list index of an item to remove, before the add or remove event has been taken into account */\n removeIdx: number;\n}\n\n// need to repeat the fields from RemoveResult and AddResult here\n// to make the discriminated union work\ninterface RemoveAndAddResult<T> {\n type: ResultType.RemoveAndAdd;\n newRange?: ListRange;\n /** the list index of an item to remove, before the add or remove event has been taken into account */\n removeIdx: number;\n /** the list index of an item to add */\n addIdx: number;\n /** the value to add at addIdx */\n value: T;\n}\n\ninterface UpdateRangeResult {\n type: ResultType.UpdateRange;\n newRange?: ListRange;\n}\n\nexport type AddRemoveResult<T> = AddResult<T> | RemoveResult | RemoveAndAddResult<T> | UpdateRangeResult;\n\nexport class ListRange extends Range {\n constructor(\n start: number,\n end: number,\n private _totalLength: number,\n private _viewportItemCount: number = end - start\n ) {\n super(start, end);\n }\n\n expand(amount: number): ListRange {\n // don't expand ranges that won't render anything\n if (this.length === 0) {\n return this;\n }\n const newStart = Math.max(0, this.start - amount);\n const newEnd = Math.min(this.totalLength, this.end + amount);\n return new ListRange(\n newStart,\n newEnd,\n this.totalLength,\n this._viewportItemCount\n );\n }\n\n get totalLength(): number {\n return this._totalLength;\n }\n\n get viewportItemCount(): number {\n return this._viewportItemCount;\n }\n\n static fromViewport(listLength: number, itemHeight: number, listHeight: number, scrollTop: number) {\n const topCount = Math.min(Math.max(0, Math.floor(scrollTop / itemHeight)), listLength);\n const itemsAfterTop = listLength - topCount;\n const viewportItemCount = listHeight !== 0 ? Math.ceil(listHeight / itemHeight) : 0;\n const renderCount = Math.min(viewportItemCount, itemsAfterTop);\n return new ListRange(topCount, topCount + renderCount, listLength, viewportItemCount);\n }\n\n queryAdd<T>(idx: number, value: T, list: Iterable<T>): AddRemoveResult<T> {\n const maxAddIdx = this.viewportItemCount > this.length ? this.end : this.end - 1;\n if (idx <= maxAddIdx) {\n // use maxAddIdx to allow to grow the range by one at a time\n // if the viewport isn't filled yet\n const addIdx = this.clampIndex(idx, maxAddIdx);\n const addValue = addIdx === idx ? value : getIteratorValueAtIdx(list[Symbol.iterator](), addIdx)!;\n return this.createAddResult<T>(addIdx, addValue);\n } else {\n // if the add happened after the range, we only update the range with the new length\n return {type: ResultType.UpdateRange, newRange: this.deriveRange(1, 0)};\n }\n }\n\n queryRemove<T>(idx: number, list: Iterable<T>): AddRemoveResult<T> {\n if (idx < this.end) {\n const removeIdx = this.clampIndex(idx);\n return this.createRemoveResult(removeIdx, list);\n } else {\n return {type: ResultType.UpdateRange, newRange: this.deriveRange(-1, 0)};\n }\n }\n\n queryMove<T>(fromIdx: number, toIdx: number, value: T, list: Iterable<T>): MoveResult | AddRemoveResult<T> | undefined {\n const fromZone = this.getIndexZone(fromIdx);\n const toZone = this.getIndexZone(toIdx);\n if (fromZone === toZone) {\n if (fromZone === RangeZone.Before || fromZone === RangeZone.After) {\n return;\n } else if (fromZone === RangeZone.Inside) {\n return {type: ResultType.Move, fromIdx, toIdx};\n }\n } else {\n const addIdx = this.clampIndex(toIdx);\n const removeIdx = this.clampIndex(fromIdx);\n const addValue = addIdx === toIdx ? value : getIteratorValueAtIdx(list[Symbol.iterator](), addIdx)!;\n return {type: ResultType.RemoveAndAdd, removeIdx, addIdx, value: addValue};\n }\n }\n\n private createAddResult<T>(addIdx: number, value: T): AddRemoveResult<T> {\n // if the view port isn't filled yet, we don't remove\n if (this.viewportItemCount > this.length) {\n return {type: ResultType.Add, addIdx, value, newRange: this.deriveRange(1, 1)};\n } else {\n const removeIdx = this.clampIndex(Number.MAX_SAFE_INTEGER);\n return {type: ResultType.RemoveAndAdd, removeIdx, addIdx, value, newRange: this.deriveRange(1, 0)};\n }\n }\n\n private createRemoveResult<T>(removeIdx: number, list: Iterable<T>): AddRemoveResult<T> {\n if (this.end < this.totalLength) {\n // we have items below the range, we can add one from there to fill the viewport\n const addIdx = this.clampIndex(Number.MAX_SAFE_INTEGER);\n // we assume the value has already been removed from the list,\n // so we can just look up the next value which is already at the same idx\n const value = getIteratorValueAtIdx(list[Symbol.iterator](), addIdx)!;\n return {type: ResultType.RemoveAndAdd, removeIdx, value, addIdx, newRange: this.deriveRange(-1, 0)};\n } else if (this.start !== 0) {\n // move the range 1 item up so we still display a viewport full of items\n const newRange = this.deriveRange(-1, 0, 1);\n const addIdx = newRange.start;\n // we assume the value has already been removed from the list,\n // so we can just look up the next value which is already at the same idx\n const value = getIteratorValueAtIdx(list[Symbol.iterator](), addIdx)!;\n return {type: ResultType.RemoveAndAdd, removeIdx, value, addIdx, newRange};\n } else {\n // we can't add at the bottom nor top, already constrained\n return {type: ResultType.Remove, removeIdx, newRange: this.deriveRange(-1, 0)};\n }\n }\n\n private deriveRange(totalLengthInc: number, viewportItemCountDecr: number, startDecr: number = 0): ListRange {\n const start = this.start - startDecr;\n const totalLength = this.totalLength + totalLengthInc;\n // prevent end being larger than totalLength\n const end = Math.min(Math.max(start, this.end - startDecr + viewportItemCountDecr), totalLength);\n return new ListRange(\n start,\n end,\n totalLength,\n this.viewportItemCount\n );\n }\n}\n\nimport {ObservableArray} from \"../../../../observable/list/ObservableArray\";\n\nexport function tests() {\n return {\n \"fromViewport\": assert => {\n const range = ListRange.fromViewport(10, 20, 90, 30);\n assert.equal(range.start, 1);\n assert.equal(range.end, 6);\n assert.equal(range.totalLength, 10);\n },\n \"fromViewport at end\": assert => {\n const itemHeight = 20;\n const range = ListRange.fromViewport(10, itemHeight, 3 * itemHeight, 7 * itemHeight);\n assert.equal(range.start, 7);\n assert.equal(range.end, 10);\n assert.equal(range.totalLength, 10);\n },\n \"fromViewport with not enough items to fill viewport\": assert => {\n const itemHeight = 20;\n const range = ListRange.fromViewport(5, itemHeight, 8 * itemHeight, 0);\n assert.equal(range.start, 0);\n assert.equal(range.end, 5);\n assert.equal(range.totalLength, 5);\n assert.equal(range.length, 5);\n assert.equal(range.viewportItemCount, 8);\n },\n \"expand at start of list\": assert => {\n const range = new ListRange(1, 5, 10);\n const expanded = range.expand(2);\n assert.equal(expanded.start, 0);\n assert.equal(expanded.end, 7);\n assert.equal(expanded.totalLength, 10);\n assert.equal(expanded.length, 7);\n },\n \"expand at end of list\": assert => {\n const range = new ListRange(7, 9, 10);\n const expanded = range.expand(2);\n assert.equal(expanded.start, 5);\n assert.equal(expanded.end, 10);\n assert.equal(expanded.totalLength, 10);\n assert.equal(expanded.length, 5);\n },\n \"expand in middle of list\": assert => {\n const range = new ListRange(4, 6, 10);\n const expanded = range.expand(2);\n assert.equal(expanded.start, 2);\n assert.equal(expanded.end, 8);\n assert.equal(expanded.totalLength, 10);\n assert.equal(expanded.length, 6);\n },\n \"queryAdd with addition before range\": assert => {\n const list = new ObservableArray([\"b\", \"c\", \"d\", \"e\"]);\n const range = new ListRange(1, 3, list.length);\n let added = false;\n list.subscribe(defaultObserverWith({\n onAdd(idx, value) {\n added = true;\n const result = range.queryAdd(idx, value, list);\n assert.deepEqual(result, {\n type: ResultType.RemoveAndAdd,\n removeIdx: 2,\n addIdx: 1,\n value: \"b\",\n newRange: new ListRange(1, 3, 5)\n });\n }\n }));\n list.insert(0, \"a\");\n assert(added);\n },\n \"queryAdd with addition within range\": assert => {\n const list = new ObservableArray([\"a\", \"b\", \"d\", \"e\"]);\n const range = new ListRange(1, 3, list.length);\n let added = false;\n list.subscribe(defaultObserverWith({\n onAdd(idx, value) {\n added = true;\n const result = range.queryAdd(idx, value, list);\n assert.deepEqual(result, {\n type: ResultType.RemoveAndAdd,\n removeIdx: 2,\n addIdx: 2,\n value: \"c\",\n newRange: new ListRange(1, 3, 5)\n });\n }\n }));\n list.insert(2, \"c\");\n assert(added);\n },\n \"queryAdd with addition after range\": assert => {\n const list = new ObservableArray([\"a\", \"b\", \"c\", \"d\"]);\n const range = new ListRange(1, 3, list.length);\n let added = false;\n list.subscribe(defaultObserverWith({\n onAdd(idx, value) {\n added = true;\n const result = range.queryAdd(idx, value, list);\n assert.deepEqual(result, {\n type: ResultType.UpdateRange,\n newRange: new ListRange(1, 3, 5)\n });\n }\n }));\n list.insert(4, \"e\");\n assert(added);\n },\n \"queryAdd with too few items to fill viewport grows the range\": assert => {\n const list = new ObservableArray([\"a\", \"b\", \"d\"]);\n const viewportItemCount = 4;\n const range = new ListRange(0, 3, list.length, viewportItemCount);\n let added = false;\n list.subscribe(defaultObserverWith({\n onAdd(idx, value) {\n added = true;\n const result = range.queryAdd(idx, value, list);\n assert.deepEqual(result, {\n type: ResultType.Add,\n newRange: new ListRange(0, 4, 4),\n addIdx: 2,\n value: \"c\"\n });\n }\n }));\n list.insert(2, \"c\");\n assert(added);\n },\n \"queryRemove with removal before range\": assert => {\n const list = new ObservableArray([\"a\", \"b\", \"c\", \"d\", \"e\"]);\n const range = new ListRange(1, 3, list.length);\n let removed = false;\n list.subscribe(defaultObserverWith({\n onRemove(idx) {\n removed = true;\n const result = range.queryRemove(idx, list);\n assert.deepEqual(result, {\n type: ResultType.RemoveAndAdd,\n removeIdx: 1,\n addIdx: 2,\n value: \"d\",\n newRange: new ListRange(1, 3, 4)\n });\n }\n }));\n list.remove(0);\n assert(removed);\n },\n \"queryRemove with removal within range\": assert => {\n const list = new ObservableArray([\"a\", \"b\", \"c\", \"d\", \"e\"]);\n const range = new ListRange(1, 3, list.length);\n let removed = false;\n list.subscribe(defaultObserverWith({\n onRemove(idx) {\n removed = true;\n const result = range.queryRemove(idx, list);\n assert.deepEqual(result, {\n type: ResultType.RemoveAndAdd,\n removeIdx: 2,\n addIdx: 2,\n value: \"d\",\n newRange: new ListRange(1, 3, 4)\n });\n assert.equal(list.length, 4);\n }\n }));\n list.remove(2);\n assert(removed);\n },\n \"queryRemove with removal after range\": assert => {\n const list = new ObservableArray([\"a\", \"b\", \"c\", \"d\", \"e\"]);\n const range = new ListRange(1, 3, list.length);\n let removed = false;\n list.subscribe(defaultObserverWith({\n onRemove(idx) {\n removed = true;\n const result = range.queryRemove(idx, list);\n assert.deepEqual(result, {\n type: ResultType.UpdateRange,\n newRange: new ListRange(1, 3, 4)\n });\n }\n }));\n list.remove(3);\n assert(removed);\n },\n \"queryRemove at bottom of range moves range one up\": assert => {\n const list = new ObservableArray([\"a\", \"b\", \"c\"]);\n const range = new ListRange(1, 3, list.length);\n let removed = false;\n list.subscribe(defaultObserverWith({\n onRemove(idx) {\n removed = true;\n const result = range.queryRemove(idx, list);\n assert.deepEqual(result, {\n newRange: new ListRange(0, 2, 2),\n type: ResultType.RemoveAndAdd,\n removeIdx: 2,\n addIdx: 0,\n value: \"a\"\n });\n }\n }));\n list.remove(2);\n assert(removed);\n },\n \"queryRemove with range on full length shrinks range\": assert => {\n const list = new ObservableArray([\"a\", \"b\", \"c\"]);\n const range = new ListRange(0, 3, list.length);\n let removed = false;\n list.subscribe(defaultObserverWith({\n onRemove(idx) {\n removed = true;\n const result = range.queryRemove(idx, list);\n assert.deepEqual(result, {\n newRange: new ListRange(0, 2, 2, 3),\n type: ResultType.Remove,\n removeIdx: 2,\n });\n }\n }));\n list.remove(2);\n assert(removed);\n },\n \"queryMove with move inside range\": assert => {\n const list = new ObservableArray([\"a\", \"b\", \"c\", \"d\", \"e\"]);\n const range = new ListRange(1, 4, list.length);\n let moved = false;\n list.subscribe(defaultObserverWith({\n onMove(fromIdx, toIdx, value) {\n moved = true;\n const result = range.queryMove(fromIdx, toIdx, value, list);\n assert.deepEqual(result, {\n type: ResultType.Move,\n fromIdx: 2,\n toIdx: 3\n });\n }\n }));\n list.move(2, 3);\n assert(moved);\n },\n \"queryMove with move from before to inside range\": assert => {\n const list = new ObservableArray([\"a\", \"b\", \"c\", \"d\", \"e\"]);\n const range = new ListRange(2, 5, list.length);\n let moved = false;\n list.subscribe(defaultObserverWith({\n onMove(fromIdx, toIdx, value) {\n moved = true;\n const result = range.queryMove(fromIdx, toIdx, value, list);\n assert.deepEqual(result, {\n type: ResultType.RemoveAndAdd,\n removeIdx: 2,\n addIdx: 3,\n value: \"a\"\n });\n }\n }));\n list.move(0, 3); // move \"a\" to after \"d\"\n assert(moved);\n },\n \"queryMove with move from after to inside range\": assert => {\n const list = new ObservableArray([\"a\", \"b\", \"c\", \"d\", \"e\"]);\n const range = new ListRange(0, 3, list.length);\n let moved = false;\n list.subscribe(defaultObserverWith({\n onMove(fromIdx, toIdx, value) {\n moved = true;\n const result = range.queryMove(fromIdx, toIdx, value, list);\n assert.deepEqual(result, {\n type: ResultType.RemoveAndAdd,\n removeIdx: 2,\n addIdx: 1,\n value: \"e\"\n });\n }\n }));\n list.move(4, 1); // move \"e\" to before \"b\"\n assert(moved);\n },\n \"queryMove with move inside range to after\": assert => {\n const list = new ObservableArray([\"a\", \"b\", \"c\", \"d\", \"e\"]);\n const range = new ListRange(0, 3, list.length);\n let moved = false;\n list.subscribe(defaultObserverWith({\n onMove(fromIdx, toIdx, value) {\n moved = true;\n const result = range.queryMove(fromIdx, toIdx, value, list);\n assert.deepEqual(result, {\n type: ResultType.RemoveAndAdd,\n removeIdx: 1,\n addIdx: 2,\n value: \"d\"\n });\n }\n }));\n list.move(1, 3); // move \"b\" to after \"d\"\n assert(moved);\n },\n \"queryMove with move inside range to before\": assert => {\n const list = new ObservableArray([\"a\", \"b\", \"c\", \"d\", \"e\"]);\n const range = new ListRange(2, 5, list.length);\n let moved = false;\n list.subscribe(defaultObserverWith({\n onMove(fromIdx, toIdx, value) {\n moved = true;\n const result = range.queryMove(fromIdx, toIdx, value, list);\n assert.deepEqual(result, {\n type: ResultType.RemoveAndAdd,\n removeIdx: 3,\n addIdx: 2,\n value: \"b\"\n });\n }\n }));\n list.move(3, 0); // move \"d\" to before \"a\"\n assert(moved);\n },\n \"queryMove with move from before range to after\": assert => {\n const list = new ObservableArray([\"a\", \"b\", \"c\", \"d\", \"e\"]);\n const range = new ListRange(1, 4, list.length);\n let moved = false;\n list.subscribe(defaultObserverWith({\n onMove(fromIdx, toIdx, value) {\n moved = true;\n const result = range.queryMove(fromIdx, toIdx, value, list);\n assert.deepEqual(result, {\n type: ResultType.RemoveAndAdd,\n removeIdx: 1,\n addIdx: 3,\n value: \"e\"\n });\n }\n }));\n list.move(0, 4); // move \"a\" to after \"e\"\n assert(moved);\n },\n // would be good to test here what multiple mutations look like with executing the result of queryXXX\n // on an array, much like we do in the view.\n };\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {tag} from \"./html\";\nimport {removeChildren, mountView} from \"./utils\";\nimport {ListRange, ResultType, AddRemoveResult} from \"./ListRange\";\nimport {ListView, IOptions as IParentOptions} from \"./ListView\";\nimport {IView} from \"./types\";\n\nexport interface IOptions<T, V> extends IParentOptions<T, V> {\n itemHeight: number;\n overflowItems?: number;\n}\n\nexport class LazyListView<T, V extends IView> extends ListView<T, V> {\n private renderRange?: ListRange;\n private height?: number;\n private itemHeight: number;\n private overflowItems: number;\n private scrollContainer?: HTMLElement;\n\n constructor(\n {itemHeight, overflowItems = 20, ...options}: IOptions<T, V>, \n childCreator: (value: T) => V\n ) {\n super(options, childCreator);\n this.itemHeight = itemHeight;\n this.overflowItems = overflowItems;\n }\n\n handleEvent(e: Event) {\n if (e.type === \"scroll\") {\n this.handleScroll();\n } else {\n super.handleEvent(e);\n }\n }\n\n handleScroll() {\n const visibleRange = this._getVisibleRange();\n // don't contain empty ranges\n // as it will prevent clearing the list\n // once it is scrolled far enough out of view\n if (visibleRange.length !== 0 && !this.renderRange!.contains(visibleRange)) {\n const prevRenderRange = this.renderRange!;\n this.renderRange = visibleRange.expand(this.overflowItems);\n this.renderUpdate(prevRenderRange, this.renderRange);\n }\n }\n \n // override\n async loadList() {\n /*\n Wait two frames for the return from mount() to be inserted into DOM.\n This should be enough, but if this gives us trouble we can always use\n MutationObserver.\n */\n await new Promise(r => requestAnimationFrame(r));\n await new Promise(r => requestAnimationFrame(r));\n\n if (!this._list) {\n return;\n }\n this._subscription = this._list.subscribe(this);\n const visibleRange = this._getVisibleRange();\n this.renderRange = visibleRange.expand(this.overflowItems);\n this._childInstances = [];\n this.reRenderFullRange(this.renderRange);\n }\n\n private _getVisibleRange() {\n const {clientHeight, scrollTop} = this.root()!;\n if (clientHeight === 0) {\n throw new Error(\"LazyListView height is 0\");\n }\n return ListRange.fromViewport(this._list.length, this.itemHeight, clientHeight, scrollTop);\n }\n\n private reRenderFullRange(range: ListRange) {\n removeChildren(this._listElement!);\n const fragment = document.createDocumentFragment();\n const it = this._list[Symbol.iterator]();\n this._childInstances!.length = 0;\n range.forEachInIterator(it, item => {\n const child = this._childCreator(item);\n this._childInstances!.push(child);\n fragment.appendChild(mountView(child, this._mountArgs));\n });\n this._listElement!.appendChild(fragment);\n this.adjustPadding(range);\n }\n\n private renderUpdate(prevRange: ListRange, newRange: ListRange) {\n if (newRange.intersects(prevRange)) {\n // remove children in reverse order so child index isn't affected by previous removals\n for (const idxInList of prevRange.reverseIterable()) {\n if (!newRange.containsIndex(idxInList)) {\n const localIdx = idxInList - prevRange.start;\n this.removeChild(localIdx);\n }\n }\n // use forEachInIterator instead of for loop as we need to advance\n // the list iterator to the start of the range first\n newRange.forEachInIterator(this._list[Symbol.iterator](), (item, idxInList) => {\n if (!prevRange.containsIndex(idxInList)) {\n const localIdx = idxInList - newRange.start;\n this.addChild(localIdx, item);\n }\n });\n this.adjustPadding(newRange);\n } else {\n this.reRenderFullRange(newRange);\n }\n }\n\n private adjustPadding(range: ListRange) {\n const paddingTop = range.start * this.itemHeight;\n const paddingBottom = (range.totalLength - range.end) * this.itemHeight;\n const style = this._listElement!.style;\n style.paddingTop = `${paddingTop}px`;\n style.paddingBottom = `${paddingBottom}px`;\n }\n\n mount() {\n const listElement = super.mount();\n this.scrollContainer = tag.div({className: \"LazyListParent\"}, listElement) as HTMLElement;\n this.scrollContainer.addEventListener(\"scroll\", this);\n return this.scrollContainer;\n }\n\n unmount() {\n this.root()!.removeEventListener(\"scroll\", this);\n this.scrollContainer = undefined;\n super.unmount();\n }\n\n root(): Element | undefined {\n return this.scrollContainer;\n }\n\n private get _listElement(): HTMLElement | undefined {\n return super.root() as HTMLElement | undefined;\n }\n\n onAdd(idx: number, value: T) {\n const result = this.renderRange!.queryAdd(idx, value, this._list);\n this.applyRemoveAddResult(result);\n }\n\n onRemove(idx: number, value: T) {\n const result = this.renderRange!.queryRemove(idx, this._list);\n this.applyRemoveAddResult(result);\n }\n\n onMove(fromIdx: number, toIdx: number, value: T) {\n const result = this.renderRange!.queryMove(fromIdx, toIdx, value, this._list);\n if (result) {\n if (result.type === ResultType.Move) {\n this.moveChild(\n this.renderRange!.toLocalIndex(result.fromIdx),\n this.renderRange!.toLocalIndex(result.toIdx)\n );\n } else {\n this.applyRemoveAddResult(result);\n }\n }\n }\n\n onUpdate(i: number, value: T, params: any) {\n if (this.renderRange!.containsIndex(i)) {\n this.updateChild(this.renderRange!.toLocalIndex(i), value, params);\n }\n }\n\n private applyRemoveAddResult(result: AddRemoveResult<T>) {\n // order is important here, the new range can have a different start\n if (result.type === ResultType.Remove || result.type === ResultType.RemoveAndAdd) {\n this.removeChild(this.renderRange!.toLocalIndex(result.removeIdx));\n }\n if (result.newRange) {\n this.renderRange = result.newRange;\n this.adjustPadding(this.renderRange)\n }\n if (result.type === ResultType.Add || result.type === ResultType.RemoveAndAdd) {\n this.addChild(this.renderRange!.toLocalIndex(result.addIdx), result.value);\n }\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../../general/TemplateView\";\nimport {AvatarView} from \"../../AvatarView.js\";\n\nexport class MemberTileView extends TemplateView {\n render(t, vm) {\n return t.li({ className: \"MemberTileView\" },\n t.a({ href: vm.detailsUrl },\n [\n t.view(new AvatarView(vm, 32)),\n t.div({ className: \"MemberTileView_name\" }, (vm) => vm.name),\n ])\n );\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {LazyListView} from \"../../general/LazyListView\";\nimport {MemberTileView} from \"./MemberTileView.js\";\n\nexport class MemberListView extends LazyListView {\n constructor(vm) {\n super({\n list: vm.memberTileViewModels,\n className: \"MemberListView\",\n itemHeight: 40\n }, tileViewModel => new MemberTileView(tileViewModel));\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {AvatarView} from \"../../AvatarView.js\";\nimport {TemplateView} from \"../../general/TemplateView\";\n\nexport class MemberDetailsView extends TemplateView {\n render(t, vm) {\n return t.div({className: \"MemberDetailsView\"},\n [ t.view(new AvatarView(vm, 128)),\n t.div({className: \"MemberDetailsView_name\"}, t.h2(vm => vm.name)),\n t.div({className: \"MemberDetailsView_id\"}, vm.userId),\n this._createSection(t, vm.i18n`Role`, vm => vm.role),\n this._createSection(t, vm.i18n`Security`, vm.isEncrypted ?\n vm.i18n`Messages in this room are end-to-end encrypted.` :\n vm.i18n`Messages in this room are not end-to-end encrypted.`\n ),\n this._createOptions(t, vm)\n ]);\n }\n\n _createSection(t, label, value) {\n return t.div({ className: \"MemberDetailsView_section\" },\n [\n t.div({className: \"MemberDetailsView_label\"}, label),\n t.div({className: \"MemberDetailsView_value\"}, value)\n ]);\n }\n\n _createOptions(t, vm) {\n return t.div({ className: \"MemberDetailsView_section\" },\n [\n t.div({className: \"MemberDetailsView_label\"}, vm.i18n`Options`),\n t.div({className: \"MemberDetailsView_options\"},\n [\n t.a({href: vm.linkToUser, target: \"_blank\", rel: \"noopener\"}, vm.i18n`Open Link to User`),\n t.button({className: \"text\", onClick: () => vm.openDirectMessage()}, vm.i18n`Open direct message`)\n ])\n ]);\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../../general/TemplateView\";\nimport {RoomDetailsView} from \"./RoomDetailsView.js\";\nimport {MemberListView} from \"./MemberListView.js\";\nimport {LoadingView} from \"../../general/LoadingView.js\";\nimport {MemberDetailsView} from \"./MemberDetailsView.js\";\n\nexport class RightPanelView extends TemplateView {\n render(t) {\n return t.div({ className: \"RightPanelView\" },\n [\n t.ifView(vm => vm.activeViewModel, vm => new ButtonsView(vm)),\n t.mapView(vm => vm.activeViewModel, vm => this._viewFromType(vm))\n ]\n );\n }\n\n _viewFromType(vm) {\n const type = vm?.type;\n switch (type) {\n case \"room-details\":\n return new RoomDetailsView(vm);\n case \"member-list\":\n return new MemberListView(vm);\n case \"member-details\":\n return new MemberDetailsView(vm);\n default:\n return new LoadingView();\n }\n }\n}\n\nclass ButtonsView extends TemplateView {\n render(t, vm) {\n return t.div({ className: \"RightPanelView_buttons\" },\n [\n t.button({\n className: {\n \"back\": true,\n \"button-utility\": true,\n \"hide\": !vm.activeViewModel.shouldShowBackButton\n }, onClick: () => vm.showPreviousPanel()}),\n t.button({className: \"close button-utility\", onClick: () => vm.closePanel()})\n ]);\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ListView} from \"../../../general/ListView\";\nimport {TemplateView} from \"../../../general/TemplateView\";\n\nexport class ReactionsView extends ListView {\n constructor(reactionsViewModel) {\n const options = {\n className: \"Timeline_messageReactions\",\n tagName: \"div\",\n list: reactionsViewModel.reactions,\n onItemClick: reactionView => reactionView.onClick(),\n }\n super(options, reactionVM => new ReactionView(reactionVM));\n }\n}\n\nclass ReactionView extends TemplateView {\n render(t, vm) {\n return t.button({\n className: {\n active: vm => vm.isActive,\n pending: vm => vm.isPending\n },\n }, [vm.key, \" \", vm => `${vm.count}`]);\n }\n\n onClick() {\n this.value.toggle();\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {renderStaticAvatar} from \"../../../avatar\";\nimport {tag} from \"../../../general/html\";\nimport {mountView} from \"../../../general/utils\";\nimport {TemplateView} from \"../../../general/TemplateView\";\nimport {Popup} from \"../../../general/Popup.js\";\nimport {Menu} from \"../../../general/Menu.js\";\nimport {ReactionsView} from \"./ReactionsView.js\";\n\nexport class BaseMessageView extends TemplateView {\n constructor(value, viewClassForTile, renderFlags, tagName = \"li\") {\n super(value);\n this._menuPopup = null;\n this._tagName = tagName;\n this._viewClassForTile = viewClassForTile;\n // TODO An enum could be nice to make code easier to read at call sites.\n this._renderFlags = renderFlags;\n }\n\n get _interactive() { return this._renderFlags?.interactive ?? true; }\n get _isReplyPreview() { return this._renderFlags?.reply; }\n\n render(t, vm) {\n const children = [this.renderMessageBody(t, vm)];\n if (this._interactive) {\n children.push(t.button({className: \"Timeline_messageOptions\"}, \"⋯\"));\n }\n const li = t.el(this._tagName, {\n className: {\n \"Timeline_message\": true,\n own: vm.isOwn,\n unsent: vm.isUnsent,\n unverified: vm.isUnverified,\n disabled: !this._interactive,\n continuation: vm => vm.isContinuation,\n },\n 'data-event-id': vm.eventId\n }, children);\n // given that there can be many tiles, we don't add\n // unneeded DOM nodes in case of a continuation, and we add it\n // with a side-effect binding to not have to create sub views,\n // as the avatar or sender doesn't need any bindings or event handlers.\n // don't use `t` from within the side-effect callback\n t.mapSideEffect(vm => vm.isContinuation, (isContinuation, wasContinuation) => {\n if (isContinuation && wasContinuation === false) {\n li.removeChild(li.querySelector(\".Timeline_messageAvatar\"));\n li.removeChild(li.querySelector(\".Timeline_messageSender\"));\n } else if (!isContinuation && !this._isReplyPreview) {\n const avatar = tag.a({href: vm.memberPanelLink, className: \"Timeline_messageAvatar\"}, [renderStaticAvatar(vm, 30)]);\n const sender = tag.div({className: `Timeline_messageSender usercolor${vm.avatarColorNumber}`}, vm.displayName);\n li.insertBefore(avatar, li.firstChild);\n li.insertBefore(sender, li.firstChild);\n }\n });\n // similarly, we could do this with a simple ifView,\n // but that adds a comment node to all messages without reactions\n let reactionsView = null;\n t.mapSideEffect(vm => vm.reactions, reactions => {\n if (reactions && this._interactive && !reactionsView) {\n reactionsView = new ReactionsView(reactions);\n this.addSubView(reactionsView);\n li.appendChild(mountView(reactionsView));\n } else if (!reactions && reactionsView) {\n li.removeChild(reactionsView.root());\n reactionsView.unmount();\n this.removeSubView(reactionsView);\n reactionsView = null;\n }\n });\n return li;\n }\n\n /* This is called by the parent ListView, which just has 1 listener for the whole list */\n onClick(evt) {\n if (evt.target.className === \"Timeline_messageOptions\") {\n this._toggleMenu(evt.target);\n }\n }\n\n _toggleMenu(button) {\n if (this._menuPopup && this._menuPopup.isOpen) {\n this._menuPopup.close();\n } else {\n const options = this.createMenuOptions(this.value);\n if (!options.length) {\n return;\n }\n this.root().classList.add(\"menuOpen\");\n const onClose = () => this.root().classList.remove(\"menuOpen\");\n this._menuPopup = new Popup(new Menu(options), onClose);\n this._menuPopup.trackInTemplateView(this);\n this._menuPopup.showRelativeTo(button, 2);\n }\n }\n\n createMenuOptions(vm) {\n const options = [];\n if (vm.canReact && vm.shape !== \"redacted\" && !vm.isPending) {\n options.push(new QuickReactionsMenuOption(vm));\n options.push(Menu.option(vm.i18n`Reply`, () => vm.startReply()));\n }\n if (vm.canAbortSending) {\n options.push(Menu.option(vm.i18n`Cancel`, () => vm.abortSending()));\n } else if (vm.canRedact) {\n options.push(Menu.option(vm.i18n`Delete`, () => vm.redact()).setDestructive());\n }\n return options;\n }\n\n renderMessageBody() {}\n}\n\nclass QuickReactionsMenuOption {\n constructor(vm) {\n this._vm = vm;\n }\n toDOM(t) {\n const emojiButtons = [\"👍\", \"👎\", \"😄\", \"🎉\", \"😕\", \"❤️\", \"🚀\", \"👀\"].map(emoji => {\n return t.button({onClick: () => this._vm.react(emoji)}, emoji);\n });\n const customButton = t.button({onClick: () => {\n const key = prompt(\"Enter your reaction (emoji)\");\n if (key) {\n this._vm.react(key);\n }\n }}, \"…\");\n return t.li({className: \"quick-reactions\"}, [...emojiButtons, customButton]);\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {renderStaticAvatar} from \"../../../avatar\";\nimport {TemplateView} from \"../../../general/TemplateView\";\n\nexport class ReplyPreviewView extends TemplateView {\n constructor(vm, viewClassForTile) {\n super(vm);\n this._viewClassForTile = viewClassForTile;\n }\n render(t, vm) {\n const TileView = this._viewClassForTile(vm);\n if (!TileView) {\n throw new Error(`Shape ${vm.shape} is unrecognized.`)\n }\n const view = new TileView(vm, this._viewClassForTile, { reply: true, interactive: false });\n return t.div(\n { className: \"ReplyPreviewView\" },\n t.blockquote([\n t.a({ className: \"link\", href: vm.permaLink }, \"In reply to\"),\n t.a({ className: \"pill\", href: vm.senderProfileLink }, [\n renderStaticAvatar(vm, 12, undefined, true),\n vm.displayName,\n ]),\n t.br(),\n t.view(view),\n ])\n );\n }\n}\n\nexport class ReplyPreviewError extends TemplateView {\n render(t) {\n return t.blockquote({ className: \"ReplyPreviewView\" }, [\n t.div({ className: \"Timeline_messageBody statusMessage\" }, \"This reply could not be found.\")\n ]);\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {tag, text} from \"../../../general/html\";\nimport {BaseMessageView} from \"./BaseMessageView.js\";\nimport {ReplyPreviewError, ReplyPreviewView} from \"./ReplyPreviewView.js\";\n\nexport class TextMessageView extends BaseMessageView {\n renderMessageBody(t, vm) {\n const time = t.time({className: {hidden: !vm.date}}, vm.date + \" \" + vm.time);\n const container = t.div({\n className: {\n \"Timeline_messageBody\": true,\n statusMessage: vm => vm.shape === \"message-status\",\n }\n }, t.mapView(vm => vm.replyTile, replyTile => {\n if (this._isReplyPreview) {\n // if this._isReplyPreview = true, this is already a reply preview, don't nest replies for now.\n return null;\n }\n else if (vm.isReply && !replyTile) {\n return new ReplyPreviewError();\n }\n else if (replyTile) {\n return new ReplyPreviewView(replyTile, this._viewClassForTile);\n }\n else {\n return null;\n }\n }));\n\n // exclude comment nodes as they are used by t.map and friends for placeholders\n const shouldRemove = (element) => element?.nodeType !== Node.COMMENT_NODE && element.className !== \"ReplyPreviewView\";\n\n t.mapSideEffect(vm => vm.body, body => {\n while (shouldRemove(container.lastChild)) {\n container.removeChild(container.lastChild);\n }\n for (const part of body.parts) {\n container.appendChild(renderPart(part));\n }\n container.appendChild(time);\n });\n\n return container;\n }\n}\n\nfunction renderList(listBlock) {\n const items = listBlock.items.map(item => tag.li(renderParts(item)));\n const start = listBlock.startOffset;\n if (start) {\n return tag.ol({ start }, items);\n } else {\n return tag.ul(items);\n }\n}\n\nfunction renderImage(imagePart) {\n const attributes = { src: imagePart.src };\n if (imagePart.width) { attributes.width = imagePart.width }\n if (imagePart.height) { attributes.height = imagePart.height }\n if (imagePart.alt) { attributes.alt = imagePart.alt }\n if (imagePart.title) { attributes.title = imagePart.title }\n return tag.img(attributes);\n}\n\nfunction renderPill(pillPart) {\n // The classes and structure are borrowed from avatar.js;\n // We don't call renderStaticAvatar because that would require\n // an intermediate object that has getAvatarUrl etc.\n const classes = `avatar size-12 usercolor${pillPart.avatarColorNumber}`;\n const avatar = tag.div({class: classes}, text(pillPart.avatarInitials));\n const children = renderParts(pillPart.children);\n children.unshift(avatar);\n return tag.a({class: \"pill\", href: pillPart.href, rel: \"noopener\", target: \"_blank\"}, children);\n}\n\nfunction renderTable(tablePart) {\n const children = [];\n if (tablePart.head) {\n const headers = tablePart.head\n .map(cell => tag.th(renderParts(cell)));\n children.push(tag.thead(tag.tr(headers)))\n }\n const rows = [];\n for (const row of tablePart.body) {\n const data = row.map(cell => tag.td(renderParts(cell)));\n rows.push(tag.tr(data));\n }\n children.push(tag.tbody(rows));\n return tag.table(children);\n}\n\n/**\n * Map from part to function that outputs DOM for the part\n */\nconst formatFunction = {\n header: headerBlock => tag[\"h\" + Math.min(6,headerBlock.level)](renderParts(headerBlock.inlines)),\n codeblock: codeBlock => tag.pre(tag.code(text(codeBlock.text))),\n table: tableBlock => renderTable(tableBlock),\n code: codePart => tag.code(text(codePart.text)),\n text: textPart => text(textPart.text),\n link: linkPart => tag.a({href: linkPart.url, className: \"link\", target: \"_blank\", rel: \"noopener\" }, renderParts(linkPart.inlines)),\n pill: renderPill,\n format: formatPart => tag[formatPart.format](renderParts(formatPart.children)),\n rule: () => tag.hr(),\n list: renderList,\n image: renderImage,\n newline: () => tag.br()\n};\n\nfunction renderPart(part) {\n const f = formatFunction[part.type];\n if (!f) {\n return text(`[unknown part type ${part.type}]`);\n }\n return f(part);\n}\n\nfunction renderParts(parts) {\n return Array.from(parts, renderPart);\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseMessageView} from \"./BaseMessageView.js\";\nimport {Menu} from \"../../../general/Menu.js\";\n\nexport class BaseMediaView extends BaseMessageView {\n renderMessageBody(t, vm) {\n const heightRatioPercent = (vm.height / vm.width) * 100; \n let spacerStyle = `padding-top: ${heightRatioPercent}%;`;\n if (vm.platform.isIE11) {\n // preserving aspect-ratio in a grid with padding percentages\n // does not work in IE11, so we assume people won't use it\n // with viewports narrower than 400px where thumbnails will get\n // scaled. If they do, the thumbnail will still scale, but\n // there will be whitespace underneath the picture\n // An alternative would be to use position: absolute but that\n // can slow down rendering, and was bleeding through the lightbox.\n spacerStyle = `height: ${vm.height}px`;\n }\n const children = [\n t.div({className: \"spacer\", style: spacerStyle}),\n this.renderMedia(t, vm),\n t.time(vm.date + \" \" + vm.time),\n ];\n const status = t.div({\n className: {\n status: true,\n hidden: vm => !vm.status\n },\n }, vm => vm.status);\n children.push(status);\n if (vm.isPending) {\n const progress = t.progress({\n min: 0,\n max: 100,\n value: vm => vm.uploadPercentage,\n className: {hidden: vm => !vm.isUploading}\n });\n children.push(progress);\n }\n return t.div({className: \"Timeline_messageBody\"}, [\n t.div({className: \"media\", style: `max-width: ${vm.width}px`, \"data-testid\": \"media\"}, children),\n t.if(vm => vm.error, t => t.p({className: \"error\"}, vm.error))\n ]);\n }\n\n createMenuOptions(vm) {\n const options = super.createMenuOptions(vm);\n if (!vm.isPending) {\n let label;\n switch (vm.shape) {\n case \"image\": label = vm.i18n`Download image`; break;\n case \"video\": label = vm.i18n`Download video`; break;\n default: label = vm.i18n`Download media`; break;\n }\n options.push(Menu.option(label, () => vm.downloadMedia()));\n }\n return options;\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseMediaView} from \"./BaseMediaView.js\";\n\nexport class ImageView extends BaseMediaView {\n renderMedia(t, vm) {\n const img = t.img({\n src: vm => vm.thumbnailUrl,\n alt: vm => vm.label,\n title: vm => vm.label,\n style: `max-width: ${vm.width}px; max-height: ${vm.height}px;`\n });\n return vm.isPending || !vm.lightboxUrl ? img : t.a({href: vm.lightboxUrl}, img);\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport function domEventAsPromise(element, successEvent) {\n return new Promise((resolve, reject) => {\n let detach;\n const handleError = evt => {\n detach();\n reject(evt.target.error);\n };\n const handleSuccess = () => {\n detach();\n resolve();\n };\n detach = () => {\n element.removeEventListener(successEvent, handleSuccess);\n element.removeEventListener(\"error\", handleError);\n };\n element.addEventListener(successEvent, handleSuccess);\n element.addEventListener(\"error\", handleError);\n });\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseMediaView} from \"./BaseMediaView.js\";\nimport {domEventAsPromise} from \"../../../../dom/utils.js\";\n\nexport class VideoView extends BaseMediaView {\n renderMedia(t) {\n const video = t.video({\n // provide empty data url if video is not decrypted yet.\n // Chrome/Electron need this to enable the play button.\n src: vm => vm.videoUrl || `data:${vm.mimeType},`,\n title: vm => vm.label,\n controls: true,\n preload: \"none\",\n poster: vm => vm.thumbnailUrl,\n onPlay: this._onPlay.bind(this),\n style: vm => `max-width: ${vm.width}px; max-height: ${vm.height}px;${vm.isPending ? \"z-index: -1\": \"\"}`\n });\n\n video.addEventListener(\"error\", this._onError.bind(this));\n\n return video;\n }\n\n async _onPlay(evt) {\n const vm = this.value;\n // download and decrypt the video if needed,\n if (!vm.videoUrl) {\n try {\n const video = evt.target;\n // this will trigger the src to update\n await vm.loadVideo();\n // important to only listen for this after src has changed,\n // or we get the error for the placeholder data url\n const loadPromise = domEventAsPromise(video, \"loadeddata\");\n // now, reload the video and play\n video.load();\n await loadPromise;\n video.play();\n } catch (err) {/* errors are already caught in error event handler */}\n } \n }\n\n _onError(evt) {\n const vm = this.value;\n const video = evt.target;\n const err = video.error;\n if (err instanceof window.MediaError && err.code === 4) {\n if (!video.src.startsWith(\"data:\")) {\n vm.setViewError(new Error(`this browser does not support videos of type ${vm.mimeType}.`));\n } else {\n // ignore placeholder url failing to load\n return;\n }\n } else {\n vm.setViewError(err);\n }\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseMessageView} from \"./BaseMessageView.js\";\n\nexport class FileView extends BaseMessageView {\n renderMessageBody(t, vm) {\n const children = [];\n if (vm.isPending) {\n children.push(vm => vm.label);\n } else {\n children.push(\n t.button({className: \"link\", onClick: () => vm.download()}, vm => vm.label),\n t.time(vm.date + \" \" + vm.time)\n );\n }\n return t.p({className: \"Timeline_messageBody statusMessage\"}, children);\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseMessageView} from \"./BaseMessageView.js\";\n\nexport class LocationView extends BaseMessageView {\n renderMessageBody(t, vm) {\n return t.p({className: \"Timeline_messageBody statusMessage\"}, [\n t.span(vm.label),\n t.a({className: \"Timeline_locationLink\", href: vm.mapsLink, target: \"_blank\", rel: \"noopener\"}, vm.i18n`Open in maps`),\n t.time(vm.date + \" \" + vm.time)\n ]);\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseMessageView} from \"./BaseMessageView.js\";\n\nexport class MissingAttachmentView extends BaseMessageView {\n renderMessageBody(t, vm) {\n return t.p({className: \"Timeline_messageBody statusMessage\"}, vm.label);\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../../../general/TemplateView\";\n\nexport class AnnouncementView extends TemplateView {\n // ignore other arguments\n constructor(vm) {\n super(vm);\n }\n\n render(t) {\n return t.li({className: \"AnnouncementView\"}, t.div(vm => vm.announcement));\n }\n \n /* This is called by the parent ListView, which just has 1 listener for the whole list */\n onClick() {}\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseMessageView} from \"./BaseMessageView.js\";\nimport {Menu} from \"../../../general/Menu.js\";\n\nexport class RedactedView extends BaseMessageView {\n renderMessageBody(t) {\n return t.p({className: \"Timeline_messageBody statusMessage\"}, vm => vm.description);\n }\n\n createMenuOptions(vm) {\n const options = super.createMenuOptions(vm);\n if (vm.isRedacting) {\n options.push(Menu.option(vm.i18n`Cancel`, () => vm.abortPendingRedaction()));\n }\n return options;\n }\n}","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../../../general/TemplateView\";\nimport {spinner} from \"../../../common.js\";\n\nexport class GapView extends TemplateView {\n // ignore other argument\n constructor(vm) {\n super(vm);\n }\n\n render(t) {\n const className = {\n GapView: true,\n isLoading: vm => vm.isLoading,\n isAtTop: vm => vm.isAtTop,\n };\n return t.li({className}, [\n spinner(t),\n t.div(vm => vm.isLoading ? vm.i18n`Loading more messages …` : vm.i18n`Not loading!`),\n t.if(vm => vm.error, t => t.strong(vm => vm.error))\n ]);\n }\n\n /* This is called by the parent ListView, which just has 1 listener for the whole list */\n onClick() {}\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TextMessageView} from \"./timeline/TextMessageView.js\";\nimport {ImageView} from \"./timeline/ImageView.js\";\nimport {VideoView} from \"./timeline/VideoView.js\";\nimport {FileView} from \"./timeline/FileView.js\";\nimport {LocationView} from \"./timeline/LocationView.js\";\nimport {MissingAttachmentView} from \"./timeline/MissingAttachmentView.js\";\nimport {AnnouncementView} from \"./timeline/AnnouncementView.js\";\nimport {RedactedView} from \"./timeline/RedactedView.js\";\nimport {SimpleTile} from \"../../../../../domain/session/room/timeline/tiles/SimpleTile.js\";\nimport {GapView} from \"./timeline/GapView.js\";\nimport type {TileViewConstructor, ViewClassForEntryFn} from \"./TimelineView\";\n\nexport function viewClassForTile(vm: SimpleTile): TileViewConstructor {\n switch (vm.shape) {\n case \"gap\":\n return GapView;\n case \"announcement\":\n return AnnouncementView;\n case \"message\":\n case \"message-status\":\n return TextMessageView;\n case \"image\":\n return ImageView;\n case \"video\":\n return VideoView;\n case \"file\":\n return FileView;\n case \"location\":\n return LocationView;\n case \"missing-attachment\":\n return MissingAttachmentView;\n case \"redacted\":\n return RedactedView;\n default:\n throw new Error(`Tiles of shape \"${vm.shape}\" are not supported, check the tileClassForEntry function in the view model`);\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {LeftPanelView} from \"./leftpanel/LeftPanelView.js\";\nimport {RoomView} from \"./room/RoomView.js\";\nimport {UnknownRoomView} from \"./room/UnknownRoomView.js\";\nimport {RoomBeingCreatedView} from \"./room/RoomBeingCreatedView.js\";\nimport {InviteView} from \"./room/InviteView.js\";\nimport {LightboxView} from \"./room/LightboxView.js\";\nimport {TemplateView} from \"../general/TemplateView\";\nimport {StaticView} from \"../general/StaticView.js\";\nimport {SessionStatusView} from \"./SessionStatusView.js\";\nimport {RoomGridView} from \"./RoomGridView.js\";\nimport {SettingsView} from \"./settings/SettingsView.js\";\nimport {CreateRoomView} from \"./CreateRoomView.js\";\nimport {RightPanelView} from \"./rightpanel/RightPanelView.js\";\nimport {viewClassForTile} from \"./room/common\";\n\nexport class SessionView extends TemplateView {\n render(t, vm) {\n return t.div({\n className: {\n \"SessionView\": true,\n \"middle-shown\": vm => !!vm.activeMiddleViewModel,\n \"right-shown\": vm => !!vm.rightPanelViewModel\n },\n }, [\n t.view(new SessionStatusView(vm.sessionStatusViewModel)),\n t.view(new LeftPanelView(vm.leftPanelViewModel)),\n t.mapView(vm => vm.activeMiddleViewModel, () => {\n if (vm.roomGridViewModel) {\n return new RoomGridView(vm.roomGridViewModel, viewClassForTile);\n } else if (vm.settingsViewModel) {\n return new SettingsView(vm.settingsViewModel);\n } else if (vm.createRoomViewModel) {\n return new CreateRoomView(vm.createRoomViewModel);\n } else if (vm.currentRoomViewModel) {\n if (vm.currentRoomViewModel.kind === \"invite\") {\n return new InviteView(vm.currentRoomViewModel);\n } else if (vm.currentRoomViewModel.kind === \"room\") {\n return new RoomView(vm.currentRoomViewModel, viewClassForTile);\n } else if (vm.currentRoomViewModel.kind === \"roomBeingCreated\") {\n return new RoomBeingCreatedView(vm.currentRoomViewModel);\n } else {\n return new UnknownRoomView(vm.currentRoomViewModel);\n }\n } else {\n return new StaticView(t => t.div({className: \"room-placeholder\"}, t.h2(vm.i18n`Choose a room on the left side.`)));\n }\n }),\n t.mapView(vm => vm.lightboxViewModel, lightboxViewModel => lightboxViewModel ? new LightboxView(lightboxViewModel) : null),\n t.mapView(vm => vm.rightPanelViewModel, rightPanelViewModel => rightPanelViewModel ? new RightPanelView(rightPanelViewModel) : null)\n ]);\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport function hydrogenGithubLink(t) {\n if (DEFINE_VERSION && DEFINE_GLOBAL_HASH) {\n return t.a({target: \"_blank\",\n href: `https://github.com/vector-im/hydrogen-web/releases/tag/v${DEFINE_VERSION}`},\n `Hydrogen v${DEFINE_VERSION} (${DEFINE_GLOBAL_HASH}) on Github`);\n } else {\n return t.a({target: \"_blank\", href: \"https://github.com/vector-im/hydrogen-web\"},\n \"Hydrogen on Github\");\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../general/TemplateView\";\n\nexport class PasswordLoginView extends TemplateView {\n render(t, vm) {\n const disabled = vm => !!vm.isBusy;\n const username = t.input({\n id: \"username\",\n type: \"text\",\n placeholder: vm.i18n`Username`,\n disabled\n });\n const password = t.input({\n id: \"password\",\n type: \"password\",\n placeholder: vm.i18n`Password`,\n disabled\n });\n \n return t.div({className: \"PasswordLoginView form\"}, [\n t.if(vm => vm.error, t => t.div({ className: \"error\" }, vm => vm.error)),\n t.form({\n onSubmit: evnt => {\n evnt.preventDefault();\n vm.login(username.value, password.value);\n }\n }, [\n t.if(vm => vm.errorMessage, (t, vm) => t.p({className: \"error\"}, vm.i18n(vm.errorMessage))),\n t.div({ className: \"form-row text\" }, [t.label({ for: \"username\" }, vm.i18n`Username`), username]),\n t.div({ className: \"form-row text\" }, [t.label({ for: \"password\" }, vm.i18n`Password`), password]),\n t.div({ className: \"button-row\" }, [\n t.button({\n className: \"button-action primary\",\n type: \"submit\",\n disabled\n }, vm.i18n`Log In`),\n ]),\n ])\n ]);\n }\n}\n\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../general/TemplateView\";\nimport {KeyBackupSettingsView} from \"../session/settings/KeyBackupSettingsView.js\";\n\nexport class AccountSetupView extends TemplateView {\n render(t, vm) {\n return t.div({className: \"Settings\" /* hack for now to get the layout right*/}, [\n t.h3(vm.i18n`Restore your encrypted history?`),\n t.ifView(vm => vm.decryptDehydratedDeviceViewModel, vm => new KeyBackupSettingsView(vm.decryptDehydratedDeviceViewModel)),\n t.map(vm => vm.deviceDecrypted, (decrypted, t) => {\n if (decrypted) {\n return t.p(vm.i18n`That worked out, you're good to go!`);\n } else {\n return t.p(vm.i18n`This will claim the dehydrated device ${vm.dehydratedDeviceId}, and will set up a new one.`);\n }\n }),\n t.div({ className: \"button-row\" }, [\n t.button({\n className: \"button-action primary\",\n onClick: () => { vm.finish(); },\n type: \"button\",\n }, vm => vm.deviceDecrypted ? vm.i18n`Continue` : vm.i18n`Continue without restoring`),\n ]),\n ]);\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../general/TemplateView\";\nimport {spinner} from \"../common.js\";\nimport {AccountSetupView} from \"./AccountSetupView.js\";\n\n/** a view used both in the login view and the loading screen\nto show the current state of loading the session.\nJust a spinner and a label, meant to be used as a paragraph */\nexport class SessionLoadStatusView extends TemplateView {\n render(t) {\n const exportLogsButtonIfFailed = t.if(vm => vm.hasError, (t, vm) => {\n return t.button({\n onClick: () => vm.exportLogs()\n }, vm.i18n`Export logs`);\n });\n const logoutButtonIfFailed = t.if(vm => vm.hasError, (t, vm) => {\n return t.button({\n onClick: () => vm.logout()\n }, vm.i18n`Log out`);\n });\n return t.div({className: \"SessionLoadStatusView\"}, [\n t.p({className: \"status\"}, [\n spinner(t, {hidden: vm => !vm.loading}),\n t.p(vm => vm.loadLabel),\n exportLogsButtonIfFailed,\n logoutButtonIfFailed\n ]),\n t.ifView(vm => vm.accountSetupViewModel, vm => new AccountSetupView(vm.accountSetupViewModel)),\n ]);\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../general/TemplateView\";\nimport {SessionLoadStatusView} from \"./SessionLoadStatusView.js\";\n\nexport class CompleteSSOView extends TemplateView {\n render(t) {\n return t.div({ className: \"CompleteSSOView\" },\n [\n t.p({ className: \"CompleteSSOView_title\" }, \"Finishing up your SSO Login\"),\n t.if(vm => vm.errorMessage, (t, vm) => t.p({className: \"error\"}, vm.i18n(vm.errorMessage))),\n t.mapView(vm => vm.loadViewModel, loadViewModel => loadViewModel ? new SessionLoadStatusView(loadViewModel) : null),\n ]\n );\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../general/TemplateView\";\nimport {hydrogenGithubLink} from \"./common.js\";\nimport {PasswordLoginView} from \"./PasswordLoginView.js\";\nimport {CompleteSSOView} from \"./CompleteSSOView.js\";\nimport {SessionLoadStatusView} from \"./SessionLoadStatusView.js\";\nimport {spinner} from \"../common.js\";\n\nexport class LoginView extends TemplateView {\n render(t, vm) {\n const disabled = vm => vm.isBusy;\n\n return t.div({className: \"PreSessionScreen\"}, [\n t.button({\n className: \"button-utility LoginView_back\",\n onClick: () => vm.goBack(),\n disabled\n }),\n t.div({className: \"logo\"}),\n t.h1([vm.i18n`Sign In`]),\n t.mapView(vm => vm.completeSSOLoginViewModel, vm => vm ? new CompleteSSOView(vm) : null),\n t.if(vm => vm.showHomeserver, (t, vm) => t.div({ className: \"LoginView_sso form-row text\" },\n [\n t.label({for: \"homeserver\"}, vm.i18n`Homeserver`),\n t.input({\n id: \"homeserver\",\n type: \"text\",\n placeholder: vm.i18n`Your matrix homeserver`,\n value: vm.homeserver,\n disabled,\n onInput: event => vm.setHomeserver(event.target.value),\n onChange: () => vm.queryHomeserver(),\n }),\n t.p({className: {\n LoginView_forwardInfo: true,\n hidden: vm => !vm.resolvedHomeserver\n }}, vm => vm.i18n`You will connect to ${vm.resolvedHomeserver}.`),\n t.if(vm => vm.errorMessage, (t, vm) => t.p({className: \"error\"}, vm.i18n(vm.errorMessage))),\n ]\n )),\n t.if(vm => vm.isFetchingLoginOptions, t => t.div({className: \"LoginView_query-spinner\"}, [spinner(t), t.p(\"Fetching available login options...\")])),\n t.mapView(vm => vm.passwordLoginViewModel, vm => vm ? new PasswordLoginView(vm): null),\n t.if(vm => vm.passwordLoginViewModel && vm.startSSOLoginViewModel, t => t.p({className: \"LoginView_separator\"}, vm.i18n`or`)),\n t.mapView(vm => vm.startSSOLoginViewModel, vm => vm ? new StartSSOLoginView(vm) : null),\n t.mapView(vm => vm.loadViewModel, loadViewModel => loadViewModel ? new SessionLoadStatusView(loadViewModel) : null),\n // use t.mapView rather than t.if to create a new view when the view model changes too\n t.p(hydrogenGithubLink(t))\n ]);\n }\n}\n\nclass StartSSOLoginView extends TemplateView {\n render(t, vm) {\n return t.div({ className: \"StartSSOLoginView\" },\n t.button({\n className: \"StartSSOLoginView_button button-action secondary\",\n type: \"button\",\n onClick: () => vm.startSSOLogin(),\n disabled: vm => vm.isBusy\n }, vm.i18n`Log in with SSO`)\n );\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView, InlineTemplateView} from \"./general/TemplateView\";\nimport {spinner} from \"./common.js\";\n\nexport class LogoutView extends TemplateView {\n render(t, vm) {\n const confirmView = new InlineTemplateView(vm, t => {\n return t.div([\n t.p(\"Are you sure you want to log out?\"),\n t.div({ className: \"button-row\" }, [\n t.a({\n className: \"button-action\",\n type: \"submit\",\n href: vm.cancelUrl,\n }, [\"Cancel\"]),\n t.button({\n className: \"button-action primary destructive\",\n type: \"submit\",\n onClick: () => vm.logout(),\n }, vm.i18n`Log out`)\n ]),\n ]);\n });\n const progressView = new InlineTemplateView(vm, t => {\n return t.p({className: \"status\", hidden: vm => !vm.showStatus}, [\n spinner(t, {hidden: vm => !vm.busy}), t.span(vm => vm.status)\n ]);\n });\n\n return t.div({className: \"LogoutScreen\"}, [\n t.div({className: \"content\"}, [\n t.mapView(vm => vm.showConfirm, showConfirm => {\n return showConfirm ? confirmView : progressView;\n })\n ]),\n ]);\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {TemplateView} from \"../general/TemplateView\";\nimport {SessionLoadStatusView} from \"./SessionLoadStatusView.js\";\n\nexport class SessionLoadView extends TemplateView {\n render(t, vm) {\n return t.div({className: \"PreSessionScreen\"}, [\n t.div({className: \"logo\"}),\n t.div({className: \"SessionLoadView\"}, [\n t.view(new SessionLoadStatusView(vm))\n ]),\n t.div({className: {\"button-row\": true, hidden: vm => vm.loading}},\n t.a({className: \"button-action primary\", href: vm.backUrl}, vm.i18n`Go back`))\n ]);\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {ListView} from \"../general/ListView\";\nimport {TemplateView} from \"../general/TemplateView\";\nimport {hydrogenGithubLink} from \"./common.js\";\nimport {SessionLoadStatusView} from \"./SessionLoadStatusView.js\";\n\nclass SessionPickerItemView extends TemplateView {\n _onDeleteClick() {\n if (confirm(\"Are you sure?\")) {\n this.value.delete();\n }\n }\n\n _onClearClick() {\n if (confirm(\"Are you sure?\")) {\n this.value.clear();\n }\n }\n\n render(t, vm) {\n return t.li([\n t.a({className: \"session-info\", href: vm.openUrl}, [\n t.div({className: `avatar usercolor${vm.avatarColorNumber}`}, vm => vm.avatarInitials),\n t.div({className: \"user-id\"}, vm => vm.label),\n ])\n ]);\n }\n}\n\nexport class SessionPickerView extends TemplateView {\n render(t, vm) {\n const sessionList = new ListView({\n list: vm.sessions,\n parentProvidesUpdates: false,\n }, sessionInfo => {\n return new SessionPickerItemView(sessionInfo);\n });\n\n return t.div({className: \"PreSessionScreen\"}, [\n t.div({className: \"logo\"}),\n t.div({className: \"SessionPickerView\"}, [\n t.h1([\"Continue as …\"]),\n t.view(sessionList),\n t.div({className: \"button-row\"}, [\n t.a({\n className: \"button-action primary\",\n href: vm.cancelUrl\n }, vm.i18n`Sign In`)\n ]),\n t.ifView(vm => vm.loadViewModel, () => new SessionLoadStatusView(vm.loadViewModel)),\n t.p(hydrogenGithubLink(t))\n ])\n ]);\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {SessionView} from \"./session/SessionView.js\";\nimport {LoginView} from \"./login/LoginView\";\nimport {LogoutView} from \"./LogoutView.js\";\nimport {SessionLoadView} from \"./login/SessionLoadView.js\";\nimport {SessionPickerView} from \"./login/SessionPickerView.js\";\nimport {TemplateView} from \"./general/TemplateView\";\nimport {StaticView} from \"./general/StaticView.js\";\n\nexport class RootView extends TemplateView {\n render(t, vm) {\n return t.mapView(vm => vm.activeSection, activeSection => {\n switch (activeSection) {\n case \"error\":\n return new StaticView(t => {\n return t.div({className: \"StatusView\"}, [\n t.h1(\"Something went wrong\"),\n t.p(vm.errorText),\n ])\n });\n case \"session\":\n return new SessionView(vm.sessionViewModel);\n case \"login\":\n return new LoginView(vm.loginViewModel);\n case \"logout\":\n return new LogoutView(vm.logoutViewModel);\n case \"picker\":\n return new SessionPickerView(vm.sessionPickerViewModel);\n case \"redirecting\":\n return new StaticView(t => t.p(\"Redirecting...\"));\n case \"loading\":\n return new SessionLoadView(vm.sessionLoadViewModel);\n default:\n throw new Error(`Unknown section: ${vm.activeSection}`);\n }\n });\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {AbortError} from \"../../../utils/error\";\n\nclass Timeout {\n constructor(ms) {\n this._reject = null;\n this._handle = null;\n this._promise = new Promise((resolve, reject) => {\n this._reject = reject;\n this._handle = setTimeout(() => {\n this._reject = null;\n resolve();\n }, ms);\n });\n }\n\n elapsed() {\n return this._promise;\n }\n\n abort() {\n if (this._reject) {\n this._reject(new AbortError());\n clearTimeout(this._handle);\n this._handle = null;\n this._reject = null;\n }\n }\n\n dispose() {\n this.abort();\n }\n}\n\nclass Interval {\n constructor(ms, callback) {\n this._handle = setInterval(callback, ms);\n }\n\n dispose() {\n if (this._handle) {\n clearInterval(this._handle);\n this._handle = null;\n }\n }\n}\n\nclass TimeMeasure {\n constructor() {\n this._start = window.performance.now();\n }\n\n measure() {\n return window.performance.now() - this._start;\n }\n}\n\nexport class Clock {\n createMeasure() {\n return new TimeMeasure();\n }\n\n createTimeout(ms) {\n return new Timeout(ms);\n }\n\n createInterval(callback, ms) {\n return new Interval(ms, callback);\n }\n\n now() {\n return Date.now();\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// 3 (imaginary) interfaces are implemented here:\n// - OfflineAvailability (done by registering the sw)\n// - UpdateService (see checkForUpdate method, and should also emit events rather than showing confirm dialog here)\n// - ConcurrentAccessBlocker (see preventConcurrentSessionAccess method)\nexport class ServiceWorkerHandler {\n constructor() {\n this._waitingForReply = new Map();\n this._messageIdCounter = 0;\n this._navigation = null;\n this._registration = null;\n this._registrationPromise = null;\n this._currentController = null;\n this.haltRequests = false;\n }\n\n setNavigation(navigation) {\n this._navigation = navigation;\n }\n\n registerAndStart(path) {\n this._registrationPromise = (async () => {\n navigator.serviceWorker.addEventListener(\"message\", this);\n navigator.serviceWorker.addEventListener(\"controllerchange\", this);\n this._registration = await navigator.serviceWorker.register(path);\n await navigator.serviceWorker.ready;\n this._currentController = navigator.serviceWorker.controller;\n this._registration.addEventListener(\"updatefound\", this);\n this._registrationPromise = null;\n // do we have a new service worker waiting to activate?\n if (this._registration.waiting && this._registration.active) {\n this._proposeUpdate();\n }\n console.log(\"Service Worker registered\");\n })();\n }\n\n _onMessage(event) {\n const {data} = event;\n const replyTo = data.replyTo;\n if (replyTo) {\n const resolve = this._waitingForReply.get(replyTo);\n if (resolve) {\n this._waitingForReply.delete(replyTo);\n resolve(data.payload);\n }\n }\n if (data.type === \"hasSessionOpen\") {\n const hasOpen = this._navigation.observe(\"session\").get() === data.payload.sessionId;\n event.source.postMessage({replyTo: data.id, payload: hasOpen});\n } else if (data.type === \"hasRoomOpen\") {\n const hasSessionOpen = this._navigation.observe(\"session\").get() === data.payload.sessionId;\n const hasRoomOpen = this._navigation.observe(\"room\").get() === data.payload.roomId;\n event.source.postMessage({replyTo: data.id, payload: hasSessionOpen && hasRoomOpen});\n } else if (data.type === \"closeSession\") {\n const {sessionId} = data.payload;\n this._closeSessionIfNeeded(sessionId).finally(() => {\n event.source.postMessage({replyTo: data.id});\n });\n } else if (data.type === \"haltRequests\") {\n // this flag is read in fetch.js\n this.haltRequests = true;\n event.source.postMessage({replyTo: data.id});\n } else if (data.type === \"openRoom\") {\n this._navigation.push(\"room\", data.payload.roomId);\n }\n }\n\n _closeSessionIfNeeded(sessionId) {\n const currentSession = this._navigation?.path.get(\"session\");\n if (sessionId && currentSession?.value === sessionId) {\n return new Promise(resolve => {\n const unsubscribe = this._navigation.pathObservable.subscribe(path => {\n const session = path.get(\"session\");\n if (!session || session.value !== sessionId) {\n unsubscribe();\n resolve();\n }\n });\n this._navigation.push(\"session\");\n });\n } else {\n return Promise.resolve();\n }\n }\n\n async _proposeUpdate() {\n if (document.hidden) {\n return;\n }\n const version = await this._sendAndWaitForReply(\"version\", null, this._registration.waiting);\n if (confirm(`Version ${version.version} (${version.buildHash}) is available. Reload to apply?`)) {\n // prevent any fetch requests from going to the service worker\n // from any client, so that it is not kept active\n // when calling skipWaiting on the new one\n await this._sendAndWaitForReply(\"haltRequests\");\n // only once all requests are blocked, ask the new\n // service worker to skipWaiting\n this._send(\"skipWaiting\", null, this._registration.waiting);\n }\n }\n\n handleEvent(event) {\n switch (event.type) {\n case \"message\":\n this._onMessage(event);\n break;\n case \"updatefound\":\n this._registration.installing.addEventListener(\"statechange\", this);\n break;\n case \"statechange\": {\n if (event.target.state === \"installed\") {\n this._proposeUpdate();\n event.target.removeEventListener(\"statechange\", this);\n }\n break;\n }\n case \"controllerchange\":\n if (!this._currentController) {\n // Clients.claim() in the SW can trigger a controllerchange event\n // if we had no SW before. This is fine,\n // and now our requests will be served from the SW.\n this._currentController = navigator.serviceWorker.controller;\n } else {\n // active service worker changed,\n // refresh, so we can get all assets \n // (and not only some if we would not refresh)\n // up to date from it\n document.location.reload();\n }\n break;\n }\n }\n\n async _send(type, payload, worker = undefined) {\n if (this._registrationPromise) {\n await this._registrationPromise;\n }\n if (!worker) {\n worker = this._registration.active;\n }\n worker.postMessage({type, payload});\n }\n\n async _sendAndWaitForReply(type, payload, worker = undefined) {\n if (this._registrationPromise) {\n await this._registrationPromise;\n }\n if (!worker) {\n worker = this._registration.active;\n }\n this._messageIdCounter += 1;\n const id = this._messageIdCounter;\n const promise = new Promise(resolve => {\n this._waitingForReply.set(id, resolve);\n });\n worker.postMessage({type, id, payload});\n return await promise;\n }\n\n async checkForUpdate() {\n if (this._registrationPromise) {\n await this._registrationPromise;\n }\n this._registration.update();\n }\n\n get version() {\n return DEFINE_VERSION;\n }\n\n get buildHash() {\n return DEFINE_GLOBAL_HASH;\n }\n\n async preventConcurrentSessionAccess(sessionId) {\n return this._sendAndWaitForReply(\"closeSession\", {sessionId});\n }\n\n async getRegistration() {\n if (this._registrationPromise) {\n await this._registrationPromise;\n }\n return this._registration;\n }\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport class NotificationService {\n constructor(serviceWorkerHandler, pushConfig) {\n this._serviceWorkerHandler = serviceWorkerHandler;\n this._pushConfig = pushConfig;\n }\n\n async enablePush(pusherFactory, defaultPayload) {\n const registration = await this._serviceWorkerHandler?.getRegistration();\n if (registration?.pushManager) {\n const subscription = await registration.pushManager.subscribe({\n userVisibleOnly: true,\n applicationServerKey: this._pushConfig.applicationServerKey,\n });\n const subscriptionData = subscription.toJSON();\n const pushkey = subscriptionData.keys.p256dh;\n const data = {\n endpoint: subscriptionData.endpoint,\n auth: subscriptionData.keys.auth,\n // don't deliver unread count push messages\n // as we don't want to show a notification in this case\n events_only: true,\n default_payload: defaultPayload\n };\n return pusherFactory.httpPusher(\n this._pushConfig.gatewayUrl,\n this._pushConfig.appId,\n pushkey,\n data\n );\n }\n }\n\n async disablePush() {\n const registration = await this._serviceWorkerHandler?.getRegistration();\n if (registration?.pushManager) {\n const subscription = await registration.pushManager.getSubscription();\n if (subscription) {\n await subscription.unsubscribe();\n }\n }\n }\n\n async isPushEnabled() {\n const registration = await this._serviceWorkerHandler?.getRegistration();\n if (registration?.pushManager) {\n const subscription = await registration.pushManager.getSubscription();\n return !!subscription;\n }\n return false;\n }\n\n async supportsPush() {\n if (!this._pushConfig) {\n return false;\n }\n const registration = await this._serviceWorkerHandler?.getRegistration();\n return registration && \"pushManager\" in registration;\n }\n\n async enableNotifications() {\n if (\"Notification\" in window) {\n return (await Notification.requestPermission()) === \"granted\";\n }\n return false;\n }\n\n async supportsNotifications() {\n return \"Notification\" in window;\n }\n\n async areNotificationsEnabled() {\n if (\"Notification\" in window) {\n return Notification.permission === \"granted\";\n } else {\n return false;\n }\n }\n\n async showNotification(title, body = undefined) {\n if (\"Notification\" in window) {\n new Notification(title, {body});\n return;\n }\n // Chrome on Android does not support the Notification constructor\n const registration = await this._serviceWorkerHandler?.getRegistration();\n registration?.showNotification(title, {body});\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseObservableValue} from \"../../../observable/ObservableValue\";\n\nexport class History extends BaseObservableValue {\n \n constructor() {\n super();\n this._lastSessionHash = undefined;\n }\n \n handleEvent(event) {\n if (event.type === \"hashchange\") {\n this.emit(this.get());\n this._storeHash(this.get());\n }\n }\n\n get() {\n /*\n All URLS in Hydrogen will use <root>/#/segment/value/...\n But for SSO, we need to handle <root>/?loginToken=<TOKEN>\n Handle that as a special case for now.\n */\n if (document.location.search.includes(\"loginToken\")) {\n return document.location.search;\n }\n return document.location.hash;\n }\n\n /** does not emit */\n replaceUrlSilently(url) {\n window.history.replaceState(null, null, url);\n this._storeHash(url);\n }\n\n /** does not emit */\n pushUrlSilently(url) {\n window.history.pushState(null, null, url);\n this._storeHash(url);\n }\n\n pushUrl(url) {\n document.location.hash = url;\n }\n\n urlAsPath(url) {\n if (url.startsWith(\"#\")) {\n return url.substr(1);\n } else {\n return url;\n }\n }\n\n pathAsUrl(path) {\n return `#${path}`;\n }\n\n onSubscribeFirst() {\n this._lastSessionHash = window.localStorage?.getItem(\"hydrogen_last_url_hash\");\n window.addEventListener('hashchange', this);\n }\n\n onUnsubscribeLast() {\n window.removeEventListener('hashchange', this);\n }\n\n _storeHash(hash) {\n window.localStorage?.setItem(\"hydrogen_last_url_hash\", hash);\n }\n\n getLastSessionUrl() {\n return this._lastSessionHash;\n }\n}\n","/*\nCopyright 2020 Bruno Windels <bruno@windels.cloud>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BaseObservableValue} from \"../../../observable/ObservableValue\";\n\nexport class OnlineStatus extends BaseObservableValue {\n constructor() {\n super();\n this._onOffline = this._onOffline.bind(this);\n this._onOnline = this._onOnline.bind(this);\n }\n\n _onOffline() {\n this.emit(false);\n }\n\n _onOnline() {\n this.emit(true);\n }\n\n get() {\n return navigator.onLine;\n }\n\n onSubscribeFirst() {\n window.addEventListener('offline', this._onOffline);\n window.addEventListener('online', this._onOnline);\n }\n\n onUnsubscribeLast() {\n window.removeEventListener('offline', this._onOffline);\n window.removeEventListener('online', this._onOnline);\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport base64 from \"base64-arraybuffer\";\n\n// turn IE11 result into promise\nfunction subtleCryptoResult(promiseOrOp, method) {\n if (promiseOrOp instanceof Promise) {\n return promiseOrOp;\n } else {\n return new Promise((resolve, reject) => {\n promiseOrOp.oncomplete = e => resolve(e.target.result);\n promiseOrOp.onerror = () => reject(new Error(\"Crypto error on \" + method));\n });\n }\n}\n\nclass HMACCrypto {\n constructor(subtleCrypto) {\n this._subtleCrypto = subtleCrypto;\n }\n /**\n * [hmac description]\n * @param {BufferSource} key\n * @param {BufferSource} mac\n * @param {BufferSource} data\n * @param {HashName} hash\n * @return {boolean}\n */\n async verify(key, mac, data, hash) {\n const opts = {\n name: 'HMAC',\n hash: {name: hashName(hash)},\n };\n const hmacKey = await subtleCryptoResult(this._subtleCrypto.importKey(\n 'raw',\n key,\n opts,\n false,\n ['verify'],\n ), \"importKey\");\n const isVerified = await subtleCryptoResult(this._subtleCrypto.verify(\n opts,\n hmacKey,\n mac,\n data,\n ), \"verify\");\n return isVerified;\n }\n\n async compute(key, data, hash) {\n const opts = {\n name: 'HMAC',\n hash: {name: hashName(hash)},\n };\n const hmacKey = await subtleCryptoResult(this._subtleCrypto.importKey(\n 'raw',\n key,\n opts,\n false,\n ['sign'],\n ), \"importKey\");\n const buffer = await subtleCryptoResult(this._subtleCrypto.sign(\n opts,\n hmacKey,\n data,\n ), \"sign\");\n return new Uint8Array(buffer);\n }\n}\n\nclass DeriveCrypto {\n constructor(subtleCrypto, crypto, cryptoExtras) {\n this._subtleCrypto = subtleCrypto;\n this._crypto = crypto;\n this._cryptoExtras = cryptoExtras;\n }\n /**\n * [pbkdf2 description]\n * @param {BufferSource} password\n * @param {Number} iterations\n * @param {BufferSource} salt\n * @param {HashName} hash\n * @param {Number} length the desired length of the generated key, in bits (not bytes!)\n * @return {BufferSource}\n */\n async pbkdf2(password, iterations, salt, hash, length) {\n if (!this._subtleCrypto.deriveBits) {\n throw new Error(\"PBKDF2 is not supported\");\n }\n const key = await subtleCryptoResult(this._subtleCrypto.importKey(\n 'raw',\n password,\n {name: 'PBKDF2'},\n false,\n ['deriveBits'],\n ), \"importKey\");\n const keybits = await subtleCryptoResult(this._subtleCrypto.deriveBits(\n {\n name: 'PBKDF2',\n salt,\n iterations,\n hash: hashName(hash),\n },\n key,\n length,\n ), \"deriveBits\");\n return new Uint8Array(keybits);\n }\n\n /**\n * [hkdf description]\n * @param {BufferSource} key [description]\n * @param {BufferSource} salt [description]\n * @param {BufferSource} info [description]\n * @param {HashName} hash the hash to use\n * @param {Number} length desired length of the generated key in bits (not bytes!)\n * @return {[type]} [description]\n */\n async hkdf(key, salt, info, hash, length) {\n if (!this._subtleCrypto.deriveBits) {\n return this._cryptoExtras.hkdf(this._crypto, key, salt, info, hash, length);\n }\n const hkdfkey = await subtleCryptoResult(this._subtleCrypto.importKey(\n 'raw',\n key,\n {name: \"HKDF\"},\n false,\n [\"deriveBits\"],\n ), \"importKey\");\n const keybits = await subtleCryptoResult(this._subtleCrypto.deriveBits({\n name: \"HKDF\",\n salt,\n info,\n hash: hashName(hash),\n },\n hkdfkey,\n length,\n ), \"deriveBits\");\n return new Uint8Array(keybits);\n }\n}\n\nclass AESCrypto {\n constructor(subtleCrypto, crypto) {\n this._subtleCrypto = subtleCrypto;\n this._crypto = crypto;\n }\n /**\n * [decrypt description]\n * @param {BufferSource} key [description]\n * @param {Object} jwkKey [description]\n * @param {BufferSource} iv [description]\n * @param {BufferSource} data [description]\n * @param {Number} counterLength the size of the counter, in bits\n * @return {BufferSource} [description]\n */\n async decryptCTR({key, jwkKey, iv, data, counterLength = 64}) {\n const opts = {\n name: \"AES-CTR\",\n counter: iv,\n length: counterLength,\n };\n let aesKey;\n try {\n const selectedKey = key || jwkKey;\n const format = jwkKey ? \"jwk\" : \"raw\";\n aesKey = await subtleCryptoResult(this._subtleCrypto.importKey(\n format,\n selectedKey,\n opts,\n false,\n ['decrypt'],\n ), \"importKey\");\n } catch (err) {\n throw new Error(`Could not import key for AES-CTR decryption: ${err.message}`);\n }\n try {\n const plaintext = await subtleCryptoResult(this._subtleCrypto.decrypt(\n // see https://developer.mozilla.org/en-US/docs/Web/API/AesCtrParams\n opts,\n aesKey,\n data,\n ), \"decrypt\");\n return new Uint8Array(plaintext);\n } catch (err) {\n throw new Error(`Could not decrypt with AES-CTR: ${err.message}`);\n }\n }\n\n async encryptCTR({key, jwkKey, iv, data}) {\n const opts = {\n name: \"AES-CTR\",\n counter: iv,\n length: 64,\n };\n let aesKey;\n const selectedKey = key || jwkKey;\n const format = jwkKey ? \"jwk\" : \"raw\";\n try {\n aesKey = await subtleCryptoResult(this._subtleCrypto.importKey(\n format,\n selectedKey,\n opts,\n false,\n ['encrypt'],\n ), \"importKey\");\n } catch (err) {\n throw new Error(`Could not import key for AES-CTR encryption: ${err.message}`);\n }\n try {\n const ciphertext = await subtleCryptoResult(this._subtleCrypto.encrypt(\n // see https://developer.mozilla.org/en-US/docs/Web/API/AesCtrParams\n opts,\n aesKey,\n data,\n ), \"encrypt\");\n return new Uint8Array(ciphertext);\n } catch (err) {\n throw new Error(`Could not encrypt with AES-CTR: ${err.message}`);\n }\n }\n\n /**\n * Generate a CTR key\n * @param {String} format \"raw\" or \"jwk\"\n * @param {Number} length 128 or 256\n * @return {Promise<Object>} an object for jwk, or a BufferSource for raw\n */\n async generateKey(format, length = 256) {\n const cryptoKey = await subtleCryptoResult(this._subtleCrypto.generateKey(\n {\"name\": \"AES-CTR\", length}, true, [\"encrypt\", \"decrypt\"]));\n return subtleCryptoResult(this._subtleCrypto.exportKey(format, cryptoKey));\n }\n\n async generateIV() {\n return generateIV(this._crypto);\n }\n}\n\nfunction generateIV(crypto) {\n const randomBytes = crypto.getRandomValues(new Uint8Array(8));\n const ivArray = new Uint8Array(16);\n for (let i = 0; i < randomBytes.length; i += 1) {\n ivArray[i] = randomBytes[i];\n }\n return ivArray;\n}\n\nfunction jwkKeyToRaw(jwkKey) {\n if (jwkKey.alg !== \"A256CTR\") {\n throw new Error(`Unknown algorithm: ${jwkKey.alg}`);\n }\n if (!jwkKey.key_ops.includes(\"decrypt\")) {\n throw new Error(`decrypt missing from key_ops`);\n }\n if (jwkKey.kty !== \"oct\") {\n throw new Error(`Invalid key type, \"oct\" expected: ${jwkKey.kty}`);\n }\n // convert base64-url to normal base64\n const base64UrlKey = jwkKey.k;\n const base64Key = base64UrlKey.replace(/-/g, \"+\").replace(/_/g, \"/\");\n return base64.decode(base64Key);\n}\n\nfunction encodeUnpaddedBase64(buffer) {\n const str = base64.encode(buffer);\n const paddingIdx = str.indexOf(\"=\");\n if (paddingIdx !== -1) {\n return str.substr(0, paddingIdx);\n } else {\n return str;\n }\n}\n\nfunction encodeUrlBase64(buffer) {\n const unpadded = encodeUnpaddedBase64(buffer);\n return unpadded.replace(/\\+/g, \"-\").replace(/\\//g, \"_\");\n}\n\nfunction rawKeyToJwk(key) {\n return {\n \"alg\": \"A256CTR\",\n \"ext\": true,\n \"k\": encodeUrlBase64(key),\n \"key_ops\": [\n \"encrypt\",\n \"decrypt\"\n ],\n \"kty\": \"oct\"\n };\n}\n\n\nclass AESLegacyCrypto {\n constructor(aesjs, crypto) {\n this._aesjs = aesjs;\n this._crypto = crypto;\n }\n /**\n * [decrypt description]\n * @param {BufferSource} key [description]\n * @param {BufferSource} iv [description]\n * @param {BufferSource} ciphertext [description]\n * @param {Number} counterLength the size of the counter, in bits\n * @return {BufferSource} [description]\n */\n async decryptCTR({key, jwkKey, iv, data, counterLength = 64}) {\n if (counterLength !== 64) {\n throw new Error(`Unsupported counter length: ${counterLength}`);\n }\n if (jwkKey) {\n key = jwkKeyToRaw(jwkKey);\n }\n const aesjs = this._aesjs;\n var aesCtr = new aesjs.ModeOfOperation.ctr(new Uint8Array(key), new aesjs.Counter(new Uint8Array(iv)));\n return aesCtr.decrypt(new Uint8Array(data));\n }\n\n async encryptCTR({key, jwkKey, iv, data}) {\n if (jwkKey) {\n key = jwkKeyToRaw(jwkKey);\n }\n const aesjs = this._aesjs;\n var aesCtr = new aesjs.ModeOfOperation.ctr(new Uint8Array(key), new aesjs.Counter(new Uint8Array(iv)));\n return aesCtr.encrypt(new Uint8Array(data));\n }\n\n /**\n * Generate a CTR key\n * @param {String} format \"raw\" or \"jwk\"\n * @param {Number} length 128 or 256\n * @return {Promise<Object>} an object for jwk, or a BufferSource for raw\n */\n async generateKey(format, length = 256) {\n let key = crypto.getRandomValues(new Uint8Array(length / 8));\n if (format === \"jwk\") {\n key = rawKeyToJwk(key);\n }\n return key;\n }\n\n async generateIV() {\n return generateIV(this._crypto);\n }\n}\n\nfunction hashName(name) {\n if (name !== \"SHA-256\" && name !== \"SHA-512\") {\n throw new Error(`Invalid hash name: ${name}`);\n }\n return name;\n}\n\nexport class Crypto {\n constructor(cryptoExtras) {\n const crypto = window.crypto || window.msCrypto;\n const subtleCrypto = crypto.subtle || crypto.webkitSubtle;\n this._subtleCrypto = subtleCrypto;\n // not exactly guaranteeing AES-CTR support\n // but in practice IE11 doesn't have this\n if (!subtleCrypto.deriveBits && cryptoExtras?.aesjs) {\n this.aes = new AESLegacyCrypto(cryptoExtras.aesjs, crypto);\n } else {\n this.aes = new AESCrypto(subtleCrypto, crypto);\n }\n this.hmac = new HMACCrypto(subtleCrypto);\n this.derive = new DeriveCrypto(subtleCrypto, this, cryptoExtras);\n }\n\n /**\n * [digest description]\n * @param {HashName} hash\n * @param {BufferSource} data\n * @return {BufferSource}\n */\n async digest(hash, data) {\n return await subtleCryptoResult(this._subtleCrypto.digest(hashName(hash), data));\n }\n\n digestSize(hash) {\n switch (hashName(hash)) {\n case \"SHA-512\": return 64;\n case \"SHA-256\": return 32;\n default: throw new Error(`Not implemented for ${hashName(hash)}`);\n }\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport async function estimateStorageUsage() {\n if (navigator?.storage?.estimate) {\n const {quota, usage} = await navigator.storage.estimate();\n return {quota, usage};\n } else {\n return {quota: null, usage: null};\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {AbortError} from \"../../../utils/error\";\n\nclass WorkerState {\n constructor(worker) {\n this.worker = worker;\n this.busy = false;\n }\n\n attach(pool) {\n this.worker.addEventListener(\"message\", pool);\n this.worker.addEventListener(\"error\", pool);\n }\n\n detach(pool) {\n this.worker.removeEventListener(\"message\", pool);\n this.worker.removeEventListener(\"error\", pool);\n }\n}\n\nclass Request {\n constructor(message, pool) {\n this._promise = new Promise((_resolve, _reject) => {\n this._resolve = _resolve;\n this._reject = _reject;\n });\n this._message = message;\n this._pool = pool;\n this._worker = null;\n }\n\n abort() {\n if (this._isNotDisposed) {\n this._pool._abortRequest(this);\n this._dispose();\n }\n }\n\n response() {\n return this._promise;\n }\n\n _dispose() {\n this._reject = null;\n this._resolve = null;\n }\n\n get _isNotDisposed() {\n return this._resolve && this._reject;\n }\n}\n\nexport class WorkerPool {\n // TODO: extract DOM specific bits and write unit tests\n constructor(path, amount) {\n this._workers = [];\n for (let i = 0; i < amount ; ++i) {\n const worker = new WorkerState(new Worker(path));\n worker.attach(this);\n this._workers[i] = worker;\n }\n this._requests = new Map();\n this._counter = 0;\n this._pendingFlag = false;\n this._init = null;\n\n }\n\n init() {\n const promise = new Promise((resolve, reject) => {\n this._init = {resolve, reject};\n });\n this.sendAll({type: \"ping\"})\n .then(this._init.resolve, this._init.reject)\n .finally(() => {\n this._init = null;\n });\n return promise;\n }\n\n handleEvent(e) {\n if (e.type === \"message\") {\n const message = e.data;\n const request = this._requests.get(message.replyToId);\n if (request) {\n request._worker.busy = false;\n if (request._isNotDisposed) {\n if (message.type === \"success\") {\n request._resolve(message.payload);\n } else if (message.type === \"error\") {\n const err = new Error(message.message);\n err.stack = message.stack;\n request._reject(err);\n }\n request._dispose();\n }\n this._requests.delete(message.replyToId);\n }\n this._sendPending();\n } else if (e.type === \"error\") {\n if (this._init) {\n this._init.reject(new Error(\"worker error during init\"));\n }\n console.error(\"worker error\", e);\n }\n }\n\n _getPendingRequest() {\n for (const r of this._requests.values()) {\n if (!r._worker) {\n return r;\n }\n }\n }\n\n _getFreeWorker() {\n for (const w of this._workers) {\n if (!w.busy) {\n return w;\n }\n }\n }\n\n _sendPending() {\n this._pendingFlag = false;\n let success;\n do {\n success = false;\n const request = this._getPendingRequest();\n if (request) {\n const worker = this._getFreeWorker();\n if (worker) {\n this._sendWith(request, worker);\n success = true;\n }\n }\n } while (success);\n }\n\n _sendWith(request, worker) {\n request._worker = worker;\n worker.busy = true;\n worker.worker.postMessage(request._message);\n }\n\n _enqueueRequest(message) {\n this._counter += 1;\n message.id = this._counter;\n const request = new Request(message, this);\n this._requests.set(message.id, request);\n return request;\n }\n\n send(message) {\n const request = this._enqueueRequest(message);\n const worker = this._getFreeWorker();\n if (worker) {\n this._sendWith(request, worker);\n }\n return request;\n }\n\n // assumes all workers are free atm\n sendAll(message) {\n const promises = this._workers.map(worker => {\n const request = this._enqueueRequest(Object.assign({}, message));\n this._sendWith(request, worker);\n return request.response();\n });\n return Promise.all(promises);\n }\n\n dispose() {\n for (const w of this._workers) {\n w.detach(this);\n w.worker.terminate();\n }\n }\n\n _trySendPendingInNextTick() {\n if (!this._pendingFlag) {\n this._pendingFlag = true;\n Promise.resolve().then(() => {\n this._sendPending();\n });\n }\n }\n\n _abortRequest(request) {\n request._reject(new AbortError());\n if (request._worker) {\n request._worker.busy = false;\n }\n this._requests.delete(request._message.id);\n // allow more requests to be aborted before trying to send other pending\n this._trySendPendingInNextTick();\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {BlobHandle} from \"./BlobHandle.js\";\nimport {domEventAsPromise} from \"./utils.js\";\n\nexport class ImageHandle {\n static async fromBlob(blob) {\n const img = await loadImgFromBlob(blob);\n const {width, height} = img;\n return new ImageHandle(blob, width, height, img);\n }\n\n constructor(blob, width, height, imgElement) {\n this.blob = blob;\n this.width = width;\n this.height = height;\n this._domElement = imgElement;\n }\n\n get maxDimension() {\n return Math.max(this.width, this.height);\n }\n\n async _getDomElement() {\n if (!this._domElement) {\n this._domElement = await loadImgFromBlob(this.blob);\n }\n return this._domElement;\n }\n\n async scale(maxDimension) {\n const aspectRatio = this.width / this.height;\n const scaleFactor = Math.min(1, maxDimension / (aspectRatio >= 1 ? this.width : this.height));\n const scaledWidth = Math.round(this.width * scaleFactor);\n const scaledHeight = Math.round(this.height * scaleFactor);\n const canvas = document.createElement(\"canvas\");\n canvas.width = scaledWidth;\n canvas.height = scaledHeight;\n const ctx = canvas.getContext(\"2d\");\n const drawableElement = await this._getDomElement();\n ctx.drawImage(drawableElement, 0, 0, scaledWidth, scaledHeight);\n let mimeType = this.blob.mimeType === \"image/jpeg\" ? \"image/jpeg\" : \"image/png\";\n let nativeBlob;\n if (canvas.toBlob) {\n nativeBlob = await new Promise(resolve => canvas.toBlob(resolve, mimeType));\n } else if (canvas.msToBlob) {\n // TODO: provide a mimetype override in blob handle for this case\n mimeType = \"image/png\";\n nativeBlob = canvas.msToBlob();\n } else {\n throw new Error(\"canvas can't be turned into blob\");\n }\n const blob = BlobHandle.fromBlob(nativeBlob);\n return new ImageHandle(blob, scaledWidth, scaledHeight, null);\n }\n\n dispose() {\n this.blob.dispose();\n }\n}\n\nexport class VideoHandle extends ImageHandle {\n get duration() {\n if (typeof this._domElement.duration === \"number\") {\n return Math.round(this._domElement.duration * 1000);\n }\n return undefined;\n }\n\n static async fromBlob(blob) {\n const video = await loadVideoFromBlob(blob);\n const {videoWidth, videoHeight} = video;\n return new VideoHandle(blob, videoWidth, videoHeight, video);\n }\n}\n\nexport function hasReadPixelPermission() {\n const canvas = document.createElement(\"canvas\");\n canvas.width = 1;\n canvas.height = 1;\n const ctx = canvas.getContext(\"2d\");\n const rgb = [\n Math.round(Math.random() * 255),\n Math.round(Math.random() * 255),\n Math.round(Math.random() * 255),\n ]\n ctx.fillStyle = `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`;\n ctx.fillRect(0, 0, 1, 1);\n const data = ctx.getImageData(0, 0, 1, 1).data;\n return data[0] === rgb[0] && data[1] === rgb[1] && data[2] === rgb[2];\n}\n\nasync function loadImgFromBlob(blob) {\n const img = document.createElement(\"img\");\n const loadPromise = domEventAsPromise(img, \"load\");\n img.src = blob.url;\n await loadPromise;\n return img;\n}\n\nasync function loadVideoFromBlob(blob) {\n const video = document.createElement(\"video\");\n video.muted = true;\n const loadPromise = domEventAsPromise(video, \"loadedmetadata\");\n video.src = blob.url;\n video.load();\n await loadPromise;\n // seek to the first 1/10s to make sure that drawing the video\n // on a canvas won't give a blank image\n const seekPromise = domEventAsPromise(video, \"seeked\");\n // needed for safari to reliably fire the seeked event,\n // somewhat hacky but using raf for example didn't do the trick\n await new Promise(r => setTimeout(r, 200));\n video.currentTime = 0.1;\n await seekPromise;\n return video;\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport async function downloadInIframe(container, iframeSrc, blobHandle, filename, isIOS) {\n let iframe = container.querySelector(\"iframe.downloadSandbox\");\n if (!iframe) {\n iframe = document.createElement(\"iframe\");\n iframe.setAttribute(\"sandbox\", \"allow-scripts allow-downloads allow-downloads-without-user-activation\");\n iframe.setAttribute(\"src\", iframeSrc);\n iframe.className = \"hidden downloadSandbox\";\n container.appendChild(iframe);\n let detach;\n await new Promise((resolve, reject) => {\n detach = () => {\n iframe.removeEventListener(\"load\", resolve);\n iframe.removeEventListener(\"error\", reject); \n }\n iframe.addEventListener(\"load\", resolve);\n iframe.addEventListener(\"error\", reject);\n });\n detach();\n }\n if (isIOS) {\n // iOS can't read a blob in a sandboxed iframe,\n // see https://github.com/vector-im/hydrogen-web/issues/244\n const buffer = await blobHandle.readAsBuffer();\n iframe.contentWindow.postMessage({\n type: \"downloadBuffer\",\n buffer,\n mimeType: blobHandle.mimeType,\n filename: filename\n }, \"*\");\n } else {\n iframe.contentWindow.postMessage({\n type: \"downloadBlob\",\n blob: blobHandle.nativeBlob,\n filename: filename\n }, \"*\");\n }\n}\n","/*! @license DOMPurify 2.3.0 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.0/LICENSE */\n\nfunction _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }\n\nvar hasOwnProperty = Object.hasOwnProperty,\n setPrototypeOf = Object.setPrototypeOf,\n isFrozen = Object.isFrozen,\n getPrototypeOf = Object.getPrototypeOf,\n getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;\nvar freeze = Object.freeze,\n seal = Object.seal,\n create = Object.create; // eslint-disable-line import/no-mutable-exports\n\nvar _ref = typeof Reflect !== 'undefined' && Reflect,\n apply = _ref.apply,\n construct = _ref.construct;\n\nif (!apply) {\n apply = function apply(fun, thisValue, args) {\n return fun.apply(thisValue, args);\n };\n}\n\nif (!freeze) {\n freeze = function freeze(x) {\n return x;\n };\n}\n\nif (!seal) {\n seal = function seal(x) {\n return x;\n };\n}\n\nif (!construct) {\n construct = function construct(Func, args) {\n return new (Function.prototype.bind.apply(Func, [null].concat(_toConsumableArray(args))))();\n };\n}\n\nvar arrayForEach = unapply(Array.prototype.forEach);\nvar arrayPop = unapply(Array.prototype.pop);\nvar arrayPush = unapply(Array.prototype.push);\n\nvar stringToLowerCase = unapply(String.prototype.toLowerCase);\nvar stringMatch = unapply(String.prototype.match);\nvar stringReplace = unapply(String.prototype.replace);\nvar stringIndexOf = unapply(String.prototype.indexOf);\nvar stringTrim = unapply(String.prototype.trim);\n\nvar regExpTest = unapply(RegExp.prototype.test);\n\nvar typeErrorCreate = unconstruct(TypeError);\n\nfunction unapply(func) {\n return function (thisArg) {\n for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n args[_key - 1] = arguments[_key];\n }\n\n return apply(func, thisArg, args);\n };\n}\n\nfunction unconstruct(func) {\n return function () {\n for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {\n args[_key2] = arguments[_key2];\n }\n\n return construct(func, args);\n };\n}\n\n/* Add properties to a lookup table */\nfunction addToSet(set, array) {\n if (setPrototypeOf) {\n // Make 'in' and truthy checks like Boolean(set.constructor)\n // independent of any properties defined on Object.prototype.\n // Prevent prototype setters from intercepting set as a this value.\n setPrototypeOf(set, null);\n }\n\n var l = array.length;\n while (l--) {\n var element = array[l];\n if (typeof element === 'string') {\n var lcElement = stringToLowerCase(element);\n if (lcElement !== element) {\n // Config presets (e.g. tags.js, attrs.js) are immutable.\n if (!isFrozen(array)) {\n array[l] = lcElement;\n }\n\n element = lcElement;\n }\n }\n\n set[element] = true;\n }\n\n return set;\n}\n\n/* Shallow clone an object */\nfunction clone(object) {\n var newObject = create(null);\n\n var property = void 0;\n for (property in object) {\n if (apply(hasOwnProperty, object, [property])) {\n newObject[property] = object[property];\n }\n }\n\n return newObject;\n}\n\n/* IE10 doesn't support __lookupGetter__ so lets'\n * simulate it. It also automatically checks\n * if the prop is function or getter and behaves\n * accordingly. */\nfunction lookupGetter(object, prop) {\n while (object !== null) {\n var desc = getOwnPropertyDescriptor(object, prop);\n if (desc) {\n if (desc.get) {\n return unapply(desc.get);\n }\n\n if (typeof desc.value === 'function') {\n return unapply(desc.value);\n }\n }\n\n object = getPrototypeOf(object);\n }\n\n function fallbackValue(element) {\n console.warn('fallback value for', element);\n return null;\n }\n\n return fallbackValue;\n}\n\nvar html = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);\n\n// SVG\nvar svg = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);\n\nvar svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);\n\n// List of SVG elements that are disallowed by default.\n// We still need to know them so that we can do namespace\n// checks properly in case one wants to add them to\n// allow-list.\nvar svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'fedropshadow', 'feimage', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']);\n\nvar mathMl = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover']);\n\n// Similarly to SVG, we want to know all MathML elements,\n// even those that we disallow by default.\nvar mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);\n\nvar text = freeze(['#text']);\n\nvar html$1 = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', 'playsinline', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'xmlns', 'slot']);\n\nvar svg$1 = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'targetx', 'targety', 'transform', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);\n\nvar mathMl$1 = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);\n\nvar xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);\n\n// eslint-disable-next-line unicorn/better-regex\nvar MUSTACHE_EXPR = seal(/\\{\\{[\\s\\S]*|[\\s\\S]*\\}\\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode\nvar ERB_EXPR = seal(/<%[\\s\\S]*|[\\s\\S]*%>/gm);\nvar DATA_ATTR = seal(/^data-[\\-\\w.\\u00B7-\\uFFFF]/); // eslint-disable-line no-useless-escape\nvar ARIA_ATTR = seal(/^aria-[\\-\\w]+$/); // eslint-disable-line no-useless-escape\nvar IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\\-]+(?:[^a-z+.\\-:]|$))/i // eslint-disable-line no-useless-escape\n);\nvar IS_SCRIPT_OR_DATA = seal(/^(?:\\w+script|data):/i);\nvar ATTR_WHITESPACE = seal(/[\\u0000-\\u0020\\u00A0\\u1680\\u180E\\u2000-\\u2029\\u205F\\u3000]/g // eslint-disable-line no-control-regex\n);\n\nvar _typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; };\n\nfunction _toConsumableArray$1(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }\n\nvar getGlobal = function getGlobal() {\n return typeof window === 'undefined' ? null : window;\n};\n\n/**\n * Creates a no-op policy for internal use only.\n * Don't export this function outside this module!\n * @param {?TrustedTypePolicyFactory} trustedTypes The policy factory.\n * @param {Document} document The document object (to determine policy name suffix)\n * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types\n * are not supported).\n */\nvar _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {\n if ((typeof trustedTypes === 'undefined' ? 'undefined' : _typeof(trustedTypes)) !== 'object' || typeof trustedTypes.createPolicy !== 'function') {\n return null;\n }\n\n // Allow the callers to control the unique policy name\n // by adding a data-tt-policy-suffix to the script element with the DOMPurify.\n // Policy creation with duplicate names throws in Trusted Types.\n var suffix = null;\n var ATTR_NAME = 'data-tt-policy-suffix';\n if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {\n suffix = document.currentScript.getAttribute(ATTR_NAME);\n }\n\n var policyName = 'dompurify' + (suffix ? '#' + suffix : '');\n\n try {\n return trustedTypes.createPolicy(policyName, {\n createHTML: function createHTML(html$$1) {\n return html$$1;\n }\n });\n } catch (_) {\n // Policy creation failed (most likely another DOMPurify script has\n // already run). Skip creating the policy, as this will only cause errors\n // if TT are enforced.\n console.warn('TrustedTypes policy ' + policyName + ' could not be created.');\n return null;\n }\n};\n\nfunction createDOMPurify() {\n var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();\n\n var DOMPurify = function DOMPurify(root) {\n return createDOMPurify(root);\n };\n\n /**\n * Version label, exposed for easier checks\n * if DOMPurify is up to date or not\n */\n DOMPurify.version = '2.3.0';\n\n /**\n * Array of elements that DOMPurify removed during sanitation.\n * Empty if nothing was removed.\n */\n DOMPurify.removed = [];\n\n if (!window || !window.document || window.document.nodeType !== 9) {\n // Not running in a browser, provide a factory function\n // so that you can pass your own Window\n DOMPurify.isSupported = false;\n\n return DOMPurify;\n }\n\n var originalDocument = window.document;\n\n var document = window.document;\n var DocumentFragment = window.DocumentFragment,\n HTMLTemplateElement = window.HTMLTemplateElement,\n Node = window.Node,\n Element = window.Element,\n NodeFilter = window.NodeFilter,\n _window$NamedNodeMap = window.NamedNodeMap,\n NamedNodeMap = _window$NamedNodeMap === undefined ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,\n Text = window.Text,\n Comment = window.Comment,\n DOMParser = window.DOMParser,\n trustedTypes = window.trustedTypes;\n\n\n var ElementPrototype = Element.prototype;\n\n var cloneNode = lookupGetter(ElementPrototype, 'cloneNode');\n var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');\n var getChildNodes = lookupGetter(ElementPrototype, 'childNodes');\n var getParentNode = lookupGetter(ElementPrototype, 'parentNode');\n\n // As per issue #47, the web-components registry is inherited by a\n // new document created via createHTMLDocument. As per the spec\n // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)\n // a new empty registry is used when creating a template contents owner\n // document, so we use that as our parent document to ensure nothing\n // is inherited.\n if (typeof HTMLTemplateElement === 'function') {\n var template = document.createElement('template');\n if (template.content && template.content.ownerDocument) {\n document = template.content.ownerDocument;\n }\n }\n\n var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);\n var emptyHTML = trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML('') : '';\n\n var _document = document,\n implementation = _document.implementation,\n createNodeIterator = _document.createNodeIterator,\n createDocumentFragment = _document.createDocumentFragment,\n getElementsByTagName = _document.getElementsByTagName;\n var importNode = originalDocument.importNode;\n\n\n var documentMode = {};\n try {\n documentMode = clone(document).documentMode ? document.documentMode : {};\n } catch (_) {}\n\n var hooks = {};\n\n /**\n * Expose whether this browser supports running the full DOMPurify.\n */\n DOMPurify.isSupported = typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9;\n\n var MUSTACHE_EXPR$$1 = MUSTACHE_EXPR,\n ERB_EXPR$$1 = ERB_EXPR,\n DATA_ATTR$$1 = DATA_ATTR,\n ARIA_ATTR$$1 = ARIA_ATTR,\n IS_SCRIPT_OR_DATA$$1 = IS_SCRIPT_OR_DATA,\n ATTR_WHITESPACE$$1 = ATTR_WHITESPACE;\n var IS_ALLOWED_URI$$1 = IS_ALLOWED_URI;\n\n /**\n * We consider the elements and attributes below to be safe. Ideally\n * don't add any new ones but feel free to remove unwanted ones.\n */\n\n /* allowed element names */\n\n var ALLOWED_TAGS = null;\n var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(html), _toConsumableArray$1(svg), _toConsumableArray$1(svgFilters), _toConsumableArray$1(mathMl), _toConsumableArray$1(text)));\n\n /* Allowed attribute names */\n var ALLOWED_ATTR = null;\n var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray$1(html$1), _toConsumableArray$1(svg$1), _toConsumableArray$1(mathMl$1), _toConsumableArray$1(xml)));\n\n /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */\n var FORBID_TAGS = null;\n\n /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */\n var FORBID_ATTR = null;\n\n /* Decide if ARIA attributes are okay */\n var ALLOW_ARIA_ATTR = true;\n\n /* Decide if custom data attributes are okay */\n var ALLOW_DATA_ATTR = true;\n\n /* Decide if unknown protocols are okay */\n var ALLOW_UNKNOWN_PROTOCOLS = false;\n\n /* Output should be safe for common template engines.\n * This means, DOMPurify removes data attributes, mustaches and ERB\n */\n var SAFE_FOR_TEMPLATES = false;\n\n /* Decide if document with <html>... should be returned */\n var WHOLE_DOCUMENT = false;\n\n /* Track whether config is already set on this instance of DOMPurify. */\n var SET_CONFIG = false;\n\n /* Decide if all elements (e.g. style, script) must be children of\n * document.body. By default, browsers might move them to document.head */\n var FORCE_BODY = false;\n\n /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html\n * string (or a TrustedHTML object if Trusted Types are supported).\n * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead\n */\n var RETURN_DOM = false;\n\n /* Decide if a DOM `DocumentFragment` should be returned, instead of a html\n * string (or a TrustedHTML object if Trusted Types are supported) */\n var RETURN_DOM_FRAGMENT = false;\n\n /* If `RETURN_DOM` or `RETURN_DOM_FRAGMENT` is enabled, decide if the returned DOM\n * `Node` is imported into the current `Document`. If this flag is not enabled the\n * `Node` will belong (its ownerDocument) to a fresh `HTMLDocument`, created by\n * DOMPurify.\n *\n * This defaults to `true` starting DOMPurify 2.2.0. Note that setting it to `false`\n * might cause XSS from attacks hidden in closed shadowroots in case the browser\n * supports Declarative Shadow: DOM https://web.dev/declarative-shadow-dom/\n */\n var RETURN_DOM_IMPORT = true;\n\n /* Try to return a Trusted Type object instead of a string, return a string in\n * case Trusted Types are not supported */\n var RETURN_TRUSTED_TYPE = false;\n\n /* Output should be free from DOM clobbering attacks? */\n var SANITIZE_DOM = true;\n\n /* Keep element content when removing element? */\n var KEEP_CONTENT = true;\n\n /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead\n * of importing it into a new Document and returning a sanitized copy */\n var IN_PLACE = false;\n\n /* Allow usage of profiles like html, svg and mathMl */\n var USE_PROFILES = {};\n\n /* Tags to ignore content of when KEEP_CONTENT is true */\n var FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);\n\n /* Tags that are safe for data: URIs */\n var DATA_URI_TAGS = null;\n var DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);\n\n /* Attributes safe for values like \"javascript:\" */\n var URI_SAFE_ATTRIBUTES = null;\n var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'summary', 'title', 'value', 'style', 'xmlns']);\n\n var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';\n var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\n var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';\n /* Document namespace */\n var NAMESPACE = HTML_NAMESPACE;\n var IS_EMPTY_INPUT = false;\n\n /* Keep a reference to config to pass to hooks */\n var CONFIG = null;\n\n /* Ideally, do not touch anything below this line */\n /* ______________________________________________ */\n\n var formElement = document.createElement('form');\n\n /**\n * _parseConfig\n *\n * @param {Object} cfg optional config literal\n */\n // eslint-disable-next-line complexity\n var _parseConfig = function _parseConfig(cfg) {\n if (CONFIG && CONFIG === cfg) {\n return;\n }\n\n /* Shield configuration object from tampering */\n if (!cfg || (typeof cfg === 'undefined' ? 'undefined' : _typeof(cfg)) !== 'object') {\n cfg = {};\n }\n\n /* Shield configuration object from prototype pollution */\n cfg = clone(cfg);\n\n /* Set configuration parameters */\n ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS;\n ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR;\n URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES;\n DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS;\n FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {};\n FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {};\n USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;\n ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true\n ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true\n ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false\n SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false\n WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false\n RETURN_DOM = cfg.RETURN_DOM || false; // Default false\n RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false\n RETURN_DOM_IMPORT = cfg.RETURN_DOM_IMPORT !== false; // Default true\n RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false\n FORCE_BODY = cfg.FORCE_BODY || false; // Default false\n SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true\n KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true\n IN_PLACE = cfg.IN_PLACE || false; // Default false\n IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1;\n NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;\n if (SAFE_FOR_TEMPLATES) {\n ALLOW_DATA_ATTR = false;\n }\n\n if (RETURN_DOM_FRAGMENT) {\n RETURN_DOM = true;\n }\n\n /* Parse profile info */\n if (USE_PROFILES) {\n ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(text)));\n ALLOWED_ATTR = [];\n if (USE_PROFILES.html === true) {\n addToSet(ALLOWED_TAGS, html);\n addToSet(ALLOWED_ATTR, html$1);\n }\n\n if (USE_PROFILES.svg === true) {\n addToSet(ALLOWED_TAGS, svg);\n addToSet(ALLOWED_ATTR, svg$1);\n addToSet(ALLOWED_ATTR, xml);\n }\n\n if (USE_PROFILES.svgFilters === true) {\n addToSet(ALLOWED_TAGS, svgFilters);\n addToSet(ALLOWED_ATTR, svg$1);\n addToSet(ALLOWED_ATTR, xml);\n }\n\n if (USE_PROFILES.mathMl === true) {\n addToSet(ALLOWED_TAGS, mathMl);\n addToSet(ALLOWED_ATTR, mathMl$1);\n addToSet(ALLOWED_ATTR, xml);\n }\n }\n\n /* Merge configuration parameters */\n if (cfg.ADD_TAGS) {\n if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {\n ALLOWED_TAGS = clone(ALLOWED_TAGS);\n }\n\n addToSet(ALLOWED_TAGS, cfg.ADD_TAGS);\n }\n\n if (cfg.ADD_ATTR) {\n if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {\n ALLOWED_ATTR = clone(ALLOWED_ATTR);\n }\n\n addToSet(ALLOWED_ATTR, cfg.ADD_ATTR);\n }\n\n if (cfg.ADD_URI_SAFE_ATTR) {\n addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);\n }\n\n /* Add #text in case KEEP_CONTENT is set to true */\n if (KEEP_CONTENT) {\n ALLOWED_TAGS['#text'] = true;\n }\n\n /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */\n if (WHOLE_DOCUMENT) {\n addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);\n }\n\n /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */\n if (ALLOWED_TAGS.table) {\n addToSet(ALLOWED_TAGS, ['tbody']);\n delete FORBID_TAGS.tbody;\n }\n\n // Prevent further manipulation of configuration.\n // Not available in IE8, Safari 5, etc.\n if (freeze) {\n freeze(cfg);\n }\n\n CONFIG = cfg;\n };\n\n var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);\n\n var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']);\n\n /* Keep track of all possible SVG and MathML tags\n * so that we can perform the namespace checks\n * correctly. */\n var ALL_SVG_TAGS = addToSet({}, svg);\n addToSet(ALL_SVG_TAGS, svgFilters);\n addToSet(ALL_SVG_TAGS, svgDisallowed);\n\n var ALL_MATHML_TAGS = addToSet({}, mathMl);\n addToSet(ALL_MATHML_TAGS, mathMlDisallowed);\n\n /**\n *\n *\n * @param {Element} element a DOM element whose namespace is being checked\n * @returns {boolean} Return false if the element has a\n * namespace that a spec-compliant parser would never\n * return. Return true otherwise.\n */\n var _checkValidNamespace = function _checkValidNamespace(element) {\n var parent = getParentNode(element);\n\n // In JSDOM, if we're inside shadow DOM, then parentNode\n // can be null. We just simulate parent in this case.\n if (!parent || !parent.tagName) {\n parent = {\n namespaceURI: HTML_NAMESPACE,\n tagName: 'template'\n };\n }\n\n var tagName = stringToLowerCase(element.tagName);\n var parentTagName = stringToLowerCase(parent.tagName);\n\n if (element.namespaceURI === SVG_NAMESPACE) {\n // The only way to switch from HTML namespace to SVG\n // is via <svg>. If it happens via any other tag, then\n // it should be killed.\n if (parent.namespaceURI === HTML_NAMESPACE) {\n return tagName === 'svg';\n }\n\n // The only way to switch from MathML to SVG is via\n // svg if parent is either <annotation-xml> or MathML\n // text integration points.\n if (parent.namespaceURI === MATHML_NAMESPACE) {\n return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);\n }\n\n // We only allow elements that are defined in SVG\n // spec. All others are disallowed in SVG namespace.\n return Boolean(ALL_SVG_TAGS[tagName]);\n }\n\n if (element.namespaceURI === MATHML_NAMESPACE) {\n // The only way to switch from HTML namespace to MathML\n // is via <math>. If it happens via any other tag, then\n // it should be killed.\n if (parent.namespaceURI === HTML_NAMESPACE) {\n return tagName === 'math';\n }\n\n // The only way to switch from SVG to MathML is via\n // <math> and HTML integration points\n if (parent.namespaceURI === SVG_NAMESPACE) {\n return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];\n }\n\n // We only allow elements that are defined in MathML\n // spec. All others are disallowed in MathML namespace.\n return Boolean(ALL_MATHML_TAGS[tagName]);\n }\n\n if (element.namespaceURI === HTML_NAMESPACE) {\n // The only way to switch from SVG to HTML is via\n // HTML integration points, and from MathML to HTML\n // is via MathML text integration points\n if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {\n return false;\n }\n\n if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {\n return false;\n }\n\n // Certain elements are allowed in both SVG and HTML\n // namespace. We need to specify them explicitly\n // so that they don't get erronously deleted from\n // HTML namespace.\n var commonSvgAndHTMLElements = addToSet({}, ['title', 'style', 'font', 'a', 'script']);\n\n // We disallow tags that are specific for MathML\n // or SVG and should never appear in HTML namespace\n return !ALL_MATHML_TAGS[tagName] && (commonSvgAndHTMLElements[tagName] || !ALL_SVG_TAGS[tagName]);\n }\n\n // The code should never reach this place (this means\n // that the element somehow got namespace that is not\n // HTML, SVG or MathML). Return false just in case.\n return false;\n };\n\n /**\n * _forceRemove\n *\n * @param {Node} node a DOM node\n */\n var _forceRemove = function _forceRemove(node) {\n arrayPush(DOMPurify.removed, { element: node });\n try {\n // eslint-disable-next-line unicorn/prefer-dom-node-remove\n node.parentNode.removeChild(node);\n } catch (_) {\n try {\n node.outerHTML = emptyHTML;\n } catch (_) {\n node.remove();\n }\n }\n };\n\n /**\n * _removeAttribute\n *\n * @param {String} name an Attribute name\n * @param {Node} node a DOM node\n */\n var _removeAttribute = function _removeAttribute(name, node) {\n try {\n arrayPush(DOMPurify.removed, {\n attribute: node.getAttributeNode(name),\n from: node\n });\n } catch (_) {\n arrayPush(DOMPurify.removed, {\n attribute: null,\n from: node\n });\n }\n\n node.removeAttribute(name);\n\n // We void attribute values for unremovable \"is\"\" attributes\n if (name === 'is' && !ALLOWED_ATTR[name]) {\n if (RETURN_DOM || RETURN_DOM_FRAGMENT) {\n try {\n _forceRemove(node);\n } catch (_) {}\n } else {\n try {\n node.setAttribute(name, '');\n } catch (_) {}\n }\n }\n };\n\n /**\n * _initDocument\n *\n * @param {String} dirty a string of dirty markup\n * @return {Document} a DOM, filled with the dirty markup\n */\n var _initDocument = function _initDocument(dirty) {\n /* Create a HTML document */\n var doc = void 0;\n var leadingWhitespace = void 0;\n\n if (FORCE_BODY) {\n dirty = '<remove></remove>' + dirty;\n } else {\n /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */\n var matches = stringMatch(dirty, /^[\\r\\n\\t ]+/);\n leadingWhitespace = matches && matches[0];\n }\n\n var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;\n /*\n * Use the DOMParser API by default, fallback later if needs be\n * DOMParser not work for svg when has multiple root element.\n */\n if (NAMESPACE === HTML_NAMESPACE) {\n try {\n doc = new DOMParser().parseFromString(dirtyPayload, 'text/html');\n } catch (_) {}\n }\n\n /* Use createHTMLDocument in case DOMParser is not available */\n if (!doc || !doc.documentElement) {\n doc = implementation.createDocument(NAMESPACE, 'template', null);\n try {\n doc.documentElement.innerHTML = IS_EMPTY_INPUT ? '' : dirtyPayload;\n } catch (_) {\n // Syntax error if dirtyPayload is invalid xml\n }\n }\n\n var body = doc.body || doc.documentElement;\n\n if (dirty && leadingWhitespace) {\n body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);\n }\n\n /* Work on whole document or just its body */\n if (NAMESPACE === HTML_NAMESPACE) {\n return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];\n }\n\n return WHOLE_DOCUMENT ? doc.documentElement : body;\n };\n\n /**\n * _createIterator\n *\n * @param {Document} root document/fragment to create iterator for\n * @return {Iterator} iterator instance\n */\n var _createIterator = function _createIterator(root) {\n return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);\n };\n\n /**\n * _isClobbered\n *\n * @param {Node} elm element to check for clobbering attacks\n * @return {Boolean} true if clobbered, false if safe\n */\n var _isClobbered = function _isClobbered(elm) {\n if (elm instanceof Text || elm instanceof Comment) {\n return false;\n }\n\n if (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function') {\n return true;\n }\n\n return false;\n };\n\n /**\n * _isNode\n *\n * @param {Node} obj object to check whether it's a DOM node\n * @return {Boolean} true is object is a DOM node\n */\n var _isNode = function _isNode(object) {\n return (typeof Node === 'undefined' ? 'undefined' : _typeof(Node)) === 'object' ? object instanceof Node : object && (typeof object === 'undefined' ? 'undefined' : _typeof(object)) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';\n };\n\n /**\n * _executeHook\n * Execute user configurable hooks\n *\n * @param {String} entryPoint Name of the hook's entry point\n * @param {Node} currentNode node to work on with the hook\n * @param {Object} data additional hook parameters\n */\n var _executeHook = function _executeHook(entryPoint, currentNode, data) {\n if (!hooks[entryPoint]) {\n return;\n }\n\n arrayForEach(hooks[entryPoint], function (hook) {\n hook.call(DOMPurify, currentNode, data, CONFIG);\n });\n };\n\n /**\n * _sanitizeElements\n *\n * @protect nodeName\n * @protect textContent\n * @protect removeChild\n *\n * @param {Node} currentNode to check for permission to exist\n * @return {Boolean} true if node was killed, false if left alive\n */\n var _sanitizeElements = function _sanitizeElements(currentNode) {\n var content = void 0;\n\n /* Execute a hook if present */\n _executeHook('beforeSanitizeElements', currentNode, null);\n\n /* Check if element is clobbered or can clobber */\n if (_isClobbered(currentNode)) {\n _forceRemove(currentNode);\n return true;\n }\n\n /* Check if tagname contains Unicode */\n if (stringMatch(currentNode.nodeName, /[\\u0080-\\uFFFF]/)) {\n _forceRemove(currentNode);\n return true;\n }\n\n /* Now let's check the element's type and name */\n var tagName = stringToLowerCase(currentNode.nodeName);\n\n /* Execute a hook if present */\n _executeHook('uponSanitizeElement', currentNode, {\n tagName: tagName,\n allowedTags: ALLOWED_TAGS\n });\n\n /* Detect mXSS attempts abusing namespace confusion */\n if (!_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\\w]/g, currentNode.innerHTML) && regExpTest(/<[/\\w]/g, currentNode.textContent)) {\n _forceRemove(currentNode);\n return true;\n }\n\n /* Remove element if anything forbids its presence */\n if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {\n /* Keep content except for bad-listed elements */\n if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {\n var parentNode = getParentNode(currentNode) || currentNode.parentNode;\n var childNodes = getChildNodes(currentNode) || currentNode.childNodes;\n\n if (childNodes && parentNode) {\n var childCount = childNodes.length;\n\n for (var i = childCount - 1; i >= 0; --i) {\n parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));\n }\n }\n }\n\n _forceRemove(currentNode);\n return true;\n }\n\n /* Check whether element has a valid namespace */\n if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {\n _forceRemove(currentNode);\n return true;\n }\n\n if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\\/no(script|embed)/i, currentNode.innerHTML)) {\n _forceRemove(currentNode);\n return true;\n }\n\n /* Sanitize element content to be template-safe */\n if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {\n /* Get the element's text content */\n content = currentNode.textContent;\n content = stringReplace(content, MUSTACHE_EXPR$$1, ' ');\n content = stringReplace(content, ERB_EXPR$$1, ' ');\n if (currentNode.textContent !== content) {\n arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() });\n currentNode.textContent = content;\n }\n }\n\n /* Execute a hook if present */\n _executeHook('afterSanitizeElements', currentNode, null);\n\n return false;\n };\n\n /**\n * _isValidAttribute\n *\n * @param {string} lcTag Lowercase tag name of containing element.\n * @param {string} lcName Lowercase attribute name.\n * @param {string} value Attribute value.\n * @return {Boolean} Returns true if `value` is valid, otherwise false.\n */\n // eslint-disable-next-line complexity\n var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {\n /* Make sure attribute cannot clobber */\n if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {\n return false;\n }\n\n /* Allow valid data-* attributes: At least one character after \"-\"\n (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)\n XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)\n We don't need to check the value; it's always URI safe. */\n if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR$$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$$1, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {\n return false;\n\n /* Check value is safe. First, is attr inert? If so, is safe */\n } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$$1, stringReplace(value, ATTR_WHITESPACE$$1, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$$1, stringReplace(value, ATTR_WHITESPACE$$1, ''))) ; else if (!value) ; else {\n return false;\n }\n\n return true;\n };\n\n /**\n * _sanitizeAttributes\n *\n * @protect attributes\n * @protect nodeName\n * @protect removeAttribute\n * @protect setAttribute\n *\n * @param {Node} currentNode to sanitize\n */\n var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {\n var attr = void 0;\n var value = void 0;\n var lcName = void 0;\n var l = void 0;\n /* Execute a hook if present */\n _executeHook('beforeSanitizeAttributes', currentNode, null);\n\n var attributes = currentNode.attributes;\n\n /* Check if we have attributes; if not we might have a text node */\n\n if (!attributes) {\n return;\n }\n\n var hookEvent = {\n attrName: '',\n attrValue: '',\n keepAttr: true,\n allowedAttributes: ALLOWED_ATTR\n };\n l = attributes.length;\n\n /* Go backwards over all attributes; safely remove bad ones */\n while (l--) {\n attr = attributes[l];\n var _attr = attr,\n name = _attr.name,\n namespaceURI = _attr.namespaceURI;\n\n value = stringTrim(attr.value);\n lcName = stringToLowerCase(name);\n\n /* Execute a hook if present */\n hookEvent.attrName = lcName;\n hookEvent.attrValue = value;\n hookEvent.keepAttr = true;\n hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set\n _executeHook('uponSanitizeAttribute', currentNode, hookEvent);\n value = hookEvent.attrValue;\n /* Did the hooks approve of the attribute? */\n if (hookEvent.forceKeepAttr) {\n continue;\n }\n\n /* Remove attribute */\n _removeAttribute(name, currentNode);\n\n /* Did the hooks approve of the attribute? */\n if (!hookEvent.keepAttr) {\n continue;\n }\n\n /* Work around a security issue in jQuery 3.0 */\n if (regExpTest(/\\/>/i, value)) {\n _removeAttribute(name, currentNode);\n continue;\n }\n\n /* Sanitize attribute content to be template-safe */\n if (SAFE_FOR_TEMPLATES) {\n value = stringReplace(value, MUSTACHE_EXPR$$1, ' ');\n value = stringReplace(value, ERB_EXPR$$1, ' ');\n }\n\n /* Is `value` valid for this attribute? */\n var lcTag = currentNode.nodeName.toLowerCase();\n if (!_isValidAttribute(lcTag, lcName, value)) {\n continue;\n }\n\n /* Handle invalid data-* attribute set by try-catching it */\n try {\n if (namespaceURI) {\n currentNode.setAttributeNS(namespaceURI, name, value);\n } else {\n /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. \"x-schema\". */\n currentNode.setAttribute(name, value);\n }\n\n arrayPop(DOMPurify.removed);\n } catch (_) {}\n }\n\n /* Execute a hook if present */\n _executeHook('afterSanitizeAttributes', currentNode, null);\n };\n\n /**\n * _sanitizeShadowDOM\n *\n * @param {DocumentFragment} fragment to iterate over recursively\n */\n var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {\n var shadowNode = void 0;\n var shadowIterator = _createIterator(fragment);\n\n /* Execute a hook if present */\n _executeHook('beforeSanitizeShadowDOM', fragment, null);\n\n while (shadowNode = shadowIterator.nextNode()) {\n /* Execute a hook if present */\n _executeHook('uponSanitizeShadowNode', shadowNode, null);\n\n /* Sanitize tags and elements */\n if (_sanitizeElements(shadowNode)) {\n continue;\n }\n\n /* Deep shadow DOM detected */\n if (shadowNode.content instanceof DocumentFragment) {\n _sanitizeShadowDOM(shadowNode.content);\n }\n\n /* Check attributes, sanitize if necessary */\n _sanitizeAttributes(shadowNode);\n }\n\n /* Execute a hook if present */\n _executeHook('afterSanitizeShadowDOM', fragment, null);\n };\n\n /**\n * Sanitize\n * Public method providing core sanitation functionality\n *\n * @param {String|Node} dirty string or DOM node\n * @param {Object} configuration object\n */\n // eslint-disable-next-line complexity\n DOMPurify.sanitize = function (dirty, cfg) {\n var body = void 0;\n var importedNode = void 0;\n var currentNode = void 0;\n var oldNode = void 0;\n var returnNode = void 0;\n /* Make sure we have a string to sanitize.\n DO NOT return early, as this will return the wrong type if\n the user has requested a DOM object rather than a string */\n IS_EMPTY_INPUT = !dirty;\n if (IS_EMPTY_INPUT) {\n dirty = '<!-->';\n }\n\n /* Stringify, in case dirty is an object */\n if (typeof dirty !== 'string' && !_isNode(dirty)) {\n // eslint-disable-next-line no-negated-condition\n if (typeof dirty.toString !== 'function') {\n throw typeErrorCreate('toString is not a function');\n } else {\n dirty = dirty.toString();\n if (typeof dirty !== 'string') {\n throw typeErrorCreate('dirty is not a string, aborting');\n }\n }\n }\n\n /* Check we can run. Otherwise fall back or ignore */\n if (!DOMPurify.isSupported) {\n if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') {\n if (typeof dirty === 'string') {\n return window.toStaticHTML(dirty);\n }\n\n if (_isNode(dirty)) {\n return window.toStaticHTML(dirty.outerHTML);\n }\n }\n\n return dirty;\n }\n\n /* Assign config vars */\n if (!SET_CONFIG) {\n _parseConfig(cfg);\n }\n\n /* Clean up removed elements */\n DOMPurify.removed = [];\n\n /* Check if dirty is correctly typed for IN_PLACE */\n if (typeof dirty === 'string') {\n IN_PLACE = false;\n }\n\n if (IN_PLACE) ; else if (dirty instanceof Node) {\n /* If dirty is a DOM element, append to an empty document to avoid\n elements being stripped by the parser */\n body = _initDocument('<!---->');\n importedNode = body.ownerDocument.importNode(dirty, true);\n if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {\n /* Node is already a body, use as is */\n body = importedNode;\n } else if (importedNode.nodeName === 'HTML') {\n body = importedNode;\n } else {\n // eslint-disable-next-line unicorn/prefer-dom-node-append\n body.appendChild(importedNode);\n }\n } else {\n /* Exit directly if we have nothing to do */\n if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&\n // eslint-disable-next-line unicorn/prefer-includes\n dirty.indexOf('<') === -1) {\n return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;\n }\n\n /* Initialize the document to work on */\n body = _initDocument(dirty);\n\n /* Check we have a DOM node from the data */\n if (!body) {\n return RETURN_DOM ? null : emptyHTML;\n }\n }\n\n /* Remove first element node (ours) if FORCE_BODY is set */\n if (body && FORCE_BODY) {\n _forceRemove(body.firstChild);\n }\n\n /* Get node iterator */\n var nodeIterator = _createIterator(IN_PLACE ? dirty : body);\n\n /* Now start iterating over the created document */\n while (currentNode = nodeIterator.nextNode()) {\n /* Fix IE's strange behavior with manipulated textNodes #89 */\n if (currentNode.nodeType === 3 && currentNode === oldNode) {\n continue;\n }\n\n /* Sanitize tags and elements */\n if (_sanitizeElements(currentNode)) {\n continue;\n }\n\n /* Shadow DOM detected, sanitize it */\n if (currentNode.content instanceof DocumentFragment) {\n _sanitizeShadowDOM(currentNode.content);\n }\n\n /* Check attributes, sanitize if necessary */\n _sanitizeAttributes(currentNode);\n\n oldNode = currentNode;\n }\n\n oldNode = null;\n\n /* If we sanitized `dirty` in-place, return it. */\n if (IN_PLACE) {\n return dirty;\n }\n\n /* Return sanitized string or DOM */\n if (RETURN_DOM) {\n if (RETURN_DOM_FRAGMENT) {\n returnNode = createDocumentFragment.call(body.ownerDocument);\n\n while (body.firstChild) {\n // eslint-disable-next-line unicorn/prefer-dom-node-append\n returnNode.appendChild(body.firstChild);\n }\n } else {\n returnNode = body;\n }\n\n if (RETURN_DOM_IMPORT) {\n /*\n AdoptNode() is not used because internal state is not reset\n (e.g. the past names map of a HTMLFormElement), this is safe\n in theory but we would rather not risk another attack vector.\n The state that is cloned by importNode() is explicitly defined\n by the specs.\n */\n returnNode = importNode.call(originalDocument, returnNode, true);\n }\n\n return returnNode;\n }\n\n var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;\n\n /* Sanitize final string template-safe */\n if (SAFE_FOR_TEMPLATES) {\n serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$$1, ' ');\n serializedHTML = stringReplace(serializedHTML, ERB_EXPR$$1, ' ');\n }\n\n return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;\n };\n\n /**\n * Public method to set the configuration once\n * setConfig\n *\n * @param {Object} cfg configuration object\n */\n DOMPurify.setConfig = function (cfg) {\n _parseConfig(cfg);\n SET_CONFIG = true;\n };\n\n /**\n * Public method to remove the configuration\n * clearConfig\n *\n */\n DOMPurify.clearConfig = function () {\n CONFIG = null;\n SET_CONFIG = false;\n };\n\n /**\n * Public method to check if an attribute value is valid.\n * Uses last set config, if any. Otherwise, uses config defaults.\n * isValidAttribute\n *\n * @param {string} tag Tag name of containing element.\n * @param {string} attr Attribute name.\n * @param {string} value Attribute value.\n * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.\n */\n DOMPurify.isValidAttribute = function (tag, attr, value) {\n /* Initialize shared config vars if necessary. */\n if (!CONFIG) {\n _parseConfig({});\n }\n\n var lcTag = stringToLowerCase(tag);\n var lcName = stringToLowerCase(attr);\n return _isValidAttribute(lcTag, lcName, value);\n };\n\n /**\n * AddHook\n * Public method to add DOMPurify hooks\n *\n * @param {String} entryPoint entry point for the hook to add\n * @param {Function} hookFunction function to execute\n */\n DOMPurify.addHook = function (entryPoint, hookFunction) {\n if (typeof hookFunction !== 'function') {\n return;\n }\n\n hooks[entryPoint] = hooks[entryPoint] || [];\n arrayPush(hooks[entryPoint], hookFunction);\n };\n\n /**\n * RemoveHook\n * Public method to remove a DOMPurify hook at a given entryPoint\n * (pops it from the stack of hooks if more are present)\n *\n * @param {String} entryPoint entry point for the hook to remove\n */\n DOMPurify.removeHook = function (entryPoint) {\n if (hooks[entryPoint]) {\n arrayPop(hooks[entryPoint]);\n }\n };\n\n /**\n * RemoveHooks\n * Public method to remove all DOMPurify hooks at a given entryPoint\n *\n * @param {String} entryPoint entry point for the hooks to remove\n */\n DOMPurify.removeHooks = function (entryPoint) {\n if (hooks[entryPoint]) {\n hooks[entryPoint] = [];\n }\n };\n\n /**\n * RemoveAllHooks\n * Public method to remove all DOMPurify hooks\n *\n */\n DOMPurify.removeAllHooks = function () {\n hooks = {};\n };\n\n return DOMPurify;\n}\n\nvar purify = createDOMPurify();\n\nexport default purify;\n//# sourceMappingURL=purify.es.js.map\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport DOMPurify from \"dompurify\"\n\nclass HTMLParseResult {\n constructor(bodyNode) {\n this._bodyNode = bodyNode;\n }\n\n get rootNodes() {\n return Array.from(this._bodyNode.childNodes);\n }\n \n getChildNodes(node) {\n return Array.from(node.childNodes);\n }\n\n getAttributeNames(node) {\n return Array.from(node.getAttributeNames());\n }\n\n getAttributeValue(node, attr) {\n return node.getAttribute(attr);\n }\n\n isTextNode(node) { \n return node.nodeType === Node.TEXT_NODE;\n }\n\n getNodeText(node) {\n return node.textContent;\n }\n\n isElementNode(node) {\n return node.nodeType === Node.ELEMENT_NODE;\n }\n\n getNodeElementName(node) {\n return node.tagName;\n }\n}\n\nconst sanitizeConfig = {\n ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|xxx|mxc):|[^a-z]|[a-z+.-]+(?:[^a-z+.-:]|$))/i,\n FORBID_TAGS: ['mx-reply'],\n KEEP_CONTENT: false,\n}\n\nexport function parseHTML(html) {\n // If DOMPurify uses DOMParser, can't we just get the built tree from it\n // instead of re-parsing?\n const sanitized = DOMPurify.sanitize(html, sanitizeConfig);\n const bodyNode = new DOMParser().parseFromString(`<!DOCTYPE html><html><body>${sanitized}</body></html>`, \"text/html\").body;\n return new HTMLParseResult(bodyNode);\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport type NormalVariant = {\n id: string;\n cssLocation: string;\n variables?: any;\n};\n\nexport type Variant = NormalVariant & {\n variantName: string;\n};\n\nexport type DefaultVariant = {\n dark: Variant;\n light: Variant;\n default: Variant;\n}\n\nexport type ThemeInformation = NormalVariant | DefaultVariant; \n\nexport enum ColorSchemePreference {\n Dark,\n Light\n};\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport function getColoredSvgString(svgString, primaryColor, secondaryColor) {\n let coloredSVGCode = svgString.replaceAll(\"#ff00ff\", primaryColor);\n coloredSVGCode = coloredSVGCode.replaceAll(\"#00ffff\", secondaryColor);\n if (svgString === coloredSVGCode) {\n throw new Error(\"svg-colorizer made no color replacements! The input svg should only contain colors #ff00ff (primary, case-sensitive) and #00ffff (secondary, case-sensitive).\");\n }\n return coloredSVGCode;\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\nimport type {Platform} from \"../Platform.js\";\nimport {getColoredSvgString} from \"./shared/svg-colorizer.mjs\";\n\ntype ParsedStructure = {\n [variableName: string]: {\n svg: Promise<{ status: number; body: string }>;\n primary: string | null;\n secondary: string | null;\n };\n};\n\nexport class IconColorizer {\n private _iconVariables: Record<string, string>;\n private _resolvedVariables: Record<string, string>;\n private _manifestLocation: string;\n private _platform: Platform;\n\n constructor(platform: Platform, iconVariables: Record<string, string>, resolvedVariables: Record<string, string>, manifestLocation: string) {\n this._platform = platform;\n this._iconVariables = iconVariables;\n this._resolvedVariables = resolvedVariables;\n this._manifestLocation = manifestLocation;\n }\n\n async toVariables(): Promise<Record<string, string>> {\n const { parsedStructure, promises } = await this._fetchAndParseIcons();\n await Promise.all(promises);\n return this._produceColoredIconVariables(parsedStructure);\n }\n\n private async _fetchAndParseIcons(): Promise<{ parsedStructure: ParsedStructure, promises: any[] }> {\n const promises: any[] = [];\n const parsedStructure: ParsedStructure = {};\n for (const [variable, url] of Object.entries(this._iconVariables)) {\n const urlObject = new URL(`https://${url}`);\n const pathWithoutQueryParams = urlObject.hostname;\n const relativePath = new URL(pathWithoutQueryParams, new URL(this._manifestLocation, window.location.origin));\n const responsePromise = this._platform.request(relativePath, { method: \"GET\", format: \"text\", cache: true, }).response()\n promises.push(responsePromise);\n const searchParams = urlObject.searchParams;\n parsedStructure[variable] = {\n svg: responsePromise,\n primary: searchParams.get(\"primary\"),\n secondary: searchParams.get(\"secondary\")\n };\n }\n return { parsedStructure, promises };\n }\n\n private async _produceColoredIconVariables(parsedStructure: ParsedStructure): Promise<Record<string, string>> {\n let coloredVariables: Record<string, string> = {};\n for (const [variable, { svg, primary, secondary }] of Object.entries(parsedStructure)) {\n const { body: svgCode } = await svg;\n if (!primary) {\n throw new Error(`Primary color variable ${primary} not in list of variables!`);\n }\n const primaryColor = this._resolvedVariables[primary], secondaryColor = this._resolvedVariables[secondary!];\n const coloredSvgCode = getColoredSvgString(svgCode, primaryColor, secondaryColor);\n const dataURI = `url('data:image/svg+xml;utf8,${encodeURIComponent(coloredSvgCode)}')`;\n coloredVariables[variable] = dataURI;\n }\n return coloredVariables;\n }\n}\n","module.exports=function(n){var t={};function r(e){if(t[e])return t[e].exports;var o=t[e]={i:e,l:!1,exports:{}};return n[e].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=n,r.c=t,r.d=function(n,t,e){r.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:e})},r.r=function(n){\"undefined\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:\"Module\"}),Object.defineProperty(n,\"__esModule\",{value:!0})},r.t=function(n,t){if(1&t&&(n=r(n)),8&t)return n;if(4&t&&\"object\"==typeof n&&n&&n.__esModule)return n;var e=Object.create(null);if(r.r(e),Object.defineProperty(e,\"default\",{enumerable:!0,value:n}),2&t&&\"string\"!=typeof n)for(var o in n)r.d(e,o,function(t){return n[t]}.bind(null,o));return e},r.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return r.d(t,\"a\",t),t},r.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},r.p=\"\",r(r.s=0)}([function(n,t,r){\"use strict\";function e(n){let t,r;const e={light:function(){return!a()},dark:a,lighten:d,darken:l,saturate:p,desaturate:function(n=0){return p(n*=-1)},increaseContrast:function(n=0){return b(n*=-1)},decreaseContrast:b,active:function(){return b(.123)},highlight:function(){return b(.1)},selected:function(){return b(.066)},text:function(){return r=s()?o(\"#333333\"):o(\"#FFFFFF\"),e},shadow:function(){return r=s()?o(\"#000000\"):o(\"#FFFFFF\"),e},hex:function(){const n=r;return r=t,\"#\"+n.map(n=>parseInt(n+\"\",10).toString(16).padStart(2,\"0\")).join(\"\")},rgb:function(){const n=r;return r=t,`rgb(${n.join()})`},rgba:function(n=1){const e=r;return r=t,`rgba(${e.join()}, ${n})`},setHex:c,setRgb:function(n=[0,0,0]){let[o,u,c]=n;return o=f(o,0,255),u=f(u,0,255),c=f(c,0,255),t=[o,u,c],r=[o,u,c],e}};function c(n=\"#000000\"){return t=o(n),r=t,e}function a(){const[n,e,o]=r;return r=t,(299*n+587*e+114*o)/1e3<128}function s(){const[n,t,e]=r;return(299*n+587*t+114*e)/1e3>=128}function l(n=0){return d(n*=-1)}function d(n=0){let[t,o,c]=i(r);return c=f(c+n,0,1),r=u([t,o,c]),e}function p(n=0){let[t,o,c]=i(r);return o=f(o+n,0,1),r=u([t,o,c]),e}function b(n=0){return s()?l(n):d(n)}return c(n),e}function o(n){if(\"string\"!=typeof n)throw new TypeError(\"Expected a string\");3===(n=n.replace(/^#/,\"\")).length&&(n=n[0]+n[0]+n[1]+n[1]+n[2]+n[2]);var t=parseInt(n,16);return[t>>16,t>>8&255,255&t]}function u(n){const[t,r,e]=n;let o,u,c;if(0===r)o=u=c=e;else{const n=function(n,t,r){return r<0&&(r+=1),r>1&&(r-=1),r<1/6?n+6*(t-n)*r:r<.5?t:r<2/3?n+(t-n)*(2/3-r)*6:n},i=e<.5?e*(1+r):e+r-e*r,a=2*e-i;o=f(n(a,i,t+1/3),0,1),u=f(n(a,i,t),0,1),c=f(n(a,i,t-1/3),0,1)}return[Math.round(255*o),Math.round(255*u),Math.round(255*c)]}r.r(t),r.d(t,\"offColor\",(function(){return e})),r.d(t,\"hexRgb\",(function(){return o})),r.d(t,\"hslToRgb\",(function(){return u})),r.d(t,\"color\",(function(){return c})),r.d(t,\"rgbToHsl\",(function(){return i}));const c=e;function i(n){const t=n[0]/255,r=n[1]/255,e=n[2]/255,o=Math.max(t,r,e),u=Math.min(t,r,e);let c=(o+u)/2,i=(o+u)/2;const f=(o+u)/2;if(o===u)c=i=0;else{const n=o-u;switch(i=f>.5?n/(2-o-u):n/(o+u),o){case t:c=(r-e)/n+(r<e?6:0);break;case r:c=(e-t)/n+2;break;case e:c=(t-r)/n+4}c/=6}return[c,i,f]}function f(n,t,r){return n=(n=n<=r?n:r)>=t?n:t}}]);","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/index.min.js');\n} else {\n module.exports = require('./cjs/index.js');\n}\n","/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\nimport * as pkg from 'off-color';\nconst offColor = pkg.offColor ?? pkg.default.offColor;\n\nexport function derive(value, operation, argument, isDark) {\n const argumentAsNumber = parseInt(argument);\n if (isDark) {\n // For dark themes, invert the operation\n if (operation === 'darker') {\n operation = \"lighter\";\n }\n else if (operation === 'lighter') {\n operation = \"darker\";\n }\n }\n switch (operation) {\n case \"darker\": {\n const newColorString = offColor(value).darken(argumentAsNumber / 100).hex();\n return newColorString;\n }\n case \"lighter\": {\n const newColorString = offColor(value).lighten(argumentAsNumber / 100).hex();\n return newColorString;\n }\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n \nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\nimport {derive} from \"./shared/color.mjs\";\n\nexport class DerivedVariables {\n private _baseVariables: Record<string, string>;\n private _variablesToDerive: string[]\n private _isDark: boolean\n private _aliases: Record<string, string> = {};\n private _derivedAliases: string[] = [];\n\n constructor(baseVariables: Record<string, string>, variablesToDerive: string[], isDark: boolean) {\n this._baseVariables = baseVariables;\n this._variablesToDerive = variablesToDerive;\n this._isDark = isDark;\n }\n\n toVariables(): Record<string, string> {\n const resolvedVariables: any = {};\n this._detectAliases();\n for (const variable of this._variablesToDerive) {\n const resolvedValue = this._derive(variable);\n if (resolvedValue) {\n resolvedVariables[variable] = resolvedValue;\n }\n }\n for (const [alias, variable] of Object.entries(this._aliases) as any) {\n resolvedVariables[alias] = this._baseVariables[variable] ?? resolvedVariables[variable];\n }\n for (const variable of this._derivedAliases) {\n const resolvedValue = this._deriveAlias(variable, resolvedVariables);\n if (resolvedValue) {\n resolvedVariables[variable] = resolvedValue;\n }\n }\n return resolvedVariables;\n }\n\n private _detectAliases(): void {\n const newVariablesToDerive: string[] = [];\n for (const variable of this._variablesToDerive) {\n const [alias, value] = variable.split(\"=\");\n if (value) {\n this._aliases[alias] = value;\n }\n else {\n newVariablesToDerive.push(variable);\n }\n }\n this._variablesToDerive = newVariablesToDerive;\n }\n\n private _derive(variable: string): string | undefined {\n const RE_VARIABLE_VALUE = /(.+)--(.+)-(.+)/;\n const matches = variable.match(RE_VARIABLE_VALUE);\n if (matches) {\n const [, baseVariable, operation, argument] = matches;\n const value = this._baseVariables[baseVariable];\n if (!value ) {\n if (this._aliases[baseVariable]) {\n this._derivedAliases.push(variable);\n return;\n }\n else {\n throw new Error(`Cannot find value for base variable \"${baseVariable}\"!`);\n }\n }\n const resolvedValue = derive(value, operation, argument, this._isDark);\n return resolvedValue;\n }\n }\n\n private _deriveAlias(variable: string, resolvedVariables: Record<string, string>): string | undefined {\n const RE_VARIABLE_VALUE = /(.+)--(.+)-(.+)/;\n const matches = variable.match(RE_VARIABLE_VALUE);\n if (matches) {\n const [, baseVariable, operation, argument] = matches;\n const value = resolvedVariables[baseVariable];\n if (!value ) {\n throw new Error(`Cannot find value for alias \"${baseVariable}\" when trying to derive ${variable}!`);\n }\n const resolvedValue = derive(value, operation, argument, this._isDark);\n return resolvedValue;\n }\n }\n}\n\nimport * as pkg from \"off-color\";\n// @ts-ignore \nconst offColor = pkg.offColor ?? pkg.default.offColor;\n\nexport function tests() {\n return {\n \"Simple variable derivation\": assert => {\n const deriver = new DerivedVariables({ \"background-color\": \"#ff00ff\" }, [\"background-color--darker-5\"], false);\n const result = deriver.toVariables();\n const resultColor = offColor(\"#ff00ff\").darken(5/100).hex();\n assert.deepEqual(result, {\"background-color--darker-5\": resultColor});\n },\n\n \"For dark themes, lighten and darken are inverted\": assert => {\n const deriver = new DerivedVariables({ \"background-color\": \"#ff00ff\" }, [\"background-color--darker-5\"], true);\n const result = deriver.toVariables();\n const resultColor = offColor(\"#ff00ff\").lighten(5/100).hex();\n assert.deepEqual(result, {\"background-color--darker-5\": resultColor});\n },\n\n \"Aliases can be derived\": assert => {\n const deriver = new DerivedVariables({ \"background-color\": \"#ff00ff\" }, [\"my-awesome-alias=background-color\",\"my-awesome-alias--darker-5\"], false);\n const result = deriver.toVariables();\n const resultColor = offColor(\"#ff00ff\").darken(5/100).hex();\n assert.deepEqual(result, {\n \"my-awesome-alias\": \"#ff00ff\",\n \"my-awesome-alias--darker-5\": resultColor,\n });\n },\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\nimport type {ThemeInformation} from \"./types\";\nimport type {Platform} from \"../../Platform.js\";\nimport type {ThemeManifest} from \"../../../types/theme\";\nimport {ColorSchemePreference} from \"./types\";\nimport {IconColorizer} from \"../IconColorizer\";\nimport {DerivedVariables} from \"../DerivedVariables\";\nimport {ILogItem} from \"../../../../logging/types\";\n\nexport class RuntimeThemeParser {\n private _themeMapping: Record<string, ThemeInformation> = {};\n private _preferredColorScheme?: ColorSchemePreference;\n private _platform: Platform;\n\n constructor(platform: Platform, preferredColorScheme?: ColorSchemePreference) {\n this._preferredColorScheme = preferredColorScheme;\n this._platform = platform;\n }\n\n async parse(manifest: ThemeManifest, baseManifest: ThemeManifest, baseManifestLocation: string, log: ILogItem): Promise<void> {\n await log.wrap(\"RuntimeThemeParser.parse\", async () => {\n const {cssLocation, derivedVariables, icons} = this._getSourceData(baseManifest, baseManifestLocation, log);\n const themeName = manifest.name;\n if (!themeName) {\n throw new Error(`Theme name not found in manifest!`);\n }\n let defaultDarkVariant: any = {}, defaultLightVariant: any = {};\n for (const [variant, variantDetails] of Object.entries(manifest.values?.variants!) as [string, any][]) {\n try {\n const themeId = `${manifest.id}-${variant}`;\n const { name: variantName, default: isDefault, dark, variables } = variantDetails;\n const resolvedVariables = new DerivedVariables(variables, derivedVariables, dark).toVariables();\n Object.assign(variables, resolvedVariables);\n const iconVariables = await new IconColorizer(this._platform, icons, variables, baseManifestLocation).toVariables();\n Object.assign(variables, resolvedVariables, iconVariables);\n const themeDisplayName = `${themeName} ${variantName}`;\n if (isDefault) {\n const defaultVariant = dark ? defaultDarkVariant : defaultLightVariant;\n Object.assign(defaultVariant, { variantName, id: themeId, cssLocation, variables });\n continue;\n }\n this._themeMapping[themeDisplayName] = { cssLocation, id: themeId, variables: variables, };\n }\n catch (e) {\n console.error(e);\n continue;\n }\n }\n if (defaultDarkVariant.id && defaultLightVariant.id) {\n const defaultVariant = this._preferredColorScheme === ColorSchemePreference.Dark ? defaultDarkVariant : defaultLightVariant;\n this._themeMapping[themeName] = { dark: defaultDarkVariant, light: defaultLightVariant, default: defaultVariant };\n }\n else {\n const variant = defaultDarkVariant.id ? defaultDarkVariant : defaultLightVariant;\n this._themeMapping[`${themeName} ${variant.variantName}`] = { id: variant.id, cssLocation: variant.cssLocation };\n }\n });\n }\n\n private _getSourceData(manifest: ThemeManifest, location: string, log: ILogItem)\n : { cssLocation: string, derivedVariables: string[], icons: Record<string, string>} {\n return log.wrap(\"getSourceData\", () => {\n const runtimeCSSLocation = manifest.source?.[\"runtime-asset\"];\n if (!runtimeCSSLocation) {\n throw new Error(`Run-time asset not found in source section for theme at ${location}`);\n }\n const cssLocation = new URL(runtimeCSSLocation, new URL(location, window.location.origin)).href;\n const derivedVariables = manifest.source?.[\"derived-variables\"];\n if (!derivedVariables) {\n throw new Error(`Derived variables not found in source section for theme at ${location}`);\n }\n const icons = manifest.source?.[\"icon\"];\n if (!icons) {\n throw new Error(`Icon mapping not found in source section for theme at ${location}`);\n }\n return { cssLocation, derivedVariables, icons };\n });\n }\n\n get themeMapping(): Record<string, ThemeInformation> {\n return this._themeMapping;\n }\n\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport type {ThemeInformation} from \"./types\";\nimport type {ThemeManifest} from \"../../../types/theme\";\nimport type {ILogItem} from \"../../../../logging/types\";\nimport {ColorSchemePreference} from \"./types\";\n\nexport class BuiltThemeParser {\n private _themeMapping: Record<string, ThemeInformation> = {};\n private _preferredColorScheme?: ColorSchemePreference;\n\n constructor(preferredColorScheme?: ColorSchemePreference) {\n this._preferredColorScheme = preferredColorScheme;\n }\n\n parse(manifest: ThemeManifest, manifestLocation: string, log: ILogItem) {\n log.wrap(\"BuiltThemeParser.parse\", () => {\n /*\n After build has finished, the source section of each theme manifest\n contains `built-assets` which is a mapping from the theme-id to\n cssLocation of theme\n */\n const builtAssets: Record<string, string> = manifest.source?.[\"built-assets\"];\n const themeName = manifest.name;\n if (!themeName) {\n throw new Error(`Theme name not found in manifest at ${manifestLocation}`);\n }\n let defaultDarkVariant: any = {}, defaultLightVariant: any = {};\n for (let [themeId, cssLocation] of Object.entries(builtAssets)) {\n try {\n /**\n * This cssLocation is relative to the location of the manifest file.\n * So we first need to resolve it relative to the root of this hydrogen instance.\n */\n cssLocation = new URL(cssLocation, new URL(manifestLocation, window.location.origin)).href;\n }\n catch {\n continue;\n }\n const variant = themeId.match(/.+-(.+)/)?.[1];\n const variantDetails = manifest.values?.variants[variant!];\n if (!variantDetails) {\n throw new Error(`Variant ${variant} is missing in manifest at ${manifestLocation}`);\n }\n const { name: variantName, default: isDefault, dark } = variantDetails;\n const themeDisplayName = `${themeName} ${variantName}`;\n if (isDefault) {\n /**\n * This is a default variant!\n * We'll add these to the themeMapping (separately) keyed with just the\n * theme-name (i.e \"Element\" instead of \"Element Dark\").\n * We need to be able to distinguish them from other variants!\n * \n * This allows us to render radio-buttons with \"dark\" and\n * \"light\" options.\n */\n const defaultVariant = dark ? defaultDarkVariant : defaultLightVariant;\n defaultVariant.variantName = variantName;\n defaultVariant.id = themeId\n defaultVariant.cssLocation = cssLocation;\n continue;\n }\n // Non-default variants are keyed in themeMapping with \"theme_name variant_name\"\n // eg: \"Element Dark\"\n this._themeMapping[themeDisplayName] = {\n cssLocation,\n id: themeId\n };\n }\n if (defaultDarkVariant.id && defaultLightVariant.id) {\n /**\n * As mentioned above, if there's both a default dark and a default light variant,\n * add them to themeMapping separately.\n */\n const defaultVariant = this._preferredColorScheme === ColorSchemePreference.Dark ? defaultDarkVariant : defaultLightVariant;\n this._themeMapping[themeName] = { dark: defaultDarkVariant, light: defaultLightVariant, default: defaultVariant };\n }\n else {\n /**\n * If only one default variant is found (i.e only dark default or light default but not both),\n * treat it like any other variant.\n */\n const variant = defaultDarkVariant.id ? defaultDarkVariant : defaultLightVariant;\n this._themeMapping[`${themeName} ${variant.variantName}`] = { id: variant.id, cssLocation: variant.cssLocation };\n }\n });\n }\n\n get themeMapping(): Record<string, ThemeInformation> {\n return this._themeMapping;\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport type {ILogItem} from \"../../../logging/types\";\nimport type {Platform} from \"../Platform.js\";\nimport {RuntimeThemeParser} from \"./parsers/RuntimeThemeParser\";\nimport type {Variant, ThemeInformation} from \"./parsers/types\";\nimport {ColorSchemePreference} from \"./parsers/types\";\nimport {BuiltThemeParser} from \"./parsers/BuiltThemeParser\";\n\nexport class ThemeLoader {\n private _platform: Platform;\n private _themeMapping: Record<string, ThemeInformation>;\n private _injectedVariables?: Record<string, string>;\n\n constructor(platform: Platform) {\n this._platform = platform;\n }\n\n async init(manifestLocations: string[], log?: ILogItem): Promise<void> {\n await this._platform.logger.wrapOrRun(log, \"ThemeLoader.init\", async (log) => {\n const results = await Promise.all(\n manifestLocations.map(location => this._platform.request(location, { method: \"GET\", format: \"json\", cache: true, }).response())\n );\n const runtimeThemeParser = new RuntimeThemeParser(this._platform, this.preferredColorScheme);\n const builtThemeParser = new BuiltThemeParser(this.preferredColorScheme);\n const runtimeThemePromises: Promise<void>[] = [];\n for (let i = 0; i < results.length; ++i) {\n const { body } = results[i];\n try {\n if (body.extends) {\n const indexOfBaseManifest = results.findIndex(manifest => manifest.body.id === body.extends);\n if (indexOfBaseManifest === -1) {\n throw new Error(`Base manifest for derived theme at ${manifestLocations[i]} not found!`);\n }\n const {body: baseManifest} = results[indexOfBaseManifest];\n const baseManifestLocation = manifestLocations[indexOfBaseManifest];\n const promise = runtimeThemeParser.parse(body, baseManifest, baseManifestLocation, log);\n runtimeThemePromises.push(promise);\n }\n else {\n builtThemeParser.parse(body, manifestLocations[i], log);\n }\n }\n catch(e) {\n console.error(e);\n }\n }\n await Promise.all(runtimeThemePromises);\n this._themeMapping = { ...builtThemeParser.themeMapping, ...runtimeThemeParser.themeMapping };\n Object.assign(this._themeMapping, builtThemeParser.themeMapping, runtimeThemeParser.themeMapping);\n this._addDefaultThemeToMapping(log);\n log.log({ l: \"Preferred colorscheme\", scheme: this.preferredColorScheme === ColorSchemePreference.Dark ? \"dark\" : \"light\" });\n log.log({ l: \"Result\", themeMapping: this._themeMapping });\n });\n }\n\n setTheme(themeName: string, themeVariant?: \"light\" | \"dark\" | \"default\", log?: ILogItem) {\n this._platform.logger.wrapOrRun(log, { l: \"change theme\", name: themeName, variant: themeVariant }, () => {\n let cssLocation: string, variables: Record<string, string>;\n let themeDetails = this._themeMapping[themeName];\n if (\"id\" in themeDetails) {\n cssLocation = themeDetails.cssLocation;\n variables = themeDetails.variables;\n }\n else {\n if (!themeVariant) {\n throw new Error(\"themeVariant is undefined!\");\n }\n cssLocation = themeDetails[themeVariant].cssLocation;\n variables = themeDetails[themeVariant].variables;\n }\n this._platform.replaceStylesheet(cssLocation);\n if (variables) {\n log?.log({l: \"Derived Theme\", variables});\n this._injectCSSVariables(variables);\n }\n else {\n this._removePreviousCSSVariables();\n }\n this._platform.settingsStorage.setString(\"theme-name\", themeName);\n if (themeVariant) {\n this._platform.settingsStorage.setString(\"theme-variant\", themeVariant);\n }\n else {\n this._platform.settingsStorage.remove(\"theme-variant\");\n }\n });\n }\n\n private _injectCSSVariables(variables: Record<string, string>): void {\n const root = document.documentElement;\n for (const [variable, value] of Object.entries(variables)) {\n root.style.setProperty(`--${variable}`, value);\n }\n this._injectedVariables = variables;\n }\n\n private _removePreviousCSSVariables(): void {\n if (!this._injectedVariables) {\n return;\n }\n const root = document.documentElement;\n for (const variable of Object.keys(this._injectedVariables)) {\n root.style.removeProperty(`--${variable}`);\n }\n this._injectedVariables = undefined;\n }\n\n /** Maps theme display name to theme information */\n get themeMapping(): Record<string, ThemeInformation> {\n return this._themeMapping;\n }\n\n async getActiveTheme(): Promise<{themeName: string, themeVariant?: string}> {\n let themeName = await this._platform.settingsStorage.getString(\"theme-name\");\n let themeVariant = await this._platform.settingsStorage.getString(\"theme-variant\");\n if (!themeName || !this._themeMapping[themeName]) {\n themeName = \"Default\" in this._themeMapping ? \"Default\" : Object.keys(this._themeMapping)[0];\n if (!this._themeMapping[themeName][themeVariant]) {\n themeVariant = \"default\" in this._themeMapping[themeName] ? \"default\" : undefined;\n }\n }\n return { themeName, themeVariant };\n }\n\n getDefaultTheme(): string | undefined {\n switch (this.preferredColorScheme) {\n case ColorSchemePreference.Dark:\n return this._platform.config[\"defaultTheme\"]?.dark;\n case ColorSchemePreference.Light:\n return this._platform.config[\"defaultTheme\"]?.light;\n }\n }\n\n private _findThemeDetailsFromId(themeId: string): {themeName: string, themeData: Partial<Variant>} | undefined {\n for (const [themeName, themeData] of Object.entries(this._themeMapping)) {\n if (\"id\" in themeData && themeData.id === themeId) {\n return { themeName, themeData };\n }\n else if (\"light\" in themeData && themeData.light?.id === themeId) {\n return { themeName, themeData: themeData.light };\n }\n else if (\"dark\" in themeData && themeData.dark?.id === themeId) {\n return { themeName, themeData: themeData.dark };\n }\n }\n }\n\n private _addDefaultThemeToMapping(log: ILogItem) {\n log.wrap(\"addDefaultThemeToMapping\", l => { \n const defaultThemeId = this.getDefaultTheme();\n if (defaultThemeId) {\n const themeDetails = this._findThemeDetailsFromId(defaultThemeId);\n if (themeDetails) {\n this._themeMapping[\"Default\"] = { id: \"default\", cssLocation: themeDetails.themeData.cssLocation! };\n const variables = themeDetails.themeData.variables;\n if (variables) {\n this._themeMapping[\"Default\"].variables = variables;\n }\n }\n }\n l.log({ l: \"Default Theme\", theme: defaultThemeId});\n });\n }\n\n get preferredColorScheme(): ColorSchemePreference | undefined {\n if (window.matchMedia(\"(prefers-color-scheme: dark)\").matches) {\n return ColorSchemePreference.Dark;\n }\n else if (window.matchMedia(\"(prefers-color-scheme: light)\").matches) {\n return ColorSchemePreference.Light;\n }\n }\n}\n","/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {createFetchRequest} from \"./dom/request/fetch.js\";\nimport {xhrRequest} from \"./dom/request/xhr.js\";\nimport {StorageFactory} from \"../../matrix/storage/idb/StorageFactory\";\nimport {SessionInfoStorage} from \"../../matrix/sessioninfo/localstorage/SessionInfoStorage\";\nimport {SettingsStorage} from \"./dom/SettingsStorage.js\";\nimport {Encoding} from \"./utils/Encoding.js\";\nimport {OlmWorker} from \"../../matrix/e2ee/OlmWorker.js\";\nimport {IDBLogger} from \"../../logging/IDBLogger\";\nimport {ConsoleLogger} from \"../../logging/ConsoleLogger\";\nimport {RootView} from \"./ui/RootView.js\";\nimport {Clock} from \"./dom/Clock.js\";\nimport {ServiceWorkerHandler} from \"./dom/ServiceWorkerHandler.js\";\nimport {NotificationService} from \"./dom/NotificationService.js\";\nimport {History} from \"./dom/History.js\";\nimport {OnlineStatus} from \"./dom/OnlineStatus.js\";\nimport {Crypto} from \"./dom/Crypto.js\";\nimport {estimateStorageUsage} from \"./dom/StorageEstimate.js\";\nimport {WorkerPool} from \"./dom/WorkerPool.js\";\nimport {BlobHandle} from \"./dom/BlobHandle.js\";\nimport {hasReadPixelPermission, ImageHandle, VideoHandle} from \"./dom/ImageHandle.js\";\nimport {downloadInIframe} from \"./dom/download.js\";\nimport {Disposables} from \"../../utils/Disposables\";\nimport {parseHTML} from \"./parsehtml.js\";\nimport {handleAvatarError} from \"./ui/avatar\";\nimport {ThemeLoader} from \"./theming/ThemeLoader\";\n\nfunction addScript(src) {\n return new Promise(function (resolve, reject) {\n var s = document.createElement(\"script\");\n s.setAttribute(\"src\", src );\n s.onload=resolve;\n s.onerror=reject;\n document.body.appendChild(s);\n });\n}\n\nasync function loadOlm(olmPaths) {\n // make crypto.getRandomValues available without\n // a prefix on IE11, needed by olm to work\n if (window.msCrypto && !window.crypto) {\n window.crypto = window.msCrypto;\n }\n if (olmPaths) {\n if (window.WebAssembly) {\n await addScript(olmPaths.wasmBundle);\n await window.Olm.init({locateFile: () => olmPaths.wasm});\n } else {\n await addScript(olmPaths.legacyBundle);\n await window.Olm.init();\n }\n return window.Olm;\n }\n return null;\n}\n// turn asset path to absolute path if it isn't already\n// so it can be loaded independent of base\nfunction assetAbsPath(assetPath) {\n if (!assetPath.startsWith(\"/\")) {\n return new URL(assetPath, document.location.href).pathname;\n }\n return assetPath;\n}\n\nasync function loadOlmWorker(assetPaths) {\n const workerPool = new WorkerPool(assetPaths.worker, 4);\n await workerPool.init();\n await workerPool.sendAll({\n type: \"load_olm\",\n path: assetAbsPath(assetPaths.olm.legacyBundle)\n });\n const olmWorker = new OlmWorker(workerPool);\n return olmWorker;\n}\n\n// needed for mobile Safari which shifts the layout viewport up without resizing it\n// when the keyboard shows (see https://bugs.webkit.org/show_bug.cgi?id=141832)\nfunction adaptUIOnVisualViewportResize(container) {\n if (!window.visualViewport) {\n return;\n }\n const handler = () => {\n const sessionView = container.querySelector('.SessionView');\n if (!sessionView) {\n return;\n }\n\n const scrollable = container.querySelector('.bottom-aligned-scroll');\n let scrollTopBefore, heightBefore, heightAfter;\n\n if (scrollable) {\n scrollTopBefore = scrollable.scrollTop;\n heightBefore = scrollable.offsetHeight;\n }\n\n // Ideally we'd use window.visualViewport.offsetTop but that seems to occasionally lag\n // behind (last tested on iOS 14.4 simulator) so we have to compute the offset manually\n const offsetTop = sessionView.offsetTop + sessionView.offsetHeight - window.visualViewport.height;\n\n container.style.setProperty('--ios-viewport-height', window.visualViewport.height.toString() + 'px');\n container.style.setProperty('--ios-viewport-top', offsetTop.toString() + 'px');\n\n if (scrollable) {\n heightAfter = scrollable.offsetHeight;\n scrollable.scrollTop = scrollTopBefore + heightBefore - heightAfter;\n }\n };\n window.visualViewport.addEventListener('resize', handler);\n return () => {\n window.visualViewport.removeEventListener('resize', handler);\n };\n}\n\nexport class Platform {\n constructor({ container, assetPaths, config, configURL, options = null, cryptoExtras = null }) {\n this._container = container;\n this._assetPaths = assetPaths;\n this._config = config;\n this._configURL = configURL;\n this.settingsStorage = new SettingsStorage(\"hydrogen_setting_v1_\");\n this.clock = new Clock();\n this.encoding = new Encoding();\n this.random = Math.random;\n this._createLogger(options?.development);\n this.history = new History();\n this.onlineStatus = new OnlineStatus();\n this._serviceWorkerHandler = null;\n if (assetPaths.serviceWorker && \"serviceWorker\" in navigator) {\n this._serviceWorkerHandler = new ServiceWorkerHandler();\n this._serviceWorkerHandler.registerAndStart(assetPaths.serviceWorker);\n }\n this.notificationService = undefined;\n // Only try to use crypto when olm is provided\n if(this._assetPaths.olm) {\n this.crypto = new Crypto(cryptoExtras);\n }\n this.storageFactory = new StorageFactory(this._serviceWorkerHandler);\n this.sessionInfoStorage = new SessionInfoStorage(\"hydrogen_sessions_v1\");\n this.estimateStorageUsage = estimateStorageUsage;\n if (typeof fetch === \"function\") {\n this.request = createFetchRequest(this.clock.createTimeout, this._serviceWorkerHandler);\n } else {\n this.request = xhrRequest;\n }\n const isIE11 = !!window.MSInputMethodContext && !!document.documentMode;\n this.isIE11 = isIE11;\n // From https://stackoverflow.com/questions/9038625/detect-if-device-is-ios/9039885\n const isIOS = /iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1) && !window.MSStream;\n this.isIOS = isIOS;\n this._disposables = new Disposables();\n this._olmPromise = undefined;\n this._workerPromise = undefined;\n this._themeLoader = import.meta.env.DEV? null: new ThemeLoader(this);\n }\n\n async init() {\n try {\n await this.logger.run(\"Platform init\", async (log) => {\n if (!this._config) {\n if (!this._configURL) {\n throw new Error(\"Neither config nor configURL was provided!\");\n }\n const {status, body}= await this.request(this._configURL, {method: \"GET\", format: \"json\", cache: true}).response();\n if (status === 404) {\n throw new Error(`Could not find ${this._configURL}. Did you copy over config.sample.json?`);\n } else if (status >= 400) {\n throw new Error(`Got status ${status} while trying to fetch ${this._configURL}`);\n }\n this._config = body;\n }\n this.notificationService = new NotificationService(\n this._serviceWorkerHandler,\n this._config.push\n );\n if (this._themeLoader) {\n const manifests = this.config[\"themeManifests\"];\n await this._themeLoader?.init(manifests, log);\n const { themeName, themeVariant } = await this._themeLoader.getActiveTheme();\n log.log({ l: \"Active theme\", name: themeName, variant: themeVariant });\n this._themeLoader.setTheme(themeName, themeVariant, log);\n }\n });\n } catch (err) {\n this._container.innerText = err.message;\n throw err;\n }\n }\n\n _createLogger(isDevelopment) {\n // Make sure that loginToken does not end up in the logs\n const transformer = (item) => {\n if (item.e?.stack) {\n item.e.stack = item.e.stack.replace(/\\/\\?loginToken=(.+)/, \"?loginToken=<snip>\");\n }\n return item;\n };\n if (isDevelopment) {\n this.logger = new ConsoleLogger({platform: this});\n } else {\n this.logger = new IDBLogger({name: \"hydrogen_logs\", platform: this, serializedTransformer: transformer});\n }\n }\n\n get updateService() {\n return this._serviceWorkerHandler;\n }\n\n loadOlm() {\n if (!this._olmPromise) {\n this._olmPromise = loadOlm(this._assetPaths.olm);\n }\n return this._olmPromise;\n }\n\n get config() {\n return this._config;\n }\n\n async loadOlmWorker() {\n if (!window.WebAssembly) {\n if (!this._workerPromise) {\n this._workerPromise = loadOlmWorker(this._assetPaths);\n }\n return this._workerPromise;\n }\n }\n\n createAndMountRootView(vm) {\n if (this.isIE11) {\n this._container.className += \" legacy\";\n }\n if (this.isIOS) {\n this._container.className += \" ios\";\n const disposable = adaptUIOnVisualViewportResize(this._container);\n if (disposable) {\n this._disposables.track(disposable);\n }\n }\n this._container.addEventListener(\"error\", handleAvatarError, true);\n this._disposables.track(() => this._container.removeEventListener(\"error\", handleAvatarError, true));\n window.__hydrogenViewModel = vm;\n const view = new RootView(vm);\n this._container.appendChild(view.mount());\n }\n\n setNavigation(navigation) {\n this._serviceWorkerHandler?.setNavigation(navigation);\n }\n\n createBlob(buffer, mimetype) {\n return BlobHandle.fromBuffer(buffer, mimetype);\n }\n\n saveFileAs(blobHandle, filename) {\n if (navigator.msSaveBlob) {\n navigator.msSaveBlob(blobHandle.nativeBlob, filename);\n } else {\n downloadInIframe(this._container, this._assetPaths.downloadSandbox, blobHandle, filename, this.isIOS);\n }\n }\n\n openFile(mimeType = null) {\n const input = document.createElement(\"input\");\n input.setAttribute(\"type\", \"file\");\n input.className = \"hidden\";\n if (mimeType) {\n input.setAttribute(\"accept\", mimeType);\n }\n const promise = new Promise(resolve => {\n const checkFile = () => {\n input.removeEventListener(\"change\", checkFile, true);\n const file = input.files[0];\n this._container.removeChild(input);\n if (file) {\n resolve({name: file.name, blob: BlobHandle.fromBlob(file)});\n } else {\n resolve();\n }\n }\n input.addEventListener(\"change\", checkFile, true);\n });\n // IE11 needs the input to be attached to the document\n this._container.appendChild(input);\n input.click();\n return promise;\n }\n\n openUrl(url) {\n location.href = url;\n }\n\n parseHTML(html) {\n return parseHTML(html);\n }\n\n async loadImage(blob) {\n return ImageHandle.fromBlob(blob);\n }\n\n async loadVideo(blob) {\n return VideoHandle.fromBlob(blob);\n }\n\n hasReadPixelPermission() {\n return hasReadPixelPermission();\n }\n\n get devicePixelRatio() {\n return window.devicePixelRatio || 1;\n }\n\n get version() {\n return DEFINE_VERSION;\n }\n\n get themeLoader() {\n return this._themeLoader;\n }\n\n replaceStylesheet(newPath) {\n const head = document.querySelector(\"head\");\n // remove default theme \n document.querySelectorAll(\".theme\").forEach(e => e.remove());\n // add new theme\n const styleTag = document.createElement(\"link\");\n styleTag.href = newPath;\n styleTag.rel = \"stylesheet\";\n styleTag.type = \"text/css\";\n styleTag.className = \"theme\";\n head.appendChild(styleTag);\n }\n\n get description() {\n return navigator.userAgent ?? \"<unknown>\";\n }\n\n dispose() {\n this._disposables.dispose();\n }\n}\n\nimport {LogItem} from \"../../logging/LogItem\";\nexport function tests() {\n return {\n \"loginToken should not be in logs\": (assert) => {\n const transformer = (item) => {\n if (item.e?.stack) {\n item.e.stack = item.e.stack.replace(/(?<=\\/\\?loginToken=).+/, \"<snip>\");\n }\n return item;\n };\n const logger = {\n _queuedItems: [],\n _serializedTransformer: transformer,\n _now: () => {}\n };\n logger.persist = IDBLogger.prototype._persistItem.bind(logger);\n const logItem = new LogItem(\"test\", 1, logger);\n logItem.error = new Error();\n logItem.error.stack = \"main http://localhost:3000/src/main.js:55\\n<anonymous> http://localhost:3000/?loginToken=secret:26\"\n logger.persist(logItem, null, false);\n const item = logger._queuedItems.pop();\n console.log(item);\n assert.strictEqual(item.json.search(\"secret\"), -1);\n }\n };\n}\n","export default \"__VITE_ASSET__3ee19dbe__\"","export default \"__VITE_ASSET__48a866e9__\"","export default \"__VITE_ASSET__bdb9a925__\"","export default \"__VITE_ASSET__b3e0f9b4__\"","export default \"__VITE_ASSET__92f1ccd0__\"","export default \"__VITE_ASSET__9dc48f49__\"","// @ts-ignore\nimport _downloadSandboxPath from \"../../assets/download-sandbox.html?url\";\n// @ts-ignore\nimport _workerPath from \"../../worker/main.js?url\";\n// @ts-ignore\nimport olmWasmPath from \"@matrix-org/olm/olm.wasm?url\";\n// @ts-ignore\nimport olmJsPath from \"@matrix-org/olm/olm.js?url\";\n// @ts-ignore\nimport olmLegacyJsPath from \"@matrix-org/olm/olm_legacy.js?url\";\n\nexport default {\n downloadSandbox: _downloadSandboxPath,\n worker: _workerPath,\n olm: {\n wasm: olmWasmPath,\n legacyBundle: olmLegacyJsPath,\n wasmBundle: olmJsPath,\n }\n};\n","\n import {main} from \"./main\";\n import {Platform} from \"./Platform\";\n import configURL from \"./assets/config.json?url\";\n import assetPaths from \"./sdk/paths/vite\";\n if (import.meta.env.PROD) {\n assetPaths.serviceWorker = \"sw.js\";\n }\n const platform = new Platform({\n container: document.body,\n assetPaths,\n configURL, \n options: {development: import.meta.env.DEV}\n });\n main(platform);\n "],"names":["Request","stringify","anotherjson","EVENT_TYPE","Range","encodeKey","decodeKey","MEMBER_EVENT_TYPE","NullLoggerInstance","EVENT_ENCRYPTED_TYPE","POWERLEVELS_EVENT_TYPE","Session","Decryption","DecryptionChanges","Encryption","DecryptionPreparation","Curve25519.Algorithm","Curve25519.BackupEncryption","OlmDecryption","OlmEncryption","MegOlmKeyLoader","MegOlmEncryption","MegOlmDecryption","ssssKeyFromCredential","ssssWriteKey","ssssRemoveKey","E2EEAccount","createSSSSKeyFromDehydratedDeviceKey","ssssReadKey","tileClassForEntry","defaultTileClassForEntry","RequestResult","base64","require$$0","text","DOMPurify","offColorModule","pkg.offColor","pkg.default"],"mappings":"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"}