Compare commits
2 Commits
42422bbe61
...
2d59be9dd3
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d59be9dd3 | |||
| bb65c96454 |
@@ -454,7 +454,7 @@ function RoomNavItem_({
|
|||||||
// Skip pure membership events
|
// Skip pure membership events
|
||||||
if (type === StateEvent.RoomMember) return null;
|
if (type === StateEvent.RoomMember) return null;
|
||||||
let body: string;
|
let body: string;
|
||||||
if (latestEvent.isEncrypted()) {
|
if (latestEvent.isDecryptionFailure()) {
|
||||||
body = 'Encrypted message';
|
body = 'Encrypted message';
|
||||||
} else if (type === MessageEvent.Sticker) {
|
} else if (type === MessageEvent.Sticker) {
|
||||||
body = 'Sticker';
|
body = 'Sticker';
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
TooltipProvider,
|
TooltipProvider,
|
||||||
config,
|
config,
|
||||||
} from 'folds';
|
} from 'folds';
|
||||||
import { Direction, EventType, MatrixEvent, MsgType, Room } from 'matrix-js-sdk';
|
import { EventType, MsgType, Room } from 'matrix-js-sdk';
|
||||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||||
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
|
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
|
||||||
import { mxcUrlToHttp } from '../../utils/matrix';
|
import { mxcUrlToHttp } from '../../utils/matrix';
|
||||||
@@ -65,53 +65,45 @@ export function MediaGallery({ room, onClose }: MediaGalleryProps) {
|
|||||||
const useAuthentication = useMediaAuthentication();
|
const useAuthentication = useMediaAuthentication();
|
||||||
|
|
||||||
const [tab, setTab] = useState<GalleryTab>('image');
|
const [tab, setTab] = useState<GalleryTab>('image');
|
||||||
const [events, setEvents] = useState<MatrixEvent[]>([]);
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [paginationToken, setPaginationToken] = useState<string | null>(null);
|
const [canLoadMore, setCanLoadMore] = useState(true);
|
||||||
|
|
||||||
const msgtype = TAB_MSGTYPES[tab];
|
const msgtype = TAB_MSGTYPES[tab];
|
||||||
|
|
||||||
const loadMedia = useCallback(
|
// Read already-decrypted events from the live timeline (works for E2EE rooms)
|
||||||
async (fromToken: string | null, append: boolean) => {
|
const getFilteredEvents = useCallback(() => {
|
||||||
|
const timeline = room.getLiveTimeline();
|
||||||
|
return timeline
|
||||||
|
.getEvents()
|
||||||
|
.filter((ev) => {
|
||||||
|
if (ev.isRedacted()) return false;
|
||||||
|
const content = ev.getContent();
|
||||||
|
return ev.getType() === EventType.RoomMessage && content.msgtype === msgtype;
|
||||||
|
})
|
||||||
|
.slice()
|
||||||
|
.reverse(); // newest first
|
||||||
|
}, [room, msgtype]);
|
||||||
|
|
||||||
|
const [events, setEvents] = useState(() => getFilteredEvents());
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setEvents(getFilteredEvents());
|
||||||
|
setCanLoadMore(true);
|
||||||
|
}, [getFilteredEvents]);
|
||||||
|
|
||||||
|
const handleLoadMore = useCallback(async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const response = await mx.createMessagesRequest(
|
const timeline = room.getLiveTimeline();
|
||||||
room.roomId,
|
const hasMore = await mx.paginateEventTimeline(timeline, { backwards: true, limit: 100 });
|
||||||
fromToken,
|
setEvents(getFilteredEvents());
|
||||||
100,
|
setCanLoadMore(hasMore);
|
||||||
Direction.Backward,
|
|
||||||
undefined,
|
|
||||||
);
|
|
||||||
const { end, chunk } = response;
|
|
||||||
const filtered = chunk
|
|
||||||
.filter(
|
|
||||||
(ev) =>
|
|
||||||
ev.type === EventType.RoomMessage &&
|
|
||||||
ev.content?.msgtype === msgtype &&
|
|
||||||
!ev.unsigned?.redacted_because,
|
|
||||||
)
|
|
||||||
.map((ev) => new MatrixEvent(ev));
|
|
||||||
|
|
||||||
setEvents((prev) => (append ? [...prev, ...filtered] : filtered));
|
|
||||||
setPaginationToken(end ?? null);
|
|
||||||
} catch {
|
} catch {
|
||||||
// silently swallow fetch errors — gallery stays showing what it has
|
// silently swallow
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
},
|
}, [mx, room, getFilteredEvents]);
|
||||||
[mx, room.roomId, msgtype],
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setEvents([]);
|
|
||||||
setPaginationToken(null);
|
|
||||||
loadMedia(null, false).catch(() => undefined);
|
|
||||||
}, [loadMedia]);
|
|
||||||
|
|
||||||
const handleLoadMore = () => {
|
|
||||||
if (paginationToken) loadMedia(paginationToken, true).catch(() => undefined);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@@ -306,7 +298,7 @@ export function MediaGallery({ room, onClose }: MediaGalleryProps) {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Load more */}
|
{/* Load more */}
|
||||||
{paginationToken !== null && !loading && (
|
{canLoadMore && !loading && events.length > 0 && (
|
||||||
<Box justifyContent="Center" style={{ padding: config.space.S200 }}>
|
<Box justifyContent="Center" style={{ padding: config.space.S200 }}>
|
||||||
<Button
|
<Button
|
||||||
size="300"
|
size="300"
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ function QuickSwitcherFeature() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleKeyDown = (e: KeyboardEvent) => {
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
if ((e.ctrlKey || e.metaKey) && e.key === 'p') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -270,14 +270,15 @@ export function Direct() {
|
|||||||
}
|
}
|
||||||
placeholder="Filter DMs…"
|
placeholder="Filter DMs…"
|
||||||
variant="Surface"
|
variant="Surface"
|
||||||
size="300"
|
size="400"
|
||||||
radii="300"
|
radii="400"
|
||||||
|
before={<Icon size="50" src={Icons.Search} />}
|
||||||
after={
|
after={
|
||||||
filterQuery ? (
|
filterQuery ? (
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={() => setFilterQuery('')}
|
onClick={() => setFilterQuery('')}
|
||||||
size="300"
|
size="400"
|
||||||
radii="300"
|
radii="Pill"
|
||||||
variant="Background"
|
variant="Background"
|
||||||
fill="None"
|
fill="None"
|
||||||
aria-label="Clear filter"
|
aria-label="Clear filter"
|
||||||
|
|||||||
@@ -402,14 +402,15 @@ export function Home() {
|
|||||||
onChange={(e: ChangeEvent<HTMLInputElement>) => setFilterQuery(e.target.value)}
|
onChange={(e: ChangeEvent<HTMLInputElement>) => setFilterQuery(e.target.value)}
|
||||||
placeholder="Filter rooms…"
|
placeholder="Filter rooms…"
|
||||||
variant="Surface"
|
variant="Surface"
|
||||||
size="300"
|
size="400"
|
||||||
radii="300"
|
radii="400"
|
||||||
|
before={<Icon size="50" src={Icons.Search} />}
|
||||||
after={
|
after={
|
||||||
filterQuery ? (
|
filterQuery ? (
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={() => setFilterQuery('')}
|
onClick={() => setFilterQuery('')}
|
||||||
size="300"
|
size="400"
|
||||||
radii="300"
|
radii="Pill"
|
||||||
variant="Background"
|
variant="Background"
|
||||||
fill="None"
|
fill="None"
|
||||||
aria-label="Clear filter"
|
aria-label="Clear filter"
|
||||||
|
|||||||
Reference in New Issue
Block a user