mirror of
https://github.com/element-hq/element-web
synced 2024-11-25 18:55:58 +03:00
Poll history: fetch more poll history (#10235)
* load more pages of polls * load more and no results messages * style no results message * remove debug * strict fixes * comments * i18n pluralisations * pluralisation the right way
This commit is contained in:
parent
f57495d3cd
commit
7c70dd9d16
7 changed files with 300 additions and 105 deletions
|
@ -41,10 +41,20 @@ limitations under the License.
|
||||||
.mx_PollHistoryList_noResults {
|
.mx_PollHistoryList_noResults {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 0 $spacing-64;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
line-height: $font-24px;
|
||||||
color: $secondary-content;
|
color: $secondary-content;
|
||||||
|
|
||||||
|
.mx_PollHistoryList_loadMorePolls {
|
||||||
|
margin-top: $spacing-16;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_PollHistoryList_loading {
|
.mx_PollHistoryList_loading {
|
||||||
|
@ -57,3 +67,7 @@ limitations under the License.
|
||||||
margin: auto auto;
|
margin: auto auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_PollHistoryList_loadMorePolls {
|
||||||
|
width: max-content;
|
||||||
|
}
|
||||||
|
|
|
@ -57,9 +57,9 @@ export const PollHistoryDialog: React.FC<PollHistoryDialogProps> = ({
|
||||||
onFinished,
|
onFinished,
|
||||||
}) => {
|
}) => {
|
||||||
const { polls } = usePollsWithRelations(room.roomId, matrixClient);
|
const { polls } = usePollsWithRelations(room.roomId, matrixClient);
|
||||||
|
const { isLoading, loadMorePolls, oldestEventTimestamp } = useFetchPastPolls(room, matrixClient);
|
||||||
const [filter, setFilter] = useState<PollHistoryFilter>("ACTIVE");
|
const [filter, setFilter] = useState<PollHistoryFilter>("ACTIVE");
|
||||||
const [focusedPollId, setFocusedPollId] = useState<string | null>(null);
|
const [focusedPollId, setFocusedPollId] = useState<string | null>(null);
|
||||||
const { isLoading } = useFetchPastPolls(room, matrixClient);
|
|
||||||
|
|
||||||
const pollStartEvents = filterAndSortPolls(polls, filter);
|
const pollStartEvents = filterAndSortPolls(polls, filter);
|
||||||
const isLoadingPollResponses = [...polls.values()].some((poll) => poll.isFetchingResponses);
|
const isLoadingPollResponses = [...polls.values()].some((poll) => poll.isFetchingResponses);
|
||||||
|
@ -78,12 +78,14 @@ export const PollHistoryDialog: React.FC<PollHistoryDialogProps> = ({
|
||||||
<PollDetail poll={focusedPoll} permalinkCreator={permalinkCreator} requestModalClose={onFinished} />
|
<PollDetail poll={focusedPoll} permalinkCreator={permalinkCreator} requestModalClose={onFinished} />
|
||||||
) : (
|
) : (
|
||||||
<PollHistoryList
|
<PollHistoryList
|
||||||
|
onItemClick={setFocusedPollId}
|
||||||
pollStartEvents={pollStartEvents}
|
pollStartEvents={pollStartEvents}
|
||||||
isLoading={isLoading || isLoadingPollResponses}
|
isLoading={isLoading || isLoadingPollResponses}
|
||||||
|
oldestFetchedEventTimestamp={oldestEventTimestamp}
|
||||||
polls={polls}
|
polls={polls}
|
||||||
filter={filter}
|
filter={filter}
|
||||||
onFilterChange={setFilter}
|
onFilterChange={setFilter}
|
||||||
onItemClick={setFocusedPollId}
|
loadMorePolls={loadMorePolls}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -24,6 +24,7 @@ import InlineSpinner from "../../elements/InlineSpinner";
|
||||||
import { PollHistoryFilter } from "./types";
|
import { PollHistoryFilter } from "./types";
|
||||||
import { PollListItem } from "./PollListItem";
|
import { PollListItem } from "./PollListItem";
|
||||||
import { PollListItemEnded } from "./PollListItemEnded";
|
import { PollListItemEnded } from "./PollListItemEnded";
|
||||||
|
import AccessibleButton from "../../elements/AccessibleButton";
|
||||||
|
|
||||||
const LoadingPolls: React.FC<{ noResultsYet?: boolean }> = ({ noResultsYet }) => (
|
const LoadingPolls: React.FC<{ noResultsYet?: boolean }> = ({ noResultsYet }) => (
|
||||||
<div
|
<div
|
||||||
|
@ -36,20 +37,93 @@ const LoadingPolls: React.FC<{ noResultsYet?: boolean }> = ({ noResultsYet }) =>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const LoadMorePolls: React.FC<{ loadMorePolls?: () => void; isLoading?: boolean }> = ({ isLoading, loadMorePolls }) =>
|
||||||
|
loadMorePolls ? (
|
||||||
|
<AccessibleButton
|
||||||
|
className="mx_PollHistoryList_loadMorePolls"
|
||||||
|
kind="link_inline"
|
||||||
|
onClick={() => loadMorePolls()}
|
||||||
|
>
|
||||||
|
{_t("Load more polls")}
|
||||||
|
{isLoading && <InlineSpinner />}
|
||||||
|
</AccessibleButton>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
const ONE_DAY_MS = 60000 * 60 * 24;
|
||||||
|
const getNoResultsMessage = (
|
||||||
|
filter: PollHistoryFilter,
|
||||||
|
oldestEventTimestamp?: number,
|
||||||
|
loadMorePolls?: () => void,
|
||||||
|
): string => {
|
||||||
|
if (!loadMorePolls) {
|
||||||
|
return filter === "ACTIVE"
|
||||||
|
? _t("There are no active polls in this room")
|
||||||
|
: _t("There are no past polls in this room");
|
||||||
|
}
|
||||||
|
|
||||||
|
// we don't know how much history has been fetched
|
||||||
|
if (!oldestEventTimestamp) {
|
||||||
|
return filter === "ACTIVE"
|
||||||
|
? _t("There are no active polls. Load more polls to view polls for previous months")
|
||||||
|
: _t("There are no past polls. Load more polls to view polls for previous months");
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchedHistoryDaysCount = Math.ceil((Date.now() - oldestEventTimestamp) / ONE_DAY_MS);
|
||||||
|
return filter === "ACTIVE"
|
||||||
|
? _t(
|
||||||
|
"There are no active polls for the past %(count)s days. Load more polls to view polls for previous months",
|
||||||
|
{ count: fetchedHistoryDaysCount },
|
||||||
|
)
|
||||||
|
: _t("There are no past polls for the past %(count)s days. Load more polls to view polls for previous months", {
|
||||||
|
count: fetchedHistoryDaysCount,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const NoResults: React.FC<{
|
||||||
|
filter: PollHistoryFilter;
|
||||||
|
oldestFetchedEventTimestamp?: number;
|
||||||
|
loadMorePolls?: () => void;
|
||||||
|
isLoading?: boolean;
|
||||||
|
}> = ({ filter, isLoading, oldestFetchedEventTimestamp, loadMorePolls }) => {
|
||||||
|
// we can't page the timeline anymore
|
||||||
|
// just use plain loader
|
||||||
|
if (!loadMorePolls && isLoading) {
|
||||||
|
return <LoadingPolls noResultsYet />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className="mx_PollHistoryList_noResults">
|
||||||
|
{getNoResultsMessage(filter, oldestFetchedEventTimestamp, loadMorePolls)}
|
||||||
|
|
||||||
|
{!!loadMorePolls && <LoadMorePolls loadMorePolls={loadMorePolls} isLoading={isLoading} />}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
type PollHistoryListProps = {
|
type PollHistoryListProps = {
|
||||||
pollStartEvents: MatrixEvent[];
|
pollStartEvents: MatrixEvent[];
|
||||||
polls: Map<string, Poll>;
|
polls: Map<string, Poll>;
|
||||||
filter: PollHistoryFilter;
|
filter: PollHistoryFilter;
|
||||||
isLoading?: boolean;
|
/**
|
||||||
|
* server ts of the oldest fetched poll
|
||||||
|
* ignoring filter
|
||||||
|
* used to render no results in last x days message
|
||||||
|
* undefined when no polls are found
|
||||||
|
*/
|
||||||
|
oldestFetchedEventTimestamp?: number;
|
||||||
onFilterChange: (filter: PollHistoryFilter) => void;
|
onFilterChange: (filter: PollHistoryFilter) => void;
|
||||||
onItemClick: (pollId: string) => void;
|
onItemClick: (pollId: string) => void;
|
||||||
|
loadMorePolls?: () => void;
|
||||||
|
isLoading?: boolean;
|
||||||
};
|
};
|
||||||
export const PollHistoryList: React.FC<PollHistoryListProps> = ({
|
export const PollHistoryList: React.FC<PollHistoryListProps> = ({
|
||||||
pollStartEvents,
|
pollStartEvents,
|
||||||
polls,
|
polls,
|
||||||
filter,
|
filter,
|
||||||
isLoading,
|
isLoading,
|
||||||
|
oldestFetchedEventTimestamp,
|
||||||
onFilterChange,
|
onFilterChange,
|
||||||
|
loadMorePolls,
|
||||||
onItemClick,
|
onItemClick,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
|
@ -81,17 +155,18 @@ export const PollHistoryList: React.FC<PollHistoryListProps> = ({
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
)}
|
)}
|
||||||
{isLoading && <LoadingPolls />}
|
{isLoading && !loadMorePolls && <LoadingPolls />}
|
||||||
|
{!!loadMorePolls && <LoadMorePolls loadMorePolls={loadMorePolls} isLoading={isLoading} />}
|
||||||
</ol>
|
</ol>
|
||||||
)}
|
)}
|
||||||
{!pollStartEvents.length && !isLoading && (
|
{!pollStartEvents.length && (
|
||||||
<span className="mx_PollHistoryList_noResults">
|
<NoResults
|
||||||
{filter === "ACTIVE"
|
oldestFetchedEventTimestamp={oldestFetchedEventTimestamp}
|
||||||
? _t("There are no active polls in this room")
|
isLoading={isLoading}
|
||||||
: _t("There are no past polls in this room")}
|
filter={filter}
|
||||||
</span>
|
loadMorePolls={loadMorePolls}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{!pollStartEvents.length && isLoading && <LoadingPolls noResultsYet />}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,41 +14,82 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { M_POLL_START } from "matrix-js-sdk/src/@types/polls";
|
import { M_POLL_START } from "matrix-js-sdk/src/@types/polls";
|
||||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||||
import { EventTimeline, EventTimelineSet, Room } from "matrix-js-sdk/src/matrix";
|
import { Direction, EventTimeline, EventTimelineSet, Room } from "matrix-js-sdk/src/matrix";
|
||||||
import { Filter, IFilterDefinition } from "matrix-js-sdk/src/filter";
|
import { Filter, IFilterDefinition } from "matrix-js-sdk/src/filter";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
|
||||||
/**
|
const getOldestEventTimestamp = (timelineSet?: EventTimelineSet): number | undefined => {
|
||||||
* Page timeline backwards until either:
|
if (!timelineSet) {
|
||||||
* - event older than endOfHistoryPeriodTimestamp is encountered
|
|
||||||
* - end of timeline is reached
|
|
||||||
* @param timelineSet - timelineset to page
|
|
||||||
* @param matrixClient - client
|
|
||||||
* @param endOfHistoryPeriodTimestamp - epoch timestamp to fetch until
|
|
||||||
* @returns void
|
|
||||||
*/
|
|
||||||
const pagePolls = async (
|
|
||||||
timelineSet: EventTimelineSet,
|
|
||||||
matrixClient: MatrixClient,
|
|
||||||
endOfHistoryPeriodTimestamp: number,
|
|
||||||
): Promise<void> => {
|
|
||||||
const liveTimeline = timelineSet.getLiveTimeline();
|
|
||||||
const events = liveTimeline.getEvents();
|
|
||||||
const oldestEventTimestamp = events[0]?.getTs() || Date.now();
|
|
||||||
const hasMorePages = !!liveTimeline.getPaginationToken(EventTimeline.BACKWARDS);
|
|
||||||
|
|
||||||
if (!hasMorePages || oldestEventTimestamp <= endOfHistoryPeriodTimestamp) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const liveTimeline = timelineSet?.getLiveTimeline();
|
||||||
|
const events = liveTimeline.getEvents();
|
||||||
|
return events[0]?.getTs();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page backwards in timeline history
|
||||||
|
* @param timelineSet - timelineset to page
|
||||||
|
* @param matrixClient - client
|
||||||
|
* @param canPageBackward - whether the timeline has more pages
|
||||||
|
* @param oldestEventTimestamp - server ts of the oldest encountered event
|
||||||
|
*/
|
||||||
|
const pagePollHistory = async (
|
||||||
|
timelineSet: EventTimelineSet,
|
||||||
|
matrixClient: MatrixClient,
|
||||||
|
): Promise<{
|
||||||
|
oldestEventTimestamp?: number;
|
||||||
|
canPageBackward: boolean;
|
||||||
|
}> => {
|
||||||
|
if (!timelineSet) {
|
||||||
|
return { canPageBackward: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
const liveTimeline = timelineSet.getLiveTimeline();
|
||||||
|
|
||||||
await matrixClient.paginateEventTimeline(liveTimeline, {
|
await matrixClient.paginateEventTimeline(liveTimeline, {
|
||||||
backwards: true,
|
backwards: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
return pagePolls(timelineSet, matrixClient, endOfHistoryPeriodTimestamp);
|
return {
|
||||||
|
oldestEventTimestamp: getOldestEventTimestamp(timelineSet),
|
||||||
|
canPageBackward: !!liveTimeline.getPaginationToken(EventTimeline.BACKWARDS),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page timeline backwards until either:
|
||||||
|
* - event older than timestamp is encountered
|
||||||
|
* - end of timeline is reached
|
||||||
|
* @param timelineSet - timeline set to page
|
||||||
|
* @param matrixClient - client
|
||||||
|
* @param timestamp - epoch timestamp to page until
|
||||||
|
* @param canPageBackward - whether the timeline has more pages
|
||||||
|
* @param oldestEventTimestamp - server ts of the oldest encountered event
|
||||||
|
*/
|
||||||
|
const fetchHistoryUntilTimestamp = async (
|
||||||
|
timelineSet: EventTimelineSet | undefined,
|
||||||
|
matrixClient: MatrixClient,
|
||||||
|
timestamp: number,
|
||||||
|
canPageBackward: boolean,
|
||||||
|
oldestEventTimestamp?: number,
|
||||||
|
): Promise<void> => {
|
||||||
|
if (!timelineSet || !canPageBackward || (oldestEventTimestamp && oldestEventTimestamp < timestamp)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const result = await pagePollHistory(timelineSet, matrixClient);
|
||||||
|
|
||||||
|
return fetchHistoryUntilTimestamp(
|
||||||
|
timelineSet,
|
||||||
|
matrixClient,
|
||||||
|
timestamp,
|
||||||
|
result.canPageBackward,
|
||||||
|
result.oldestEventTimestamp,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ONE_DAY_MS = 60000 * 60 * 24;
|
const ONE_DAY_MS = 60000 * 60 * 24;
|
||||||
|
@ -57,35 +98,73 @@ const ONE_DAY_MS = 60000 * 60 * 24;
|
||||||
* @param timelineSet - timelineset to page
|
* @param timelineSet - timelineset to page
|
||||||
* @param matrixClient - client
|
* @param matrixClient - client
|
||||||
* @param historyPeriodDays - number of days of history to fetch, from current day
|
* @param historyPeriodDays - number of days of history to fetch, from current day
|
||||||
* @returns isLoading - true while fetching history
|
* @returns isLoading - true while fetching
|
||||||
|
* @returns oldestEventTimestamp - timestamp of oldest encountered poll, undefined when no polls found in timeline so far
|
||||||
|
* @returns loadMorePolls - function to page timeline backwards, undefined when timeline cannot be paged backwards
|
||||||
|
* @returns loadTimelineHistory - loads timeline history for the given history period
|
||||||
*/
|
*/
|
||||||
const useTimelineHistory = (
|
const useTimelineHistory = (
|
||||||
timelineSet: EventTimelineSet | null,
|
timelineSet: EventTimelineSet | undefined,
|
||||||
matrixClient: MatrixClient,
|
matrixClient: MatrixClient,
|
||||||
historyPeriodDays: number,
|
historyPeriodDays: number,
|
||||||
): { isLoading: boolean } => {
|
): {
|
||||||
|
isLoading: boolean;
|
||||||
|
oldestEventTimestamp?: number;
|
||||||
|
loadTimelineHistory: () => Promise<void>;
|
||||||
|
loadMorePolls?: () => Promise<void>;
|
||||||
|
} => {
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const [oldestEventTimestamp, setOldestEventTimestamp] = useState<number | undefined>(undefined);
|
||||||
|
const [canPageBackward, setCanPageBackward] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
const loadTimelineHistory = useCallback(async () => {
|
||||||
|
const endOfHistoryPeriodTimestamp = Date.now() - ONE_DAY_MS * historyPeriodDays;
|
||||||
|
setIsLoading(true);
|
||||||
|
try {
|
||||||
|
const liveTimeline = timelineSet?.getLiveTimeline();
|
||||||
|
const canPageBackward = !!liveTimeline?.getPaginationToken(Direction.Backward);
|
||||||
|
const oldestEventTimestamp = getOldestEventTimestamp(timelineSet);
|
||||||
|
|
||||||
|
await fetchHistoryUntilTimestamp(
|
||||||
|
timelineSet,
|
||||||
|
matrixClient,
|
||||||
|
endOfHistoryPeriodTimestamp,
|
||||||
|
canPageBackward,
|
||||||
|
oldestEventTimestamp,
|
||||||
|
);
|
||||||
|
|
||||||
|
setCanPageBackward(!!timelineSet?.getLiveTimeline()?.getPaginationToken(EventTimeline.BACKWARDS));
|
||||||
|
setOldestEventTimestamp(getOldestEventTimestamp(timelineSet));
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Failed to fetch room polls history", error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, [historyPeriodDays, timelineSet, matrixClient]);
|
||||||
|
|
||||||
|
const loadMorePolls = useCallback(async () => {
|
||||||
if (!timelineSet) {
|
if (!timelineSet) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const endOfHistoryPeriodTimestamp = Date.now() - ONE_DAY_MS * historyPeriodDays;
|
setIsLoading(true);
|
||||||
|
try {
|
||||||
|
const result = await pagePollHistory(timelineSet, matrixClient);
|
||||||
|
|
||||||
const doFetchHistory = async (): Promise<void> => {
|
setCanPageBackward(result.canPageBackward);
|
||||||
setIsLoading(true);
|
setOldestEventTimestamp(result.oldestEventTimestamp);
|
||||||
try {
|
} catch (error) {
|
||||||
await pagePolls(timelineSet, matrixClient, endOfHistoryPeriodTimestamp);
|
logger.error("Failed to fetch room polls history", error);
|
||||||
} catch (error) {
|
} finally {
|
||||||
logger.error("Failed to fetch room polls history", error);
|
setIsLoading(false);
|
||||||
} finally {
|
}
|
||||||
setIsLoading(false);
|
}, [timelineSet, matrixClient]);
|
||||||
}
|
|
||||||
};
|
|
||||||
doFetchHistory();
|
|
||||||
}, [timelineSet, historyPeriodDays, matrixClient]);
|
|
||||||
|
|
||||||
return { isLoading };
|
return {
|
||||||
|
isLoading,
|
||||||
|
oldestEventTimestamp,
|
||||||
|
loadTimelineHistory,
|
||||||
|
loadMorePolls: canPageBackward ? loadMorePolls : undefined,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const filterDefinition: IFilterDefinition = {
|
const filterDefinition: IFilterDefinition = {
|
||||||
|
@ -97,18 +176,24 @@ const filterDefinition: IFilterDefinition = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch poll start events in the last N days of room history
|
* Fetches poll start events in the last N days of room history
|
||||||
* @param room - room to fetch history for
|
* @param room - room to fetch history for
|
||||||
* @param matrixClient - client
|
* @param matrixClient - client
|
||||||
* @param historyPeriodDays - number of days of history to fetch, from current day
|
* @param historyPeriodDays - number of days of history to fetch, from current day
|
||||||
* @returns isLoading - true while fetching history
|
* @returns isLoading - true while fetching history
|
||||||
|
* @returns oldestEventTimestamp - timestamp of oldest encountered poll, undefined when no polls found in timeline so far
|
||||||
|
* @returns loadMorePolls - function to page timeline backwards, undefined when timeline cannot be paged backwards
|
||||||
*/
|
*/
|
||||||
export const useFetchPastPolls = (
|
export const useFetchPastPolls = (
|
||||||
room: Room,
|
room: Room,
|
||||||
matrixClient: MatrixClient,
|
matrixClient: MatrixClient,
|
||||||
historyPeriodDays = 30,
|
historyPeriodDays = 30,
|
||||||
): { isLoading: boolean } => {
|
): {
|
||||||
const [timelineSet, setTimelineSet] = useState<EventTimelineSet | null>(null);
|
isLoading: boolean;
|
||||||
|
oldestEventTimestamp?: number;
|
||||||
|
loadMorePolls?: () => Promise<void>;
|
||||||
|
} => {
|
||||||
|
const [timelineSet, setTimelineSet] = useState<EventTimelineSet | undefined>(undefined);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const filter = new Filter(matrixClient.getSafeUserId());
|
const filter = new Filter(matrixClient.getSafeUserId());
|
||||||
|
@ -123,7 +208,15 @@ export const useFetchPastPolls = (
|
||||||
getFilteredTimelineSet();
|
getFilteredTimelineSet();
|
||||||
}, [room, matrixClient]);
|
}, [room, matrixClient]);
|
||||||
|
|
||||||
const { isLoading } = useTimelineHistory(timelineSet, matrixClient, historyPeriodDays);
|
const { isLoading, oldestEventTimestamp, loadMorePolls, loadTimelineHistory } = useTimelineHistory(
|
||||||
|
timelineSet,
|
||||||
|
matrixClient,
|
||||||
|
historyPeriodDays,
|
||||||
|
);
|
||||||
|
|
||||||
return { isLoading };
|
useEffect(() => {
|
||||||
|
loadTimelineHistory();
|
||||||
|
}, [loadTimelineHistory]);
|
||||||
|
|
||||||
|
return { isLoading, oldestEventTimestamp, loadMorePolls };
|
||||||
};
|
};
|
||||||
|
|
|
@ -3143,8 +3143,15 @@
|
||||||
"Active polls": "Active polls",
|
"Active polls": "Active polls",
|
||||||
"Past polls": "Past polls",
|
"Past polls": "Past polls",
|
||||||
"Loading polls": "Loading polls",
|
"Loading polls": "Loading polls",
|
||||||
|
"Load more polls": "Load more polls",
|
||||||
"There are no active polls in this room": "There are no active polls in this room",
|
"There are no active polls in this room": "There are no active polls in this room",
|
||||||
"There are no past polls in this room": "There are no past polls in this room",
|
"There are no past polls in this room": "There are no past polls in this room",
|
||||||
|
"There are no active polls. Load more polls to view polls for previous months": "There are no active polls. Load more polls to view polls for previous months",
|
||||||
|
"There are no past polls. Load more polls to view polls for previous months": "There are no past polls. Load more polls to view polls for previous months",
|
||||||
|
"There are no active polls for the past %(count)s days. Load more polls to view polls for previous months|other": "There are no active polls for the past %(count)s days. Load more polls to view polls for previous months",
|
||||||
|
"There are no active polls for the past %(count)s days. Load more polls to view polls for previous months|one": "There are no active polls for the past day. Load more polls to view polls for previous months",
|
||||||
|
"There are no past polls for the past %(count)s days. Load more polls to view polls for previous months|other": "There are no past polls for the past %(count)s days. Load more polls to view polls for previous months",
|
||||||
|
"There are no past polls for the past %(count)s days. Load more polls to view polls for previous months|one": "There are no past polls for the past day. Load more polls to view polls for previous months",
|
||||||
"View poll": "View poll",
|
"View poll": "View poll",
|
||||||
"Send custom account data event": "Send custom account data event",
|
"Send custom account data event": "Send custom account data event",
|
||||||
"Send custom room account data event": "Send custom room account data event",
|
"Send custom room account data event": "Send custom room account data event",
|
||||||
|
|
|
@ -176,12 +176,20 @@ describe("<PollHistoryDialog />", () => {
|
||||||
expect(mockClient.paginateEventTimeline).toHaveBeenCalledTimes(1);
|
expect(mockClient.paginateEventTimeline).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("displays loader and list while paging timeline", async () => {
|
it("renders a no polls message when there are no active polls in the room", async () => {
|
||||||
|
const { getByText } = getComponent();
|
||||||
|
await flushPromises();
|
||||||
|
|
||||||
|
expect(getByText("There are no active polls in this room")).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders a no polls message and a load more button when not at end of timeline", async () => {
|
||||||
const timelineSet = room.getOrCreateFilteredTimelineSet(expectedFilter);
|
const timelineSet = room.getOrCreateFilteredTimelineSet(expectedFilter);
|
||||||
const liveTimeline = timelineSet.getLiveTimeline();
|
const liveTimeline = timelineSet.getLiveTimeline();
|
||||||
const tenDaysAgoTs = now - 60000 * 60 * 24 * 10;
|
const fourtyDaysAgoTs = now - 60000 * 60 * 24 * 40;
|
||||||
|
const pollStart = makePollStartEvent("Question?", userId, undefined, { ts: fourtyDaysAgoTs, id: "1" });
|
||||||
|
|
||||||
jest.spyOn(liveTimeline, "getEvents").mockReset().mockReturnValue([]);
|
jest.spyOn(liveTimeline, "getEvents").mockReset().mockReturnValueOnce([]).mockReturnValueOnce([pollStart]);
|
||||||
|
|
||||||
// mock three pages of timeline history
|
// mock three pages of timeline history
|
||||||
jest.spyOn(liveTimeline, "getPaginationToken")
|
jest.spyOn(liveTimeline, "getPaginationToken")
|
||||||
|
@ -189,57 +197,24 @@ describe("<PollHistoryDialog />", () => {
|
||||||
.mockReturnValueOnce("test-pagination-token-2")
|
.mockReturnValueOnce("test-pagination-token-2")
|
||||||
.mockReturnValueOnce("test-pagination-token-3");
|
.mockReturnValueOnce("test-pagination-token-3");
|
||||||
|
|
||||||
// reference to pagination resolve, so we can assert between pages
|
const { getByText } = getComponent();
|
||||||
let resolvePagination1: (value: boolean) => void | undefined;
|
|
||||||
let resolvePagination2: (value: boolean) => void | undefined;
|
|
||||||
mockClient.paginateEventTimeline
|
|
||||||
.mockImplementationOnce(async (_p) => {
|
|
||||||
const pollStart = makePollStartEvent("Question?", userId, undefined, { ts: now, id: "1" });
|
|
||||||
jest.spyOn(liveTimeline, "getEvents").mockReturnValue([pollStart]);
|
|
||||||
room.processPollEvents([pollStart]);
|
|
||||||
return new Promise((resolve) => (resolvePagination1 = resolve));
|
|
||||||
})
|
|
||||||
.mockImplementationOnce(async (_p) => {
|
|
||||||
const pollStart = makePollStartEvent("Older question?", userId, undefined, {
|
|
||||||
ts: tenDaysAgoTs,
|
|
||||||
id: "2",
|
|
||||||
});
|
|
||||||
jest.spyOn(liveTimeline, "getEvents").mockReturnValue([pollStart]);
|
|
||||||
room.processPollEvents([pollStart]);
|
|
||||||
return new Promise((resolve) => (resolvePagination2 = resolve));
|
|
||||||
});
|
|
||||||
|
|
||||||
const { getByText, queryByText } = getComponent();
|
|
||||||
|
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
|
||||||
expect(mockClient.paginateEventTimeline).toHaveBeenCalledTimes(1);
|
expect(mockClient.paginateEventTimeline).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
resolvePagination1!(true);
|
expect(getByText("There are no active polls. Load more polls to view polls for previous months")).toBeTruthy();
|
||||||
|
|
||||||
|
fireEvent.click(getByText("Load more polls"));
|
||||||
|
|
||||||
|
// paged again
|
||||||
|
expect(mockClient.paginateEventTimeline).toHaveBeenCalledTimes(2);
|
||||||
|
// load more polls button still in UI, with loader
|
||||||
|
expect(getByText("Load more polls")).toMatchSnapshot();
|
||||||
|
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
|
||||||
// first page has results, display immediately
|
// no more spinner
|
||||||
expect(getByText("Question?")).toBeInTheDocument();
|
expect(getByText("Load more polls")).toMatchSnapshot();
|
||||||
// but we are still fetching history, diaply loader
|
|
||||||
expect(getByText("Loading polls")).toBeInTheDocument();
|
|
||||||
|
|
||||||
resolvePagination2!(true);
|
|
||||||
await flushPromises();
|
|
||||||
|
|
||||||
// additional results addeds
|
|
||||||
expect(getByText("Older question?")).toBeInTheDocument();
|
|
||||||
expect(getByText("Question?")).toBeInTheDocument();
|
|
||||||
// finished paging
|
|
||||||
expect(queryByText("Loading polls")).not.toBeInTheDocument();
|
|
||||||
|
|
||||||
expect(mockClient.paginateEventTimeline).toHaveBeenCalledTimes(3);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("renders a no polls message when there are no active polls in the room", async () => {
|
|
||||||
const { getByText } = getComponent();
|
|
||||||
await flushPromises();
|
|
||||||
|
|
||||||
expect(getByText("There are no active polls in this room")).toBeTruthy();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders a no past polls message when there are no past polls in the room", async () => {
|
it("renders a no past polls message when there are no past polls in the room", async () => {
|
||||||
|
|
|
@ -168,3 +168,32 @@ exports[`<PollHistoryDialog /> renders a list of active polls when there are pol
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`<PollHistoryDialog /> renders a no polls message and a load more button when not at end of timeline 1`] = `
|
||||||
|
<div
|
||||||
|
class="mx_AccessibleButton mx_PollHistoryList_loadMorePolls mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link_inline"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
Load more polls
|
||||||
|
<div
|
||||||
|
class="mx_InlineSpinner"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-label="Loading…"
|
||||||
|
class="mx_InlineSpinner_icon mx_Spinner_icon"
|
||||||
|
style="width: 16px; height: 16px;"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`<PollHistoryDialog /> renders a no polls message and a load more button when not at end of timeline 2`] = `
|
||||||
|
<div
|
||||||
|
class="mx_AccessibleButton mx_PollHistoryList_loadMorePolls mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link_inline"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
Load more polls
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
Loading…
Reference in a new issue