Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4486f02bc6 |
@@ -16,7 +16,7 @@ import { SettingTile } from '../setting-tile';
|
||||
import { UsageSwitcher } from './UsageSwitcher';
|
||||
import { ImagePackProfile, ImagePackProfileEdit } from './PackMeta';
|
||||
import * as css from './style.css';
|
||||
import { useFilePicker } from '../../hooks/useFilePicker';
|
||||
import { useStrippedFilePicker } from '../../hooks/useStrippedFilePicker';
|
||||
import { CompactUploadCardRenderer } from '../upload-card';
|
||||
import { UploadSuccess } from '../../state/upload';
|
||||
import { getImageInfo, TUploadContent } from '../../utils/matrix';
|
||||
@@ -60,7 +60,7 @@ export const ImagePackContent = as<'div', ImagePackContentProps>(
|
||||
[imagePack, savedImages, uploadedImages],
|
||||
);
|
||||
|
||||
const pickFiles = useFilePicker(
|
||||
const pickFiles = useStrippedFilePicker(
|
||||
useCallback(
|
||||
(pickedFiles: File[]) => {
|
||||
const uniqueFiles = pickedFiles.map((file) => {
|
||||
|
||||
@@ -19,7 +19,7 @@ import { nameInitials } from '../../utils/common';
|
||||
import { BreakWord } from '../../styles/Text.css';
|
||||
import { LINKIFY_OPTS } from '../../plugins/react-custom-html-parser';
|
||||
import { ContainerColor } from '../../styles/ContainerColor.css';
|
||||
import { useFilePicker } from '../../hooks/useFilePicker';
|
||||
import { useStrippedFilePicker } from '../../hooks/useStrippedFilePicker';
|
||||
import { useObjectURL } from '../../hooks/useObjectURL';
|
||||
import { createUploadAtom, UploadSuccess } from '../../state/upload';
|
||||
import { CompactUploadCardRenderer } from '../upload-card';
|
||||
@@ -111,7 +111,7 @@ export function ImagePackProfileEdit({ meta, onCancel, onSave }: ImagePackProfil
|
||||
return undefined;
|
||||
}, [imageFile]);
|
||||
|
||||
const pickFile = useFilePicker(setImageFile, false);
|
||||
const pickFile = useStrippedFilePicker(setImageFile, false);
|
||||
|
||||
const handleRemoveUpload = useCallback(() => {
|
||||
setImageFile(undefined);
|
||||
|
||||
@@ -40,7 +40,7 @@ import { useImagePackRooms } from '../../../hooks/useImagePackRooms';
|
||||
import { roomToParentsAtom } from '../../../state/room/roomToParents';
|
||||
import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
|
||||
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
||||
import { useFilePicker } from '../../../hooks/useFilePicker';
|
||||
import { useStrippedFilePicker } from '../../../hooks/useStrippedFilePicker';
|
||||
import { CompactUploadCardRenderer } from '../../../components/upload-card';
|
||||
import { createUploadAtom, UploadSuccess } from '../../../state/upload';
|
||||
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
|
||||
@@ -68,7 +68,7 @@ function EditPower({ maxPower, power, tag, onSave, onClose }: EditPowerProps) {
|
||||
const imagePackRooms = useImagePackRooms(room.roomId, roomToParents);
|
||||
|
||||
const [iconFile, setIconFile] = useState<File>();
|
||||
const pickFile = useFilePicker(setIconFile, false);
|
||||
const pickFile = useStrippedFilePicker(setIconFile, false);
|
||||
|
||||
const [tagColor, setTagColor] = useState<string | undefined>(tag?.color);
|
||||
const [tagIcon, setTagIcon] = useState<MemberPowerTagIcon | undefined>(tag?.icon);
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { shouldShowScreenshareNotice } from './useScreenshareNotices';
|
||||
|
||||
test('shouldShowScreenshareNotice: no "window was closed" when an admin turned sharing off (#297)', () => {
|
||||
assert.equal(shouldShowScreenshareNotice('ended', false), false);
|
||||
assert.equal(shouldShowScreenshareNotice('ended', true), true);
|
||||
assert.equal(shouldShowScreenshareNotice('no-frames', false), true);
|
||||
assert.equal(shouldShowScreenshareNotice('alone', true), true);
|
||||
});
|
||||
@@ -1,9 +1,8 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { Icons } from 'folds';
|
||||
import { CallEmbed } from '../plugins/call';
|
||||
import { toastQueueAtom } from '../state/toast';
|
||||
import { useRoomCallPolicy } from './useRoomCallPolicy';
|
||||
|
||||
export type ScreenshareNoticeKind = 'ended' | 'no-frames' | 'alone';
|
||||
|
||||
@@ -20,32 +19,18 @@ export const describeScreenshareNotice = (kind: ScreenshareNoticeKind): string |
|
||||
}
|
||||
};
|
||||
|
||||
/** Pure: a policy-forced stop is explained by its own toast (#297). */
|
||||
export const shouldShowScreenshareNotice = (
|
||||
kind: ScreenshareNoticeKind,
|
||||
allowScreenshare: boolean,
|
||||
): boolean => !(kind === 'ended' && !allowScreenshare);
|
||||
|
||||
/**
|
||||
* [Gitea EC#39] Toast the fork's io.lotus.screenshare_notice: shared window
|
||||
* closed, no frames for a while, or a long share with nobody watching.
|
||||
*/
|
||||
export function useScreenshareNotices(embed: CallEmbed): void {
|
||||
const setToast = useSetAtom(toastQueueAtom);
|
||||
// [Gitea #297] When a room admin turns screen sharing off, the share ends
|
||||
// and the fork reports `ended` ("the shared window was closed"), which is
|
||||
// wrong; useCallPolicyRevokedToast already says what happened.
|
||||
const { allowScreenshare } = useRoomCallPolicy(embed.room);
|
||||
const allowRef = useRef(allowScreenshare);
|
||||
allowRef.current = allowScreenshare;
|
||||
useEffect(
|
||||
() =>
|
||||
embed.listenAction<{ data?: { kind?: ScreenshareNoticeKind } }>(
|
||||
'io.lotus.screenshare_notice',
|
||||
(evt) => {
|
||||
const kind = evt.detail?.data?.kind as ScreenshareNoticeKind;
|
||||
if (!shouldShowScreenshareNotice(kind, allowRef.current)) return;
|
||||
const body = describeScreenshareNotice(kind);
|
||||
const body = describeScreenshareNotice(evt.detail?.data?.kind as ScreenshareNoticeKind);
|
||||
if (!body) return;
|
||||
setToast({
|
||||
id: `screenshare-${evt.detail?.data?.kind}-${Date.now()}`,
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useFilePicker } from './useFilePicker';
|
||||
import { useSetting } from '../state/hooks/settings';
|
||||
import { settingsAtom } from '../state/settings';
|
||||
import { stripImageMetadata } from '../utils/stripImageMetadata';
|
||||
|
||||
/**
|
||||
* [Gitea #332] `useFilePicker` for images that get published (emoji / sticker
|
||||
* packs, pack avatars, power-level badges): applies "Remove Photo Metadata"
|
||||
* like message attachments and avatars, so a phone photo's GPS location and
|
||||
* timestamp don't go to every member of the room or space.
|
||||
*/
|
||||
export const useStrippedFilePicker = <M extends boolean | undefined = undefined>(
|
||||
onSelect: (file: M extends true ? File[] : File) => void,
|
||||
multiple?: M,
|
||||
) => {
|
||||
const [strip] = useSetting(settingsAtom, 'stripImageMetadata');
|
||||
const handle = useCallback(
|
||||
(picked: File | File[]) => {
|
||||
if (!strip) {
|
||||
onSelect(picked as M extends true ? File[] : File);
|
||||
return;
|
||||
}
|
||||
const clean = (f: File) => stripImageMetadata(f).then((r) => r.file);
|
||||
if (Array.isArray(picked)) {
|
||||
Promise.all(picked.map(clean)).then((files) =>
|
||||
onSelect(files as M extends true ? File[] : File),
|
||||
);
|
||||
} else {
|
||||
clean(picked).then((file) => onSelect(file as M extends true ? File[] : File));
|
||||
}
|
||||
},
|
||||
[strip, onSelect],
|
||||
);
|
||||
return useFilePicker(handle as (file: M extends true ? File[] : File) => void, multiple);
|
||||
};
|
||||
Reference in New Issue
Block a user