import * as css from './ImageViewer.css';
import { useZoom } from '../../hooks/useZoom';
import { usePan } from '../../hooks/usePan';
+import { downloadMedia } from '../../utils/matrix';
export type ImageViewerProps = {
alt: string;
const { zoom, zoomIn, zoomOut, setZoom } = useZoom(0.2);
const { pan, cursor, onMouseDown } = usePan(zoom !== 1);
- const handleDownload = () => {
- FileSaver.saveAs(src, alt);
+ const handleDownload = async () => {
+ const fileContent = await downloadMedia(src);
+ FileSaver.saveAs(fileContent, alt);
};
return (
import { Range } from 'react-range';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
-import { getFileSrcUrl } from './util';
import { IAudioInfo } from '../../../../types/matrix/common';
import {
PlayTimeCallback,
} from '../../../hooks/media';
import { useThrottle } from '../../../hooks/useThrottle';
import { secondsToMinutesAndSeconds } from '../../../utils/common';
-import { mxcUrlToHttp } from '../../../utils/matrix';
+import {
+ decryptFile,
+ downloadEncryptedMedia,
+ downloadMedia,
+ mxcUrlToHttp,
+} from '../../../utils/matrix';
import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
const PLAY_TIME_THROTTLE_OPS = {
const useAuthentication = useMediaAuthentication();
const [srcState, loadSrc] = useAsyncCallback(
- useCallback(
- () => getFileSrcUrl(mxcUrlToHttp(mx, url, useAuthentication) ?? '', mimeType, encInfo, true),
- [mx, url, useAuthentication, mimeType, encInfo]
- )
+ useCallback(async () => {
+ const mediaUrl = mxcUrlToHttp(mx, url, useAuthentication) ?? url;
+ const fileContent = encInfo
+ ? await downloadEncryptedMedia(mediaUrl, (encBuf) => decryptFile(encBuf, mimeType, encInfo))
+ : await downloadMedia(mediaUrl);
+ return URL.createObjectURL(fileContent);
+ }, [mx, url, useAuthentication, mimeType, encInfo])
);
const audioRef = useRef<HTMLAudioElement | null>(null);
import { IFileInfo } from '../../../../types/matrix/common';
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
-import { getFileSrcUrl, getSrcFile } from './util';
import { bytesToSize } from '../../../utils/common';
import {
READABLE_EXT_TO_MIME_TYPE,
} from '../../../utils/mimeTypes';
import * as css from './style.css';
import { stopPropagation } from '../../../utils/keyboard';
-import { mxcUrlToHttp } from '../../../utils/matrix';
+import {
+ decryptFile,
+ downloadEncryptedMedia,
+ downloadMedia,
+ mxcUrlToHttp,
+} from '../../../utils/matrix';
import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
const renderErrorButton = (retry: () => void, text: string) => (
const useAuthentication = useMediaAuthentication();
const [textViewer, setTextViewer] = useState(false);
- const loadSrc = useCallback(
- () => getFileSrcUrl(mxcUrlToHttp(mx, url, useAuthentication) ?? '', mimeType, encInfo),
- [mx, url, useAuthentication, mimeType, encInfo]
- );
-
const [textState, loadText] = useAsyncCallback(
useCallback(async () => {
- const src = await loadSrc();
- const blob = await getSrcFile(src);
- const text = blob.text();
+ const mediaUrl = mxcUrlToHttp(mx, url, useAuthentication) ?? url;
+ const fileContent = encInfo
+ ? await downloadEncryptedMedia(mediaUrl, (encBuf) => decryptFile(encBuf, mimeType, encInfo))
+ : await downloadMedia(mediaUrl);
+
+ const text = fileContent.text();
setTextViewer(true);
return text;
- }, [loadSrc])
+ }, [mx, useAuthentication, mimeType, encInfo, url])
);
return (
const [pdfState, loadPdf] = useAsyncCallback(
useCallback(async () => {
- const httpUrl = await getFileSrcUrl(mxcUrlToHttp(mx, url, useAuthentication) ?? '', mimeType, encInfo);
+ const mediaUrl = mxcUrlToHttp(mx, url, useAuthentication) ?? url;
+ const fileContent = encInfo
+ ? await downloadEncryptedMedia(mediaUrl, (encBuf) => decryptFile(encBuf, mimeType, encInfo))
+ : await downloadMedia(mediaUrl);
setPdfViewer(true);
- return httpUrl;
+ return URL.createObjectURL(fileContent);
}, [mx, url, useAuthentication, mimeType, encInfo])
);
const [downloadState, download] = useAsyncCallback(
useCallback(async () => {
- const httpUrl = await getFileSrcUrl(mxcUrlToHttp(mx, url, useAuthentication) ?? '', mimeType, encInfo);
- FileSaver.saveAs(httpUrl, body);
- return httpUrl;
+ const mediaUrl = mxcUrlToHttp(mx, url, useAuthentication) ?? url;
+ const fileContent = encInfo
+ ? await downloadEncryptedMedia(mediaUrl, (encBuf) => decryptFile(encBuf, mimeType, encInfo))
+ : await downloadMedia(mediaUrl);
+
+ const fileURL = URL.createObjectURL(fileContent);
+ FileSaver.saveAs(fileURL, body);
+ return fileURL;
}, [mx, url, useAuthentication, mimeType, encInfo, body])
);
import { IImageInfo, MATRIX_BLUR_HASH_PROPERTY_NAME } from '../../../../types/matrix/common';
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
-import { getFileSrcUrl } from './util';
import * as css from './style.css';
import { bytesToSize } from '../../../utils/common';
import { FALLBACK_MIMETYPE } from '../../../utils/mimeTypes';
import { stopPropagation } from '../../../utils/keyboard';
-import { mxcUrlToHttp } from '../../../utils/matrix';
+import { decryptFile, downloadEncryptedMedia, mxcUrlToHttp } from '../../../utils/matrix';
import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
type RenderViewerProps = {
const [viewer, setViewer] = useState(false);
const [srcState, loadSrc] = useAsyncCallback(
- useCallback(
- () => getFileSrcUrl(mxcUrlToHttp(mx, url, useAuthentication) ?? '', mimeType || FALLBACK_MIMETYPE, encInfo),
- [mx, url, useAuthentication, mimeType, encInfo]
- )
+ useCallback(async () => {
+ const mediaUrl = mxcUrlToHttp(mx, url, useAuthentication) ?? url;
+ if (encInfo) {
+ const fileContent = await downloadEncryptedMedia(mediaUrl, (encBuf) =>
+ decryptFile(encBuf, mimeType ?? FALLBACK_MIMETYPE, encInfo)
+ );
+ return URL.createObjectURL(fileContent);
+ }
+ return mediaUrl;
+ }, [mx, url, useAuthentication, mimeType, encInfo])
);
const handleLoad = () => {
import { IThumbnailContent } from '../../../../types/matrix/common';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
-import { getFileSrcUrl } from './util';
-import { mxcUrlToHttp } from '../../../utils/matrix';
+import { decryptFile, downloadEncryptedMedia, mxcUrlToHttp } from '../../../utils/matrix';
import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
+import { FALLBACK_MIMETYPE } from '../../../utils/mimeTypes';
export type ThumbnailContentProps = {
info: IThumbnailContent;
const useAuthentication = useMediaAuthentication();
const [thumbSrcState, loadThumbSrc] = useAsyncCallback(
- useCallback(() => {
+ useCallback(async () => {
const thumbInfo = info.thumbnail_info;
const thumbMxcUrl = info.thumbnail_file?.url ?? info.thumbnail_url;
+ const encInfo = info.thumbnail_file;
if (typeof thumbMxcUrl !== 'string' || typeof thumbInfo?.mimetype !== 'string') {
throw new Error('Failed to load thumbnail');
}
- return getFileSrcUrl(
- mxcUrlToHttp(mx, thumbMxcUrl, useAuthentication) ?? '',
- thumbInfo.mimetype,
- info.thumbnail_file
- );
+
+ const mediaUrl = mxcUrlToHttp(mx, thumbMxcUrl, useAuthentication) ?? thumbMxcUrl;
+ if (encInfo) {
+ const fileContent = await downloadEncryptedMedia(mediaUrl, (encBuf) =>
+ decryptFile(encBuf, thumbInfo.mimetype ?? FALLBACK_MIMETYPE, encInfo)
+ );
+ return URL.createObjectURL(fileContent);
+ }
+
+ return mediaUrl;
}, [mx, info, useAuthentication])
);
import * as css from './style.css';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
-import { getFileSrcUrl } from './util';
import { bytesToSize } from '../../../../util/common';
import { millisecondsToMinutesAndSeconds } from '../../../utils/common';
-import { mxcUrlToHttp } from '../../../utils/matrix';
+import {
+ decryptFile,
+ downloadEncryptedMedia,
+ downloadMedia,
+ mxcUrlToHttp,
+} from '../../../utils/matrix';
import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
type RenderVideoProps = {
const [error, setError] = useState(false);
const [srcState, loadSrc] = useAsyncCallback(
- useCallback(
- () => getFileSrcUrl(mxcUrlToHttp(mx, url, useAuthentication) ?? '', mimeType, encInfo, true),
- [mx, url, useAuthentication, mimeType, encInfo]
- )
+ useCallback(async () => {
+ const mediaUrl = mxcUrlToHttp(mx, url, useAuthentication) ?? url;
+ const fileContent = encInfo
+ ? await downloadEncryptedMedia(mediaUrl, (encBuf) =>
+ decryptFile(encBuf, mimeType, encInfo)
+ )
+ : await downloadMedia(mediaUrl);
+ return URL.createObjectURL(fileContent);
+ }, [mx, url, useAuthentication, mimeType, encInfo])
);
const handleLoad = () => {
+++ /dev/null
-import { EncryptedAttachmentInfo } from 'browser-encrypt-attachment';
-import { decryptFile } from '../../../utils/matrix';
-
-export const getFileSrcUrl = async (
- httpUrl: string,
- mimeType: string,
- encInfo?: EncryptedAttachmentInfo,
- forceFetch?: boolean
-): Promise<string> => {
- if (encInfo) {
- if (typeof httpUrl !== 'string') throw new Error('Malformed event');
- const encRes = await fetch(httpUrl, { method: 'GET' });
- const encData = await encRes.arrayBuffer();
- const decryptedBlob = await decryptFile(encData, mimeType, encInfo);
- return URL.createObjectURL(decryptedBlob);
- }
- if (forceFetch) {
- const res = await fetch(httpUrl, { method: 'GET' });
- const blob = await res.blob();
- return URL.createObjectURL(blob);
- }
-
- return httpUrl;
-};
-
-export const getSrcFile = async (src: string): Promise<Blob> => {
- const res = await fetch(src, { method: 'GET' });
- const blob = await res.blob();
- return blob;
-};
EventTimelineSet,
EventTimelineSetHandlerMap,
IContent,
- IEncryptedFile,
MatrixClient,
MatrixEvent,
Room,
import { isKeyHotkey } from 'is-hotkey';
import { Opts as LinkifyOpts } from 'linkifyjs';
import { useTranslation } from 'react-i18next';
-import {
- decryptFile,
- eventWithShortcode,
- factoryEventSentBy,
- getMxIdLocalPart,
-} from '../../utils/matrix';
+import { eventWithShortcode, factoryEventSentBy, getMxIdLocalPart } from '../../utils/matrix';
import { useMatrixClient } from '../../hooks/useMatrixClient';
import { useVirtualPaginator, ItemRange } from '../../hooks/useVirtualPaginator';
import { useAlive } from '../../hooks/useAlive';
return baseIndex + eventIndex;
};
-export const factoryGetFileSrcUrl =
- (httpUrl: string, mimeType: string, encFile?: IEncryptedFile) => async (): Promise<string> => {
- if (encFile) {
- if (typeof httpUrl !== 'string') throw new Error('Malformed event');
- const encRes = await fetch(httpUrl, { method: 'GET' });
- const encData = await encRes.arrayBuffer();
- const decryptedBlob = await decryptFile(encData, mimeType, encFile);
- return URL.createObjectURL(decryptedBlob);
- }
- return httpUrl;
- };
-
type RoomTimelineProps = {
room: Room;
eventId?: string;
range:
offsetRange > 0
? {
- start: currentTimeline.range.start + offsetRange,
- end: currentTimeline.range.end + offsetRange,
- }
+ start: currentTimeline.range.start + offsetRange,
+ end: currentTimeline.range.end + offsetRange,
+ }
: { ...currentTimeline.range },
}));
};
if (
!paginationToken &&
getTimelinesEventsCount(lTimelines) !==
- getTimelinesEventsCount(getLinkedTimelines(timelineToPaginate))
+ getTimelinesEventsCount(getLinkedTimelines(timelineToPaginate))
) {
recalibratePagination(lTimelines, timelinesEventsCount, backwards);
return;
const [focusItem, setFocusItem] = useState<
| {
- index: number;
- scrollTo: boolean;
- highlight: boolean;
- }
+ index: number;
+ scrollTo: boolean;
+ highlight: boolean;
+ }
| undefined
>();
const alive = useAlive();
const editableEvtId = editableEvt?.getId();
if (!editableEvtId) return;
setEditId(editableEvtId);
- evt.preventDefault()
+ evt.preventDefault();
}
},
[mx, room, editor]
const eventJSX = reactionOrEditEvent(mEvent)
? null
: renderMatrixEvent(
- mEvent.getType(),
- typeof mEvent.getStateKey() === 'string',
- mEventId,
- mEvent,
- item,
- timelineSet,
- collapsed
- );
+ mEvent.getType(),
+ typeof mEvent.getStateKey() === 'string',
+ mEventId,
+ mEvent,
+ item,
+ timelineSet,
+ collapsed
+ );
prevEvent = mEvent;
isPrevRendered = !!eventJSX;
{!canPaginateBack && rangeAtStart && getItems().length > 0 && (
<div
style={{
- padding: `${config.space.S700} ${config.space.S400} ${config.space.S600} ${messageLayout === 1 ? config.space.S400 : toRem(64)
- }`,
+ padding: `${config.space.S700} ${config.space.S400} ${config.space.S600} ${
+ messageLayout === 1 ? config.space.S400 : toRem(64)
+ }`,
}}
>
<RoomIntro room={room} />
-import React, {
- useState, useMemo, useReducer, useEffect,
-} from 'react';
+import React, { useState, useMemo, useReducer, useEffect } from 'react';
import PropTypes from 'prop-types';
import './ImagePack.scss';
import ImagePackItem from './ImagePackItem';
import ImagePackUpload from './ImagePackUpload';
import { useMatrixClient } from '../../hooks/useMatrixClient';
-
-const renameImagePackItem = (shortcode) => new Promise((resolve) => {
- let isCompleted = false;
-
- openReusableDialog(
- <Text variant="s1" weight="medium">Rename</Text>,
- (requestClose) => (
- <div style={{ padding: 'var(--sp-normal)' }}>
- <form
- onSubmit={(e) => {
- e.preventDefault();
- const sc = e.target.shortcode.value;
- if (sc.trim() === '') return;
- isCompleted = true;
- resolve(sc.trim());
- requestClose();
- }}
- >
- <Input
- value={shortcode}
- name="shortcode"
- label="Shortcode"
- autoFocus
- required
- />
- <div style={{ height: 'var(--sp-normal)' }} />
- <Button variant="primary" type="submit">Rename</Button>
- </form>
- </div>
- ),
- () => {
- if (!isCompleted) resolve(null);
- },
- );
-});
+import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
+
+const renameImagePackItem = (shortcode) =>
+ new Promise((resolve) => {
+ let isCompleted = false;
+
+ openReusableDialog(
+ <Text variant="s1" weight="medium">
+ Rename
+ </Text>,
+ (requestClose) => (
+ <div style={{ padding: 'var(--sp-normal)' }}>
+ <form
+ onSubmit={(e) => {
+ e.preventDefault();
+ const sc = e.target.shortcode.value;
+ if (sc.trim() === '') return;
+ isCompleted = true;
+ resolve(sc.trim());
+ requestClose();
+ }}
+ >
+ <Input value={shortcode} name="shortcode" label="Shortcode" autoFocus required />
+ <div style={{ height: 'var(--sp-normal)' }} />
+ <Button variant="primary" type="submit">
+ Rename
+ </Button>
+ </form>
+ </div>
+ ),
+ () => {
+ if (!isCompleted) resolve(null);
+ }
+ );
+ });
function getUsage(usage) {
if (usage.includes('emoticon') && usage.includes('sticker')) return 'both';
const pack = useMemo(() => {
const packEvent = room.currentState.getStateEvents('im.ponies.room_emotes', stateKey);
- return ImagePackBuilder.parsePack(packEvent.getId(), packEvent.getContent())
+ return ImagePackBuilder.parsePack(packEvent.getId(), packEvent.getContent());
}, [room, stateKey]);
const sendPackContent = (content) => {
const mx = useMatrixClient();
const pack = useMemo(() => {
const packEvent = mx.getAccountData('im.ponies.user_emotes');
- return ImagePackBuilder.parsePack(mx.getUserId(), packEvent?.getContent() ?? {
- pack: { display_name: 'Personal' },
- images: {},
- })
+ return ImagePackBuilder.parsePack(
+ mx.getUserId(),
+ packEvent?.getContent() ?? {
+ pack: { display_name: 'Personal' },
+ images: {},
+ }
+ );
}, [mx]);
const sendPackContent = (content) => {
if (typeof key !== 'string') return undefined;
let newKey = key?.replace(/\s/g, '_');
if (pack.getImages().get(newKey)) {
- newKey = suffixRename(
- newKey,
- (suffixedKey) => pack.getImages().get(suffixedKey),
- );
+ newKey = suffixRename(newKey, (suffixedKey) => pack.getImages().get(suffixedKey));
}
return newKey;
};
'Delete',
`Are you sure that you want to delete "${key}"?`,
'Delete',
- 'danger',
+ 'danger'
);
if (!isConfirmed) return;
pack.removeImage(key);
const room = mx.getRoom(roomId);
const [viewMore, setViewMore] = useState(false);
const [isGlobal, setIsGlobal] = useState(isGlobalPack(mx, roomId, stateKey));
+ const useAuthentication = useMediaAuthentication();
const { pack, sendPackContent } = useRoomImagePack(roomId, stateKey);
'Delete Pack',
`Are you sure that you want to delete "${pack.displayName}"?`,
'Delete',
- 'danger',
+ 'danger'
);
if (!isConfirmed) return;
handlePackDelete(stateKey);
return (
<div className="image-pack">
<ImagePackProfile
- avatarUrl={pack.avatarUrl ? mx.mxcUrlToHttp(pack.avatarUrl, 42, 42, 'crop') : null}
+ avatarUrl={
+ pack.avatarUrl
+ ? mx.mxcUrlToHttp(
+ pack.avatarUrl,
+ 42,
+ 42,
+ 'crop',
+ undefined,
+ undefined,
+ useAuthentication
+ )
+ : null
+ }
displayName={pack.displayName ?? 'Unknown'}
attribution={pack.attribution}
usage={getUsage(pack.usage)}
onAvatarChange={canChange ? handleAvatarChange : null}
onEditProfile={canChange ? handleEditProfile : null}
/>
- { canChange && (
- <ImagePackUpload onUpload={handleAddItem} />
- )}
- { images.length === 0 ? null : (
+ {canChange && <ImagePackUpload onUpload={handleAddItem} />}
+ {images.length === 0 ? null : (
<div>
<div className="image-pack__header">
<Text variant="b3">Image</Text>
{images.map(([shortcode, image]) => (
<ImagePackItem
key={shortcode}
- url={mx.mxcUrlToHttp(image.mxc)}
+ url={mx.mxcUrlToHttp(
+ image.mxc,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ useAuthentication
+ )}
shortcode={shortcode}
usage={getUsage(image.usage)}
onUsageChange={canChange ? handleUsageItem : undefined}
<div className="image-pack__footer">
{pack.images.size > 2 && (
<Button onClick={() => setViewMore(!viewMore)}>
- {
- viewMore
- ? 'View less'
- : `View ${pack.images.size - 2} more`
- }
+ {viewMore ? 'View less' : `View ${pack.images.size - 2} more`}
+ </Button>
+ )}
+ {handlePackDelete && (
+ <Button variant="danger" onClick={handleDeletePack}>
+ Delete Pack
</Button>
)}
- { handlePackDelete && <Button variant="danger" onClick={handleDeletePack}>Delete Pack</Button>}
</div>
)}
<div className="image-pack__global">
function ImagePackUser() {
const mx = useMatrixClient();
const [viewMore, setViewMore] = useState(false);
+ const useAuthentication = useMediaAuthentication();
const { pack, sendPackContent } = useUserImagePack();
return (
<div className="image-pack">
<ImagePackProfile
- avatarUrl={pack.avatarUrl ? mx.mxcUrlToHttp(pack.avatarUrl, 42, 42, 'crop') : null}
+ avatarUrl={
+ pack.avatarUrl
+ ? mx.mxcUrlToHttp(
+ pack.avatarUrl,
+ 42,
+ 42,
+ 'crop',
+ undefined,
+ undefined,
+ useAuthentication
+ )
+ : null
+ }
displayName={pack.displayName ?? 'Personal'}
attribution={pack.attribution}
usage={getUsage(pack.usage)}
onEditProfile={handleEditProfile}
/>
<ImagePackUpload onUpload={handleAddItem} />
- { images.length === 0 ? null : (
+ {images.length === 0 ? null : (
<div>
<div className="image-pack__header">
<Text variant="b3">Image</Text>
{images.map(([shortcode, image]) => (
<ImagePackItem
key={shortcode}
- url={mx.mxcUrlToHttp(image.mxc)}
+ url={mx.mxcUrlToHttp(
+ image.mxc,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ useAuthentication
+ )}
shortcode={shortcode}
usage={getUsage(image.usage)}
onUsageChange={handleUsageItem}
))}
</div>
)}
- {(pack.images.size > 2) && (
+ {pack.images.size > 2 && (
<div className="image-pack__footer">
<Button onClick={() => setViewMore(!viewMore)}>
- {
- viewMore
- ? 'View less'
- : `View ${pack.images.size - 2} more`
- }
+ {viewMore ? 'View less' : `View ${pack.images.size - 2} more`}
</Button>
</div>
)}
<div className="image-pack-global">
<MenuHeader>Global packs</MenuHeader>
<div>
- {
- roomIdToStateKeys.size > 0
- ? [...roomIdToStateKeys].map(([roomId, stateKeys]) => {
- const room = mx.getRoom(roomId);
+ {roomIdToStateKeys.size > 0 ? (
+ [...roomIdToStateKeys].map(([roomId, stateKeys]) => {
+ const room = mx.getRoom(roomId);
+ return stateKeys.map((stateKey) => {
+ const data = room.currentState.getStateEvents('im.ponies.room_emotes', stateKey);
+ const pack = ImagePackBuilder.parsePack(data?.getId(), data?.getContent());
+ if (!pack) return null;
return (
- stateKeys.map((stateKey) => {
- const data = room.currentState.getStateEvents('im.ponies.room_emotes', stateKey);
- const pack = ImagePackBuilder.parsePack(data?.getId(), data?.getContent());
- if (!pack) return null;
- return (
- <div className="image-pack__global" key={pack.id}>
- <Checkbox variant="positive" onToggle={() => handleChange(roomId, stateKey)} isActive />
- <div>
- <Text variant="b2">{pack.displayName ?? 'Unknown'}</Text>
- <Text variant="b3">{room.name}</Text>
- </div>
- </div>
- );
- })
+ <div className="image-pack__global" key={pack.id}>
+ <Checkbox
+ variant="positive"
+ onToggle={() => handleChange(roomId, stateKey)}
+ isActive
+ />
+ <div>
+ <Text variant="b2">{pack.displayName ?? 'Unknown'}</Text>
+ <Text variant="b3">{room.name}</Text>
+ </div>
+ </div>
);
- })
- : <div className="image-pack-global__empty"><Text>No global packs</Text></div>
- }
+ });
+ })
+ ) : (
+ <div className="image-pack-global__empty">
+ <Text>No global packs</Text>
+ </div>
+ )}
</div>
</div>
);
import { useRoomNavigate } from '../../hooks/useRoomNavigate';
import { getDMRoomFor } from '../../utils/matrix';
import { useMatrixClient } from '../../hooks/useMatrixClient';
+import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
function InviteUser({ isOpen, roomId, searchTerm, onRequestClose }) {
const [isSearching, updateIsSearching] = useState(false);
const [searchQuery, updateSearchQuery] = useState({});
const [users, updateUsers] = useState([]);
+ const useAuthentication = useMediaAuthentication();
const [procUsers, updateProcUsers] = useState(new Set()); // proc stands for processing.
const [procUserError, updateUserProcError] = useState(new Map());
key={userId}
avatarSrc={
typeof user.avatar_url === 'string'
- ? mx.mxcUrlToHttp(user.avatar_url, 42, 42, 'crop')
+ ? mx.mxcUrlToHttp(
+ user.avatar_url,
+ 42,
+ 42,
+ 'crop',
+ undefined,
+ undefined,
+ useAuthentication
+ )
: null
}
name={name}
import './ProfileEditor.scss';
import { useMatrixClient } from '../../hooks/useMatrixClient';
+import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
function ProfileEditor({ userId }) {
const [isEditing, setIsEditing] = useState(false);
const mx = useMatrixClient();
const user = mx.getUser(mx.getUserId());
+ const useAuthentication = useMediaAuthentication();
const displayNameRef = useRef(null);
const [avatarSrc, setAvatarSrc] = useState(
- user.avatarUrl ? mx.mxcUrlToHttp(user.avatarUrl, 80, 80, 'crop') : null
+ user.avatarUrl
+ ? mx.mxcUrlToHttp(user.avatarUrl, 80, 80, 'crop', undefined, undefined, useAuthentication)
+ : null
);
const [username, setUsername] = useState(user.displayName);
const [disabled, setDisabled] = useState(true);
let isMounted = true;
mx.getProfileInfo(mx.getUserId()).then((info) => {
if (!isMounted) return;
- setAvatarSrc(info.avatar_url ? mx.mxcUrlToHttp(info.avatar_url, 80, 80, 'crop') : null);
+ setAvatarSrc(
+ info.avatar_url
+ ? mx.mxcUrlToHttp(
+ info.avatar_url,
+ 80,
+ 80,
+ 'crop',
+ undefined,
+ undefined,
+ useAuthentication
+ )
+ : null
+ );
setUsername(info.displayname);
});
return () => {
isMounted = false;
};
- }, [mx, userId]);
+ }, [mx, userId, useAuthentication]);
const handleAvatarUpload = async (url) => {
if (url === null) {
return;
}
mx.setAvatarUrl(url);
- setAvatarSrc(mx.mxcUrlToHttp(url, 80, 80, 'crop'));
+ setAvatarSrc(mx.mxcUrlToHttp(url, 80, 80, 'crop', undefined, undefined, useAuthentication));
};
const saveDisplayName = () => {
import { useRoomNavigate } from '../../hooks/useRoomNavigate';
import { getDMRoomFor } from '../../utils/matrix';
import { useMatrixClient } from '../../hooks/useMatrixClient';
+import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
function ModerationTools({ roomId, userId }) {
const mx = useMatrixClient();
function ProfileViewer() {
const [isOpen, roomId, userId, closeDialog, handleAfterClose] = useToggleDialog();
useRerenderOnProfileChange(roomId, userId);
+ const useAuthentication = useMediaAuthentication();
const mx = useMatrixClient();
const room = mx.getRoom(roomId);
const username = roomMember ? getUsernameOfRoomMember(roomMember) : getUsername(mx, userId);
const avatarMxc = roomMember?.getMxcAvatarUrl?.() || mx.getUser(userId)?.avatarUrl;
const avatarUrl =
- avatarMxc && avatarMxc !== 'null' ? mx.mxcUrlToHttp(avatarMxc, 80, 80, 'crop') : null;
+ avatarMxc && avatarMxc !== 'null'
+ ? mx.mxcUrlToHttp(avatarMxc, 80, 80, 'crop', undefined, undefined, useAuthentication)
+ : null;
const powerLevel = roomMember?.powerLevel || 0;
const myPowerLevel = room.getMember(mx.getUserId())?.powerLevel || 0;
allowRedirects,
useAuthentication
);
+
+export const downloadMedia = async (src: string): Promise<Blob> => {
+ // this request is authenticated by service worker
+ const res = await fetch(src, { method: 'GET' });
+ const blob = await res.blob();
+ return blob;
+};
+
+export const downloadEncryptedMedia = async (
+ src: string,
+ decryptContent: (buf: ArrayBuffer) => Promise<Blob>
+): Promise<Blob> => {
+ const encryptedContent = await downloadMedia(src);
+ const decryptedContent = await decryptContent(await encryptedContent.arrayBuffer());
+
+ return decryptedContent;
+};