matrix-js-sdk 41.5.0 → 41.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/lib/ReEmitter.js +1 -1
- package/lib/ReEmitter.js.map +1 -1
- package/lib/ToDeviceMessageQueue.js +2 -2
- package/lib/ToDeviceMessageQueue.js.map +1 -1
- package/lib/autodiscovery.js +0 -1
- package/lib/autodiscovery.js.map +1 -1
- package/lib/client.js +112 -165
- package/lib/client.js.map +1 -1
- package/lib/common-crypto/CryptoBackend.js +0 -2
- package/lib/common-crypto/CryptoBackend.js.map +1 -1
- package/lib/content-helpers.js +3 -5
- package/lib/content-helpers.js.map +1 -1
- package/lib/content-repo.js +7 -1
- package/lib/content-repo.js.map +1 -1
- package/lib/crypto/store/indexeddb-crypto-store-backend.js +9 -11
- package/lib/crypto/store/indexeddb-crypto-store-backend.js.map +1 -1
- package/lib/crypto/store/indexeddb-crypto-store.js +0 -2
- package/lib/crypto/store/indexeddb-crypto-store.js.map +1 -1
- package/lib/crypto/store/localStorage-crypto-store.js +11 -9
- package/lib/crypto/store/localStorage-crypto-store.js.map +1 -1
- package/lib/crypto/store/memory-crypto-store.js +11 -9
- package/lib/crypto/store/memory-crypto-store.js.map +1 -1
- package/lib/crypto-api/index.js +58 -58
- package/lib/crypto-api/index.js.map +1 -1
- package/lib/embedded.js +29 -45
- package/lib/embedded.js.map +1 -1
- package/lib/extensible_events_v1/MessageEvent.js +15 -15
- package/lib/extensible_events_v1/MessageEvent.js.map +1 -1
- package/lib/extensible_events_v1/PollEndEvent.js +8 -8
- package/lib/extensible_events_v1/PollEndEvent.js.map +1 -1
- package/lib/extensible_events_v1/PollResponseEvent.js +0 -1
- package/lib/extensible_events_v1/PollResponseEvent.js.map +1 -1
- package/lib/extensible_events_v1/PollStartEvent.js +28 -28
- package/lib/extensible_events_v1/PollStartEvent.js.map +1 -1
- package/lib/feature.js +5 -1
- package/lib/feature.js.map +1 -1
- package/lib/filter-component.js +4 -1
- package/lib/filter-component.js.map +1 -1
- package/lib/filter.js +1 -3
- package/lib/filter.js.map +1 -1
- package/lib/http-api/errors.js +20 -16
- package/lib/http-api/errors.js.map +1 -1
- package/lib/http-api/fetch.js +4 -6
- package/lib/http-api/fetch.js.map +1 -1
- package/lib/http-api/refresh.js +10 -12
- package/lib/http-api/refresh.js.map +1 -1
- package/lib/interactive-auth.js +1 -11
- package/lib/interactive-auth.js.map +1 -1
- package/lib/logger.js +0 -2
- package/lib/logger.js.map +1 -1
- package/lib/matrixrtc/CallMembership.js +43 -57
- package/lib/matrixrtc/CallMembership.js.map +1 -1
- package/lib/matrixrtc/MatrixRTCSession.js +19 -24
- package/lib/matrixrtc/MatrixRTCSession.js.map +1 -1
- package/lib/matrixrtc/MatrixRTCSessionManager.js +2 -3
- package/lib/matrixrtc/MatrixRTCSessionManager.js.map +1 -1
- package/lib/matrixrtc/MembershipManager.js +23 -30
- package/lib/matrixrtc/MembershipManager.js.map +1 -1
- package/lib/matrixrtc/MembershipManagerActionScheduler.js +1 -2
- package/lib/matrixrtc/MembershipManagerActionScheduler.js.map +1 -1
- package/lib/matrixrtc/RTCEncryptionManager.js +8 -11
- package/lib/matrixrtc/RTCEncryptionManager.js.map +1 -1
- package/lib/matrixrtc/ToDeviceKeyTransport.js +4 -4
- package/lib/matrixrtc/ToDeviceKeyTransport.js.map +1 -1
- package/lib/matrixrtc/utils.js +7 -1
- package/lib/matrixrtc/utils.js.map +1 -1
- package/lib/models/MSC3089TreeSpace.js +5 -7
- package/lib/models/MSC3089TreeSpace.js.map +1 -1
- package/lib/models/beacon.js +5 -11
- package/lib/models/beacon.js.map +1 -1
- package/lib/models/device.js +0 -7
- package/lib/models/device.js.map +1 -1
- package/lib/models/event-context.js +1 -2
- package/lib/models/event-context.js.map +1 -1
- package/lib/models/event-timeline-set.js +16 -26
- package/lib/models/event-timeline-set.js.map +1 -1
- package/lib/models/event-timeline.js +8 -15
- package/lib/models/event-timeline.js.map +1 -1
- package/lib/models/event.js +20 -37
- package/lib/models/event.js.map +1 -1
- package/lib/models/invites-ignorer.js +8 -12
- package/lib/models/invites-ignorer.js.map +1 -1
- package/lib/models/poll.js +7 -13
- package/lib/models/poll.js.map +1 -1
- package/lib/models/read-receipt.js +8 -4
- package/lib/models/read-receipt.js.map +1 -1
- package/lib/models/related-relations.js +0 -2
- package/lib/models/related-relations.js.map +1 -1
- package/lib/models/relations-container.js +4 -6
- package/lib/models/relations-container.js.map +1 -1
- package/lib/models/relations.js +7 -10
- package/lib/models/relations.js.map +1 -1
- package/lib/models/room-member.js +2 -24
- package/lib/models/room-member.js.map +1 -1
- package/lib/models/room-receipts.js +25 -22
- package/lib/models/room-receipts.js.map +1 -1
- package/lib/models/room-state.js +2 -2
- package/lib/models/room-state.js.map +1 -1
- package/lib/models/room-sticky-events.js +20 -5
- package/lib/models/room-sticky-events.js.map +1 -1
- package/lib/models/room.js +82 -105
- package/lib/models/room.js.map +1 -1
- package/lib/models/thread.js +12 -57
- package/lib/models/thread.js.map +1 -1
- package/lib/models/user.js +1 -20
- package/lib/models/user.js.map +1 -1
- package/lib/oidc/authorize.js +23 -32
- package/lib/oidc/authorize.js.map +1 -1
- package/lib/oidc/tokenRefresher.js +8 -11
- package/lib/oidc/tokenRefresher.js.map +1 -1
- package/lib/pushprocessor.js +8 -5
- package/lib/pushprocessor.js.map +1 -1
- package/lib/receipt-accumulator.js +12 -3
- package/lib/receipt-accumulator.js.map +1 -1
- package/lib/rendezvous/MSC4108SignInWithQR.d.ts.map +1 -1
- package/lib/rendezvous/MSC4108SignInWithQR.js +10 -23
- package/lib/rendezvous/MSC4108SignInWithQR.js.map +1 -1
- package/lib/rendezvous/channels/MSC4108SecureChannel.js +5 -10
- package/lib/rendezvous/channels/MSC4108SecureChannel.js.map +1 -1
- package/lib/rendezvous/transports/MSC4108RendezvousSession.js +5 -15
- package/lib/rendezvous/transports/MSC4108RendezvousSession.js.map +1 -1
- package/lib/room-hierarchy.js +7 -12
- package/lib/room-hierarchy.js.map +1 -1
- package/lib/rust-crypto/DehydratedDeviceManager.js +2 -3
- package/lib/rust-crypto/DehydratedDeviceManager.js.map +1 -1
- package/lib/rust-crypto/KeyClaimManager.js +1 -2
- package/lib/rust-crypto/KeyClaimManager.js.map +1 -1
- package/lib/rust-crypto/OutgoingRequestProcessor.js +11 -4
- package/lib/rust-crypto/OutgoingRequestProcessor.js.map +1 -1
- package/lib/rust-crypto/OutgoingRequestsManager.js +12 -12
- package/lib/rust-crypto/OutgoingRequestsManager.js.map +1 -1
- package/lib/rust-crypto/PerSessionKeyBackupDownloader.js +3 -5
- package/lib/rust-crypto/PerSessionKeyBackupDownloader.js.map +1 -1
- package/lib/rust-crypto/RoomEncryptor.js +6 -6
- package/lib/rust-crypto/RoomEncryptor.js.map +1 -1
- package/lib/rust-crypto/backup.js +22 -16
- package/lib/rust-crypto/backup.js.map +1 -1
- package/lib/rust-crypto/device-converter.js +13 -4
- package/lib/rust-crypto/device-converter.js.map +1 -1
- package/lib/rust-crypto/index.js +1 -3
- package/lib/rust-crypto/index.js.map +1 -1
- package/lib/rust-crypto/libolm_migration.js +13 -15
- package/lib/rust-crypto/libolm_migration.js.map +1 -1
- package/lib/rust-crypto/rust-crypto.js +52 -59
- package/lib/rust-crypto/rust-crypto.js.map +1 -1
- package/lib/rust-crypto/verification.js +10 -10
- package/lib/rust-crypto/verification.js.map +1 -1
- package/lib/scheduler.js +2 -2
- package/lib/scheduler.js.map +1 -1
- package/lib/secret-storage.js +16 -10
- package/lib/secret-storage.js.map +1 -1
- package/lib/serverCapabilities.js +2 -5
- package/lib/serverCapabilities.js.map +1 -1
- package/lib/sliding-sync-sdk.js +4 -9
- package/lib/sliding-sync-sdk.js.map +1 -1
- package/lib/sliding-sync.js +4 -9
- package/lib/sliding-sync.js.map +1 -1
- package/lib/store/indexeddb-local-backend.js +13 -8
- package/lib/store/indexeddb-local-backend.js.map +1 -1
- package/lib/store/indexeddb-remote-backend.js +6 -7
- package/lib/store/indexeddb-remote-backend.js.map +1 -1
- package/lib/store/indexeddb-store-worker.js +1 -2
- package/lib/store/indexeddb-store-worker.js.map +1 -1
- package/lib/store/indexeddb.js +4 -2
- package/lib/store/indexeddb.js.map +1 -1
- package/lib/store/memory.js +0 -4
- package/lib/store/memory.js.map +1 -1
- package/lib/sync-accumulator.js +2 -4
- package/lib/sync-accumulator.js.map +1 -1
- package/lib/sync.js +36 -43
- package/lib/sync.js.map +1 -1
- package/lib/timeline-window.js +2 -6
- package/lib/timeline-window.js.map +1 -1
- package/lib/utils/decryptAESSecretStorageItem.js +5 -1
- package/lib/utils/decryptAESSecretStorageItem.js.map +1 -1
- package/lib/utils/encryptAESSecretStorageItem.js +5 -1
- package/lib/utils/encryptAESSecretStorageItem.js.map +1 -1
- package/lib/utils.js +35 -20
- package/lib/utils.js.map +1 -1
- package/lib/webrtc/call.js +13 -45
- package/lib/webrtc/call.js.map +1 -1
- package/lib/webrtc/callEventHandler.js +0 -5
- package/lib/webrtc/callEventHandler.js.map +1 -1
- package/lib/webrtc/callFeed.js +0 -15
- package/lib/webrtc/callFeed.js.map +1 -1
- package/lib/webrtc/groupCall.js +82 -89
- package/lib/webrtc/groupCall.js.map +1 -1
- package/lib/webrtc/groupCallEventHandler.js +6 -7
- package/lib/webrtc/groupCallEventHandler.js.map +1 -1
- package/lib/webrtc/mediaHandler.js +8 -15
- package/lib/webrtc/mediaHandler.js.map +1 -1
- package/lib/webrtc/stats/callStatsReportGatherer.js +2 -5
- package/lib/webrtc/stats/callStatsReportGatherer.js.map +1 -1
- package/lib/webrtc/stats/connectionStatsReportBuilder.js +5 -1
- package/lib/webrtc/stats/connectionStatsReportBuilder.js.map +1 -1
- package/lib/webrtc/stats/groupCallStats.js +3 -4
- package/lib/webrtc/stats/groupCallStats.js.map +1 -1
- package/lib/webrtc/stats/media/mediaTrackStats.js +3 -3
- package/lib/webrtc/stats/media/mediaTrackStats.js.map +1 -1
- package/lib/webrtc/stats/media/mediaTrackStatsHandler.js +1 -1
- package/lib/webrtc/stats/media/mediaTrackStatsHandler.js.map +1 -1
- package/package.json +3 -3
- package/src/rendezvous/MSC4108SignInWithQR.ts +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invites-ignorer.js","names":["EventTimeline","Preset","globToRegexp","EventType","IGNORE_INVITES_ACCOUNT_EVENT_KEY","POLICIES_ACCOUNT_EVENT_TYPE","PolicyRecommendation","PolicyScope","scopeToEventTypeMap","User","PolicyRuleUser","Room","PolicyRuleRoom","Server","PolicyRuleServer","IgnoredInvites","constructor","client","addRule","scope","entity","reason","_this","_asyncToGenerator","target","getOrCreateTargetRoom","response","sendStateEvent","roomId","recommendation","Ban","event_id","removeRule","event","_this2","redactEvent","getRoomId","getId","addSource","_this3","joinRoom","sources","getOrCreateSourceRooms","map","room","includes","push","withIgnoreInvitesPolicies","ignoreInvitesPolicies","getRuleForInvite","_ref","_this4","sender","policyRooms","senderServer","split","roomServer","state","getUnfilteredTimelineSet","getLiveTimeline","getState","FORWARDS","entities","events","getStateEvents","content","getContent","glob","regexp","RegExp","_unused","test","_this5","getIgnoreInvitesPolicies","getRoom","createRoom","name","preset","PrivateChat","room_id","_this6","hasChanges","Array","isArray","sourceRooms","filter","length","getPoliciesAndIgnoreInvitesPolicies","cb","_this7","policies","setAccountData","key","altName","_this$client$getAccou","value","getAccountData","hasIgnoreInvitesPolicies"],"sources":["../../src/models/invites-ignorer.ts"],"sourcesContent":["/*\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 MatrixClient } from \"../client.ts\";\nimport { type IContent, type MatrixEvent } from \"./event.ts\";\nimport { EventTimeline } from \"./event-timeline.ts\";\nimport { Preset } from \"../@types/partials.ts\";\nimport { globToRegexp } from \"../utils.ts\";\nimport { type Room } from \"./room.ts\";\nimport { EventType, type StateEvents } from \"../@types/event.ts\";\nimport {\n IGNORE_INVITES_ACCOUNT_EVENT_KEY,\n POLICIES_ACCOUNT_EVENT_TYPE,\n PolicyRecommendation,\n PolicyScope,\n} from \"./invites-ignorer-types.ts\";\n\nexport { IGNORE_INVITES_ACCOUNT_EVENT_KEY, POLICIES_ACCOUNT_EVENT_TYPE, PolicyRecommendation, PolicyScope };\n\nconst scopeToEventTypeMap: Record<PolicyScope, keyof StateEvents> = {\n [PolicyScope.User]: EventType.PolicyRuleUser,\n [PolicyScope.Room]: EventType.PolicyRuleRoom,\n [PolicyScope.Server]: EventType.PolicyRuleServer,\n};\n\n/**\n * A container for ignored invites.\n *\n * # Performance\n *\n * This implementation is extremely naive. It expects that we are dealing\n * with a very short list of sources (e.g. only one). If real-world\n * applications turn out to require longer lists, we may need to rework\n * our data structures.\n */\nexport class IgnoredInvites {\n public constructor(private readonly client: MatrixClient) {}\n\n /**\n * Add a new rule.\n *\n * @param scope - The scope for this rule.\n * @param entity - The entity covered by this rule. Globs are supported.\n * @param reason - A human-readable reason for introducing this new rule.\n * @returns The event id for the new rule.\n */\n public async addRule(scope: PolicyScope, entity: string, reason: string): Promise<string> {\n const target = await this.getOrCreateTargetRoom();\n const response = await this.client.sendStateEvent(target.roomId, scopeToEventTypeMap[scope], {\n entity,\n reason,\n recommendation: PolicyRecommendation.Ban,\n });\n return response.event_id;\n }\n\n /**\n * Remove a rule.\n */\n public async removeRule(event: MatrixEvent): Promise<void> {\n await this.client.redactEvent(event.getRoomId()!, event.getId()!);\n }\n\n /**\n * Add a new room to the list of sources. If the user isn't a member of the\n * room, attempt to join it.\n *\n * @param roomId - A valid room id. If this room is already in the list\n * of sources, it will not be duplicated.\n * @returns `true` if the source was added, `false` if it was already present.\n * @throws If `roomId` isn't the id of a room that the current user is already\n * member of or can join.\n *\n * # Safety\n *\n * This method will rewrite the `Policies` object in the user's account data.\n * This rewrite is inherently racy and could overwrite or be overwritten by\n * other concurrent rewrites of the same object.\n */\n public async addSource(roomId: string): Promise<boolean> {\n // We attempt to join the room *before* calling\n // `await this.getOrCreateSourceRooms()` to decrease the duration\n // of the racy section.\n await this.client.joinRoom(roomId);\n // Race starts.\n const sources = (await this.getOrCreateSourceRooms()).map((room) => room.roomId);\n if (sources.includes(roomId)) {\n return false;\n }\n sources.push(roomId);\n await this.withIgnoreInvitesPolicies((ignoreInvitesPolicies) => {\n ignoreInvitesPolicies.sources = sources;\n });\n\n // Race ends.\n return true;\n }\n\n /**\n * Find out whether an invite should be ignored.\n *\n * @param params\n * @param params.sender - The user id for the user who issued the invite.\n * @param params.roomId - The room to which the user is invited.\n * @returns A rule matching the entity, if any was found, `null` otherwise.\n */\n public async getRuleForInvite({\n sender,\n roomId,\n }: {\n sender: string;\n roomId: string;\n }): Promise<Readonly<MatrixEvent | null>> {\n // In this implementation, we perform a very naive lookup:\n // - search in each policy room;\n // - turn each (potentially glob) rule entity into a regexp.\n //\n // Real-world testing will tell us whether this is performant enough.\n // In the (unfortunately likely) case it isn't, there are several manners\n // in which we could optimize this:\n // - match several entities per go;\n // - pre-compile each rule entity into a regexp;\n // - pre-compile entire rooms into a single regexp.\n const policyRooms = await this.getOrCreateSourceRooms();\n const senderServer = sender.split(\":\")[1];\n const roomServer = roomId.split(\":\")[1];\n for (const room of policyRooms) {\n const state = room.getUnfilteredTimelineSet().getLiveTimeline().getState(EventTimeline.FORWARDS)!;\n\n for (const { scope, entities } of [\n { scope: PolicyScope.Room, entities: [roomId] },\n { scope: PolicyScope.User, entities: [sender] },\n { scope: PolicyScope.Server, entities: [senderServer, roomServer] },\n ]) {\n const events = state.getStateEvents(scopeToEventTypeMap[scope]);\n for (const event of events) {\n const content = event.getContent();\n if (content?.recommendation != PolicyRecommendation.Ban) {\n // Ignoring invites only looks at `m.ban` recommendations.\n continue;\n }\n const glob = content?.entity;\n if (!glob) {\n // Invalid event.\n continue;\n }\n let regexp: RegExp;\n try {\n regexp = new RegExp(globToRegexp(glob));\n } catch {\n // Assume invalid event.\n continue;\n }\n for (const entity of entities) {\n if (entity && regexp.test(entity)) {\n return event;\n }\n }\n // No match.\n }\n }\n }\n return null;\n }\n\n /**\n * Get the target room, i.e. the room in which any new rule should be written.\n *\n * If there is no target room setup, a target room is created.\n *\n * Note: This method is public for testing reasons. Most clients should not need\n * to call it directly.\n *\n * # Safety\n *\n * This method will rewrite the `Policies` object in the user's account data.\n * This rewrite is inherently racy and could overwrite or be overwritten by\n * other concurrent rewrites of the same object.\n */\n public async getOrCreateTargetRoom(): Promise<Room> {\n const ignoreInvitesPolicies = this.getIgnoreInvitesPolicies();\n let target = ignoreInvitesPolicies.target;\n // Validate `target`. If it is invalid, trash out the current `target`\n // and create a new room.\n if (typeof target !== \"string\") {\n target = null;\n }\n if (target) {\n // Check that the room exists and is valid.\n const room = this.client.getRoom(target);\n if (room) {\n return room;\n } else {\n target = null;\n }\n }\n // We need to create our own policy room for ignoring invites.\n target = (\n await this.client.createRoom({\n name: \"Individual Policy Room\",\n preset: Preset.PrivateChat,\n })\n ).room_id;\n await this.withIgnoreInvitesPolicies((ignoreInvitesPolicies) => {\n ignoreInvitesPolicies.target = target;\n });\n\n // Since we have just called `createRoom`, `getRoom` should not be `null`.\n return this.client.getRoom(target)!;\n }\n\n /**\n * Get the list of source rooms, i.e. the rooms from which rules need to be read.\n *\n * If no source rooms are setup, the target room is used as sole source room.\n *\n * Note: This method is public for testing reasons. Most clients should not need\n * to call it directly.\n *\n * # Safety\n *\n * This method will rewrite the `Policies` object in the user's account data.\n * This rewrite is inherently racy and could overwrite or be overwritten by\n * other concurrent rewrites of the same object.\n */\n public async getOrCreateSourceRooms(): Promise<Room[]> {\n const ignoreInvitesPolicies = this.getIgnoreInvitesPolicies();\n let sources: string[] = ignoreInvitesPolicies.sources;\n\n // Validate `sources`. If it is invalid, trash out the current `sources`\n // and create a new list of sources from `target`.\n let hasChanges = false;\n if (!Array.isArray(sources)) {\n // `sources` could not be an array.\n hasChanges = true;\n sources = [];\n }\n let sourceRooms = sources\n // `sources` could contain non-string / invalid room ids\n .filter((roomId) => typeof roomId === \"string\")\n .map((roomId) => this.client.getRoom(roomId))\n .filter((room) => !!room) as Room[];\n if (sourceRooms.length != sources.length) {\n hasChanges = true;\n }\n if (sourceRooms.length == 0) {\n // `sources` could be empty (possibly because we've removed\n // invalid content)\n const target = await this.getOrCreateTargetRoom();\n hasChanges = true;\n sourceRooms = [target];\n }\n if (hasChanges) {\n // Reload `policies`/`ignoreInvitesPolicies` in case it has been changed\n // during or by our call to `this.getTargetRoom()`.\n await this.withIgnoreInvitesPolicies((ignoreInvitesPolicies) => {\n ignoreInvitesPolicies.sources = sources;\n });\n }\n return sourceRooms;\n }\n\n /**\n * Fetch the `IGNORE_INVITES_POLICIES` object from account data.\n *\n * If both an unstable prefix version and a stable prefix version are available,\n * it will return the stable prefix version preferentially.\n *\n * The result is *not* validated but is guaranteed to be a non-null object.\n *\n * @returns A non-null object.\n */\n private getIgnoreInvitesPolicies(): { [key: string]: any } {\n return this.getPoliciesAndIgnoreInvitesPolicies().ignoreInvitesPolicies;\n }\n\n /**\n * Modify in place the `IGNORE_INVITES_POLICIES` object from account data.\n */\n private async withIgnoreInvitesPolicies(\n cb: (ignoreInvitesPolicies: { [key: string]: any }) => void,\n ): Promise<void> {\n const { policies, ignoreInvitesPolicies } = this.getPoliciesAndIgnoreInvitesPolicies();\n cb(ignoreInvitesPolicies);\n policies[IGNORE_INVITES_ACCOUNT_EVENT_KEY.name] = ignoreInvitesPolicies;\n await this.client.setAccountData(POLICIES_ACCOUNT_EVENT_TYPE.name, policies);\n }\n\n /**\n * As `getIgnoreInvitesPolicies` but also return the `POLICIES_ACCOUNT_EVENT_TYPE`\n * object.\n */\n private getPoliciesAndIgnoreInvitesPolicies(): {\n policies: { [key: string]: any };\n ignoreInvitesPolicies: { [key: string]: any };\n } {\n let policies: IContent = {};\n for (const key of [POLICIES_ACCOUNT_EVENT_TYPE.name, POLICIES_ACCOUNT_EVENT_TYPE.altName]) {\n if (!key) {\n continue;\n }\n const value = this.client.getAccountData(key)?.getContent();\n if (value) {\n policies = value;\n break;\n }\n }\n\n let ignoreInvitesPolicies = {};\n let hasIgnoreInvitesPolicies = false;\n for (const key of [IGNORE_INVITES_ACCOUNT_EVENT_KEY.name, IGNORE_INVITES_ACCOUNT_EVENT_KEY.altName]) {\n if (!key) {\n continue;\n }\n const value = policies[key];\n if (value && typeof value == \"object\") {\n ignoreInvitesPolicies = value;\n hasIgnoreInvitesPolicies = true;\n break;\n }\n }\n if (!hasIgnoreInvitesPolicies) {\n policies[IGNORE_INVITES_ACCOUNT_EVENT_KEY.name] = ignoreInvitesPolicies;\n }\n\n return { policies, ignoreInvitesPolicies };\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"invites-ignorer.js","names":["EventTimeline","Preset","globToRegexp","EventType","IGNORE_INVITES_ACCOUNT_EVENT_KEY","POLICIES_ACCOUNT_EVENT_TYPE","PolicyRecommendation","PolicyScope","scopeToEventTypeMap","User","PolicyRuleUser","Room","PolicyRuleRoom","Server","PolicyRuleServer","IgnoredInvites","constructor","client","addRule","scope","entity","reason","_this","_asyncToGenerator","target","getOrCreateTargetRoom","response","sendStateEvent","roomId","recommendation","Ban","event_id","removeRule","event","_this2","redactEvent","getRoomId","getId","addSource","_this3","joinRoom","sources","getOrCreateSourceRooms","map","room","includes","push","withIgnoreInvitesPolicies","ignoreInvitesPolicies","getRuleForInvite","_ref","_this4","sender","policyRooms","senderServer","split","roomServer","state","getUnfilteredTimelineSet","getLiveTimeline","getState","FORWARDS","_ref3","entities","events","getStateEvents","content","getContent","glob","regexp","RegExp","_unused","test","_this5","getIgnoreInvitesPolicies","getRoom","createRoom","name","preset","PrivateChat","room_id","_this6","hasChanges","Array","isArray","sourceRooms","filter","length","getPoliciesAndIgnoreInvitesPolicies","cb","_this7","_this7$getPoliciesAnd","policies","setAccountData","key","altName","_this$client$getAccou","value","getAccountData","hasIgnoreInvitesPolicies"],"sources":["../../src/models/invites-ignorer.ts"],"sourcesContent":["/*\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 MatrixClient } from \"../client.ts\";\nimport { type IContent, type MatrixEvent } from \"./event.ts\";\nimport { EventTimeline } from \"./event-timeline.ts\";\nimport { Preset } from \"../@types/partials.ts\";\nimport { globToRegexp } from \"../utils.ts\";\nimport { type Room } from \"./room.ts\";\nimport { EventType, type StateEvents } from \"../@types/event.ts\";\nimport {\n IGNORE_INVITES_ACCOUNT_EVENT_KEY,\n POLICIES_ACCOUNT_EVENT_TYPE,\n PolicyRecommendation,\n PolicyScope,\n} from \"./invites-ignorer-types.ts\";\n\nexport { IGNORE_INVITES_ACCOUNT_EVENT_KEY, POLICIES_ACCOUNT_EVENT_TYPE, PolicyRecommendation, PolicyScope };\n\nconst scopeToEventTypeMap: Record<PolicyScope, keyof StateEvents> = {\n [PolicyScope.User]: EventType.PolicyRuleUser,\n [PolicyScope.Room]: EventType.PolicyRuleRoom,\n [PolicyScope.Server]: EventType.PolicyRuleServer,\n};\n\n/**\n * A container for ignored invites.\n *\n * # Performance\n *\n * This implementation is extremely naive. It expects that we are dealing\n * with a very short list of sources (e.g. only one). If real-world\n * applications turn out to require longer lists, we may need to rework\n * our data structures.\n */\nexport class IgnoredInvites {\n public constructor(private readonly client: MatrixClient) {}\n\n /**\n * Add a new rule.\n *\n * @param scope - The scope for this rule.\n * @param entity - The entity covered by this rule. Globs are supported.\n * @param reason - A human-readable reason for introducing this new rule.\n * @returns The event id for the new rule.\n */\n public async addRule(scope: PolicyScope, entity: string, reason: string): Promise<string> {\n const target = await this.getOrCreateTargetRoom();\n const response = await this.client.sendStateEvent(target.roomId, scopeToEventTypeMap[scope], {\n entity,\n reason,\n recommendation: PolicyRecommendation.Ban,\n });\n return response.event_id;\n }\n\n /**\n * Remove a rule.\n */\n public async removeRule(event: MatrixEvent): Promise<void> {\n await this.client.redactEvent(event.getRoomId()!, event.getId()!);\n }\n\n /**\n * Add a new room to the list of sources. If the user isn't a member of the\n * room, attempt to join it.\n *\n * @param roomId - A valid room id. If this room is already in the list\n * of sources, it will not be duplicated.\n * @returns `true` if the source was added, `false` if it was already present.\n * @throws If `roomId` isn't the id of a room that the current user is already\n * member of or can join.\n *\n * # Safety\n *\n * This method will rewrite the `Policies` object in the user's account data.\n * This rewrite is inherently racy and could overwrite or be overwritten by\n * other concurrent rewrites of the same object.\n */\n public async addSource(roomId: string): Promise<boolean> {\n // We attempt to join the room *before* calling\n // `await this.getOrCreateSourceRooms()` to decrease the duration\n // of the racy section.\n await this.client.joinRoom(roomId);\n // Race starts.\n const sources = (await this.getOrCreateSourceRooms()).map((room) => room.roomId);\n if (sources.includes(roomId)) {\n return false;\n }\n sources.push(roomId);\n await this.withIgnoreInvitesPolicies((ignoreInvitesPolicies) => {\n ignoreInvitesPolicies.sources = sources;\n });\n\n // Race ends.\n return true;\n }\n\n /**\n * Find out whether an invite should be ignored.\n *\n * @param params\n * @param params.sender - The user id for the user who issued the invite.\n * @param params.roomId - The room to which the user is invited.\n * @returns A rule matching the entity, if any was found, `null` otherwise.\n */\n public async getRuleForInvite({\n sender,\n roomId,\n }: {\n sender: string;\n roomId: string;\n }): Promise<Readonly<MatrixEvent | null>> {\n // In this implementation, we perform a very naive lookup:\n // - search in each policy room;\n // - turn each (potentially glob) rule entity into a regexp.\n //\n // Real-world testing will tell us whether this is performant enough.\n // In the (unfortunately likely) case it isn't, there are several manners\n // in which we could optimize this:\n // - match several entities per go;\n // - pre-compile each rule entity into a regexp;\n // - pre-compile entire rooms into a single regexp.\n const policyRooms = await this.getOrCreateSourceRooms();\n const senderServer = sender.split(\":\")[1];\n const roomServer = roomId.split(\":\")[1];\n for (const room of policyRooms) {\n const state = room.getUnfilteredTimelineSet().getLiveTimeline().getState(EventTimeline.FORWARDS)!;\n\n for (const { scope, entities } of [\n { scope: PolicyScope.Room, entities: [roomId] },\n { scope: PolicyScope.User, entities: [sender] },\n { scope: PolicyScope.Server, entities: [senderServer, roomServer] },\n ]) {\n const events = state.getStateEvents(scopeToEventTypeMap[scope]);\n for (const event of events) {\n const content = event.getContent();\n if (content?.recommendation != PolicyRecommendation.Ban) {\n // Ignoring invites only looks at `m.ban` recommendations.\n continue;\n }\n const glob = content?.entity;\n if (!glob) {\n // Invalid event.\n continue;\n }\n let regexp: RegExp;\n try {\n regexp = new RegExp(globToRegexp(glob));\n } catch {\n // Assume invalid event.\n continue;\n }\n for (const entity of entities) {\n if (entity && regexp.test(entity)) {\n return event;\n }\n }\n // No match.\n }\n }\n }\n return null;\n }\n\n /**\n * Get the target room, i.e. the room in which any new rule should be written.\n *\n * If there is no target room setup, a target room is created.\n *\n * Note: This method is public for testing reasons. Most clients should not need\n * to call it directly.\n *\n * # Safety\n *\n * This method will rewrite the `Policies` object in the user's account data.\n * This rewrite is inherently racy and could overwrite or be overwritten by\n * other concurrent rewrites of the same object.\n */\n public async getOrCreateTargetRoom(): Promise<Room> {\n const ignoreInvitesPolicies = this.getIgnoreInvitesPolicies();\n let target = ignoreInvitesPolicies.target;\n // Validate `target`. If it is invalid, trash out the current `target`\n // and create a new room.\n if (typeof target !== \"string\") {\n target = null;\n }\n if (target) {\n // Check that the room exists and is valid.\n const room = this.client.getRoom(target);\n if (room) {\n return room;\n } else {\n target = null;\n }\n }\n // We need to create our own policy room for ignoring invites.\n target = (\n await this.client.createRoom({\n name: \"Individual Policy Room\",\n preset: Preset.PrivateChat,\n })\n ).room_id;\n await this.withIgnoreInvitesPolicies((ignoreInvitesPolicies) => {\n ignoreInvitesPolicies.target = target;\n });\n\n // Since we have just called `createRoom`, `getRoom` should not be `null`.\n return this.client.getRoom(target)!;\n }\n\n /**\n * Get the list of source rooms, i.e. the rooms from which rules need to be read.\n *\n * If no source rooms are setup, the target room is used as sole source room.\n *\n * Note: This method is public for testing reasons. Most clients should not need\n * to call it directly.\n *\n * # Safety\n *\n * This method will rewrite the `Policies` object in the user's account data.\n * This rewrite is inherently racy and could overwrite or be overwritten by\n * other concurrent rewrites of the same object.\n */\n public async getOrCreateSourceRooms(): Promise<Room[]> {\n const ignoreInvitesPolicies = this.getIgnoreInvitesPolicies();\n let sources: string[] = ignoreInvitesPolicies.sources;\n\n // Validate `sources`. If it is invalid, trash out the current `sources`\n // and create a new list of sources from `target`.\n let hasChanges = false;\n if (!Array.isArray(sources)) {\n // `sources` could not be an array.\n hasChanges = true;\n sources = [];\n }\n let sourceRooms = sources\n // `sources` could contain non-string / invalid room ids\n .filter((roomId) => typeof roomId === \"string\")\n .map((roomId) => this.client.getRoom(roomId))\n .filter((room) => !!room) as Room[];\n if (sourceRooms.length != sources.length) {\n hasChanges = true;\n }\n if (sourceRooms.length == 0) {\n // `sources` could be empty (possibly because we've removed\n // invalid content)\n const target = await this.getOrCreateTargetRoom();\n hasChanges = true;\n sourceRooms = [target];\n }\n if (hasChanges) {\n // Reload `policies`/`ignoreInvitesPolicies` in case it has been changed\n // during or by our call to `this.getTargetRoom()`.\n await this.withIgnoreInvitesPolicies((ignoreInvitesPolicies) => {\n ignoreInvitesPolicies.sources = sources;\n });\n }\n return sourceRooms;\n }\n\n /**\n * Fetch the `IGNORE_INVITES_POLICIES` object from account data.\n *\n * If both an unstable prefix version and a stable prefix version are available,\n * it will return the stable prefix version preferentially.\n *\n * The result is *not* validated but is guaranteed to be a non-null object.\n *\n * @returns A non-null object.\n */\n private getIgnoreInvitesPolicies(): { [key: string]: any } {\n return this.getPoliciesAndIgnoreInvitesPolicies().ignoreInvitesPolicies;\n }\n\n /**\n * Modify in place the `IGNORE_INVITES_POLICIES` object from account data.\n */\n private async withIgnoreInvitesPolicies(\n cb: (ignoreInvitesPolicies: { [key: string]: any }) => void,\n ): Promise<void> {\n const { policies, ignoreInvitesPolicies } = this.getPoliciesAndIgnoreInvitesPolicies();\n cb(ignoreInvitesPolicies);\n policies[IGNORE_INVITES_ACCOUNT_EVENT_KEY.name] = ignoreInvitesPolicies;\n await this.client.setAccountData(POLICIES_ACCOUNT_EVENT_TYPE.name, policies);\n }\n\n /**\n * As `getIgnoreInvitesPolicies` but also return the `POLICIES_ACCOUNT_EVENT_TYPE`\n * object.\n */\n private getPoliciesAndIgnoreInvitesPolicies(): {\n policies: { [key: string]: any };\n ignoreInvitesPolicies: { [key: string]: any };\n } {\n let policies: IContent = {};\n for (const key of [POLICIES_ACCOUNT_EVENT_TYPE.name, POLICIES_ACCOUNT_EVENT_TYPE.altName]) {\n if (!key) {\n continue;\n }\n const value = this.client.getAccountData(key)?.getContent();\n if (value) {\n policies = value;\n break;\n }\n }\n\n let ignoreInvitesPolicies = {};\n let hasIgnoreInvitesPolicies = false;\n for (const key of [IGNORE_INVITES_ACCOUNT_EVENT_KEY.name, IGNORE_INVITES_ACCOUNT_EVENT_KEY.altName]) {\n if (!key) {\n continue;\n }\n const value = policies[key];\n if (value && typeof value == \"object\") {\n ignoreInvitesPolicies = value;\n hasIgnoreInvitesPolicies = true;\n break;\n }\n }\n if (!hasIgnoreInvitesPolicies) {\n policies[IGNORE_INVITES_ACCOUNT_EVENT_KEY.name] = ignoreInvitesPolicies;\n }\n\n return { policies, ignoreInvitesPolicies };\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
package/lib/models/poll.js
CHANGED
|
@@ -46,15 +46,8 @@ var filterResponseRelations = (relationEvents, pollEndTimestamp) => {
|
|
|
46
46
|
export class Poll extends TypedEventEmitter {
|
|
47
47
|
constructor(rootEvent, matrixClient, room) {
|
|
48
48
|
super();
|
|
49
|
-
this.rootEvent = rootEvent;
|
|
50
|
-
this.matrixClient = matrixClient;
|
|
51
|
-
this.room = room;
|
|
52
|
-
_defineProperty(this, "roomId", void 0);
|
|
53
|
-
_defineProperty(this, "pollEvent", void 0);
|
|
54
49
|
_defineProperty(this, "_isFetchingResponses", false);
|
|
55
|
-
_defineProperty(this, "relationsNextBatch", void 0);
|
|
56
50
|
_defineProperty(this, "responses", null);
|
|
57
|
-
_defineProperty(this, "endEvent", void 0);
|
|
58
51
|
/**
|
|
59
52
|
* Keep track of undecryptable relations
|
|
60
53
|
* As incomplete result sets affect poll results
|
|
@@ -68,6 +61,9 @@ export class Poll extends TypedEventEmitter {
|
|
|
68
61
|
this.emit(PollEvent.UndecryptableRelations, this.undecryptableRelationsCount);
|
|
69
62
|
}
|
|
70
63
|
});
|
|
64
|
+
this.rootEvent = rootEvent;
|
|
65
|
+
this.matrixClient = matrixClient;
|
|
66
|
+
this.room = room;
|
|
71
67
|
if (!this.rootEvent.getRoomId() || !this.rootEvent.getId()) {
|
|
72
68
|
throw new Error("Invalid poll start event.");
|
|
73
69
|
}
|
|
@@ -127,9 +123,8 @@ export class Poll extends TypedEventEmitter {
|
|
|
127
123
|
return;
|
|
128
124
|
}
|
|
129
125
|
var pollEndTimestamp = ((_this$endEvent2 = this.endEvent) === null || _this$endEvent2 === void 0 ? void 0 : _this$endEvent2.getTs()) || Number.MAX_SAFE_INTEGER;
|
|
130
|
-
var
|
|
131
|
-
responseEvents
|
|
132
|
-
} = filterResponseRelations([event], pollEndTimestamp);
|
|
126
|
+
var _filterResponseRelati = filterResponseRelations([event], pollEndTimestamp),
|
|
127
|
+
responseEvents = _filterResponseRelati.responseEvents;
|
|
133
128
|
this.countUndecryptableEvents([event]);
|
|
134
129
|
if (responseEvents.length) {
|
|
135
130
|
responseEvents.forEach(event => {
|
|
@@ -160,9 +155,8 @@ export class Poll extends TypedEventEmitter {
|
|
|
160
155
|
_this2.emit(PollEvent.End);
|
|
161
156
|
}
|
|
162
157
|
var pollCloseTimestamp = ((_this2$endEvent = _this2.endEvent) === null || _this2$endEvent === void 0 ? void 0 : _this2$endEvent.getTs()) || Number.MAX_SAFE_INTEGER;
|
|
163
|
-
var
|
|
164
|
-
responseEvents
|
|
165
|
-
} = filterResponseRelations(allRelations.events, pollCloseTimestamp);
|
|
158
|
+
var _filterResponseRelati2 = filterResponseRelations(allRelations.events, pollCloseTimestamp),
|
|
159
|
+
responseEvents = _filterResponseRelati2.responseEvents;
|
|
166
160
|
responseEvents.forEach(event => {
|
|
167
161
|
responses.addEvent(event);
|
|
168
162
|
});
|
package/lib/models/poll.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"poll.js","names":["M_POLL_START","M_POLL_END","M_POLL_RESPONSE","Relations","TypedEventEmitter","PollEvent","filterResponseRelations","relationEvents","pollEndTimestamp","responseEvents","filter","event","isDecryptionFailure","matches","getType","getTs","Poll","constructor","rootEvent","matrixClient","room","_defineProperty","Set","events","undecryptableEventIds","map","getId","previousCount","undecryptableRelationsCount","undecryptableRelationEventIds","emit","UndecryptableRelations","getRoomId","Error","roomId","pollEvent","unstableExtensibleEvent","pollId","endEventId","_this$endEvent","endEvent","isEnded","isFetchingResponses","_isFetchingResponses","size","getResponses","_this","_asyncToGenerator","responses","fetchResponses","onNewRelation","_this$endEvent2","validateEndEvent","refilterResponsesOnEnd","End","Number","MAX_SAFE_INTEGER","countUndecryptableEvents","length","forEach","addEvent","Responses","_this2","_this2$endEvent","_allRelations$nextBat","allRelations","relations","undefined","from","relationsNextBatch","Promise","all","decryptEventIfNeeded","name","altName","pollEndEvent","find","pollCloseTimestamp","nextBatch","_this$endEvent3","getRelations","_this$responses","removeEvent","roomCurrentState","currentState","endEventSender","getSender","maySendRedactionForEvent","isPollEvent","eventType"],"sources":["../../src/models/poll.ts"],"sourcesContent":["/*\nCopyright 2023 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 { M_POLL_START } from \"matrix-events-sdk\";\n\nimport { M_POLL_END, M_POLL_RESPONSE } from \"../@types/polls.ts\";\nimport { type MatrixClient } from \"../client.ts\";\nimport { type PollStartEvent } from \"../extensible_events_v1/PollStartEvent.ts\";\nimport { type MatrixEvent } from \"./event.ts\";\nimport { Relations } from \"./relations.ts\";\nimport { type Room } from \"./room.ts\";\nimport { TypedEventEmitter } from \"./typed-event-emitter.ts\";\n\nexport enum PollEvent {\n New = \"Poll.new\",\n End = \"Poll.end\",\n Update = \"Poll.update\",\n Responses = \"Poll.Responses\",\n Destroy = \"Poll.Destroy\",\n UndecryptableRelations = \"Poll.UndecryptableRelations\",\n}\n\nexport type PollEventHandlerMap = {\n [PollEvent.Update]: (event: MatrixEvent, poll: Poll) => void;\n [PollEvent.Destroy]: (pollIdentifier: string) => void;\n [PollEvent.End]: () => void;\n [PollEvent.Responses]: (responses: Relations) => void;\n [PollEvent.UndecryptableRelations]: (count: number) => void;\n};\n\nconst filterResponseRelations = (\n relationEvents: MatrixEvent[],\n pollEndTimestamp: number,\n): {\n responseEvents: MatrixEvent[];\n} => {\n const responseEvents = relationEvents.filter((event) => {\n if (event.isDecryptionFailure()) {\n return;\n }\n return (\n M_POLL_RESPONSE.matches(event.getType()) &&\n // From MSC3381:\n // \"Votes sent on or before the end event's timestamp are valid votes\"\n event.getTs() <= pollEndTimestamp\n );\n });\n\n return { responseEvents };\n};\n\nexport class Poll extends TypedEventEmitter<Exclude<PollEvent, PollEvent.New>, PollEventHandlerMap> {\n public readonly roomId: string;\n public readonly pollEvent: PollStartEvent;\n private _isFetchingResponses = false;\n private relationsNextBatch: string | undefined;\n private responses: null | Relations = null;\n private endEvent: MatrixEvent | undefined;\n /**\n * Keep track of undecryptable relations\n * As incomplete result sets affect poll results\n */\n private undecryptableRelationEventIds = new Set<string>();\n\n public constructor(\n public readonly rootEvent: MatrixEvent,\n private matrixClient: MatrixClient,\n private room: Room,\n ) {\n super();\n if (!this.rootEvent.getRoomId() || !this.rootEvent.getId()) {\n throw new Error(\"Invalid poll start event.\");\n }\n this.roomId = this.rootEvent.getRoomId()!;\n this.pollEvent = this.rootEvent.unstableExtensibleEvent as unknown as PollStartEvent;\n }\n\n public get pollId(): string {\n return this.rootEvent.getId()!;\n }\n\n public get endEventId(): string | undefined {\n return this.endEvent?.getId();\n }\n\n public get isEnded(): boolean {\n return !!this.endEvent;\n }\n\n public get isFetchingResponses(): boolean {\n return this._isFetchingResponses;\n }\n\n public get undecryptableRelationsCount(): number {\n return this.undecryptableRelationEventIds.size;\n }\n\n public async getResponses(): Promise<Relations> {\n // if we have already fetched some responses\n // just return them\n if (this.responses) {\n return this.responses;\n }\n\n // if there is no fetching in progress\n // start fetching\n if (!this.isFetchingResponses) {\n await this.fetchResponses();\n }\n // return whatever responses we got from the first page\n return this.responses!;\n }\n\n /**\n *\n * @param event - event with a relation to the rootEvent\n * @returns void\n */\n public onNewRelation(event: MatrixEvent): void {\n if (M_POLL_END.matches(event.getType()) && this.validateEndEvent(event)) {\n this.endEvent = event;\n this.refilterResponsesOnEnd();\n this.emit(PollEvent.End);\n }\n\n // wait for poll responses to be initialised\n if (!this.responses) {\n return;\n }\n\n const pollEndTimestamp = this.endEvent?.getTs() || Number.MAX_SAFE_INTEGER;\n const { responseEvents } = filterResponseRelations([event], pollEndTimestamp);\n\n this.countUndecryptableEvents([event]);\n\n if (responseEvents.length) {\n responseEvents.forEach((event) => {\n this.responses!.addEvent(event);\n });\n\n this.emit(PollEvent.Responses, this.responses);\n }\n }\n\n private async fetchResponses(): Promise<void> {\n this._isFetchingResponses = true;\n\n // we want:\n // - stable and unstable M_POLL_RESPONSE\n // - stable and unstable M_POLL_END\n // so make one api call and filter by event type client side\n const allRelations = await this.matrixClient.relations(\n this.roomId,\n this.rootEvent.getId()!,\n \"m.reference\",\n undefined,\n {\n from: this.relationsNextBatch || undefined,\n },\n );\n\n await Promise.all(allRelations.events.map((event) => this.matrixClient.decryptEventIfNeeded(event)));\n\n const responses =\n this.responses ||\n new Relations(\"m.reference\", M_POLL_RESPONSE.name, this.matrixClient, [M_POLL_RESPONSE.altName!]);\n\n const pollEndEvent = allRelations.events.find((event) => M_POLL_END.matches(event.getType()));\n\n if (this.validateEndEvent(pollEndEvent)) {\n this.endEvent = pollEndEvent;\n this.refilterResponsesOnEnd();\n this.emit(PollEvent.End);\n }\n\n const pollCloseTimestamp = this.endEvent?.getTs() || Number.MAX_SAFE_INTEGER;\n\n const { responseEvents } = filterResponseRelations(allRelations.events, pollCloseTimestamp);\n\n responseEvents.forEach((event) => {\n responses.addEvent(event);\n });\n\n this.relationsNextBatch = allRelations.nextBatch ?? undefined;\n this.responses = responses;\n this.countUndecryptableEvents(allRelations.events);\n\n // while there are more pages of relations\n // fetch them\n if (this.relationsNextBatch) {\n // don't await\n // we want to return the first page as soon as possible\n this.fetchResponses();\n } else {\n // no more pages\n this._isFetchingResponses = false;\n }\n\n // emit after updating _isFetchingResponses state\n this.emit(PollEvent.Responses, this.responses);\n }\n\n /**\n * Only responses made before the poll ended are valid\n * Refilter after an end event is recieved\n * To ensure responses are valid\n */\n private refilterResponsesOnEnd(): void {\n if (!this.responses) {\n return;\n }\n\n const pollEndTimestamp = this.endEvent?.getTs() || Number.MAX_SAFE_INTEGER;\n this.responses.getRelations().forEach((event) => {\n if (event.getTs() > pollEndTimestamp) {\n this.responses?.removeEvent(event);\n }\n });\n\n this.emit(PollEvent.Responses, this.responses);\n }\n\n private countUndecryptableEvents = (events: MatrixEvent[]): void => {\n const undecryptableEventIds = events\n .filter((event) => event.isDecryptionFailure())\n .map((event) => event.getId()!);\n\n const previousCount = this.undecryptableRelationsCount;\n this.undecryptableRelationEventIds = new Set([...this.undecryptableRelationEventIds, ...undecryptableEventIds]);\n\n if (this.undecryptableRelationsCount !== previousCount) {\n this.emit(PollEvent.UndecryptableRelations, this.undecryptableRelationsCount);\n }\n };\n\n private validateEndEvent(endEvent?: MatrixEvent): boolean {\n if (!endEvent) {\n return false;\n }\n /**\n * Repeated end events are ignored -\n * only the first (valid) closure event by origin_server_ts is counted.\n */\n if (this.endEvent && this.endEvent.getTs() < endEvent.getTs()) {\n return false;\n }\n\n /**\n * MSC3381\n * If a m.poll.end event is received from someone other than the poll creator or user with permission to redact\n * others' messages in the room, the event must be ignored by clients due to being invalid.\n */\n const roomCurrentState = this.room.currentState;\n const endEventSender = endEvent.getSender();\n return (\n !!endEventSender &&\n (endEventSender === this.rootEvent.getSender() ||\n roomCurrentState.maySendRedactionForEvent(this.rootEvent, endEventSender))\n );\n }\n}\n\n/**\n * Tests whether the event is a start, response or end poll event.\n *\n * @param event - Event to test\n * @returns true if the event is a poll event, else false\n */\nexport const isPollEvent = (event: MatrixEvent): boolean => {\n const eventType = event.getType();\n return M_POLL_START.matches(eventType) || M_POLL_RESPONSE.matches(eventType) || M_POLL_END.matches(eventType);\n};\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"poll.js","names":["M_POLL_START","M_POLL_END","M_POLL_RESPONSE","Relations","TypedEventEmitter","PollEvent","filterResponseRelations","relationEvents","pollEndTimestamp","responseEvents","filter","event","isDecryptionFailure","matches","getType","getTs","Poll","constructor","rootEvent","matrixClient","room","_defineProperty","Set","events","undecryptableEventIds","map","getId","previousCount","undecryptableRelationsCount","undecryptableRelationEventIds","emit","UndecryptableRelations","getRoomId","Error","roomId","pollEvent","unstableExtensibleEvent","pollId","endEventId","_this$endEvent","endEvent","isEnded","isFetchingResponses","_isFetchingResponses","size","getResponses","_this","_asyncToGenerator","responses","fetchResponses","onNewRelation","_this$endEvent2","validateEndEvent","refilterResponsesOnEnd","End","Number","MAX_SAFE_INTEGER","_filterResponseRelati","countUndecryptableEvents","length","forEach","addEvent","Responses","_this2","_this2$endEvent","_allRelations$nextBat","allRelations","relations","undefined","from","relationsNextBatch","Promise","all","decryptEventIfNeeded","name","altName","pollEndEvent","find","pollCloseTimestamp","_filterResponseRelati2","nextBatch","_this$endEvent3","getRelations","_this$responses","removeEvent","roomCurrentState","currentState","endEventSender","getSender","maySendRedactionForEvent","isPollEvent","eventType"],"sources":["../../src/models/poll.ts"],"sourcesContent":["/*\nCopyright 2023 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 { M_POLL_START } from \"matrix-events-sdk\";\n\nimport { M_POLL_END, M_POLL_RESPONSE } from \"../@types/polls.ts\";\nimport { type MatrixClient } from \"../client.ts\";\nimport { type PollStartEvent } from \"../extensible_events_v1/PollStartEvent.ts\";\nimport { type MatrixEvent } from \"./event.ts\";\nimport { Relations } from \"./relations.ts\";\nimport { type Room } from \"./room.ts\";\nimport { TypedEventEmitter } from \"./typed-event-emitter.ts\";\n\nexport enum PollEvent {\n New = \"Poll.new\",\n End = \"Poll.end\",\n Update = \"Poll.update\",\n Responses = \"Poll.Responses\",\n Destroy = \"Poll.Destroy\",\n UndecryptableRelations = \"Poll.UndecryptableRelations\",\n}\n\nexport type PollEventHandlerMap = {\n [PollEvent.Update]: (event: MatrixEvent, poll: Poll) => void;\n [PollEvent.Destroy]: (pollIdentifier: string) => void;\n [PollEvent.End]: () => void;\n [PollEvent.Responses]: (responses: Relations) => void;\n [PollEvent.UndecryptableRelations]: (count: number) => void;\n};\n\nconst filterResponseRelations = (\n relationEvents: MatrixEvent[],\n pollEndTimestamp: number,\n): {\n responseEvents: MatrixEvent[];\n} => {\n const responseEvents = relationEvents.filter((event) => {\n if (event.isDecryptionFailure()) {\n return;\n }\n return (\n M_POLL_RESPONSE.matches(event.getType()) &&\n // From MSC3381:\n // \"Votes sent on or before the end event's timestamp are valid votes\"\n event.getTs() <= pollEndTimestamp\n );\n });\n\n return { responseEvents };\n};\n\nexport class Poll extends TypedEventEmitter<Exclude<PollEvent, PollEvent.New>, PollEventHandlerMap> {\n public readonly roomId: string;\n public readonly pollEvent: PollStartEvent;\n private _isFetchingResponses = false;\n private relationsNextBatch: string | undefined;\n private responses: null | Relations = null;\n private endEvent: MatrixEvent | undefined;\n /**\n * Keep track of undecryptable relations\n * As incomplete result sets affect poll results\n */\n private undecryptableRelationEventIds = new Set<string>();\n\n public constructor(\n public readonly rootEvent: MatrixEvent,\n private matrixClient: MatrixClient,\n private room: Room,\n ) {\n super();\n if (!this.rootEvent.getRoomId() || !this.rootEvent.getId()) {\n throw new Error(\"Invalid poll start event.\");\n }\n this.roomId = this.rootEvent.getRoomId()!;\n this.pollEvent = this.rootEvent.unstableExtensibleEvent as unknown as PollStartEvent;\n }\n\n public get pollId(): string {\n return this.rootEvent.getId()!;\n }\n\n public get endEventId(): string | undefined {\n return this.endEvent?.getId();\n }\n\n public get isEnded(): boolean {\n return !!this.endEvent;\n }\n\n public get isFetchingResponses(): boolean {\n return this._isFetchingResponses;\n }\n\n public get undecryptableRelationsCount(): number {\n return this.undecryptableRelationEventIds.size;\n }\n\n public async getResponses(): Promise<Relations> {\n // if we have already fetched some responses\n // just return them\n if (this.responses) {\n return this.responses;\n }\n\n // if there is no fetching in progress\n // start fetching\n if (!this.isFetchingResponses) {\n await this.fetchResponses();\n }\n // return whatever responses we got from the first page\n return this.responses!;\n }\n\n /**\n *\n * @param event - event with a relation to the rootEvent\n * @returns void\n */\n public onNewRelation(event: MatrixEvent): void {\n if (M_POLL_END.matches(event.getType()) && this.validateEndEvent(event)) {\n this.endEvent = event;\n this.refilterResponsesOnEnd();\n this.emit(PollEvent.End);\n }\n\n // wait for poll responses to be initialised\n if (!this.responses) {\n return;\n }\n\n const pollEndTimestamp = this.endEvent?.getTs() || Number.MAX_SAFE_INTEGER;\n const { responseEvents } = filterResponseRelations([event], pollEndTimestamp);\n\n this.countUndecryptableEvents([event]);\n\n if (responseEvents.length) {\n responseEvents.forEach((event) => {\n this.responses!.addEvent(event);\n });\n\n this.emit(PollEvent.Responses, this.responses);\n }\n }\n\n private async fetchResponses(): Promise<void> {\n this._isFetchingResponses = true;\n\n // we want:\n // - stable and unstable M_POLL_RESPONSE\n // - stable and unstable M_POLL_END\n // so make one api call and filter by event type client side\n const allRelations = await this.matrixClient.relations(\n this.roomId,\n this.rootEvent.getId()!,\n \"m.reference\",\n undefined,\n {\n from: this.relationsNextBatch || undefined,\n },\n );\n\n await Promise.all(allRelations.events.map((event) => this.matrixClient.decryptEventIfNeeded(event)));\n\n const responses =\n this.responses ||\n new Relations(\"m.reference\", M_POLL_RESPONSE.name, this.matrixClient, [M_POLL_RESPONSE.altName!]);\n\n const pollEndEvent = allRelations.events.find((event) => M_POLL_END.matches(event.getType()));\n\n if (this.validateEndEvent(pollEndEvent)) {\n this.endEvent = pollEndEvent;\n this.refilterResponsesOnEnd();\n this.emit(PollEvent.End);\n }\n\n const pollCloseTimestamp = this.endEvent?.getTs() || Number.MAX_SAFE_INTEGER;\n\n const { responseEvents } = filterResponseRelations(allRelations.events, pollCloseTimestamp);\n\n responseEvents.forEach((event) => {\n responses.addEvent(event);\n });\n\n this.relationsNextBatch = allRelations.nextBatch ?? undefined;\n this.responses = responses;\n this.countUndecryptableEvents(allRelations.events);\n\n // while there are more pages of relations\n // fetch them\n if (this.relationsNextBatch) {\n // don't await\n // we want to return the first page as soon as possible\n this.fetchResponses();\n } else {\n // no more pages\n this._isFetchingResponses = false;\n }\n\n // emit after updating _isFetchingResponses state\n this.emit(PollEvent.Responses, this.responses);\n }\n\n /**\n * Only responses made before the poll ended are valid\n * Refilter after an end event is recieved\n * To ensure responses are valid\n */\n private refilterResponsesOnEnd(): void {\n if (!this.responses) {\n return;\n }\n\n const pollEndTimestamp = this.endEvent?.getTs() || Number.MAX_SAFE_INTEGER;\n this.responses.getRelations().forEach((event) => {\n if (event.getTs() > pollEndTimestamp) {\n this.responses?.removeEvent(event);\n }\n });\n\n this.emit(PollEvent.Responses, this.responses);\n }\n\n private countUndecryptableEvents = (events: MatrixEvent[]): void => {\n const undecryptableEventIds = events\n .filter((event) => event.isDecryptionFailure())\n .map((event) => event.getId()!);\n\n const previousCount = this.undecryptableRelationsCount;\n this.undecryptableRelationEventIds = new Set([...this.undecryptableRelationEventIds, ...undecryptableEventIds]);\n\n if (this.undecryptableRelationsCount !== previousCount) {\n this.emit(PollEvent.UndecryptableRelations, this.undecryptableRelationsCount);\n }\n };\n\n private validateEndEvent(endEvent?: MatrixEvent): boolean {\n if (!endEvent) {\n return false;\n }\n /**\n * Repeated end events are ignored -\n * only the first (valid) closure event by origin_server_ts is counted.\n */\n if (this.endEvent && this.endEvent.getTs() < endEvent.getTs()) {\n return false;\n }\n\n /**\n * MSC3381\n * If a m.poll.end event is received from someone other than the poll creator or user with permission to redact\n * others' messages in the room, the event must be ignored by clients due to being invalid.\n */\n const roomCurrentState = this.room.currentState;\n const endEventSender = endEvent.getSender();\n return (\n !!endEventSender &&\n (endEventSender === this.rootEvent.getSender() ||\n roomCurrentState.maySendRedactionForEvent(this.rootEvent, endEventSender))\n );\n }\n}\n\n/**\n * Tests whether the event is a start, response or end poll event.\n *\n * @param event - Event to test\n * @returns true if the event is a poll event, else false\n */\nexport const isPollEvent = (event: MatrixEvent): boolean => {\n const eventType = event.getType();\n return M_POLL_START.matches(eventType) || M_POLL_RESPONSE.matches(eventType) || M_POLL_END.matches(eventType);\n};\n"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAASA,YAAY,QAAQ,mBAAmB;AAEhD,SAASC,UAAU,EAAEC,eAAe,QAAQ,oBAAoB;AAIhE,SAASC,SAAS,QAAQ,gBAAgB;AAE1C,SAASC,iBAAiB,QAAQ,0BAA0B;AAE5D,WAAYC,SAAS,0BAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAAA,OAATA,SAAS;AAAA;AAiBrB,IAAMC,uBAAuB,GAAGA,CAC5BC,cAA6B,EAC7BC,gBAAwB,KAGvB;EACD,IAAMC,cAAc,GAAGF,cAAc,CAACG,MAAM,CAAEC,KAAK,IAAK;IACpD,IAAIA,KAAK,CAACC,mBAAmB,CAAC,CAAC,EAAE;MAC7B;IACJ;IACA,OACIV,eAAe,CAACW,OAAO,CAACF,KAAK,CAACG,OAAO,CAAC,CAAC,CAAC;IACxC;IACA;IACAH,KAAK,CAACI,KAAK,CAAC,CAAC,IAAIP,gBAAgB;EAEzC,CAAC,CAAC;EAEF,OAAO;IAAEC;EAAe,CAAC;AAC7B,CAAC;AAED,OAAO,MAAMO,IAAI,SAASZ,iBAAiB,CAAyD;EAazFa,WAAWA,CACEC,SAAsB,EAC9BC,YAA0B,EAC1BC,IAAU,EACpB;IACE,KAAK,CAAC,CAAC;IAACC,eAAA,+BAfmB,KAAK;IAAAA,eAAA,oBAEE,IAAI;IAE1C;AACJ;AACA;AACA;IAHIA,eAAA,wCAIwC,IAAIC,GAAG,CAAS,CAAC;IAAAD,eAAA,mCAgKrBE,MAAqB,IAAW;MAChE,IAAMC,qBAAqB,GAAGD,MAAM,CAC/Bb,MAAM,CAAEC,KAAK,IAAKA,KAAK,CAACC,mBAAmB,CAAC,CAAC,CAAC,CAC9Ca,GAAG,CAAEd,KAAK,IAAKA,KAAK,CAACe,KAAK,CAAC,CAAE,CAAC;MAEnC,IAAMC,aAAa,GAAG,IAAI,CAACC,2BAA2B;MACtD,IAAI,CAACC,6BAA6B,GAAG,IAAIP,GAAG,CAAC,CAAC,GAAG,IAAI,CAACO,6BAA6B,EAAE,GAAGL,qBAAqB,CAAC,CAAC;MAE/G,IAAI,IAAI,CAACI,2BAA2B,KAAKD,aAAa,EAAE;QACpD,IAAI,CAACG,IAAI,CAACzB,SAAS,CAAC0B,sBAAsB,EAAE,IAAI,CAACH,2BAA2B,CAAC;MACjF;IACJ,CAAC;IAAA,KAxKmBV,SAAsB,GAAtBA,SAAsB;IAAA,KAC9BC,YAA0B,GAA1BA,YAA0B;IAAA,KAC1BC,IAAU,GAAVA,IAAU;IAGlB,IAAI,CAAC,IAAI,CAACF,SAAS,CAACc,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAACd,SAAS,CAACQ,KAAK,CAAC,CAAC,EAAE;MACxD,MAAM,IAAIO,KAAK,CAAC,2BAA2B,CAAC;IAChD;IACA,IAAI,CAACC,MAAM,GAAG,IAAI,CAAChB,SAAS,CAACc,SAAS,CAAC,CAAE;IACzC,IAAI,CAACG,SAAS,GAAG,IAAI,CAACjB,SAAS,CAACkB,uBAAoD;EACxF;EAEA,IAAWC,MAAMA,CAAA,EAAW;IACxB,OAAO,IAAI,CAACnB,SAAS,CAACQ,KAAK,CAAC,CAAC;EACjC;EAEA,IAAWY,UAAUA,CAAA,EAAuB;IAAA,IAAAC,cAAA;IACxC,QAAAA,cAAA,GAAO,IAAI,CAACC,QAAQ,cAAAD,cAAA,uBAAbA,cAAA,CAAeb,KAAK,CAAC,CAAC;EACjC;EAEA,IAAWe,OAAOA,CAAA,EAAY;IAC1B,OAAO,CAAC,CAAC,IAAI,CAACD,QAAQ;EAC1B;EAEA,IAAWE,mBAAmBA,CAAA,EAAY;IACtC,OAAO,IAAI,CAACC,oBAAoB;EACpC;EAEA,IAAWf,2BAA2BA,CAAA,EAAW;IAC7C,OAAO,IAAI,CAACC,6BAA6B,CAACe,IAAI;EAClD;EAEaC,YAAYA,CAAA,EAAuB;IAAA,IAAAC,KAAA;IAAA,OAAAC,iBAAA;MAC5C;MACA;MACA,IAAID,KAAI,CAACE,SAAS,EAAE;QAChB,OAAOF,KAAI,CAACE,SAAS;MACzB;;MAEA;MACA;MACA,IAAI,CAACF,KAAI,CAACJ,mBAAmB,EAAE;QAC3B,MAAMI,KAAI,CAACG,cAAc,CAAC,CAAC;MAC/B;MACA;MACA,OAAOH,KAAI,CAACE,SAAS;IAAE;EAC3B;;EAEA;AACJ;AACA;AACA;AACA;EACWE,aAAaA,CAACvC,KAAkB,EAAQ;IAAA,IAAAwC,eAAA;IAC3C,IAAIlD,UAAU,CAACY,OAAO,CAACF,KAAK,CAACG,OAAO,CAAC,CAAC,CAAC,IAAI,IAAI,CAACsC,gBAAgB,CAACzC,KAAK,CAAC,EAAE;MACrE,IAAI,CAAC6B,QAAQ,GAAG7B,KAAK;MACrB,IAAI,CAAC0C,sBAAsB,CAAC,CAAC;MAC7B,IAAI,CAACvB,IAAI,CAACzB,SAAS,CAACiD,GAAG,CAAC;IAC5B;;IAEA;IACA,IAAI,CAAC,IAAI,CAACN,SAAS,EAAE;MACjB;IACJ;IAEA,IAAMxC,gBAAgB,GAAG,EAAA2C,eAAA,OAAI,CAACX,QAAQ,cAAAW,eAAA,uBAAbA,eAAA,CAAepC,KAAK,CAAC,CAAC,KAAIwC,MAAM,CAACC,gBAAgB;IAC1E,IAAAC,qBAAA,GAA2BnD,uBAAuB,CAAC,CAACK,KAAK,CAAC,EAAEH,gBAAgB,CAAC;MAArEC,cAAc,GAAAgD,qBAAA,CAAdhD,cAAc;IAEtB,IAAI,CAACiD,wBAAwB,CAAC,CAAC/C,KAAK,CAAC,CAAC;IAEtC,IAAIF,cAAc,CAACkD,MAAM,EAAE;MACvBlD,cAAc,CAACmD,OAAO,CAAEjD,KAAK,IAAK;QAC9B,IAAI,CAACqC,SAAS,CAAEa,QAAQ,CAAClD,KAAK,CAAC;MACnC,CAAC,CAAC;MAEF,IAAI,CAACmB,IAAI,CAACzB,SAAS,CAACyD,SAAS,EAAE,IAAI,CAACd,SAAS,CAAC;IAClD;EACJ;EAEcC,cAAcA,CAAA,EAAkB;IAAA,IAAAc,MAAA;IAAA,OAAAhB,iBAAA;MAAA,IAAAiB,eAAA,EAAAC,qBAAA;MAC1CF,MAAI,CAACpB,oBAAoB,GAAG,IAAI;;MAEhC;MACA;MACA;MACA;MACA,IAAMuB,YAAY,SAASH,MAAI,CAAC5C,YAAY,CAACgD,SAAS,CAClDJ,MAAI,CAAC7B,MAAM,EACX6B,MAAI,CAAC7C,SAAS,CAACQ,KAAK,CAAC,CAAC,EACtB,aAAa,EACb0C,SAAS,EACT;QACIC,IAAI,EAAEN,MAAI,CAACO,kBAAkB,IAAIF;MACrC,CACJ,CAAC;MAED,MAAMG,OAAO,CAACC,GAAG,CAACN,YAAY,CAAC3C,MAAM,CAACE,GAAG,CAAEd,KAAK,IAAKoD,MAAI,CAAC5C,YAAY,CAACsD,oBAAoB,CAAC9D,KAAK,CAAC,CAAC,CAAC;MAEpG,IAAMqC,SAAS,GACXe,MAAI,CAACf,SAAS,IACd,IAAI7C,SAAS,CAAC,aAAa,EAAED,eAAe,CAACwE,IAAI,EAAEX,MAAI,CAAC5C,YAAY,EAAE,CAACjB,eAAe,CAACyE,OAAO,CAAE,CAAC;MAErG,IAAMC,YAAY,GAAGV,YAAY,CAAC3C,MAAM,CAACsD,IAAI,CAAElE,KAAK,IAAKV,UAAU,CAACY,OAAO,CAACF,KAAK,CAACG,OAAO,CAAC,CAAC,CAAC,CAAC;MAE7F,IAAIiD,MAAI,CAACX,gBAAgB,CAACwB,YAAY,CAAC,EAAE;QACrCb,MAAI,CAACvB,QAAQ,GAAGoC,YAAY;QAC5Bb,MAAI,CAACV,sBAAsB,CAAC,CAAC;QAC7BU,MAAI,CAACjC,IAAI,CAACzB,SAAS,CAACiD,GAAG,CAAC;MAC5B;MAEA,IAAMwB,kBAAkB,GAAG,EAAAd,eAAA,GAAAD,MAAI,CAACvB,QAAQ,cAAAwB,eAAA,uBAAbA,eAAA,CAAejD,KAAK,CAAC,CAAC,KAAIwC,MAAM,CAACC,gBAAgB;MAE5E,IAAAuB,sBAAA,GAA2BzE,uBAAuB,CAAC4D,YAAY,CAAC3C,MAAM,EAAEuD,kBAAkB,CAAC;QAAnFrE,cAAc,GAAAsE,sBAAA,CAAdtE,cAAc;MAEtBA,cAAc,CAACmD,OAAO,CAAEjD,KAAK,IAAK;QAC9BqC,SAAS,CAACa,QAAQ,CAAClD,KAAK,CAAC;MAC7B,CAAC,CAAC;MAEFoD,MAAI,CAACO,kBAAkB,IAAAL,qBAAA,GAAGC,YAAY,CAACc,SAAS,cAAAf,qBAAA,cAAAA,qBAAA,GAAIG,SAAS;MAC7DL,MAAI,CAACf,SAAS,GAAGA,SAAS;MAC1Be,MAAI,CAACL,wBAAwB,CAACQ,YAAY,CAAC3C,MAAM,CAAC;;MAElD;MACA;MACA,IAAIwC,MAAI,CAACO,kBAAkB,EAAE;QACzB;QACA;QACAP,MAAI,CAACd,cAAc,CAAC,CAAC;MACzB,CAAC,MAAM;QACH;QACAc,MAAI,CAACpB,oBAAoB,GAAG,KAAK;MACrC;;MAEA;MACAoB,MAAI,CAACjC,IAAI,CAACzB,SAAS,CAACyD,SAAS,EAAEC,MAAI,CAACf,SAAS,CAAC;IAAC;EACnD;;EAEA;AACJ;AACA;AACA;AACA;EACYK,sBAAsBA,CAAA,EAAS;IAAA,IAAA4B,eAAA;IACnC,IAAI,CAAC,IAAI,CAACjC,SAAS,EAAE;MACjB;IACJ;IAEA,IAAMxC,gBAAgB,GAAG,EAAAyE,eAAA,OAAI,CAACzC,QAAQ,cAAAyC,eAAA,uBAAbA,eAAA,CAAelE,KAAK,CAAC,CAAC,KAAIwC,MAAM,CAACC,gBAAgB;IAC1E,IAAI,CAACR,SAAS,CAACkC,YAAY,CAAC,CAAC,CAACtB,OAAO,CAAEjD,KAAK,IAAK;MAC7C,IAAIA,KAAK,CAACI,KAAK,CAAC,CAAC,GAAGP,gBAAgB,EAAE;QAAA,IAAA2E,eAAA;QAClC,CAAAA,eAAA,OAAI,CAACnC,SAAS,cAAAmC,eAAA,eAAdA,eAAA,CAAgBC,WAAW,CAACzE,KAAK,CAAC;MACtC;IACJ,CAAC,CAAC;IAEF,IAAI,CAACmB,IAAI,CAACzB,SAAS,CAACyD,SAAS,EAAE,IAAI,CAACd,SAAS,CAAC;EAClD;EAeQI,gBAAgBA,CAACZ,QAAsB,EAAW;IACtD,IAAI,CAACA,QAAQ,EAAE;MACX,OAAO,KAAK;IAChB;IACA;AACR;AACA;AACA;IACQ,IAAI,IAAI,CAACA,QAAQ,IAAI,IAAI,CAACA,QAAQ,CAACzB,KAAK,CAAC,CAAC,GAAGyB,QAAQ,CAACzB,KAAK,CAAC,CAAC,EAAE;MAC3D,OAAO,KAAK;IAChB;;IAEA;AACR;AACA;AACA;AACA;IACQ,IAAMsE,gBAAgB,GAAG,IAAI,CAACjE,IAAI,CAACkE,YAAY;IAC/C,IAAMC,cAAc,GAAG/C,QAAQ,CAACgD,SAAS,CAAC,CAAC;IAC3C,OACI,CAAC,CAACD,cAAc,KACfA,cAAc,KAAK,IAAI,CAACrE,SAAS,CAACsE,SAAS,CAAC,CAAC,IAC1CH,gBAAgB,CAACI,wBAAwB,CAAC,IAAI,CAACvE,SAAS,EAAEqE,cAAc,CAAC,CAAC;EAEtF;AACJ;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,IAAMG,WAAW,GAAI/E,KAAkB,IAAc;EACxD,IAAMgF,SAAS,GAAGhF,KAAK,CAACG,OAAO,CAAC,CAAC;EACjC,OAAOd,YAAY,CAACa,OAAO,CAAC8E,SAAS,CAAC,IAAIzF,eAAe,CAACW,OAAO,CAAC8E,SAAS,CAAC,IAAI1F,UAAU,CAACY,OAAO,CAAC8E,SAAS,CAAC;AACjH,CAAC","ignoreList":[]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
1
2
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
3
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
3
4
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
@@ -74,7 +75,10 @@ export class ReadReceipt extends TypedEventEmitter {
|
|
|
74
75
|
var _this$receipts$get$ge, _this$receipts$get;
|
|
75
76
|
var ignoreSynthesized = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
76
77
|
var receiptType = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ReceiptType.Read;
|
|
77
|
-
var
|
|
78
|
+
var _ref = (_this$receipts$get$ge = (_this$receipts$get = this.receipts.get(receiptType)) === null || _this$receipts$get === void 0 ? void 0 : _this$receipts$get.get(userId)) !== null && _this$receipts$get$ge !== void 0 ? _this$receipts$get$ge : [null, null],
|
|
79
|
+
_ref2 = _slicedToArray(_ref, 2),
|
|
80
|
+
realReceipt = _ref2[0],
|
|
81
|
+
syntheticReceipt = _ref2[1];
|
|
78
82
|
if (ignoreSynthesized) {
|
|
79
83
|
return realReceipt;
|
|
80
84
|
}
|
|
@@ -191,7 +195,7 @@ export class ReadReceipt extends TypedEventEmitter {
|
|
|
191
195
|
return false;
|
|
192
196
|
}
|
|
193
197
|
getLatestReceipt(userId, ignoreSynthesized) {
|
|
194
|
-
var
|
|
198
|
+
var _ref3, _ref4;
|
|
195
199
|
// XXX: This is very very ugly and I hope I won't have to ever add a new
|
|
196
200
|
// receipt type here again. IMHO this should be done by the server in
|
|
197
201
|
// some more intelligent manner or the client should just use timestamps
|
|
@@ -207,10 +211,10 @@ export class ReadReceipt extends TypedEventEmitter {
|
|
|
207
211
|
|
|
208
212
|
// The public receipt is more likely to drift out of date so the private
|
|
209
213
|
// one has precedence
|
|
210
|
-
if (!comparison) return (
|
|
214
|
+
if (!comparison) return (_ref3 = privateReadReceipt !== null && privateReadReceipt !== void 0 ? privateReadReceipt : publicReadReceipt) !== null && _ref3 !== void 0 ? _ref3 : null;
|
|
211
215
|
|
|
212
216
|
// If public read receipt is older, return the private one
|
|
213
|
-
return (
|
|
217
|
+
return (_ref4 = comparison < 0 ? privateReadReceipt : publicReadReceipt) !== null && _ref4 !== void 0 ? _ref4 : null;
|
|
214
218
|
}
|
|
215
219
|
addReceiptToStructure(eventId, receiptType, userId, receipt, synthetic) {
|
|
216
220
|
var _pair$ReceiptPairSynt2, _pair$ReceiptPairSynt3;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read-receipt.js","names":["MAIN_ROOM_TIMELINE","ReceiptType","TypedEventEmitter","isSupportedReceiptType","MatrixEvent","EventType","MapWithDefault","NotificationCountType","logger","inMainTimelineForReceipt","threadIdForReceipt","synthesizeReceipt","userId","event","receiptType","unthreaded","arguments","length","undefined","content","getId","_objectSpread","ts","getTs","thread_id","type","Receipt","room_id","getRoomId","ReceiptPairRealIndex","ReceiptPairSyntheticIndex","ReadReceipt","constructor","_defineProperty","Map","getReadReceiptForUserId","_this$receipts$get$ge","_this$receipts$get","ignoreSynthesized","Read","realReceipt","syntheticReceipt","receipts","get","compareReceipts","a","b","_this$getUnfilteredTi","getUnfilteredTimelineSet","compareEventOrdering","eventId","data","getEventReadUpTo","latestReceipt","getLatestReceipt","receiptPointsAtConsistentEvent","receipt","_receipt$data","findEventById","eventIsInMainTimeline","threadRootId","warn","concat","_ref","_ref2","publicReadReceipt","privateReadReceipt","ReadPrivate","comparison","addReceiptToStructure","synthetic","_pair$ReceiptPairSynt2","_pair$ReceiptPairSynt3","receiptTypesMap","getOrCreate","pair","set","existingReceipt","_pair$ReceiptPairSynt","wrappedReceipt","ordering","preferSynthetic","cachedReceipt","newCachedReceipt","receiptCacheByEventId","previousEventId","filter","r","delete","push","getReceiptsForEvent","fixupNotifications","lastEvent","timeline","getSender","setUnread","Total","Highlight","addLocalEchoReceipt","e","addReceipt","getUsersReadUpTo","map"],"sources":["../../src/models/read-receipt.ts"],"sourcesContent":["/*\nCopyright 2022 The Matrix.org Foundation C.I.C.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n http://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR 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 type CachedReceipt,\n MAIN_ROOM_TIMELINE,\n type Receipt,\n type ReceiptCache,\n ReceiptType,\n type WrappedReceipt,\n} from \"../@types/read_receipts.ts\";\nimport { type ListenerMap, TypedEventEmitter } from \"./typed-event-emitter.ts\";\nimport { isSupportedReceiptType } from \"../utils.ts\";\nimport { MatrixEvent } from \"./event.ts\";\nimport { EventType } from \"../@types/event.ts\";\nimport { type EventTimelineSet } from \"./event-timeline-set.ts\";\nimport { MapWithDefault } from \"../utils.ts\";\nimport { NotificationCountType } from \"./room.ts\";\nimport { logger } from \"../logger.ts\";\nimport { inMainTimelineForReceipt, threadIdForReceipt } from \"../client.ts\";\n\n/**\n * Create a synthetic receipt for the given event\n * @param userId - The user ID if the receipt sender\n * @param event - The event that is to be acknowledged\n * @param receiptType - The type of receipt\n * @param unthreaded - the receipt is unthreaded\n * @returns a new event with the synthetic receipt in it\n */\nexport function synthesizeReceipt(\n userId: string,\n event: MatrixEvent,\n receiptType: ReceiptType,\n unthreaded = false,\n): MatrixEvent {\n return new MatrixEvent({\n content: {\n [event.getId()!]: {\n [receiptType]: {\n [userId]: {\n ts: event.getTs(),\n ...(!unthreaded && { thread_id: threadIdForReceipt(event) }),\n },\n },\n },\n },\n type: EventType.Receipt,\n room_id: event.getRoomId(),\n });\n}\n\nconst ReceiptPairRealIndex = 0;\nconst ReceiptPairSyntheticIndex = 1;\n\nexport abstract class ReadReceipt<\n Events extends string,\n Arguments extends ListenerMap<Events>,\n SuperclassArguments extends ListenerMap<any> = Arguments,\n> extends TypedEventEmitter<Events, Arguments, SuperclassArguments> {\n // receipts should clobber based on receipt_type and user_id pairs hence\n // the form of this structure. This is sub-optimal for the exposed APIs\n // which pass in an event ID and get back some receipts, so we also store\n // a pre-cached list for this purpose.\n // Map: receipt type → user Id → receipt\n private receipts = new MapWithDefault<\n string,\n Map<string, [realReceipt: WrappedReceipt | null, syntheticReceipt: WrappedReceipt | null]>\n >(() => new Map());\n private receiptCacheByEventId: ReceiptCache = new Map();\n\n public abstract getUnfilteredTimelineSet(): EventTimelineSet;\n public abstract get timeline(): MatrixEvent[];\n\n /**\n * Gets the latest receipt for a given user in the room\n * @param userId - The id of the user for which we want the receipt\n * @param ignoreSynthesized - Whether to ignore synthesized receipts or not\n * @param receiptType - Optional. The type of the receipt we want to get\n * @returns the latest receipts of the chosen type for the chosen user\n */\n public getReadReceiptForUserId(\n userId: string,\n ignoreSynthesized = false,\n receiptType = ReceiptType.Read,\n ): WrappedReceipt | null {\n const [realReceipt, syntheticReceipt] = this.receipts.get(receiptType)?.get(userId) ?? [null, null];\n if (ignoreSynthesized) {\n return realReceipt;\n }\n\n return syntheticReceipt ?? realReceipt;\n }\n\n private compareReceipts(a: WrappedReceipt, b: WrappedReceipt): number {\n // Try compare them in our unfiltered timeline set order, falling back to receipt timestamp which should be\n // relatively sane as receipts are set only by the originating homeserver so as long as its clock doesn't\n // jump around then it should be valid.\n return this.getUnfilteredTimelineSet().compareEventOrdering(a.eventId, b.eventId) ?? a.data.ts - b.data.ts;\n }\n\n /**\n * Get the ID of the event that a given user has read up to, or null if:\n * - we have received no read receipts for them, or\n * - the receipt we have points at an event we don't have, or\n * - the thread ID in the receipt does not match the thread root of the\n * referenced event.\n *\n * (The event might not exist if it is not loaded, and the thread ID might\n * not match if the event has moved thread because it was redacted.)\n *\n * @param userId - The user ID to get read receipt event ID for\n * @param ignoreSynthesized - If true, return only receipts that have been\n * sent by the server, not implicit ones generated\n * by the JS SDK.\n * @returns ID of the latest existing event that the given user has read, or null.\n */\n public getEventReadUpTo(userId: string, ignoreSynthesized = false): string | null {\n // Find what the latest receipt says is the latest event we have read\n const latestReceipt = this.getLatestReceipt(userId, ignoreSynthesized);\n\n if (!latestReceipt) {\n return null;\n }\n\n return this.receiptPointsAtConsistentEvent(latestReceipt) ? latestReceipt.eventId : null;\n }\n\n /**\n * Returns true if the event pointed at by this receipt exists, and its\n * threadRootId is consistent with the thread information in the receipt.\n */\n private receiptPointsAtConsistentEvent(receipt: WrappedReceipt): boolean {\n const event = this.findEventById(receipt.eventId);\n if (!event) {\n // If the receipt points at a non-existent event, we have multiple\n // possibilities:\n //\n // 1. We don't have the event because it's not loaded yet - probably\n // it's old and we're best off ignoring the receipt - we can just\n // send a new one when we read a new event.\n //\n // 2. We have a bug e.g. we misclassified this event into the wrong\n // thread.\n //\n // 3. The referenced event moved out of this thread (e.g. because it\n // was deleted.)\n //\n // 4. The receipt had the incorrect thread ID (due to a bug in a\n // client, or malicious behaviour).\n\n // This receipt is not \"valid\" because it doesn't point at an event\n // we have. We want to pretend it doesn't exist.\n return false;\n }\n\n if (!receipt.data?.thread_id) {\n // If this is an unthreaded receipt, it could point at any event, so\n // there is no need to validate further - this receipt is valid.\n return true;\n }\n // Otherwise it is a threaded receipt...\n\n if (receipt.data.thread_id === MAIN_ROOM_TIMELINE) {\n // The receipt is for the main timeline: we check that the event is\n // in the main timeline.\n\n // Check if the event is in the main timeline\n const eventIsInMainTimeline = inMainTimelineForReceipt(event);\n\n if (eventIsInMainTimeline) {\n // The receipt is for the main timeline, and so is the event, so\n // the receipt is valid.\n return true;\n }\n } else {\n // The receipt is for a different thread (not the main timeline)\n\n if (event.threadRootId === receipt.data.thread_id) {\n // If the receipt and event agree on the thread ID, the receipt\n // is valid.\n return true;\n }\n }\n\n // The receipt thread ID disagrees with the event thread ID. There are 2\n // possibilities:\n //\n // 1. The event moved to a different thread after the receipt was\n // created. This can happen if the event was redacted because that\n // moves it to the main timeline.\n //\n // 2. There is a bug somewhere - either we put the event into the wrong\n // thread, or someone sent an incorrect receipt.\n //\n // In many cases, we won't get here because the call to findEventById\n // would have already returned null. We include this check to cover\n // cases when `this` is a room, meaning findEventById will find events\n // in any thread, and to be defensive against unforeseen code paths.\n logger.warn(\n `Ignoring receipt because its thread_id (${receipt.data.thread_id}) disagrees ` +\n `with the thread root (${event.threadRootId}) of the referenced event ` +\n `(event ID = ${receipt.eventId})`,\n );\n\n // This receipt is not \"valid\" because it disagrees with us about what\n // thread the event is in. We want to pretend it doesn't exist.\n return false;\n }\n\n private getLatestReceipt(userId: string, ignoreSynthesized: boolean): WrappedReceipt | null {\n // XXX: This is very very ugly and I hope I won't have to ever add a new\n // receipt type here again. IMHO this should be done by the server in\n // some more intelligent manner or the client should just use timestamps\n\n const publicReadReceipt = this.getReadReceiptForUserId(userId, ignoreSynthesized, ReceiptType.Read);\n const privateReadReceipt = this.getReadReceiptForUserId(userId, ignoreSynthesized, ReceiptType.ReadPrivate);\n\n // If we have both, compare them\n let comparison: number | null | undefined;\n if (publicReadReceipt?.eventId && privateReadReceipt?.eventId) {\n comparison = this.compareReceipts(publicReadReceipt, privateReadReceipt);\n }\n\n // The public receipt is more likely to drift out of date so the private\n // one has precedence\n if (!comparison) return privateReadReceipt ?? publicReadReceipt ?? null;\n\n // If public read receipt is older, return the private one\n return (comparison < 0 ? privateReadReceipt : publicReadReceipt) ?? null;\n }\n\n public addReceiptToStructure(\n eventId: string,\n receiptType: ReceiptType,\n userId: string,\n receipt: Receipt,\n synthetic: boolean,\n ): void {\n const receiptTypesMap = this.receipts.getOrCreate(receiptType);\n let pair = receiptTypesMap.get(userId);\n\n if (!pair) {\n pair = [null, null];\n receiptTypesMap.set(userId, pair);\n }\n\n let existingReceipt = pair[ReceiptPairRealIndex];\n if (synthetic) {\n existingReceipt = pair[ReceiptPairSyntheticIndex] ?? pair[ReceiptPairRealIndex];\n }\n\n const wrappedReceipt: WrappedReceipt = {\n eventId,\n data: receipt,\n };\n\n if (existingReceipt) {\n // We only want to add this receipt if we think it is later than the one we already have.\n // This is managed server-side, but because we synthesize RRs locally we have to do it here too.\n const ordering = this.compareReceipts(existingReceipt, wrappedReceipt);\n if (ordering >= 0) {\n return;\n }\n }\n\n const realReceipt = synthetic ? pair[ReceiptPairRealIndex] : wrappedReceipt;\n const syntheticReceipt = synthetic ? wrappedReceipt : pair[ReceiptPairSyntheticIndex];\n\n let ordering: number | null = null;\n if (realReceipt && syntheticReceipt) {\n ordering = this.getUnfilteredTimelineSet().compareEventOrdering(\n realReceipt.eventId,\n syntheticReceipt.eventId,\n );\n }\n\n const preferSynthetic = ordering === null || ordering < 0;\n\n // we don't bother caching just real receipts by event ID as there's nothing that would read it.\n // Take the current cached receipt before we overwrite the pair elements.\n const cachedReceipt = pair[ReceiptPairSyntheticIndex] ?? pair[ReceiptPairRealIndex];\n\n if (synthetic && preferSynthetic) {\n pair[ReceiptPairSyntheticIndex] = wrappedReceipt;\n } else if (!synthetic) {\n pair[ReceiptPairRealIndex] = wrappedReceipt;\n\n if (!preferSynthetic) {\n pair[ReceiptPairSyntheticIndex] = null;\n }\n }\n\n const newCachedReceipt = pair[ReceiptPairSyntheticIndex] ?? pair[ReceiptPairRealIndex];\n if (cachedReceipt === newCachedReceipt) return;\n\n // clean up any previous cache entry\n if (cachedReceipt && this.receiptCacheByEventId.get(cachedReceipt.eventId)) {\n const previousEventId = cachedReceipt.eventId;\n // Remove the receipt we're about to clobber out of existence from the cache\n this.receiptCacheByEventId.set(\n previousEventId,\n this.receiptCacheByEventId.get(previousEventId)!.filter((r) => {\n return r.type !== receiptType || r.userId !== userId;\n }),\n );\n\n if (this.receiptCacheByEventId.get(previousEventId)!.length < 1) {\n this.receiptCacheByEventId.delete(previousEventId); // clean up the cache keys\n }\n }\n\n // cache the new one\n if (!this.receiptCacheByEventId.get(eventId)) {\n this.receiptCacheByEventId.set(eventId, []);\n }\n this.receiptCacheByEventId.get(eventId)!.push({\n userId: userId,\n type: receiptType as ReceiptType,\n data: receipt,\n });\n }\n\n /**\n * Get a list of receipts for the given event.\n * @param event - the event to get receipts for\n * @returns A list of receipts with a userId, type and data keys or\n * an empty list.\n */\n public getReceiptsForEvent(event: MatrixEvent): CachedReceipt[] {\n return this.receiptCacheByEventId.get(event.getId()!) || [];\n }\n\n public abstract addReceipt(event: MatrixEvent, synthetic: boolean): void;\n\n public abstract setUnread(type: NotificationCountType, count: number): void;\n\n /**\n * Look in this room/thread's timeline to find an event. If `this` is a\n * room, we look in all threads, but if `this` is a thread, we look only\n * inside this thread.\n */\n public abstract findEventById(eventId: string): MatrixEvent | undefined;\n\n /**\n * This issue should also be addressed on synapse's side and is tracked as part\n * of https://github.com/matrix-org/synapse/issues/14837\n *\n * Retrieves the read receipt for the logged in user and checks if it matches\n * the last event in the room and whether that event originated from the logged\n * in user.\n * Under those conditions we can consider the context as read. This is useful\n * because we never send read receipts against our own events\n * @param userId - the logged in user\n */\n public fixupNotifications(userId: string): void {\n const receipt = this.getReadReceiptForUserId(userId, false);\n\n const lastEvent = this.timeline[this.timeline.length - 1];\n if (lastEvent && receipt?.eventId === lastEvent.getId() && userId === lastEvent.getSender()) {\n this.setUnread(NotificationCountType.Total, 0);\n this.setUnread(NotificationCountType.Highlight, 0);\n }\n }\n\n /**\n * Add a temporary local-echo receipt to the room to reflect in the\n * client the fact that we've sent one.\n * @param userId - The user ID if the receipt sender\n * @param e - The event that is to be acknowledged\n * @param receiptType - The type of receipt\n * @param unthreaded - the receipt is unthreaded\n */\n public addLocalEchoReceipt(userId: string, e: MatrixEvent, receiptType: ReceiptType, unthreaded = false): void {\n this.addReceipt(synthesizeReceipt(userId, e, receiptType, unthreaded), true);\n }\n\n /**\n * Get a list of user IDs who have <b>read up to</b> the given event.\n * @param event - the event to get read receipts for.\n * @returns A list of user IDs.\n */\n public getUsersReadUpTo(event: MatrixEvent): string[] {\n return this.getReceiptsForEvent(event)\n .filter(function (receipt) {\n return isSupportedReceiptType(receipt.type);\n })\n .map(function (receipt) {\n return receipt.userId;\n });\n }\n\n /**\n * Determines if the given user has read a particular event ID with the known\n * history of the room. This is not a definitive check as it relies only on\n * what is available to the room at the time of execution.\n * @param userId - The user ID to check the read state of.\n * @param eventId - The event ID to check if the user read.\n * @returns True if the user has read the event, false otherwise.\n */\n public abstract hasUserReadEvent(userId: string, eventId: string): boolean;\n\n /**\n * Returns the most recent unthreaded receipt for a given user\n * @param userId - the MxID of the User\n * @returns an unthreaded Receipt. Can be undefined if receipts have been disabled\n * or a user chooses to use private read receipts (or we have simply not received\n * a receipt from this user yet).\n *\n * @deprecated use `hasUserReadEvent` or `getEventReadUpTo` instead\n */\n public abstract getLastUnthreadedReceiptFor(userId: string): Receipt | undefined;\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"read-receipt.js","names":["MAIN_ROOM_TIMELINE","ReceiptType","TypedEventEmitter","isSupportedReceiptType","MatrixEvent","EventType","MapWithDefault","NotificationCountType","logger","inMainTimelineForReceipt","threadIdForReceipt","synthesizeReceipt","userId","event","receiptType","unthreaded","arguments","length","undefined","content","getId","_objectSpread","ts","getTs","thread_id","type","Receipt","room_id","getRoomId","ReceiptPairRealIndex","ReceiptPairSyntheticIndex","ReadReceipt","constructor","_defineProperty","Map","getReadReceiptForUserId","_this$receipts$get$ge","_this$receipts$get","ignoreSynthesized","Read","_ref","receipts","get","_ref2","_slicedToArray","realReceipt","syntheticReceipt","compareReceipts","a","b","_this$getUnfilteredTi","getUnfilteredTimelineSet","compareEventOrdering","eventId","data","getEventReadUpTo","latestReceipt","getLatestReceipt","receiptPointsAtConsistentEvent","receipt","_receipt$data","findEventById","eventIsInMainTimeline","threadRootId","warn","concat","_ref3","_ref4","publicReadReceipt","privateReadReceipt","ReadPrivate","comparison","addReceiptToStructure","synthetic","_pair$ReceiptPairSynt2","_pair$ReceiptPairSynt3","receiptTypesMap","getOrCreate","pair","set","existingReceipt","_pair$ReceiptPairSynt","wrappedReceipt","ordering","preferSynthetic","cachedReceipt","newCachedReceipt","receiptCacheByEventId","previousEventId","filter","r","delete","push","getReceiptsForEvent","fixupNotifications","lastEvent","timeline","getSender","setUnread","Total","Highlight","addLocalEchoReceipt","e","addReceipt","getUsersReadUpTo","map"],"sources":["../../src/models/read-receipt.ts"],"sourcesContent":["/*\nCopyright 2022 The Matrix.org Foundation C.I.C.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n http://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR 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 type CachedReceipt,\n MAIN_ROOM_TIMELINE,\n type Receipt,\n type ReceiptCache,\n ReceiptType,\n type WrappedReceipt,\n} from \"../@types/read_receipts.ts\";\nimport { type ListenerMap, TypedEventEmitter } from \"./typed-event-emitter.ts\";\nimport { isSupportedReceiptType } from \"../utils.ts\";\nimport { MatrixEvent } from \"./event.ts\";\nimport { EventType } from \"../@types/event.ts\";\nimport { type EventTimelineSet } from \"./event-timeline-set.ts\";\nimport { MapWithDefault } from \"../utils.ts\";\nimport { NotificationCountType } from \"./room.ts\";\nimport { logger } from \"../logger.ts\";\nimport { inMainTimelineForReceipt, threadIdForReceipt } from \"../client.ts\";\n\n/**\n * Create a synthetic receipt for the given event\n * @param userId - The user ID if the receipt sender\n * @param event - The event that is to be acknowledged\n * @param receiptType - The type of receipt\n * @param unthreaded - the receipt is unthreaded\n * @returns a new event with the synthetic receipt in it\n */\nexport function synthesizeReceipt(\n userId: string,\n event: MatrixEvent,\n receiptType: ReceiptType,\n unthreaded = false,\n): MatrixEvent {\n return new MatrixEvent({\n content: {\n [event.getId()!]: {\n [receiptType]: {\n [userId]: {\n ts: event.getTs(),\n ...(!unthreaded && { thread_id: threadIdForReceipt(event) }),\n },\n },\n },\n },\n type: EventType.Receipt,\n room_id: event.getRoomId(),\n });\n}\n\nconst ReceiptPairRealIndex = 0;\nconst ReceiptPairSyntheticIndex = 1;\n\nexport abstract class ReadReceipt<\n Events extends string,\n Arguments extends ListenerMap<Events>,\n SuperclassArguments extends ListenerMap<any> = Arguments,\n> extends TypedEventEmitter<Events, Arguments, SuperclassArguments> {\n // receipts should clobber based on receipt_type and user_id pairs hence\n // the form of this structure. This is sub-optimal for the exposed APIs\n // which pass in an event ID and get back some receipts, so we also store\n // a pre-cached list for this purpose.\n // Map: receipt type → user Id → receipt\n private receipts = new MapWithDefault<\n string,\n Map<string, [realReceipt: WrappedReceipt | null, syntheticReceipt: WrappedReceipt | null]>\n >(() => new Map());\n private receiptCacheByEventId: ReceiptCache = new Map();\n\n public abstract getUnfilteredTimelineSet(): EventTimelineSet;\n public abstract get timeline(): MatrixEvent[];\n\n /**\n * Gets the latest receipt for a given user in the room\n * @param userId - The id of the user for which we want the receipt\n * @param ignoreSynthesized - Whether to ignore synthesized receipts or not\n * @param receiptType - Optional. The type of the receipt we want to get\n * @returns the latest receipts of the chosen type for the chosen user\n */\n public getReadReceiptForUserId(\n userId: string,\n ignoreSynthesized = false,\n receiptType = ReceiptType.Read,\n ): WrappedReceipt | null {\n const [realReceipt, syntheticReceipt] = this.receipts.get(receiptType)?.get(userId) ?? [null, null];\n if (ignoreSynthesized) {\n return realReceipt;\n }\n\n return syntheticReceipt ?? realReceipt;\n }\n\n private compareReceipts(a: WrappedReceipt, b: WrappedReceipt): number {\n // Try compare them in our unfiltered timeline set order, falling back to receipt timestamp which should be\n // relatively sane as receipts are set only by the originating homeserver so as long as its clock doesn't\n // jump around then it should be valid.\n return this.getUnfilteredTimelineSet().compareEventOrdering(a.eventId, b.eventId) ?? a.data.ts - b.data.ts;\n }\n\n /**\n * Get the ID of the event that a given user has read up to, or null if:\n * - we have received no read receipts for them, or\n * - the receipt we have points at an event we don't have, or\n * - the thread ID in the receipt does not match the thread root of the\n * referenced event.\n *\n * (The event might not exist if it is not loaded, and the thread ID might\n * not match if the event has moved thread because it was redacted.)\n *\n * @param userId - The user ID to get read receipt event ID for\n * @param ignoreSynthesized - If true, return only receipts that have been\n * sent by the server, not implicit ones generated\n * by the JS SDK.\n * @returns ID of the latest existing event that the given user has read, or null.\n */\n public getEventReadUpTo(userId: string, ignoreSynthesized = false): string | null {\n // Find what the latest receipt says is the latest event we have read\n const latestReceipt = this.getLatestReceipt(userId, ignoreSynthesized);\n\n if (!latestReceipt) {\n return null;\n }\n\n return this.receiptPointsAtConsistentEvent(latestReceipt) ? latestReceipt.eventId : null;\n }\n\n /**\n * Returns true if the event pointed at by this receipt exists, and its\n * threadRootId is consistent with the thread information in the receipt.\n */\n private receiptPointsAtConsistentEvent(receipt: WrappedReceipt): boolean {\n const event = this.findEventById(receipt.eventId);\n if (!event) {\n // If the receipt points at a non-existent event, we have multiple\n // possibilities:\n //\n // 1. We don't have the event because it's not loaded yet - probably\n // it's old and we're best off ignoring the receipt - we can just\n // send a new one when we read a new event.\n //\n // 2. We have a bug e.g. we misclassified this event into the wrong\n // thread.\n //\n // 3. The referenced event moved out of this thread (e.g. because it\n // was deleted.)\n //\n // 4. The receipt had the incorrect thread ID (due to a bug in a\n // client, or malicious behaviour).\n\n // This receipt is not \"valid\" because it doesn't point at an event\n // we have. We want to pretend it doesn't exist.\n return false;\n }\n\n if (!receipt.data?.thread_id) {\n // If this is an unthreaded receipt, it could point at any event, so\n // there is no need to validate further - this receipt is valid.\n return true;\n }\n // Otherwise it is a threaded receipt...\n\n if (receipt.data.thread_id === MAIN_ROOM_TIMELINE) {\n // The receipt is for the main timeline: we check that the event is\n // in the main timeline.\n\n // Check if the event is in the main timeline\n const eventIsInMainTimeline = inMainTimelineForReceipt(event);\n\n if (eventIsInMainTimeline) {\n // The receipt is for the main timeline, and so is the event, so\n // the receipt is valid.\n return true;\n }\n } else {\n // The receipt is for a different thread (not the main timeline)\n\n if (event.threadRootId === receipt.data.thread_id) {\n // If the receipt and event agree on the thread ID, the receipt\n // is valid.\n return true;\n }\n }\n\n // The receipt thread ID disagrees with the event thread ID. There are 2\n // possibilities:\n //\n // 1. The event moved to a different thread after the receipt was\n // created. This can happen if the event was redacted because that\n // moves it to the main timeline.\n //\n // 2. There is a bug somewhere - either we put the event into the wrong\n // thread, or someone sent an incorrect receipt.\n //\n // In many cases, we won't get here because the call to findEventById\n // would have already returned null. We include this check to cover\n // cases when `this` is a room, meaning findEventById will find events\n // in any thread, and to be defensive against unforeseen code paths.\n logger.warn(\n `Ignoring receipt because its thread_id (${receipt.data.thread_id}) disagrees ` +\n `with the thread root (${event.threadRootId}) of the referenced event ` +\n `(event ID = ${receipt.eventId})`,\n );\n\n // This receipt is not \"valid\" because it disagrees with us about what\n // thread the event is in. We want to pretend it doesn't exist.\n return false;\n }\n\n private getLatestReceipt(userId: string, ignoreSynthesized: boolean): WrappedReceipt | null {\n // XXX: This is very very ugly and I hope I won't have to ever add a new\n // receipt type here again. IMHO this should be done by the server in\n // some more intelligent manner or the client should just use timestamps\n\n const publicReadReceipt = this.getReadReceiptForUserId(userId, ignoreSynthesized, ReceiptType.Read);\n const privateReadReceipt = this.getReadReceiptForUserId(userId, ignoreSynthesized, ReceiptType.ReadPrivate);\n\n // If we have both, compare them\n let comparison: number | null | undefined;\n if (publicReadReceipt?.eventId && privateReadReceipt?.eventId) {\n comparison = this.compareReceipts(publicReadReceipt, privateReadReceipt);\n }\n\n // The public receipt is more likely to drift out of date so the private\n // one has precedence\n if (!comparison) return privateReadReceipt ?? publicReadReceipt ?? null;\n\n // If public read receipt is older, return the private one\n return (comparison < 0 ? privateReadReceipt : publicReadReceipt) ?? null;\n }\n\n public addReceiptToStructure(\n eventId: string,\n receiptType: ReceiptType,\n userId: string,\n receipt: Receipt,\n synthetic: boolean,\n ): void {\n const receiptTypesMap = this.receipts.getOrCreate(receiptType);\n let pair = receiptTypesMap.get(userId);\n\n if (!pair) {\n pair = [null, null];\n receiptTypesMap.set(userId, pair);\n }\n\n let existingReceipt = pair[ReceiptPairRealIndex];\n if (synthetic) {\n existingReceipt = pair[ReceiptPairSyntheticIndex] ?? pair[ReceiptPairRealIndex];\n }\n\n const wrappedReceipt: WrappedReceipt = {\n eventId,\n data: receipt,\n };\n\n if (existingReceipt) {\n // We only want to add this receipt if we think it is later than the one we already have.\n // This is managed server-side, but because we synthesize RRs locally we have to do it here too.\n const ordering = this.compareReceipts(existingReceipt, wrappedReceipt);\n if (ordering >= 0) {\n return;\n }\n }\n\n const realReceipt = synthetic ? pair[ReceiptPairRealIndex] : wrappedReceipt;\n const syntheticReceipt = synthetic ? wrappedReceipt : pair[ReceiptPairSyntheticIndex];\n\n let ordering: number | null = null;\n if (realReceipt && syntheticReceipt) {\n ordering = this.getUnfilteredTimelineSet().compareEventOrdering(\n realReceipt.eventId,\n syntheticReceipt.eventId,\n );\n }\n\n const preferSynthetic = ordering === null || ordering < 0;\n\n // we don't bother caching just real receipts by event ID as there's nothing that would read it.\n // Take the current cached receipt before we overwrite the pair elements.\n const cachedReceipt = pair[ReceiptPairSyntheticIndex] ?? pair[ReceiptPairRealIndex];\n\n if (synthetic && preferSynthetic) {\n pair[ReceiptPairSyntheticIndex] = wrappedReceipt;\n } else if (!synthetic) {\n pair[ReceiptPairRealIndex] = wrappedReceipt;\n\n if (!preferSynthetic) {\n pair[ReceiptPairSyntheticIndex] = null;\n }\n }\n\n const newCachedReceipt = pair[ReceiptPairSyntheticIndex] ?? pair[ReceiptPairRealIndex];\n if (cachedReceipt === newCachedReceipt) return;\n\n // clean up any previous cache entry\n if (cachedReceipt && this.receiptCacheByEventId.get(cachedReceipt.eventId)) {\n const previousEventId = cachedReceipt.eventId;\n // Remove the receipt we're about to clobber out of existence from the cache\n this.receiptCacheByEventId.set(\n previousEventId,\n this.receiptCacheByEventId.get(previousEventId)!.filter((r) => {\n return r.type !== receiptType || r.userId !== userId;\n }),\n );\n\n if (this.receiptCacheByEventId.get(previousEventId)!.length < 1) {\n this.receiptCacheByEventId.delete(previousEventId); // clean up the cache keys\n }\n }\n\n // cache the new one\n if (!this.receiptCacheByEventId.get(eventId)) {\n this.receiptCacheByEventId.set(eventId, []);\n }\n this.receiptCacheByEventId.get(eventId)!.push({\n userId: userId,\n type: receiptType as ReceiptType,\n data: receipt,\n });\n }\n\n /**\n * Get a list of receipts for the given event.\n * @param event - the event to get receipts for\n * @returns A list of receipts with a userId, type and data keys or\n * an empty list.\n */\n public getReceiptsForEvent(event: MatrixEvent): CachedReceipt[] {\n return this.receiptCacheByEventId.get(event.getId()!) || [];\n }\n\n public abstract addReceipt(event: MatrixEvent, synthetic: boolean): void;\n\n public abstract setUnread(type: NotificationCountType, count: number): void;\n\n /**\n * Look in this room/thread's timeline to find an event. If `this` is a\n * room, we look in all threads, but if `this` is a thread, we look only\n * inside this thread.\n */\n public abstract findEventById(eventId: string): MatrixEvent | undefined;\n\n /**\n * This issue should also be addressed on synapse's side and is tracked as part\n * of https://github.com/matrix-org/synapse/issues/14837\n *\n * Retrieves the read receipt for the logged in user and checks if it matches\n * the last event in the room and whether that event originated from the logged\n * in user.\n * Under those conditions we can consider the context as read. This is useful\n * because we never send read receipts against our own events\n * @param userId - the logged in user\n */\n public fixupNotifications(userId: string): void {\n const receipt = this.getReadReceiptForUserId(userId, false);\n\n const lastEvent = this.timeline[this.timeline.length - 1];\n if (lastEvent && receipt?.eventId === lastEvent.getId() && userId === lastEvent.getSender()) {\n this.setUnread(NotificationCountType.Total, 0);\n this.setUnread(NotificationCountType.Highlight, 0);\n }\n }\n\n /**\n * Add a temporary local-echo receipt to the room to reflect in the\n * client the fact that we've sent one.\n * @param userId - The user ID if the receipt sender\n * @param e - The event that is to be acknowledged\n * @param receiptType - The type of receipt\n * @param unthreaded - the receipt is unthreaded\n */\n public addLocalEchoReceipt(userId: string, e: MatrixEvent, receiptType: ReceiptType, unthreaded = false): void {\n this.addReceipt(synthesizeReceipt(userId, e, receiptType, unthreaded), true);\n }\n\n /**\n * Get a list of user IDs who have <b>read up to</b> the given event.\n * @param event - the event to get read receipts for.\n * @returns A list of user IDs.\n */\n public getUsersReadUpTo(event: MatrixEvent): string[] {\n return this.getReceiptsForEvent(event)\n .filter(function (receipt) {\n return isSupportedReceiptType(receipt.type);\n })\n .map(function (receipt) {\n return receipt.userId;\n });\n }\n\n /**\n * Determines if the given user has read a particular event ID with the known\n * history of the room. This is not a definitive check as it relies only on\n * what is available to the room at the time of execution.\n * @param userId - The user ID to check the read state of.\n * @param eventId - The event ID to check if the user read.\n * @returns True if the user has read the event, false otherwise.\n */\n public abstract hasUserReadEvent(userId: string, eventId: string): boolean;\n\n /**\n * Returns the most recent unthreaded receipt for a given user\n * @param userId - the MxID of the User\n * @returns an unthreaded Receipt. Can be undefined if receipts have been disabled\n * or a user chooses to use private read receipts (or we have simply not received\n * a receipt from this user yet).\n *\n * @deprecated use `hasUserReadEvent` or `getEventReadUpTo` instead\n */\n public abstract getLastUnthreadedReceiptFor(userId: string): Receipt | undefined;\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
1
|
/*
|
|
3
2
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
|
4
3
|
|
|
@@ -17,7 +16,6 @@ limitations under the License.
|
|
|
17
16
|
|
|
18
17
|
export class RelatedRelations {
|
|
19
18
|
constructor(relations) {
|
|
20
|
-
_defineProperty(this, "relations", void 0);
|
|
21
19
|
this.relations = relations.filter(r => !!r);
|
|
22
20
|
}
|
|
23
21
|
getRelations() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"related-relations.js","names":["RelatedRelations","constructor","relations","
|
|
1
|
+
{"version":3,"file":"related-relations.js","names":["RelatedRelations","constructor","relations","filter","r","getRelations","reduce","c","p","on","ev","fn","forEach","off"],"sources":["../../src/models/related-relations.ts"],"sourcesContent":["/*\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 Relations, type RelationsEvent, type EventHandlerMap } from \"./relations.ts\";\nimport { type MatrixEvent } from \"./event.ts\";\nimport { type Listener } from \"./typed-event-emitter.ts\";\n\nexport class RelatedRelations {\n private relations: Relations[];\n\n public constructor(relations: Relations[]) {\n this.relations = relations.filter((r) => !!r);\n }\n\n public getRelations(): MatrixEvent[] {\n return this.relations.reduce<MatrixEvent[]>((c, p) => [...c, ...p.getRelations()], []);\n }\n\n public on<T extends RelationsEvent>(ev: T, fn: Listener<RelationsEvent, EventHandlerMap, T>): void {\n this.relations.forEach((r) => r.on(ev, fn));\n }\n\n public off<T extends RelationsEvent>(ev: T, fn: Listener<RelationsEvent, EventHandlerMap, T>): void {\n this.relations.forEach((r) => r.off(ev, fn));\n }\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAMA,OAAO,MAAMA,gBAAgB,CAAC;EAGnBC,WAAWA,CAACC,SAAsB,EAAE;IACvC,IAAI,CAACA,SAAS,GAAGA,SAAS,CAACC,MAAM,CAAEC,CAAC,IAAK,CAAC,CAACA,CAAC,CAAC;EACjD;EAEOC,YAAYA,CAAA,EAAkB;IACjC,OAAO,IAAI,CAACH,SAAS,CAACI,MAAM,CAAgB,CAACC,CAAC,EAAEC,CAAC,KAAK,CAAC,GAAGD,CAAC,EAAE,GAAGC,CAAC,CAACH,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;EAC1F;EAEOI,EAAEA,CAA2BC,EAAK,EAAEC,EAAgD,EAAQ;IAC/F,IAAI,CAACT,SAAS,CAACU,OAAO,CAAER,CAAC,IAAKA,CAAC,CAACK,EAAE,CAACC,EAAE,EAAEC,EAAE,CAAC,CAAC;EAC/C;EAEOE,GAAGA,CAA2BH,EAAK,EAAEC,EAAgD,EAAQ;IAChG,IAAI,CAACT,SAAS,CAACU,OAAO,CAAER,CAAC,IAAKA,CAAC,CAACS,GAAG,CAACH,EAAE,EAAEC,EAAE,CAAC,CAAC;EAChD;AACJ","ignoreList":[]}
|
|
@@ -19,11 +19,11 @@ import { Relations } from "./relations.js";
|
|
|
19
19
|
import { EventStatus, MatrixEventEvent } from "./event.js";
|
|
20
20
|
export class RelationsContainer {
|
|
21
21
|
constructor(client, room) {
|
|
22
|
-
this.client = client;
|
|
23
|
-
this.room = room;
|
|
24
22
|
// A tree of objects to access a set of related children for an event, as in:
|
|
25
23
|
// this.relations.get(parentEventId).get(relationType).get(relationEventType)
|
|
26
24
|
_defineProperty(this, "relations", new Map());
|
|
25
|
+
this.client = client;
|
|
26
|
+
this.room = room;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -100,10 +100,8 @@ export class RelationsContainer {
|
|
|
100
100
|
event.once(MatrixEventEvent.Decrypted, onEventDecrypted);
|
|
101
101
|
return;
|
|
102
102
|
}
|
|
103
|
-
var
|
|
104
|
-
|
|
105
|
-
rel_type: relationType
|
|
106
|
-
} = relation;
|
|
103
|
+
var relatesToEventId = relation.event_id,
|
|
104
|
+
relationType = relation.rel_type;
|
|
107
105
|
var eventType = event.getType();
|
|
108
106
|
var relationsForEvent = this.relations.get(relatesToEventId);
|
|
109
107
|
if (!relationsForEvent) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relations-container.js","names":["Relations","EventStatus","MatrixEventEvent","RelationsContainer","constructor","client","room","_defineProperty","Map","getChildEventsForEvent","eventId","relationType","eventType","_this$relations$get","relations","get","getAllChildEventsForEvent","parentEventId","_this$relations$get2","relationsForEvent","events","relationsRecord","values","push","getRelations","aggregateParentEvent","event","getId","relationsWithRelType","relationsWithEventType","setTargetEvent","aggregateChildEvent","timelineSet","isRedacted","status","CANCELLED","relation","getRelation","onEventDecrypted","isDecryptionFailure","once","Decrypted","isBeingDecrypted","shouldAttemptDecryption","event_id","relatesToEventId","rel_type","getType","set","_this$room","_ref","_timelineSet$findEven","relatesToEvent","findEventById","getPendingEvent","addEvent"],"sources":["../../src/models/relations-container.ts"],"sourcesContent":["/*\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 { Relations } from \"./relations.ts\";\nimport { type EventType, type RelationType } from \"../@types/event.ts\";\nimport { EventStatus, type MatrixEvent, MatrixEventEvent } from \"./event.ts\";\nimport { type EventTimelineSet } from \"./event-timeline-set.ts\";\nimport { type MatrixClient } from \"../client.ts\";\nimport { type Room } from \"./room.ts\";\n\nexport class RelationsContainer {\n // A tree of objects to access a set of related children for an event, as in:\n // this.relations.get(parentEventId).get(relationType).get(relationEventType)\n private relations = new Map<string, Map<RelationType | string, Map<EventType | string, Relations>>>();\n\n public constructor(\n private readonly client: MatrixClient,\n private readonly room?: Room,\n ) {}\n\n /**\n * Get a collection of child events to a given event in this timeline set.\n *\n * @param eventId - The ID of the event that you'd like to access child events for.\n * For example, with annotations, this would be the ID of the event being annotated.\n * @param relationType - The type of relationship involved, such as \"m.annotation\", \"m.reference\", \"m.replace\", etc.\n * @param eventType - The relation event's type, such as \"m.reaction\", etc.\n * @throws If `eventId</code>, <code>relationType</code> or <code>eventType`\n * are not valid.\n *\n * @returns\n * A container for relation events or undefined if there are no relation events for\n * the relationType.\n */\n public getChildEventsForEvent(\n eventId: string,\n relationType: RelationType | string,\n eventType: EventType | string,\n ): Relations | undefined {\n return this.relations.get(eventId)?.get(relationType)?.get(eventType);\n }\n\n public getAllChildEventsForEvent(parentEventId: string): MatrixEvent[] {\n const relationsForEvent =\n this.relations.get(parentEventId) ?? new Map<RelationType | string, Map<EventType | string, Relations>>();\n const events: MatrixEvent[] = [];\n for (const relationsRecord of relationsForEvent.values()) {\n for (const relations of relationsRecord.values()) {\n events.push(...relations.getRelations());\n }\n }\n return events;\n }\n\n /**\n * Set an event as the target event if any Relations exist for it already.\n * Child events can point to other child events as their parent, so this method may be\n * called for events which are also logically child events.\n *\n * @param event - The event to check as relation target.\n */\n public aggregateParentEvent(event: MatrixEvent): void {\n const relationsForEvent = this.relations.get(event.getId()!);\n if (!relationsForEvent) return;\n\n for (const relationsWithRelType of relationsForEvent.values()) {\n for (const relationsWithEventType of relationsWithRelType.values()) {\n relationsWithEventType.setTargetEvent(event);\n }\n }\n }\n\n /**\n * Add relation events to the relevant relation collection.\n *\n * @param event - The new child event to be aggregated.\n * @param timelineSet - The event timeline set within which to search for the related event if any.\n */\n public aggregateChildEvent(event: MatrixEvent, timelineSet?: EventTimelineSet): void {\n if (event.isRedacted() || event.status === EventStatus.CANCELLED) {\n return;\n }\n\n const relation = event.getRelation();\n if (!relation) return;\n\n const onEventDecrypted = (): void => {\n if (event.isDecryptionFailure()) {\n // This could for example happen if the encryption keys are not yet available.\n // The event may still be decrypted later. Register the listener again.\n event.once(MatrixEventEvent.Decrypted, onEventDecrypted);\n return;\n }\n\n this.aggregateChildEvent(event, timelineSet);\n };\n\n // If the event is currently encrypted, wait until it has been decrypted.\n if (event.isBeingDecrypted() || event.shouldAttemptDecryption()) {\n event.once(MatrixEventEvent.Decrypted, onEventDecrypted);\n return;\n }\n\n const { event_id: relatesToEventId, rel_type: relationType } = relation;\n const eventType = event.getType();\n\n let relationsForEvent = this.relations.get(relatesToEventId!);\n if (!relationsForEvent) {\n relationsForEvent = new Map<RelationType | string, Map<EventType | string, Relations>>();\n this.relations.set(relatesToEventId!, relationsForEvent);\n }\n\n let relationsWithRelType = relationsForEvent.get(relationType!);\n if (!relationsWithRelType) {\n relationsWithRelType = new Map<EventType | string, Relations>();\n relationsForEvent.set(relationType!, relationsWithRelType);\n }\n\n let relationsWithEventType = relationsWithRelType.get(eventType);\n if (!relationsWithEventType) {\n relationsWithEventType = new Relations(relationType!, eventType, this.client);\n relationsWithRelType.set(eventType, relationsWithEventType);\n\n const room = this.room ?? timelineSet?.room;\n const relatesToEvent =\n timelineSet?.findEventById(relatesToEventId!) ??\n room?.findEventById(relatesToEventId!) ??\n room?.getPendingEvent(relatesToEventId!);\n if (relatesToEvent) {\n relationsWithEventType.setTargetEvent(relatesToEvent);\n }\n }\n\n relationsWithEventType.addEvent(event);\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"relations-container.js","names":["Relations","EventStatus","MatrixEventEvent","RelationsContainer","constructor","client","room","_defineProperty","Map","getChildEventsForEvent","eventId","relationType","eventType","_this$relations$get","relations","get","getAllChildEventsForEvent","parentEventId","_this$relations$get2","relationsForEvent","events","relationsRecord","values","push","getRelations","aggregateParentEvent","event","getId","relationsWithRelType","relationsWithEventType","setTargetEvent","aggregateChildEvent","timelineSet","isRedacted","status","CANCELLED","relation","getRelation","onEventDecrypted","isDecryptionFailure","once","Decrypted","isBeingDecrypted","shouldAttemptDecryption","relatesToEventId","event_id","rel_type","getType","set","_this$room","_ref","_timelineSet$findEven","relatesToEvent","findEventById","getPendingEvent","addEvent"],"sources":["../../src/models/relations-container.ts"],"sourcesContent":["/*\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 { Relations } from \"./relations.ts\";\nimport { type EventType, type RelationType } from \"../@types/event.ts\";\nimport { EventStatus, type MatrixEvent, MatrixEventEvent } from \"./event.ts\";\nimport { type EventTimelineSet } from \"./event-timeline-set.ts\";\nimport { type MatrixClient } from \"../client.ts\";\nimport { type Room } from \"./room.ts\";\n\nexport class RelationsContainer {\n // A tree of objects to access a set of related children for an event, as in:\n // this.relations.get(parentEventId).get(relationType).get(relationEventType)\n private relations = new Map<string, Map<RelationType | string, Map<EventType | string, Relations>>>();\n\n public constructor(\n private readonly client: MatrixClient,\n private readonly room?: Room,\n ) {}\n\n /**\n * Get a collection of child events to a given event in this timeline set.\n *\n * @param eventId - The ID of the event that you'd like to access child events for.\n * For example, with annotations, this would be the ID of the event being annotated.\n * @param relationType - The type of relationship involved, such as \"m.annotation\", \"m.reference\", \"m.replace\", etc.\n * @param eventType - The relation event's type, such as \"m.reaction\", etc.\n * @throws If `eventId</code>, <code>relationType</code> or <code>eventType`\n * are not valid.\n *\n * @returns\n * A container for relation events or undefined if there are no relation events for\n * the relationType.\n */\n public getChildEventsForEvent(\n eventId: string,\n relationType: RelationType | string,\n eventType: EventType | string,\n ): Relations | undefined {\n return this.relations.get(eventId)?.get(relationType)?.get(eventType);\n }\n\n public getAllChildEventsForEvent(parentEventId: string): MatrixEvent[] {\n const relationsForEvent =\n this.relations.get(parentEventId) ?? new Map<RelationType | string, Map<EventType | string, Relations>>();\n const events: MatrixEvent[] = [];\n for (const relationsRecord of relationsForEvent.values()) {\n for (const relations of relationsRecord.values()) {\n events.push(...relations.getRelations());\n }\n }\n return events;\n }\n\n /**\n * Set an event as the target event if any Relations exist for it already.\n * Child events can point to other child events as their parent, so this method may be\n * called for events which are also logically child events.\n *\n * @param event - The event to check as relation target.\n */\n public aggregateParentEvent(event: MatrixEvent): void {\n const relationsForEvent = this.relations.get(event.getId()!);\n if (!relationsForEvent) return;\n\n for (const relationsWithRelType of relationsForEvent.values()) {\n for (const relationsWithEventType of relationsWithRelType.values()) {\n relationsWithEventType.setTargetEvent(event);\n }\n }\n }\n\n /**\n * Add relation events to the relevant relation collection.\n *\n * @param event - The new child event to be aggregated.\n * @param timelineSet - The event timeline set within which to search for the related event if any.\n */\n public aggregateChildEvent(event: MatrixEvent, timelineSet?: EventTimelineSet): void {\n if (event.isRedacted() || event.status === EventStatus.CANCELLED) {\n return;\n }\n\n const relation = event.getRelation();\n if (!relation) return;\n\n const onEventDecrypted = (): void => {\n if (event.isDecryptionFailure()) {\n // This could for example happen if the encryption keys are not yet available.\n // The event may still be decrypted later. Register the listener again.\n event.once(MatrixEventEvent.Decrypted, onEventDecrypted);\n return;\n }\n\n this.aggregateChildEvent(event, timelineSet);\n };\n\n // If the event is currently encrypted, wait until it has been decrypted.\n if (event.isBeingDecrypted() || event.shouldAttemptDecryption()) {\n event.once(MatrixEventEvent.Decrypted, onEventDecrypted);\n return;\n }\n\n const { event_id: relatesToEventId, rel_type: relationType } = relation;\n const eventType = event.getType();\n\n let relationsForEvent = this.relations.get(relatesToEventId!);\n if (!relationsForEvent) {\n relationsForEvent = new Map<RelationType | string, Map<EventType | string, Relations>>();\n this.relations.set(relatesToEventId!, relationsForEvent);\n }\n\n let relationsWithRelType = relationsForEvent.get(relationType!);\n if (!relationsWithRelType) {\n relationsWithRelType = new Map<EventType | string, Relations>();\n relationsForEvent.set(relationType!, relationsWithRelType);\n }\n\n let relationsWithEventType = relationsWithRelType.get(eventType);\n if (!relationsWithEventType) {\n relationsWithEventType = new Relations(relationType!, eventType, this.client);\n relationsWithRelType.set(eventType, relationsWithEventType);\n\n const room = this.room ?? timelineSet?.room;\n const relatesToEvent =\n timelineSet?.findEventById(relatesToEventId!) ??\n room?.findEventById(relatesToEventId!) ??\n room?.getPendingEvent(relatesToEventId!);\n if (relatesToEvent) {\n relationsWithEventType.setTargetEvent(relatesToEvent);\n }\n }\n\n relationsWithEventType.addEvent(event);\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
package/lib/models/relations.js
CHANGED
|
@@ -51,9 +51,6 @@ export class Relations extends TypedEventEmitter {
|
|
|
51
51
|
var _this;
|
|
52
52
|
super();
|
|
53
53
|
_this = this;
|
|
54
|
-
this.relationType = relationType;
|
|
55
|
-
this.eventType = eventType;
|
|
56
|
-
this.altEventTypes = altEventTypes;
|
|
57
54
|
_defineProperty(this, "relationEventIds", new Set());
|
|
58
55
|
_defineProperty(this, "relations", new Set());
|
|
59
56
|
_defineProperty(this, "annotationsByKey", {});
|
|
@@ -62,7 +59,6 @@ export class Relations extends TypedEventEmitter {
|
|
|
62
59
|
_defineProperty(this, "targetEvent", null);
|
|
63
60
|
_defineProperty(this, "creationEmitted", false);
|
|
64
61
|
_defineProperty(this, "replacementUpdateId", 0);
|
|
65
|
-
_defineProperty(this, "client", void 0);
|
|
66
62
|
/**
|
|
67
63
|
* Listens for event status changes to remove cancelled events.
|
|
68
64
|
*
|
|
@@ -111,6 +107,9 @@ export class Relations extends TypedEventEmitter {
|
|
|
111
107
|
return _ref.apply(this, arguments);
|
|
112
108
|
};
|
|
113
109
|
}());
|
|
110
|
+
this.relationType = relationType;
|
|
111
|
+
this.eventType = eventType;
|
|
112
|
+
this.altEventTypes = altEventTypes;
|
|
114
113
|
this.client = client instanceof Room ? client.client : client;
|
|
115
114
|
}
|
|
116
115
|
|
|
@@ -189,9 +188,8 @@ export class Relations extends TypedEventEmitter {
|
|
|
189
188
|
}
|
|
190
189
|
addAnnotationToAggregation(event) {
|
|
191
190
|
var _event$getRelation;
|
|
192
|
-
var {
|
|
193
|
-
key
|
|
194
|
-
} = (_event$getRelation = event.getRelation()) !== null && _event$getRelation !== void 0 ? _event$getRelation : {};
|
|
191
|
+
var _ref2 = (_event$getRelation = event.getRelation()) !== null && _event$getRelation !== void 0 ? _event$getRelation : {},
|
|
192
|
+
key = _ref2.key;
|
|
195
193
|
if (!key) return;
|
|
196
194
|
var eventsForKey = this.annotationsByKey[key];
|
|
197
195
|
if (!eventsForKey) {
|
|
@@ -216,9 +214,8 @@ export class Relations extends TypedEventEmitter {
|
|
|
216
214
|
}
|
|
217
215
|
removeAnnotationFromAggregation(event) {
|
|
218
216
|
var _event$getRelation2;
|
|
219
|
-
var {
|
|
220
|
-
key
|
|
221
|
-
} = (_event$getRelation2 = event.getRelation()) !== null && _event$getRelation2 !== void 0 ? _event$getRelation2 : {};
|
|
217
|
+
var _ref3 = (_event$getRelation2 = event.getRelation()) !== null && _event$getRelation2 !== void 0 ? _event$getRelation2 : {},
|
|
218
|
+
key = _ref3.key;
|
|
222
219
|
if (!key) return;
|
|
223
220
|
var eventsForKey = this.annotationsByKey[key];
|
|
224
221
|
if (eventsForKey) {
|