Introduce an entirely new system for handling message preview copy
This reverts earlier changes made to textForEvent as they are no longer needed. This also implements an entire tree of textForEvent-like behaviour as the previews need to be different, which is easiest done with its own stack.
This commit is contained in:
parent
752b2acc56
commit
acf56559e1
19 changed files with 799 additions and 76 deletions
|
@ -19,32 +19,86 @@ import { ActionPayload } from "../../dispatcher/payloads";
|
|||
import { AsyncStoreWithClient } from "../AsyncStoreWithClient";
|
||||
import defaultDispatcher from "../../dispatcher/dispatcher";
|
||||
import { RoomListStoreTempProxy } from "./RoomListStoreTempProxy";
|
||||
import { textForEvent } from "../../TextForEvent";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { _t } from "../../languageHandler";
|
||||
import { MessageEventPreview } from "./previews/MessageEventPreview";
|
||||
import { NameEventPreview } from "./previews/NameEventPreview";
|
||||
import { TagID } from "./models";
|
||||
import { isNullOrUndefined } from "matrix-js-sdk/src/utils";
|
||||
import { TopicEventPreview } from "./previews/TopicEventPreview";
|
||||
import { MembershipEventPreview } from "./previews/MembershipEventPreview";
|
||||
import { HistoryVisibilityEventPreview } from "./previews/HistoryVisibilityEventPreview";
|
||||
import { CallInviteEventPreview } from "./previews/CallInviteEventPreview";
|
||||
import { CallAnswerEventPreview } from "./previews/CallAnswerEventPreview";
|
||||
import { CallHangupEvent } from "./previews/CallHangupEvent";
|
||||
import { EncryptionEventPreview } from "./previews/EncryptionEventPreview";
|
||||
import { ThirdPartyInviteEventPreview } from "./previews/ThirdPartyInviteEventPreview";
|
||||
import { StickerEventPreview } from "./previews/StickerEventPreview";
|
||||
import { ReactionEventPreview } from "./previews/ReactionEventPreview";
|
||||
import { CreationEventPreview } from "./previews/CreationEventPreview";
|
||||
|
||||
const PREVIEWABLE_EVENTS = [
|
||||
// This is the same list from RiotX
|
||||
{type: "m.room.message", isState: false},
|
||||
{type: "m.room.name", isState: true},
|
||||
{type: "m.room.topic", isState: true},
|
||||
{type: "m.room.member", isState: true},
|
||||
{type: "m.room.history_visibility", isState: true},
|
||||
{type: "m.call.invite", isState: false},
|
||||
{type: "m.call.hangup", isState: false},
|
||||
{type: "m.call.answer", isState: false},
|
||||
{type: "m.room.encrypted", isState: false},
|
||||
{type: "m.room.encryption", isState: true},
|
||||
{type: "m.room.third_party_invite", isState: true},
|
||||
{type: "m.sticker", isState: false},
|
||||
{type: "m.room.create", isState: true},
|
||||
];
|
||||
const PREVIEWS = {
|
||||
'm.room.message': {
|
||||
isState: false,
|
||||
previewer: new MessageEventPreview(),
|
||||
},
|
||||
'm.room.name': {
|
||||
isState: true,
|
||||
previewer: new NameEventPreview(),
|
||||
},
|
||||
'm.room.topic': {
|
||||
isState: true,
|
||||
previewer: new TopicEventPreview(),
|
||||
},
|
||||
'm.room.member': {
|
||||
isState: true,
|
||||
previewer: new MembershipEventPreview(),
|
||||
},
|
||||
'm.room.history_visibility': {
|
||||
isState: true,
|
||||
previewer: new HistoryVisibilityEventPreview(),
|
||||
},
|
||||
'm.call.invite': {
|
||||
isState: false,
|
||||
previewer: new CallInviteEventPreview(),
|
||||
},
|
||||
'm.call.answer': {
|
||||
isState: false,
|
||||
previewer: new CallAnswerEventPreview(),
|
||||
},
|
||||
'm.call.hangup': {
|
||||
isState: false,
|
||||
previewer: new CallHangupEvent(),
|
||||
},
|
||||
'm.room.encryption': {
|
||||
isState: true,
|
||||
previewer: new EncryptionEventPreview(),
|
||||
},
|
||||
'm.room.third_party_invite': {
|
||||
isState: true,
|
||||
previewer: new ThirdPartyInviteEventPreview(),
|
||||
},
|
||||
'm.sticker': {
|
||||
isState: false,
|
||||
previewer: new StickerEventPreview(),
|
||||
},
|
||||
'm.reaction': {
|
||||
isState: false,
|
||||
previewer: new ReactionEventPreview(),
|
||||
},
|
||||
'm.room.create': {
|
||||
isState: true,
|
||||
previewer: new CreationEventPreview(),
|
||||
},
|
||||
};
|
||||
|
||||
// The maximum number of events we're willing to look back on to get a preview.
|
||||
const MAX_EVENTS_BACKWARDS = 50;
|
||||
|
||||
// type merging ftw
|
||||
type TAG_ANY = "im.vector.any";
|
||||
const TAG_ANY: TAG_ANY = "im.vector.any";
|
||||
|
||||
interface IState {
|
||||
[roomId: string]: string | null; // null indicates the preview is empty
|
||||
[roomId: string]: Map<TagID | TAG_ANY, string | null>; // null indicates the preview is empty / irrelevant
|
||||
}
|
||||
|
||||
export class MessagePreviewStore extends AsyncStoreWithClient<IState> {
|
||||
|
@ -61,39 +115,76 @@ export class MessagePreviewStore extends AsyncStoreWithClient<IState> {
|
|||
/**
|
||||
* Gets the pre-translated preview for a given room
|
||||
* @param room The room to get the preview for.
|
||||
* @param inTagId The tag ID in which the room resides
|
||||
* @returns The preview, or null if none present.
|
||||
*/
|
||||
public getPreviewForRoom(room: Room): string {
|
||||
public getPreviewForRoom(room: Room, inTagId: TagID): string {
|
||||
if (!room) return null; // invalid room, just return nothing
|
||||
|
||||
// It's faster to do a lookup this way than it is to use Object.keys().includes()
|
||||
// We only want to generate a preview if there's one actually missing and not explicitly
|
||||
// set as 'none'.
|
||||
const val = this.state[room.roomId];
|
||||
if (val !== null && typeof(val) !== "string") {
|
||||
this.generatePreview(room);
|
||||
}
|
||||
if (!val) this.generatePreview(room, inTagId);
|
||||
|
||||
return this.state[room.roomId];
|
||||
const previews = this.state[room.roomId];
|
||||
if (!previews) return null;
|
||||
|
||||
if (!previews.has(inTagId)) {
|
||||
return previews.get(TAG_ANY);
|
||||
}
|
||||
return previews.get(inTagId);
|
||||
}
|
||||
|
||||
private generatePreview(room: Room) {
|
||||
private generatePreview(room: Room, tagId?: TagID) {
|
||||
const events = room.timeline;
|
||||
if (!events) return; // should only happen in tests
|
||||
|
||||
let map = this.state[room.roomId];
|
||||
if (!map) {
|
||||
map = new Map<TagID | TAG_ANY, string | null>();
|
||||
|
||||
// We set the state later with the map, so no need to send an update now
|
||||
}
|
||||
|
||||
// Set the tags so we know what to generate
|
||||
if (!map.has(TAG_ANY)) map.set(TAG_ANY, null);
|
||||
if (tagId && !map.has(tagId)) map.set(tagId, null);
|
||||
|
||||
let changed = false;
|
||||
for (let i = events.length - 1; i >= 0; i--) {
|
||||
if (i === events.length - MAX_EVENTS_BACKWARDS) return; // limit reached
|
||||
|
||||
const event = events[i];
|
||||
const preview = this.generatePreviewForEvent(event);
|
||||
if (preview.isPreviewable) {
|
||||
// noinspection JSIgnoredPromiseFromCall - the AsyncStore handles concurrent calls
|
||||
this.updateState({[room.roomId]: preview.preview});
|
||||
return; // break - we found some text
|
||||
const previewDef = PREVIEWS[event.getType()];
|
||||
if (!previewDef) continue;
|
||||
if (previewDef.isState && isNullOrUndefined(event.getStateKey())) continue;
|
||||
|
||||
const anyPreview = previewDef.previewer.getTextFor(event, null);
|
||||
if (!anyPreview) continue; // not previewable for some reason
|
||||
|
||||
changed = changed || anyPreview !== map.get(TAG_ANY);
|
||||
map.set(TAG_ANY, anyPreview);
|
||||
|
||||
const tagsToGenerate = Array.from(map.keys()).filter(t => t !== TAG_ANY); // we did the any tag above
|
||||
for (const genTagId of tagsToGenerate) {
|
||||
const realTagId: TagID = genTagId === TAG_ANY ? null : genTagId;
|
||||
const preview = previewDef.previewer.getTextFor(event, realTagId);
|
||||
if (preview === anyPreview) {
|
||||
changed = changed || anyPreview !== map.get(genTagId);
|
||||
map.delete(genTagId);
|
||||
} else {
|
||||
changed = changed || preview !== map.get(genTagId);
|
||||
map.set(genTagId, preview);
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
// Update state for good measure - causes emit for update
|
||||
// noinspection JSIgnoredPromiseFromCall - the AsyncStore handles concurrent calls
|
||||
this.updateState({[room.roomId]: map});
|
||||
}
|
||||
return; // we're done
|
||||
}
|
||||
|
||||
// if we didn't find anything, subscribe ourselves to an update
|
||||
// At this point, we didn't generate a preview so clear it
|
||||
// noinspection JSIgnoredPromiseFromCall - the AsyncStore handles concurrent calls
|
||||
this.updateState({[room.roomId]: null});
|
||||
}
|
||||
|
@ -107,28 +198,7 @@ export class MessagePreviewStore extends AsyncStoreWithClient<IState> {
|
|||
if (payload.action === 'MatrixActions.Room.timeline' || payload.action === 'MatrixActions.Event.decrypted') {
|
||||
const event = payload.event; // TODO: Type out the dispatcher
|
||||
if (!Object.keys(this.state).includes(event.getRoomId())) return; // not important
|
||||
|
||||
const preview = this.generatePreviewForEvent(event);
|
||||
if (preview.isPreviewable) {
|
||||
await this.updateState({[event.getRoomId()]: preview.preview});
|
||||
return; // break - we found some text
|
||||
}
|
||||
this.generatePreview(this.matrixClient.getRoom(event.getRoomId()), TAG_ANY);
|
||||
}
|
||||
}
|
||||
|
||||
private generatePreviewForEvent(event: MatrixEvent): { isPreviewable: boolean, preview: string } {
|
||||
if (PREVIEWABLE_EVENTS.some(p => p.type === event.getType() && p.isState === event.isState())) {
|
||||
const isSelf = event.getSender() === this.matrixClient.getUserId();
|
||||
let text = textForEvent(event, /*skipUserPrefix=*/isSelf);
|
||||
if (!text || text.trim().length === 0) text = null; // force null if useless to us
|
||||
if (text && isSelf) {
|
||||
// XXX: i18n doesn't really work here if the language doesn't support prefixing.
|
||||
// We'd ideally somehow route the `You:` bit to the textForEvent call, however
|
||||
// threading that through is non-trivial.
|
||||
text = _t("You: %(message)s", {message: text});
|
||||
}
|
||||
return {isPreviewable: true, preview: text};
|
||||
}
|
||||
return {isPreviewable: false, preview: null};
|
||||
}
|
||||
}
|
||||
|
|
35
src/stores/room-list/previews/CallAnswerEventPreview.ts
Normal file
35
src/stores/room-list/previews/CallAnswerEventPreview.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IPreview } from "./IPreview";
|
||||
import { TagID } from "../models";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { getSenderName, isSelf, shouldPrefixMessagesIn } from "./utils";
|
||||
import { _t } from "../../../languageHandler";
|
||||
|
||||
export class CallAnswerEventPreview implements IPreview {
|
||||
public getTextFor(event: MatrixEvent, tagId?: TagID): string {
|
||||
if (shouldPrefixMessagesIn(event.getRoomId(), tagId)) {
|
||||
if (isSelf(event)) {
|
||||
return _t("You joined the call");
|
||||
} else {
|
||||
return _t("%(senderName)s joined the call", {senderName: getSenderName(event)});
|
||||
}
|
||||
} else {
|
||||
return _t("Call in progress");
|
||||
}
|
||||
}
|
||||
}
|
35
src/stores/room-list/previews/CallHangupEvent.ts
Normal file
35
src/stores/room-list/previews/CallHangupEvent.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IPreview } from "./IPreview";
|
||||
import { TagID } from "../models";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { getSenderName, isSelf, shouldPrefixMessagesIn } from "./utils";
|
||||
import { _t } from "../../../languageHandler";
|
||||
|
||||
export class CallHangupEvent implements IPreview {
|
||||
public getTextFor(event: MatrixEvent, tagId?: TagID): string {
|
||||
if (shouldPrefixMessagesIn(event.getRoomId(), tagId)) {
|
||||
if (isSelf(event)) {
|
||||
return _t("You left the call");
|
||||
} else {
|
||||
return _t("%(senderName)s left the call", {senderName: getSenderName(event)});
|
||||
}
|
||||
} else {
|
||||
return _t("Call ended");
|
||||
}
|
||||
}
|
||||
}
|
39
src/stores/room-list/previews/CallInviteEventPreview.ts
Normal file
39
src/stores/room-list/previews/CallInviteEventPreview.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IPreview } from "./IPreview";
|
||||
import { TagID } from "../models";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { getSenderName, isSelf, shouldPrefixMessagesIn } from "./utils";
|
||||
import { _t } from "../../../languageHandler";
|
||||
|
||||
export class CallInviteEventPreview implements IPreview {
|
||||
public getTextFor(event: MatrixEvent, tagId?: TagID): string {
|
||||
if (shouldPrefixMessagesIn(event.getRoomId(), tagId)) {
|
||||
if (isSelf(event)) {
|
||||
return _t("You started a call");
|
||||
} else {
|
||||
return _t("%(senderName)s started a call", {senderName: getSenderName(event)});
|
||||
}
|
||||
} else {
|
||||
if (isSelf(event)) {
|
||||
return _t("Waiting for answer");
|
||||
} else {
|
||||
return _t("%(senderName)s is calling", {senderName: getSenderName(event)});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
31
src/stores/room-list/previews/CreationEventPreview.ts
Normal file
31
src/stores/room-list/previews/CreationEventPreview.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IPreview } from "./IPreview";
|
||||
import { TagID } from "../models";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { getSenderName, isSelf } from "./utils";
|
||||
import { _t } from "../../../languageHandler";
|
||||
|
||||
export class CreationEventPreview implements IPreview {
|
||||
public getTextFor(event: MatrixEvent, tagId?: TagID): string {
|
||||
if (isSelf(event)) {
|
||||
return _t("You created the room");
|
||||
} else {
|
||||
return _t("%(senderName)s created the room", {senderName: getSenderName(event)});
|
||||
}
|
||||
}
|
||||
}
|
31
src/stores/room-list/previews/EncryptionEventPreview.ts
Normal file
31
src/stores/room-list/previews/EncryptionEventPreview.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IPreview } from "./IPreview";
|
||||
import { TagID } from "../models";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { getSenderName, isSelf } from "./utils";
|
||||
import { _t } from "../../../languageHandler";
|
||||
|
||||
export class EncryptionEventPreview implements IPreview {
|
||||
public getTextFor(event: MatrixEvent, tagId?: TagID): string {
|
||||
if (isSelf(event)) {
|
||||
return _t("You made the chat encrypted");
|
||||
} else {
|
||||
return _t("%(senderName)s made the chat encrypted", {senderName: getSenderName(event)});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IPreview } from "./IPreview";
|
||||
import { TagID } from "../models";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { getSenderName, isSelf } from "./utils";
|
||||
import { _t } from "../../../languageHandler";
|
||||
|
||||
export class HistoryVisibilityEventPreview implements IPreview {
|
||||
public getTextFor(event: MatrixEvent, tagId?: TagID): string {
|
||||
const visibility = event.getContent()['history_visibility'];
|
||||
const isUs = isSelf(event);
|
||||
|
||||
if (visibility === 'invited' || visibility === 'joined') {
|
||||
return isUs
|
||||
? _t("You made history visible to new members")
|
||||
: _t("%(senderName)s made history visible to new members", {senderName: getSenderName(event)});
|
||||
} else if (visibility === 'world_readable') {
|
||||
return isUs
|
||||
? _t("You made history visible to anyone")
|
||||
: _t("%(senderName)s made history visible to anyone", {senderName: getSenderName(event)});
|
||||
} else { // shared, default
|
||||
return isUs
|
||||
? _t("You made history visible to future members")
|
||||
: _t("%(senderName)s made history visible to future members", {senderName: getSenderName(event)});
|
||||
}
|
||||
}
|
||||
}
|
31
src/stores/room-list/previews/IPreview.ts
Normal file
31
src/stores/room-list/previews/IPreview.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { TagID } from "../models";
|
||||
|
||||
/**
|
||||
* Represents an event preview.
|
||||
*/
|
||||
export interface IPreview {
|
||||
/**
|
||||
* Gets the text which represents the event as a preview.
|
||||
* @param event The event to preview.
|
||||
* @param tagId Optional. The tag where the room the event was sent in resides.
|
||||
* @returns The preview.
|
||||
*/
|
||||
getTextFor(event: MatrixEvent, tagId?: TagID): string;
|
||||
}
|
90
src/stores/room-list/previews/MembershipEventPreview.ts
Normal file
90
src/stores/room-list/previews/MembershipEventPreview.ts
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IPreview } from "./IPreview";
|
||||
import { TagID } from "../models";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { getTargetName, isSelfTarget } from "./utils";
|
||||
import { _t } from "../../../languageHandler";
|
||||
|
||||
export class MembershipEventPreview implements IPreview {
|
||||
public getTextFor(event: MatrixEvent, tagId?: TagID): string {
|
||||
const newMembership = event.getContent()['membership'];
|
||||
const oldMembership = event.getPrevContent()['membership'];
|
||||
const reason = event.getContent()['reason'];
|
||||
const isUs = isSelfTarget(event);
|
||||
|
||||
if (newMembership === 'invite') {
|
||||
return isUs
|
||||
? _t("You were invited")
|
||||
: _t("%(targetName)s was invited", {targetName: getTargetName(event)});
|
||||
} else if (newMembership === 'leave' && oldMembership !== 'invite') {
|
||||
if (event.getSender() === event.getStateKey()) {
|
||||
return isUs
|
||||
? _t("You left")
|
||||
: _t("%(targetName)s left", {targetName: getTargetName(event)});
|
||||
} else {
|
||||
if (reason) {
|
||||
return isUs
|
||||
? _t("You were kicked (%(reason)s)", {reason})
|
||||
: _t("%(targetName)s was kicked (%(reason)s)", {targetName: getTargetName(event), reason});
|
||||
} else {
|
||||
return isUs
|
||||
? _t("You were kicked")
|
||||
: _t("%(targetName)s was kicked", {targetName: getTargetName(event)});
|
||||
}
|
||||
}
|
||||
} else if (newMembership === 'leave' && oldMembership === 'invite') {
|
||||
if (event.getSender() === event.getStateKey()) {
|
||||
return isUs
|
||||
? _t("You rejected the invite")
|
||||
: _t("%(targetName)s rejected the invite", {targetName: getTargetName(event)});
|
||||
} else {
|
||||
return isUs
|
||||
? _t("You were uninvited")
|
||||
: _t("%(targetName)s was uninvited", {targetName: getTargetName(event)});
|
||||
}
|
||||
} else if (newMembership === 'ban') {
|
||||
if (reason) {
|
||||
return isUs
|
||||
? _t("You were banned (%(reason)s)", {reason})
|
||||
: _t("%(targetName)s was banned (%(reason)s)", {targetName: getTargetName(event), reason});
|
||||
} else {
|
||||
return isUs
|
||||
? _t("You were banned")
|
||||
: _t("%(targetName)s was banned", {targetName: getTargetName(event)});
|
||||
}
|
||||
} else if (newMembership === 'join' && oldMembership !== 'join') {
|
||||
return isUs
|
||||
? _t("You joined")
|
||||
: _t("%(targetName)s joined", {targetName: getTargetName(event)});
|
||||
} else {
|
||||
const isDisplayNameChange = event.getContent()['displayname'] !== event.getPrevContent()['displayname'];
|
||||
const isAvatarChange = event.getContent()['avatar_url'] !== event.getPrevContent()['avatar_url'];
|
||||
if (isDisplayNameChange) {
|
||||
return isUs
|
||||
? _t("You changed your name")
|
||||
: _t("%(targetName)s changed their name", {targetName: getTargetName(event)});
|
||||
} else if (isAvatarChange) {
|
||||
return isUs
|
||||
? _t("You changed your avatar")
|
||||
: _t("%(targetName)s changed their avatar", {targetName: getTargetName(event)});
|
||||
} else {
|
||||
return null; // no change
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
55
src/stores/room-list/previews/MessageEventPreview.ts
Normal file
55
src/stores/room-list/previews/MessageEventPreview.ts
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IPreview } from "./IPreview";
|
||||
import { TagID } from "../models";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import { getSenderName, isSelf, shouldPrefixMessagesIn } from "./utils";
|
||||
import ReplyThread from "../../../components/views/elements/ReplyThread";
|
||||
|
||||
export class MessageEventPreview implements IPreview {
|
||||
public getTextFor(event: MatrixEvent, tagId?: TagID): string {
|
||||
let eventContent = event.getContent();
|
||||
|
||||
if (event.isRelation("m.replace")) {
|
||||
// It's an edit, generate the preview on the new text
|
||||
eventContent = event.getContent()['m.new_content'];
|
||||
}
|
||||
|
||||
let body = (eventContent['body'] || '').trim();
|
||||
const msgtype = eventContent['msgtype'];
|
||||
if (!body || !msgtype) return null; // invalid event, no preview
|
||||
|
||||
// XXX: Newer relations have a getRelation() function which is not compatible with replies.
|
||||
const mRelatesTo = event.getWireContent()['m.relates_to'];
|
||||
if (mRelatesTo && mRelatesTo['m.in_reply_to']) {
|
||||
// If this is a reply, get the real reply and use that
|
||||
body = (ReplyThread.stripPlainReply(body) || '').trim();
|
||||
if (!body) return null; // invalid event, no preview
|
||||
}
|
||||
|
||||
if (msgtype === 'm.emote') {
|
||||
return _t("%(senderName)s %(emote)s", {senderName: getSenderName(event), emote: body});
|
||||
}
|
||||
|
||||
if (isSelf(event) || !shouldPrefixMessagesIn(event.getRoomId(), tagId)) {
|
||||
return body;
|
||||
} else {
|
||||
return _t("%(senderName)s: %(message)s", {senderName: getSenderName(event), message: body});
|
||||
}
|
||||
}
|
||||
}
|
31
src/stores/room-list/previews/NameEventPreview.ts
Normal file
31
src/stores/room-list/previews/NameEventPreview.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IPreview } from "./IPreview";
|
||||
import { TagID } from "../models";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { getSenderName, isSelf } from "./utils";
|
||||
import { _t } from "../../../languageHandler";
|
||||
|
||||
export class NameEventPreview implements IPreview {
|
||||
public getTextFor(event: MatrixEvent, tagId?: TagID): string {
|
||||
if (isSelf(event)) {
|
||||
return _t("You changed the room name");
|
||||
} else {
|
||||
return _t("%(senderName)s changed the room name", {senderName: getSenderName(event)});
|
||||
}
|
||||
}
|
||||
}
|
34
src/stores/room-list/previews/ReactionEventPreview.ts
Normal file
34
src/stores/room-list/previews/ReactionEventPreview.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IPreview } from "./IPreview";
|
||||
import { TagID } from "../models";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { getSenderName, isSelf, shouldPrefixMessagesIn } from "./utils";
|
||||
import { _t } from "../../../languageHandler";
|
||||
|
||||
export class ReactionEventPreview implements IPreview {
|
||||
public getTextFor(event: MatrixEvent, tagId?: TagID): string {
|
||||
const reaction = event.getRelation().key;
|
||||
if (!reaction) return;
|
||||
|
||||
if (isSelf(event) || !shouldPrefixMessagesIn(event.getRoomId(), tagId)) {
|
||||
return reaction;
|
||||
} else {
|
||||
return _t("%(senderName)s: %(reaction)s", {senderName: getSenderName(event), reaction});
|
||||
}
|
||||
}
|
||||
}
|
34
src/stores/room-list/previews/StickerEventPreview.ts
Normal file
34
src/stores/room-list/previews/StickerEventPreview.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IPreview } from "./IPreview";
|
||||
import { TagID } from "../models";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { getSenderName, isSelf, shouldPrefixMessagesIn } from "./utils";
|
||||
import { _t } from "../../../languageHandler";
|
||||
|
||||
export class StickerEventPreview implements IPreview {
|
||||
public getTextFor(event: MatrixEvent, tagId?: TagID): string {
|
||||
const stickerName = event.getContent()['body'];
|
||||
if (!stickerName) return null;
|
||||
|
||||
if (isSelf(event) || !shouldPrefixMessagesIn(event.getRoomId(), tagId)) {
|
||||
return stickerName;
|
||||
} else {
|
||||
return _t("%(senderName)s: %(stickerName)s", {senderName: getSenderName(event), stickerName});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IPreview } from "./IPreview";
|
||||
import { TagID } from "../models";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { getSenderName, isSelf } from "./utils";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import { isValid3pidInvite } from "../../../RoomInvite";
|
||||
|
||||
export class ThirdPartyInviteEventPreview implements IPreview {
|
||||
public getTextFor(event: MatrixEvent, tagId?: TagID): string {
|
||||
if (!isValid3pidInvite(event)) {
|
||||
const targetName = event.getPrevContent().display_name || _t("Someone");
|
||||
if (isSelf(event)) {
|
||||
return _t("You uninvited %(targetName)s", {targetName});
|
||||
} else {
|
||||
return _t("%(senderName)s uninvited %(targetName)s", {senderName: getSenderName(event), targetName});
|
||||
}
|
||||
} else {
|
||||
const targetName = event.getContent().display_name;
|
||||
if (isSelf(event)) {
|
||||
return _t("You invited %(targetName)s", {targetName});
|
||||
} else {
|
||||
return _t("%(senderName)s invited %(targetName)s", {senderName: getSenderName(event), targetName});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
31
src/stores/room-list/previews/TopicEventPreview.ts
Normal file
31
src/stores/room-list/previews/TopicEventPreview.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IPreview } from "./IPreview";
|
||||
import { TagID } from "../models";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { getSenderName, isSelf } from "./utils";
|
||||
import { _t } from "../../../languageHandler";
|
||||
|
||||
export class TopicEventPreview implements IPreview {
|
||||
public getTextFor(event: MatrixEvent, tagId?: TagID): string {
|
||||
if (isSelf(event)) {
|
||||
return _t("You changed the room topic");
|
||||
} else {
|
||||
return _t("%(senderName)s changed the room topic", {senderName: getSenderName(event)});
|
||||
}
|
||||
}
|
||||
}
|
49
src/stores/room-list/previews/utils.ts
Normal file
49
src/stores/room-list/previews/utils.ts
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import { DefaultTagID, TagID } from "../models";
|
||||
|
||||
export function isSelf(event: MatrixEvent): boolean {
|
||||
const selfUserId = MatrixClientPeg.get().getUserId();
|
||||
if (event.getType() === 'm.room.member') {
|
||||
return event.getStateKey() === selfUserId;
|
||||
}
|
||||
return event.getSender() === selfUserId;
|
||||
}
|
||||
|
||||
export function isSelfTarget(event: MatrixEvent): boolean {
|
||||
const selfUserId = MatrixClientPeg.get().getUserId();
|
||||
return event.getStateKey() === selfUserId;
|
||||
}
|
||||
|
||||
export function shouldPrefixMessagesIn(roomId: string, tagId: TagID): boolean {
|
||||
if (tagId !== DefaultTagID.DM) return true;
|
||||
|
||||
// We don't prefix anything in 1:1s
|
||||
const room = MatrixClientPeg.get().getRoom(roomId);
|
||||
if (!room) return true;
|
||||
return room.currentState.getJoinedMemberCount() !== 2;
|
||||
}
|
||||
|
||||
export function getSenderName(event: MatrixEvent): string {
|
||||
return event.sender ? event.sender.name : event.getSender();
|
||||
}
|
||||
|
||||
export function getTargetName(event: MatrixEvent): string {
|
||||
return event.target ? event.target.name : event.getStateKey();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue