Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions apps/desktop2/src/components/main/body/sessions/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { StickyNoteIcon } from "lucide-react";
import { useMemo } from "react";
import { useMemo, useState } from "react";

import NoteEditor from "@hypr/tiptap/editor";
import * as persisted from "../../../../store/tinybase/persisted";
import { rowIdfromTab, type Tab } from "../../../../store/zustand/tabs";
import { type TabItem, TabItemBase } from "../shared";
import { InnerHeader } from "./inner-header";
import { OuterHeader } from "./outer-header";
import { AudioPlayer } from "./player";
import { TitleInput } from "./title-input";

export const TabItemNote: TabItem = ({ tab, handleClose, handleSelect }) => {
Expand All @@ -26,6 +27,7 @@ export const TabItemNote: TabItem = ({ tab, handleClose, handleSelect }) => {
export function TabContentNote({ tab }: { tab: Tab }) {
const sessionId = rowIdfromTab(tab);
const sessionRow = persisted.UI.useRow("sessions", sessionId, persisted.STORE_ID);
const [showAudioPlayer, setShowAudioPlayer] = useState(false);

const editorKey = useMemo(
() => `session-${sessionId}-raw`,
Expand All @@ -51,7 +53,12 @@ export function TabContentNote({ tab }: { tab: Tab }) {
return (
<div className="flex flex-col px-4 py-1 rounded-lg border">
<div className="py-1">
<OuterHeader sessionRow={sessionRow} sessionId={sessionId} />
<OuterHeader
sessionRow={sessionRow}
sessionId={sessionId}
onToggleAudioPlayer={() => setShowAudioPlayer(!showAudioPlayer)}
isAudioPlayerVisible={showAudioPlayer}
/>
</div>

<TitleInput
Expand All @@ -78,6 +85,7 @@ export function TabContentNote({ tab }: { tab: Tab }) {
},
}}
/>
{showAudioPlayer && <AudioPlayer url="https://www2.cs.uic.edu/~i101/SoundFiles/gettysburg10.wav" />}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Replace hardcoded URL with dynamic session audio URL.

The audio player uses a hardcoded URL instead of dynamically loading the audio file associated with the current session. This will prevent the player from working with actual session recordings. Ensure the sessionRow contains an audio file URL property (e.g., audio_url, recording_url, or similar) and use it here.

Expected pattern:

-      {showAudioPlayer && <AudioPlayer url="https://www2.cs.uic.edu/~i101/SoundFiles/gettysburg10.wav" />}
+      {showAudioPlayer && sessionRow.audio_url && <AudioPlayer url={sessionRow.audio_url} />}

Note: Adjust the property name based on your actual schema.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{showAudioPlayer && <AudioPlayer url="https://www2.cs.uic.edu/~i101/SoundFiles/gettysburg10.wav" />}
{showAudioPlayer && sessionRow.audio_url && <AudioPlayer url={sessionRow.audio_url} />}
🤖 Prompt for AI Agents
In apps/desktop2/src/components/main/body/sessions/index.tsx around line 88, the
AudioPlayer is using a hardcoded WAV URL; replace it with the current session's
audio property (e.g., sessionRow.audio_url or sessionRow.recording_url) so the
player loads the correct recording. Pass that property into the AudioPlayer's
url prop, and add a null/empty check (only render AudioPlayer if showAudioPlayer
&& sessionRow?.audio_url) or fallback to sessionRow.recording_url if your schema
uses that name. Ensure you pick the correct property name from the sessionRow
shape and update the prop usage accordingly.

</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import { RecordingButton } from "./recording";
import { ShareButton } from "./share";

export function OuterHeader(
{ sessionRow, sessionId }: {
{ sessionRow, sessionId, onToggleAudioPlayer, isAudioPlayerVisible }: {
sessionRow: ReturnType<typeof persisted.UI.useRow<"sessions">>;
sessionId: string;
onToggleAudioPlayer: () => void;
isAudioPlayerVisible: boolean;
},
) {
const currentUserId = persisted.UI.useCell("configs", "singleton", "user_id", persisted.STORE_ID) as
Expand All @@ -38,7 +40,11 @@ export function OuterHeader(
sessionId={sessionId}
currentUserId={currentUserId}
/>
<RecordingButton sessionRow={sessionRow} />
<RecordingButton
sessionRow={sessionRow}
onToggle={onToggleAudioPlayer}
isActive={isAudioPlayerVisible}
/>
<ListenButton sessionRow={sessionRow} />
<ShareButton sessionRow={sessionRow} />
<OthersButton sessionRow={sessionRow} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import { type SessionRowProp } from "./types";

export function RecordingButton({ sessionRow: _sessionRow }: SessionRowProp) {
export function RecordingButton({
sessionRow: _sessionRow,
onToggle,
isActive,
}: SessionRowProp & { onToggle: () => void; isActive: boolean }) {
return (
<button className="text-xs">
<button
onClick={onToggle}
className={`text-xs transition-opacity ${isActive ? "opacity-100" : "opacity-50 hover:opacity-75"}`}
>
🎙️ 02:27
</button>
);
Expand Down
57 changes: 57 additions & 0 deletions apps/desktop2/src/components/main/body/sessions/player.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { useWavesurfer } from "@wavesurfer/react";
import { Pause, Play } from "lucide-react";
import { useCallback, useRef } from "react";

export function AudioPlayer({ url }: { url: string }) {
const containerRef = useRef<HTMLDivElement>(null);

const { wavesurfer, isPlaying, currentTime } = useWavesurfer({
container: containerRef,
height: 60,
waveColor: "#666666",
progressColor: "#333333",
cursorColor: "#ffffff",
cursorWidth: 2,
barWidth: 3,
barGap: 2,
barRadius: 3,
url,
dragToSeek: true,
hideScrollbar: true,
normalize: true,
});

const onPlayPause = useCallback(() => {
wavesurfer?.playPause();
}, [wavesurfer]);

const duration = wavesurfer?.getDuration() || 0;

return (
<div className="w-full bg-black border-t border-gray-800">
<div className="flex items-center gap-4 px-6 py-4 w-full max-w-full">
<button
onClick={onPlayPause}
className="flex items-center justify-center w-12 h-12 rounded-full bg-gray-700 hover:bg-gray-600 transition-colors flex-shrink-0"
>
{isPlaying
? <Pause className="w-6 h-6 text-white" fill="currentColor" />
: <Play className="w-6 h-6 text-white" fill="currentColor" />}
</button>

<div className="flex items-center gap-2 text-sm text-gray-400 flex-shrink-0 min-w-[100px]">
<span>{formatTime(currentTime)}</span>
<span>/</span>
<span>{formatTime(duration)}</span>
</div>

<div ref={containerRef} className="flex-1 min-w-0" style={{ minHeight: "60px", width: "100%" }} />
</div>
</div>
);
}

const formatTime = (seconds: number) =>
[Math.floor(seconds / 60), Math.floor(seconds % 60)]
.map((v) => String(v).padStart(2, "0"))
.join(":");
Loading