Skip to content

Commit

Permalink
sending custom emoji with the new attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewRyanChama committed Mar 13, 2023
1 parent 4bf0df2 commit 49e8fc1
Show file tree
Hide file tree
Showing 11 changed files with 75 additions and 27 deletions.
8 changes: 2 additions & 6 deletions src/HtmlUtils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ const sanitizeHtmlParams: IExtendedSanitizeOptions = {
div: ["data-mx-maths"],
a: ["href", "name", "target", "rel"], // remote target: custom to matrix
// img tags also accept width/height, we just map those to max-width & max-height during transformation
img: ["src", "alt", "title", "style", "data-mx-emoticon"],
img: ["src", "alt", "title", "style", "data-mx-emoticon", "data-mx-pack-url"],
ol: ["start"],
code: ["class"], // We don't actually allow all classes, we filter them in transformTags
},
Expand Down Expand Up @@ -617,11 +617,7 @@ export function bodyToHtml(content: IContent, highlights: Optional<string[]>, op

const match = BIGEMOJI_REGEX.exec(contentBodyTrimmed);
const matched = match && match[0] && match[0].length === contentBodyTrimmed.length;
emojiBody =
(matched || isAllHtmlEmoji) &&
(strippedBody === safeBody || // replies have the html fallbacks, account for that here
content.formatted_body === undefined ||
(!content.formatted_body.includes("http:") && !content.formatted_body.includes("https:")));
emojiBody = (matched || isAllHtmlEmoji);
}

