fix/polish: wave 1+2 improvements across six features
CI / Build & Quality Checks (push) Successful in 10m25s
CI / Build & Quality Checks (push) Successful in 10m25s
B1 - GIF upload progress: spinner on GIF button + disabled state while
fetch+upload is in flight; clears on success or error
B2 - PiP position persistence: drag end saves left/top to localStorage;
entering PiP restores saved position (clamped to current viewport)
B3 - PiP snap-to-corner: double-click the PiP overlay snaps to nearest
corner with a 180ms CSS transition; saves new position
B4 - Device sessions loading state: useOtherUserDevices now returns
{status:'loading'|'error'|'success', devices} instead of bare
array; UserDeviceSessions shows spinner while loading
B5 - Device sessions error state: catch in hook sets status:'error';
panel shows warning icon + 'Could not load sessions' message
B6 - Screenshare fullscreen Safari guard: hide button when
document.fullscreenEnabled is false (iOS Safari, some mobile)
B7 - Status save error: show critical-coloured error text below Save
button when saveState.status === AsyncStatus.Error
B9 - Encrypted search coverage counter: 'X / Y cached' badge next to
'Encrypted Rooms' heading using existing localResult fields
D2 - PiP screenshare spotlight: track auto-spotlight in a ref; release
spotlight when screenshare ends while in PiP mode
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,32 +8,37 @@ export type UserDevice = {
|
||||
displayName?: string;
|
||||
};
|
||||
|
||||
export function useOtherUserDevices(userId: string): UserDevice[] | undefined {
|
||||
export type UserDevicesState =
|
||||
| { status: 'loading' }
|
||||
| { status: 'error' }
|
||||
| { status: 'success'; devices: UserDevice[] };
|
||||
|
||||
export function useOtherUserDevices(userId: string): UserDevicesState {
|
||||
const mx = useMatrixClient();
|
||||
const crossSigningActive = useCrossSigningActive();
|
||||
const [devices, setDevices] = useState<UserDevice[] | undefined>(undefined);
|
||||
const [state, setState] = useState<UserDevicesState>({ status: 'loading' });
|
||||
|
||||
const fetchDevices = useCallback(async () => {
|
||||
const crypto = mx.getCrypto();
|
||||
if (!crypto || !crossSigningActive) {
|
||||
setDevices(undefined);
|
||||
setState({ status: 'success', devices: [] });
|
||||
return;
|
||||
}
|
||||
setState({ status: 'loading' });
|
||||
try {
|
||||
const deviceMap = await crypto.getUserDeviceInfo([userId], true);
|
||||
const userDevices = deviceMap.get(userId);
|
||||
if (!userDevices) {
|
||||
setDevices([]);
|
||||
return;
|
||||
}
|
||||
setDevices(
|
||||
Array.from(userDevices.values()).map((device) => ({
|
||||
deviceId: device.deviceId,
|
||||
displayName: device.displayName,
|
||||
})),
|
||||
);
|
||||
setState({
|
||||
status: 'success',
|
||||
devices: userDevices
|
||||
? Array.from(userDevices.values()).map((device) => ({
|
||||
deviceId: device.deviceId,
|
||||
displayName: device.displayName,
|
||||
}))
|
||||
: [],
|
||||
});
|
||||
} catch {
|
||||
setDevices(undefined);
|
||||
setState({ status: 'error' });
|
||||
}
|
||||
}, [mx, userId, crossSigningActive]);
|
||||
|
||||
@@ -50,5 +55,5 @@ export function useOtherUserDevices(userId: string): UserDevice[] | undefined {
|
||||
),
|
||||
);
|
||||
|
||||
return devices;
|
||||
return state;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user