nits
This commit is contained in:
@@ -15,6 +15,7 @@
|
|||||||
"invalidate-gcs-cache": "gsutil setmeta -h 'Cache-Control:no-cache, no-store, must-revalidate' gs://flowy-releases/llink/darwin/arm64/RELEASES.json && gsutil setmeta -h 'Cache-Control:no-cache, no-store, must-revalidate' gs://flowy-releases/llink/darwin/x64/RELEASES.json && gsutil setmeta -h 'Cache-Control:no-cache, no-store, must-revalidate' gs://flowy-releases/llink/win32/x64/RELEASES",
|
"invalidate-gcs-cache": "gsutil setmeta -h 'Cache-Control:no-cache, no-store, must-revalidate' gs://flowy-releases/llink/darwin/arm64/RELEASES.json && gsutil setmeta -h 'Cache-Control:no-cache, no-store, must-revalidate' gs://flowy-releases/llink/darwin/x64/RELEASES.json && gsutil setmeta -h 'Cache-Control:no-cache, no-store, must-revalidate' gs://flowy-releases/llink/win32/x64/RELEASES",
|
||||||
"lint": "eslint --ext .ts,.tsx .",
|
"lint": "eslint --ext .ts,.tsx .",
|
||||||
"format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"",
|
"format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"",
|
||||||
|
"format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"",
|
||||||
"compile": "npx tsc --noEmit 2>&1 | grep '^src/'",
|
"compile": "npx tsc --noEmit 2>&1 | grep '^src/'",
|
||||||
"web:dev": "cross-env APP_ENV=dev vite --config vite.web.config.mts",
|
"web:dev": "cross-env APP_ENV=dev vite --config vite.web.config.mts",
|
||||||
"web:build": "cross-env APP_ENV=prod vite build --config vite.web.config.mts",
|
"web:build": "cross-env APP_ENV=prod vite build --config vite.web.config.mts",
|
||||||
|
|||||||
@@ -136,7 +136,6 @@ export function ComposeOverlay({
|
|||||||
const cancel = useCallback(() => {
|
const cancel = useCallback(() => {
|
||||||
setStepSync('idle');
|
setStepSync('idle');
|
||||||
setError(null);
|
setError(null);
|
||||||
setTextContent('');
|
|
||||||
setMediaStream(null);
|
setMediaStream(null);
|
||||||
setReviewBlob(null);
|
setReviewBlob(null);
|
||||||
setReviewDurationMs(0);
|
setReviewDurationMs(0);
|
||||||
@@ -150,8 +149,7 @@ export function ComposeOverlay({
|
|||||||
|
|
||||||
const addAttachments = useCallback(
|
const addAttachments = useCallback(
|
||||||
async (files: File[]) => {
|
async (files: File[]) => {
|
||||||
const currentCount = attachments.length;
|
const available = MAX_ATTACHMENTS - attachments.length;
|
||||||
const available = MAX_ATTACHMENTS - currentCount;
|
|
||||||
if (available <= 0) {
|
if (available <= 0) {
|
||||||
toast.error(`Maximum ${MAX_ATTACHMENTS} attachments`);
|
toast.error(`Maximum ${MAX_ATTACHMENTS} attachments`);
|
||||||
return;
|
return;
|
||||||
@@ -404,6 +402,7 @@ export function ComposeOverlay({
|
|||||||
try {
|
try {
|
||||||
await createChildParticle(targetPath);
|
await createChildParticle(targetPath);
|
||||||
cancel();
|
cancel();
|
||||||
|
setTextContent('');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!handleQuotaError(err)) throw err;
|
if (!handleQuotaError(err)) throw err;
|
||||||
}
|
}
|
||||||
@@ -413,7 +412,7 @@ export function ComposeOverlay({
|
|||||||
setStepSync,
|
setStepSync,
|
||||||
createChildParticle,
|
createChildParticle,
|
||||||
cancel,
|
cancel,
|
||||||
handleQuotaError,
|
handleQuotaError
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// New stream mode: create stream + first child
|
// New stream mode: create stream + first child
|
||||||
@@ -434,8 +433,8 @@ export function ComposeOverlay({
|
|||||||
|
|
||||||
const streamChildrenPath = particlePath(networkId, [streamId]);
|
const streamChildrenPath = particlePath(networkId, [streamId]);
|
||||||
await createChildParticle(streamChildrenPath);
|
await createChildParticle(streamChildrenPath);
|
||||||
|
|
||||||
cancel();
|
cancel();
|
||||||
|
setTextContent('');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!handleQuotaError(err)) throw err;
|
if (!handleQuotaError(err)) throw err;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,10 +23,13 @@ export function TextReactionInput({
|
|||||||
|
|
||||||
useSuspendPlayback(open, 'text-reaction');
|
useSuspendPlayback(open, 'text-reaction');
|
||||||
|
|
||||||
// Clear the input each time the popup opens.
|
|
||||||
if (open !== prevOpen) {
|
if (open !== prevOpen) {
|
||||||
setPrevOpen(open);
|
setPrevOpen(open);
|
||||||
if (open) setValue('');
|
|
||||||
|
// NOTE: perform side effects here when opening
|
||||||
|
if (open) {
|
||||||
|
setValue('')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -44,6 +47,7 @@ export function TextReactionInput({
|
|||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
if (!canSubmit) return;
|
if (!canSubmit) return;
|
||||||
onSubmit(trimmed);
|
onSubmit(trimmed);
|
||||||
|
setValue('');
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -36,8 +36,7 @@ function usePreviewStream(
|
|||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [prevEnabled, setPrevEnabled] = useState(enabled);
|
const [prevEnabled, setPrevEnabled] = useState(enabled);
|
||||||
|
|
||||||
// Clear the preview when disabled; the effect below only manages the
|
// Clear the preview when disabled
|
||||||
// getUserMedia subscription.
|
|
||||||
if (enabled !== prevEnabled) {
|
if (enabled !== prevEnabled) {
|
||||||
setPrevEnabled(enabled);
|
setPrevEnabled(enabled);
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ export function useChannel(channelId: string | null): UseChannelResult {
|
|||||||
client.off(channelId, 'leave', onLeave);
|
client.off(channelId, 'leave', onLeave);
|
||||||
client.off(channelId, 'message', onMessage);
|
client.off(channelId, 'message', onMessage);
|
||||||
client.unsubscribe(channelId);
|
client.unsubscribe(channelId);
|
||||||
// Clear on teardown so a new channel doesn't briefly show stale data.
|
|
||||||
setPresence([]);
|
setPresence([]);
|
||||||
setMessages([]);
|
setMessages([]);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -75,8 +75,15 @@ export function useMediaDevices(): UseMediaDevicesResult {
|
|||||||
}, [refresh]);
|
}, [refresh]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
audioInputs: devices.filter((d) => d.kind === 'audioinput'),
|
// Before permission is granted, enumerateDevices returns placeholder
|
||||||
videoInputs: devices.filter((d) => d.kind === 'videoinput'),
|
// entries with an empty deviceId — filter them out so consumers never
|
||||||
|
// render an empty-value <SelectItem />, which Radix rejects.
|
||||||
|
audioInputs: devices.filter(
|
||||||
|
(d) => d.kind === 'audioinput' && d.deviceId !== '',
|
||||||
|
),
|
||||||
|
videoInputs: devices.filter(
|
||||||
|
(d) => d.kind === 'videoinput' && d.deviceId !== '',
|
||||||
|
),
|
||||||
permissionState,
|
permissionState,
|
||||||
refresh,
|
refresh,
|
||||||
requestLabels,
|
requestLabels,
|
||||||
|
|||||||
@@ -3,11 +3,6 @@ import { useEffect, useState } from 'react';
|
|||||||
/**
|
/**
|
||||||
* Creates an object URL for a Blob/File and revokes it when the source changes
|
* Creates an object URL for a Blob/File and revokes it when the source changes
|
||||||
* or the component unmounts. Returns null when given null.
|
* or the component unmounts. Returns null when given null.
|
||||||
*
|
|
||||||
* `createObjectURL` is an imperative side effect that must run inside an effect,
|
|
||||||
* so publishing the resulting URL to state here is genuine external-resource
|
|
||||||
* synchronization rather than a render cascade — hence the single, contained
|
|
||||||
* lint suppression below.
|
|
||||||
*/
|
*/
|
||||||
export function useObjectUrl(source: Blob | null): string | null {
|
export function useObjectUrl(source: Blob | null): string | null {
|
||||||
const [url, setUrl] = useState<string | null>(null);
|
const [url, setUrl] = useState<string | null>(null);
|
||||||
@@ -15,7 +10,7 @@ export function useObjectUrl(source: Blob | null): string | null {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!source) return;
|
if (!source) return;
|
||||||
const objectUrl = URL.createObjectURL(source);
|
const objectUrl = URL.createObjectURL(source);
|
||||||
// Intended external-resource publish (see hook doc), not a render cascade.
|
// Intended external-resource publish to sync, not a render cascade.
|
||||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||||
setUrl(objectUrl);
|
setUrl(objectUrl);
|
||||||
return () => {
|
return () => {
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ export function useLiveParticle(path: ParticlePath): UseLiveParticleResult {
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
// Reset on teardown so a new path doesn't flash the previous particle.
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
setParticle(null);
|
setParticle(null);
|
||||||
@@ -121,7 +120,6 @@ export function useLiveParticleChildren(
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
// Reset on teardown so a new path doesn't flash the previous children.
|
|
||||||
setChildren([]);
|
setChildren([]);
|
||||||
setError(null);
|
setError(null);
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
@@ -168,7 +166,6 @@ export function useLiveLatestChild(
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
// Reset on teardown so a new path doesn't flash the previous child.
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
setLatestChild(null);
|
setLatestChild(null);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user