fix(mobile): auto-follow newly sent particles in stream view #226
@@ -56,6 +56,13 @@ interface ComposeDockProps {
|
|||||||
silentPresence?: boolean;
|
silentPresence?: boolean;
|
||||||
submitMedia?: (params: SubmitMediaParams) => Promise<void>;
|
submitMedia?: (params: SubmitMediaParams) => Promise<void>;
|
||||||
submitText?: (content: string) => Promise<void>;
|
submitText?: (content: string) => Promise<void>;
|
||||||
|
/**
|
||||||
|
* Called with the new particle's id right after it's created on the default
|
||||||
|
* send path. Not fired when `submitMedia`/`submitText` overrides are supplied,
|
||||||
|
* since those own the created particle themselves. Lets the stream follow a
|
||||||
|
* just-sent particle when the user was at the end.
|
||||||
|
*/
|
||||||
|
onParticleCreated?: (particleId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ComposeDock({
|
export function ComposeDock({
|
||||||
@@ -64,6 +71,7 @@ export function ComposeDock({
|
|||||||
silentPresence = false,
|
silentPresence = false,
|
||||||
submitMedia,
|
submitMedia,
|
||||||
submitText: submitTextOverride,
|
submitText: submitTextOverride,
|
||||||
|
onParticleCreated,
|
||||||
}: ComposeDockProps) {
|
}: ComposeDockProps) {
|
||||||
const userId = useAuthStore((s) => s.user?.id);
|
const userId = useAuthStore((s) => s.user?.id);
|
||||||
|
|
||||||
@@ -147,7 +155,7 @@ export function ComposeDock({
|
|||||||
source: "camera",
|
source: "camera",
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await uploadMediaParticle({
|
const particleId = await uploadMediaParticle({
|
||||||
networkId,
|
networkId,
|
||||||
targetPath,
|
targetPath,
|
||||||
fileUri: captured.uri,
|
fileUri: captured.uri,
|
||||||
@@ -156,6 +164,7 @@ export function ComposeDock({
|
|||||||
source: "camera",
|
source: "camera",
|
||||||
createdByHumanId: userId,
|
createdByHumanId: userId,
|
||||||
});
|
});
|
||||||
|
onParticleCreated?.(particleId);
|
||||||
}
|
}
|
||||||
void Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
|
void Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
|
||||||
setUi({ kind: "idle" });
|
setUi({ kind: "idle" });
|
||||||
@@ -174,12 +183,13 @@ export function ComposeDock({
|
|||||||
if (submitTextOverride) {
|
if (submitTextOverride) {
|
||||||
await submitTextOverride(content);
|
await submitTextOverride(content);
|
||||||
} else {
|
} else {
|
||||||
await createTextParticle({
|
const particleId = await createTextParticle({
|
||||||
networkId,
|
networkId,
|
||||||
targetPath,
|
targetPath,
|
||||||
content,
|
content,
|
||||||
createdByHumanId: userId,
|
createdByHumanId: userId,
|
||||||
});
|
});
|
||||||
|
onParticleCreated?.(particleId);
|
||||||
}
|
}
|
||||||
void Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
|
void Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -105,8 +105,15 @@ function StreamViewInner({ streamParticle, path, onExit }: StreamViewProps) {
|
|||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const { composingUsers } = useStreamComposing();
|
const { composingUsers } = useStreamComposing();
|
||||||
|
|
||||||
const { children, currentParticle, currentIndex, status, next, prev } =
|
const {
|
||||||
useStreamPlayback(streamParticle, path);
|
children,
|
||||||
|
currentParticle,
|
||||||
|
currentIndex,
|
||||||
|
status,
|
||||||
|
next,
|
||||||
|
prev,
|
||||||
|
goToParticle,
|
||||||
|
} = useStreamPlayback(streamParticle, path);
|
||||||
|
|
||||||
const paused = usePlaybackPauseStore(selectIsPaused);
|
const paused = usePlaybackPauseStore(selectIsPaused);
|
||||||
const composing = usePlaybackPauseStore(selectIsComposing);
|
const composing = usePlaybackPauseStore(selectIsComposing);
|
||||||
@@ -257,6 +264,22 @@ function StreamViewInner({ streamParticle, path, onExit }: StreamViewProps) {
|
|||||||
|
|
||||||
const openReactions = useCallback(() => setReactionsOpen(true), []);
|
const openReactions = useCallback(() => setReactionsOpen(true), []);
|
||||||
|
|
||||||
|
// When the local user sends a particle while sitting on the last one, follow
|
||||||
|
// it forward so they stay caught up and the stream isn't left unseen. If they
|
||||||
|
// were browsing earlier particles, leave their position untouched. `children`
|
||||||
|
// is still the pre-send snapshot here (the live query hasn't delivered the new
|
||||||
|
// particle yet), so the last slot is children.length - 1; goToParticle resolves
|
||||||
|
// once it arrives. Mirrors desktop's StreamView.handleParticleCreated.
|
||||||
|
const handleParticleCreated = useCallback(
|
||||||
|
(particleId: string) => {
|
||||||
|
if (currentIndex === -1) return;
|
||||||
|
if (currentIndex === children.length - 1) {
|
||||||
|
goToParticle(particleId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[children.length, currentIndex, goToParticle],
|
||||||
|
);
|
||||||
|
|
||||||
// Reset progress whenever the active particle changes.
|
// Reset progress whenever the active particle changes.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setProgress(0);
|
setProgress(0);
|
||||||
@@ -642,7 +665,11 @@ function StreamViewInner({ streamParticle, path, onExit }: StreamViewProps) {
|
|||||||
{/* Compose dock + recording overlays. Sits above the GestureDetector
|
{/* Compose dock + recording overlays. Sits above the GestureDetector
|
||||||
so its hold-FAB pan gesture isn't competed-with by the StreamView
|
so its hold-FAB pan gesture isn't competed-with by the StreamView
|
||||||
tap zones. */}
|
tap zones. */}
|
||||||
<ComposeDock networkId={networkId} targetPath={path} />
|
<ComposeDock
|
||||||
|
networkId={networkId}
|
||||||
|
targetPath={path}
|
||||||
|
onParticleCreated={handleParticleCreated}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Reaction sheet — slides up over everything, suspends playback
|
{/* Reaction sheet — slides up over everything, suspends playback
|
||||||
internally while open. */}
|
internally while open. */}
|
||||||
|
|||||||
Reference in New Issue
Block a user