Poll history - read only list of polls in current timeline (#10055)

* add settings while under development

* very basic tests for roomsummarycard

* empty poll history dialog and option in room summary

* pollS history in settings

* render an ugly list of polls in current timeline

* readonly poll history list items

* fix scroll window

* use short year code in date format, tidy

* no results message + tests

* strict fix

* mock intldatetimeformat for stable date formatting

* extract date format fn into date-utils

* jsdoc
This commit is contained in:
Kerry 2023-02-03 10:39:23 +13:00 committed by GitHub
parent 544baa30ed
commit ebb8408f28
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 572 additions and 7 deletions

View file

@ -269,3 +269,16 @@ export function formatPreciseDuration(durationMs: number): string {
}
return _t("%(value)ss", { value: seconds });
}
/**
* Formats a timestamp to a short date
* (eg 25/12/22 in uk locale)
* localised by system locale
* @param timestamp - epoch timestamp
* @returns {string} formattedDate
*/
export const formatLocalDateShort = (timestamp: number): string =>
new Intl.DateTimeFormat(
undefined, // locales
{ day: "2-digit", month: "2-digit", year: "2-digit" },
).format(timestamp);

View file

@ -15,19 +15,26 @@ limitations under the License.
*/
import React from "react";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { _t } from "../../../../languageHandler";
import BaseDialog from "../BaseDialog";
import { IDialogProps } from "../IDialogProps";
import { PollHistoryList } from "./PollHistoryList";
import { getPolls } from "./usePollHistory";
type PollHistoryDialogProps = Pick<IDialogProps, "onFinished"> & {
roomId: string;
matrixClient: MatrixClient;
};
export const PollHistoryDialog: React.FC<PollHistoryDialogProps> = ({ roomId, matrixClient, onFinished }) => {
const pollStartEvents = getPolls(roomId, matrixClient);
export const PollHistoryDialog: React.FC<PollHistoryDialogProps> = ({ onFinished }) => {
return (
<BaseDialog title={_t("Polls history")} onFinished={onFinished}>
{/* @TODO(kerrya) to be implemented in PSG-906 */}
<div className="mx_PollHistoryDialog_content">
<PollHistoryList pollStartEvents={pollStartEvents} />
</div>
</BaseDialog>
);
};

View file

@ -0,0 +1,40 @@
/*
Copyright 2023 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 from "react";
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
import PollListItem from "./PollListItem";
import { _t } from "../../../../languageHandler";
type PollHistoryListProps = {
pollStartEvents: MatrixEvent[];
};
export const PollHistoryList: React.FC<PollHistoryListProps> = ({ pollStartEvents }) => {
return (
<div className="mx_PollHistoryList">
{!!pollStartEvents.length ? (
<ol className="mx_PollHistoryList_list">
{pollStartEvents.map((pollStartEvent) => (
<PollListItem key={pollStartEvent.getId()!} event={pollStartEvent} />
))}
</ol>
) : (
<span className="mx_PollHistoryList_noResults">{_t("There are no polls in this room")}</span>
)}
</div>
);
};

View file

@ -0,0 +1,43 @@
/*
Copyright 2023 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 from "react";
import { PollStartEvent } from "matrix-js-sdk/src/extensible_events_v1/PollStartEvent";
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
import { Icon as PollIcon } from "../../../../../res/img/element-icons/room/composer/poll.svg";
import { formatLocalDateShort } from "../../../../DateUtils";
interface Props {
event: MatrixEvent;
}
const PollListItem: React.FC<Props> = ({ event }) => {
const pollEvent = event.unstableExtensibleEvent as unknown as PollStartEvent;
if (!pollEvent) {
return null;
}
const formattedDate = formatLocalDateShort(event.getTs());
return (
<li data-testid={`pollListItem-${event.getId()!}`} className="mx_PollListItem">
<span>{formattedDate}</span>
<PollIcon className="mx_PollListItem_icon" />
<span className="mx_PollListItem_question">{pollEvent.question.text}</span>
</li>
);
};
export default PollListItem;

View file

@ -0,0 +1,40 @@
/*
Copyright 2023 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 { M_POLL_START } from "matrix-js-sdk/src/@types/polls";
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
import { MatrixClient } from "matrix-js-sdk/src/client";
/**
* Get poll start events in a rooms live timeline
* @param roomId - id of room to retrieve polls for
* @param matrixClient - client
* @returns {MatrixEvent[]} - array fo poll start events
*/
export const getPolls = (roomId: string, matrixClient: MatrixClient): MatrixEvent[] => {
const room = matrixClient.getRoom(roomId);
if (!room) {
throw new Error("Cannot find room");
}
// @TODO(kerrya) poll history will be actively fetched in PSG-1043
// for now, just display polls that are in the current timeline
const timelineEvents = room.getLiveTimeline().getEvents();
const pollStartEvents = timelineEvents.filter((event) => M_POLL_START.matches(event.getType()));
return pollStartEvents;
};

View file

@ -286,6 +286,7 @@ const RoomSummaryCard: React.FC<IProps> = ({ room, onClose }) => {
const onRoomPollHistoryClick = (): void => {
Modal.createDialog(PollHistoryDialog, {
roomId: room.roomId,
matrixClient: cli,
});
};
@ -353,7 +354,11 @@ const RoomSummaryCard: React.FC<IProps> = ({ room, onClose }) => {
{_t("Export chat")}
</Button>
)}
<Button className="mx_RoomSummaryCard_icon_share" onClick={onShareRoomClick}>
<Button
data-testid="shareRoomButton"
className="mx_RoomSummaryCard_icon_share"
onClick={onShareRoomClick}
>
{_t("Share room")}
</Button>
<Button className="mx_RoomSummaryCard_icon_settings" onClick={onRoomSettingsClick}>

View file

@ -3141,6 +3141,7 @@
"<b>Warning</b>: You should only set up key backup from a trusted computer.": "<b>Warning</b>: You should only set up key backup from a trusted computer.",
"Access your secure message history and set up secure messaging by entering your Security Key.": "Access your secure message history and set up secure messaging by entering your Security Key.",
"If you've forgotten your Security Key you can <button>set up new recovery options</button>": "If you've forgotten your Security Key you can <button>set up new recovery options</button>",
"There are no polls in this room": "There are no polls in this room",
"Send custom account data event": "Send custom account data event",
"Send custom room account data event": "Send custom room account data event",
"Event Type": "Event Type",