const className = classNames({
Expand Down
1 change: 1 addition & 0 deletions src/autocomplete/Autocompleter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export interface ICompletion {
// If provided, apply a LINK entity to the completion with the
// data = { url: href }.
href?: string;
customEmoji?: ICustomEmoji;
}

const PROVIDERS = [UserProvider, RoomProvider, EmojiProvider, NotifProvider, CommandProvider, SpaceProvider];
Expand Down
5 changes: 3 additions & 2 deletions src/autocomplete/EmojiProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export default class EmojiProvider extends AutocompleteProvider {

// Load this room's image sets.
const imageSetEvents = room.currentState.getStateEvents("im.ponies.room_emotes");
let loadedImages: ICustomEmoji[] = imageSetEvents.flatMap((imageSetEvent) => loadImageSet(imageSetEvent));
let loadedImages: ICustomEmoji[] = imageSetEvents.flatMap((imageSetEvent) => loadImageSet(imageSetEvent, room));

// Global emotes from rooms
const cli = MatrixClientPeg.get();
Expand All @@ -106,7 +106,7 @@ export default class EmojiProvider extends AutocompleteProvider {
const packRoom = cli.getRoom(key);
const packRoomImageSetEvents = packRoom.currentState.getStateEvents("im.ponies.room_emotes");
const moreLoadedImages: ICustomEmoji[] = packRoomImageSetEvents.flatMap((packRoomImageSetEvents) =>
loadImageSet(packRoomImageSetEvents),
loadImageSet(packRoomImageSetEvents, packRoom),
);
loadedImages = [...loadedImages, ...moreLoadedImages];
}
Expand Down Expand Up @@ -210,6 +210,7 @@ export default class EmojiProvider extends AutocompleteProvider {
<img className="mx_customEmoji_image" src={mediaUrl} alt={c.emoji.shortcodes[0]} />
</PillCompletion>
),
customEmoji: c.emoji,
range,
} as const;
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/emojipicker/EmojiPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class EmojiPicker extends React.Component<IProps, IState> {
let loadedImages: ICustomEmoji[];
if (props.room) {
const imageSetEvents = props.room.currentState.getStateEvents("im.ponies.room_emotes");
loadedImages = imageSetEvents.flatMap((imageSetEvent) => loadImageSet(imageSetEvent));
loadedImages = imageSetEvents.flatMap((imageSetEvent) => loadImageSet(imageSetEvent, props.room));
} else {
loadedImages = [];
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/rooms/BasicMessageComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -883,7 +883,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
if ("unicode" in emoji) {
emojiPart = partCreator.emoji(emoji.unicode);
} else {
emojiPart = partCreator.customEmoji(emoji.shortcodes[0], emoji.url);
emojiPart = partCreator.customEmoji(emoji.shortcodes[0], emoji.url, emoji.roomId, emoji.eventId);
}
model.transform(() => {
const addedLen = model.insert([emojiPart], position);
Expand Down
2 changes: 1 addition & 1 deletion src/editor/autocomplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export default class AutocompleteWrapperModel {
// command needs special handling for auto complete, but also renders as plain texts
return [(this.partCreator as CommandPartCreator).command(text)];
case "customEmoji":
return [this.partCreator.customEmoji(text, completionId)];
return [this.partCreator.customEmoji(text, completionId, completion.customEmoji?.roomId, completion.customEmoji?.eventId)];
default:
// used for emoji and other plain text completion replacement
return this.partCreator.plainWithEmoji(text);
Expand Down
11 changes: 9 additions & 2 deletions src/editor/deserialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { MsgType } from "matrix-js-sdk/src/@types/event";

import { checkBlockNode } from "../HtmlUtils";
import { getPrimaryPermalinkEntity } from "../utils/permalinks/Permalinks";
import { getPrimaryPermalinkEntity, parsePermalink } from "../utils/permalinks/Permalinks";
import { Part, PartCreator, Type } from "./parts";
import SdkConfig from "../SdkConfig";
import { textToHtmlRainbow } from "../utils/colour";
import { stripPlainReply } from "../utils/Reply";
import { PermalinkParts } from "../utils/permalinks/PermalinkConstructor";

const LIST_TYPES = ["UL", "OL", "LI"];

Expand Down Expand Up @@ -97,7 +98,13 @@ function parseImage(n: Node, pc: PartCreator, opts: IParseOptions): Part[] {
const isCustomEmoji = elm.hasAttribute("data-mx-emoticon");
if (isCustomEmoji) {
const shortcode = elm.title || elm.alt || ":SHORTCODE_MISSING:";
return [pc.customEmoji(shortcode, src)];
// parse the link
const packUrl = elm.getAttribute("data-mx-pack-url");
let permalinkParts: PermalinkParts = null;
if (packUrl) {
permalinkParts = parsePermalink(packUrl);
}
return [pc.customEmoji(shortcode, src, permalinkParts?.roomIdOrAlias, permalinkParts?.eventId)];
}
return pc.plainWithEmoji(`![${escape(alt)}](${src})`);
}
Expand Down
31 changes: 25 additions & 6 deletions src/editor/parts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ interface ISerializedPillPart {
type: Type.AtRoomPill | Type.RoomPill | Type.UserPill | Type.CustomEmoji;
text: string;
resourceId?: string;
roomId?: string;
eventId?: string;
}

export type SerializedPart = ISerializedPart | ISerializedPillPart;
Expand Down Expand Up @@ -85,7 +87,12 @@ interface IPillPart extends Omit<IBasePart, "type" | "resourceId"> {
resourceId: string;
}

export type Part = IBasePart | IPillCandidatePart | IPillPart;
export interface ICustomEmojiPart extends IPillPart {
roomId?: string;
eventId?: string;
}

export type Part = IBasePart | IPillCandidatePart | IPillPart | ICustomEmojiPart;

abstract class BasePart {
protected _text: string;
Expand Down Expand Up @@ -416,7 +423,9 @@ export class EmojiPart extends BasePart implements IBasePart {
}
}

class CustomEmojiPart extends PillPart implements IPillPart {
class CustomEmojiPart extends PillPart implements ICustomEmojiPart {
public roomId?: string;
public eventId?: string;
protected get className(): string {
return "mx_CustomEmojiPill mx_Pill";
}
Expand All @@ -432,8 +441,10 @@ class CustomEmojiPart extends PillPart implements IPillPart {

this.setAvatarVars(node, url, this.text[0]);
}
public constructor(shortCode: string, url: string) {
public constructor(shortCode: string, url: string, roomId?: string, eventId?: string) {
super(url, shortCode);
this.roomId = roomId;
this.eventId = eventId;
}
protected acceptsInsertion(chr: string): boolean {
return false;
Expand All @@ -450,6 +461,14 @@ class CustomEmojiPart extends PillPart implements IPillPart {
public get canEdit(): boolean {
return false;
}

public serialize(): ISerializedPillPart {
return {
...super.serialize(),
roomId: this.roomId,
eventId: this.eventId
};
}
}

class RoomPillPart extends PillPart {
Expand Down Expand Up @@ -620,7 +639,7 @@ export class PartCreator {
case Type.Emoji:
return this.emoji(part.text);
case Type.CustomEmoji:
return this.customEmoji(part.text, part.resourceId);
return this.customEmoji(part.text, part.resourceId, part.roomId, part.eventId);
case Type.AtRoomPill:
return this.atRoomPill(part.text);
case Type.PillCandidate:
Expand Down Expand Up @@ -699,8 +718,8 @@ export class PartCreator {
return parts;
}

public customEmoji(shortcode: string, url: string): CustomEmojiPart {
return new CustomEmojiPart(shortcode, url);
public customEmoji(shortcode: string, url: string, roomId?: string, eventId?: string): CustomEmojiPart {
return new CustomEmojiPart(shortcode, url, roomId, eventId);
}

public createMentionParts(
Expand Down
14 changes: 12 additions & 2 deletions src/editor/serialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ import escapeHtml from "escape-html";
import _ from "lodash";

import Markdown from "../Markdown";
import { makeGenericPermalink } from "../utils/permalinks/Permalinks";
import { makeGenericPermalink, makeRoomPermalink } from "../utils/permalinks/Permalinks";
import EditorModel from "./model";
import SettingsStore from "../settings/SettingsStore";
import SdkConfig from "../SdkConfig";
import { Type } from "./parts";
import { ICustomEmojiPart, Type } from "./parts";

export function mdSerialize(model: EditorModel): string {
return model.parts.reduce((html, part) => {
Expand Down Expand Up @@ -53,6 +53,16 @@ export function mdSerialize(model: EditorModel): string {
`[${part.text.replace(/[[\\\]]/g, (c) => "\\" + c)}](${makeGenericPermalink(part.resourceId)})`
);
case Type.CustomEmoji:
const customEmojiPart : ICustomEmojiPart = part;
if (customEmojiPart.roomId) {
const permalink = makeRoomPermalink(customEmojiPart.roomId, customEmojiPart.eventId);
return (
html +
`<img data-mx-emoticon height="18" src="${encodeURI(part.resourceId)}"` +
` data-mx-pack-url="${permalink}"` +
` title=":${_.escape(part.text)}:" alt=":${_.escape(part.text)}:">`
);
}
return (
html +
`<img data-mx-emoticon height="18" src="${encodeURI(part.resourceId)}"` +
Expand Down
14 changes: 12 additions & 2 deletions src/emojipicker/customemoji.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,27 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import { MatrixEvent } from "matrix-js-sdk/src/matrix";
import { JoinRule, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";

export function loadImageSet(imageSetEvent: MatrixEvent): ICustomEmoji[] {
export function loadImageSet(imageSetEvent: MatrixEvent, room?: Room): ICustomEmoji[] {
const loadedImages: ICustomEmoji[] = [];
const images = imageSetEvent.getContent().images;
let eventId : string | undefined;
let roomId : string | undefined;
if (!images) {
return [];
}
if (room.getJoinRule() === JoinRule.Public) {
eventId = imageSetEvent?.getId();
roomId = room?.roomId;
}
for (const imageKey in images) {
const imageData = images[imageKey];
loadedImages.push({
shortcodes: [imageKey],
url: imageData.url,
roomId: roomId,
eventId: eventId,
});
}
return loadedImages;
Expand All @@ -36,4 +44,6 @@ export interface ICustomEmoji {
shortcodes: string[];
emoticon?: string;
url: string;
roomId?: string;
eventId?: string;
}
12 changes: 8 additions & 4 deletions src/utils/permalinks/Permalinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,23 +275,27 @@ export function makeUserPermalink(userId: string): string {
return getPermalinkConstructor().forUser(userId);
}

export function makeRoomPermalink(roomId: string): string {
export function makeRoomPermalink(roomId: string, eventId?: string): string {
if (!roomId) {
throw new Error("can't permalink a falsy roomId");
}

// If the roomId isn't actually a room ID, don't try to list the servers.
// Aliases are already routable, and don't need extra information.
if (roomId[0] !== "!") return getPermalinkConstructor().forRoom(roomId, []);
if (roomId[0] !== "!") return eventId ? getPermalinkConstructor().forRoom(roomId, []) : getPermalinkConstructor().forEvent(roomId, eventId, []);

const client = MatrixClientPeg.get();
const room = client.getRoom(roomId);
if (!room) {
return getPermalinkConstructor().forRoom(roomId, []);
return eventId ? getPermalinkConstructor().forRoom(roomId, []) : getPermalinkConstructor().forEvent(roomId, eventId, []);
}
const permalinkCreator = new RoomPermalinkCreator(room);
permalinkCreator.load();
return permalinkCreator.forShareableRoom();
if (eventId) {
return permalinkCreator.forEvent(eventId);
} else {
return permalinkCreator.forShareableRoom();
}
}

export function isPermalinkHost(host: string): boolean {
Expand Down

0 comments on commit 49e8fc1

Please sign in to comment.