From f4df9e10b39389abf17f8bcdf67baa8942b162e3 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Mon, 19 Aug 2024 16:52:21 +0200 Subject: [PATCH] Wip pinned separator --- res/css/_components.pcss | 1 + res/css/views/messages/_PinnedSeparator.pcss | 24 +++++++++++++++ src/components/structures/MessagePanel.tsx | 11 +++++++ .../views/messages/PinnedSeparator.tsx | 30 +++++++++++++++++++ src/i18n/strings/en_EN.json | 2 ++ src/utils/PinningUtils.ts | 20 ++++++++++++- 6 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 res/css/views/messages/_PinnedSeparator.pcss create mode 100644 src/components/views/messages/PinnedSeparator.tsx diff --git a/res/css/_components.pcss b/res/css/_components.pcss index 96c285bc0a6..9227f07a69d 100644 --- a/res/css/_components.pcss +++ b/res/css/_components.pcss @@ -249,6 +249,7 @@ @import "./views/messages/_MessageActionBar.pcss"; @import "./views/messages/_MessageTimestamp.pcss"; @import "./views/messages/_MjolnirBody.pcss"; +@import "./views/messages/_PinnedSeparator.pcss"; @import "./views/messages/_ReactionsRow.pcss"; @import "./views/messages/_ReactionsRowButton.pcss"; @import "./views/messages/_RedactedBody.pcss"; diff --git a/res/css/views/messages/_PinnedSeparator.pcss b/res/css/views/messages/_PinnedSeparator.pcss new file mode 100644 index 00000000000..cf1c9495cf4 --- /dev/null +++ b/res/css/views/messages/_PinnedSeparator.pcss @@ -0,0 +1,24 @@ +/* + * Copyright 2024 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. + */ + +.mx_PinnedSeparator { + text-align: end; + border-bottom: 1px solid var(--cpd-color-icon-accent-primary); + font: var(--cpd-font-body-sm-semibold); + color: var(--cpd-color-text-action-accent); + text-transform: uppercase; + margin-bottom: var(--cpd-space-1x); +} diff --git a/src/components/structures/MessagePanel.tsx b/src/components/structures/MessagePanel.tsx index 07b600484ac..9b704d1574d 100644 --- a/src/components/structures/MessagePanel.tsx +++ b/src/components/structures/MessagePanel.tsx @@ -56,6 +56,8 @@ import { MainGrouper } from "./grouper/MainGrouper"; import { CreationGrouper } from "./grouper/CreationGrouper"; import { _t } from "../../languageHandler"; import { getLateEventInfo } from "./grouper/LateEventGrouper"; +import PinningUtils from "../../utils/PinningUtils"; +import { PinnedSeparator } from "../views/messages/PinnedSeparator"; const CONTINUATION_MAX_INTERVAL = 5 * 60 * 1000; // 5 minutes const continuedTypes = [EventType.Sticker, EventType.RoomMessage]; @@ -767,6 +769,15 @@ export default class MessagePanel extends React.Component { } const cli = MatrixClientPeg.safeGet(); + + if (SettingsStore.getValue("feature_pinning") && PinningUtils.isPinned(cli, mxEv)) { + ret.push( +
  • + +
  • , + ); + } + let lastInSection = true; if (nextEventWithTile) { const nextEv = nextEventWithTile; diff --git a/src/components/views/messages/PinnedSeparator.tsx b/src/components/views/messages/PinnedSeparator.tsx new file mode 100644 index 00000000000..371a8db1957 --- /dev/null +++ b/src/components/views/messages/PinnedSeparator.tsx @@ -0,0 +1,30 @@ +/* + * Copyright 2024 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 React, { JSX } from "react"; + +import { _t } from "../../../languageHandler"; + +/** + * A separator to be displayed between pinned messages and the rest of the timeline. + */ +export function PinnedSeparator(): JSX.Element { + return ( +
    + {_t("timeline|pinned_separator")} +
    + ); +} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 1b7b1f2ed99..7c6dde7b0be 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -3523,6 +3523,8 @@ "no_permission_messages_before_join": "You don't have permission to view messages from before you joined.", "pending_moderation": "Message pending moderation", "pending_moderation_reason": "Message pending moderation: %(reason)s", + "pinned_separator": "pinned", + "pinned_separator_description": "The following message is pinned.", "reactions": { "add_reaction_prompt": "Add reaction", "custom_reaction_fallback_label": "Custom reaction", diff --git a/src/utils/PinningUtils.ts b/src/utils/PinningUtils.ts index f9750a3ed22..16ec4da45e9 100644 --- a/src/utils/PinningUtils.ts +++ b/src/utils/PinningUtils.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { MatrixEvent, EventType, M_POLL_START } from "matrix-js-sdk/src/matrix"; +import { MatrixEvent, EventType, M_POLL_START, MatrixClient, EventTimeline } from "matrix-js-sdk/src/matrix"; export default class PinningUtils { /** @@ -38,4 +38,22 @@ export default class PinningUtils { return true; } + + /** + * Determines if the given event is pinned. + * @param matrixClient + * @param mxEvent + */ + public static isPinned(matrixClient: MatrixClient, mxEvent: MatrixEvent): boolean { + const room = matrixClient.getRoom(mxEvent.getRoomId()); + if (!room) return false; + + const pinnedEvent = room + .getLiveTimeline() + .getState(EventTimeline.FORWARDS) + ?.getStateEvents(EventType.RoomPinnedEvents, ""); + if (!pinnedEvent) return false; + const content = pinnedEvent.getContent(); + return content.pinned && Array.isArray(content.pinned) && content.pinned.includes(mxEvent.getId()); + } }