|
1 | | -import { useState } from "react"; |
| 1 | +import { useState, useRef } from "react"; |
2 | 2 | import { useStore, LoopTrack } from "@/lib/store"; |
3 | 3 | import { Mic, Play, Square, Trash2, Volume2, VolumeX, Repeat, X, Download } from "lucide-react"; |
4 | 4 | import { motion, AnimatePresence } from "framer-motion"; |
@@ -30,9 +30,9 @@ export function LooperPanel() { |
30 | 30 | animate={{ width: "auto", opacity: 1 }} |
31 | 31 | exit={{ width: 0, opacity: 0 }} |
32 | 32 | transition={{ type: "spring", stiffness: 300, damping: 30 }} |
33 | | - className="h-full border-l border-border bg-background/95 backdrop-blur-xl shadow-2xl z-40 bg-background flex flex-col overflow-hidden max-w-[90vw] md:max-w-md" |
| 33 | + className="h-full border-l border-border bg-background/95 backdrop-blur-xl shadow-2xl z-40 bg-background flex flex-col overflow-hidden w-full sm:w-auto sm:max-w-md" |
34 | 34 | > |
35 | | - <div className="w-[320px] md:w-80 h-full flex flex-col p-4"> |
| 35 | + <div className="w-screen sm:w-80 h-full flex flex-col p-4"> |
36 | 36 | <div className="flex items-center justify-between mb-6 mt-4"> |
37 | 37 | <h2 className="text-xl font-bold flex items-center gap-2"> |
38 | 38 | <span className="w-3 h-3 rounded-full bg-red-500 animate-pulse" /> |
@@ -74,6 +74,29 @@ function LoopTrackCard({ |
74 | 74 | onVolume: (v: number) => void, onMute: () => void |
75 | 75 | }) { |
76 | 76 | const [showVolume, setShowVolume] = useState(false); |
| 77 | + const longPressTimer = useRef<NodeJS.Timeout | null>(null); |
| 78 | + const isLongPress = useRef(false); |
| 79 | + |
| 80 | + const handleTouchStart = () => { |
| 81 | + isLongPress.current = false; |
| 82 | + longPressTimer.current = setTimeout(() => { |
| 83 | + isLongPress.current = true; |
| 84 | + setShowVolume(!showVolume); |
| 85 | + }, 400); // 400ms long press |
| 86 | + }; |
| 87 | + |
| 88 | + const handleTouchEnd = () => { |
| 89 | + if (longPressTimer.current) { |
| 90 | + clearTimeout(longPressTimer.current); |
| 91 | + } |
| 92 | + }; |
| 93 | + |
| 94 | + const handleClick = () => { |
| 95 | + // Only trigger mute if it wasn't a long press |
| 96 | + if (!isLongPress.current) { |
| 97 | + onMute(); |
| 98 | + } |
| 99 | + }; |
77 | 100 | const isRecording = track.state === "recording"; |
78 | 101 | const isPlaying = track.state === "playing"; |
79 | 102 | const hasLoop = track.state !== "empty" && track.state !== "recording"; |
@@ -137,10 +160,13 @@ function LoopTrackCard({ |
137 | 160 |
|
138 | 161 | {/* Volume Button */} |
139 | 162 | <button |
140 | | - onClick={onMute} |
| 163 | + onClick={handleClick} |
141 | 164 | onContextMenu={(e) => { e.preventDefault(); setShowVolume(!showVolume); }} |
142 | | - className={`w-8 h-8 rounded-full border border-border flex items-center justify-center transition-all ${track.muted ? "bg-destructive/20 text-destructive border-destructive/50" : "bg-background hover:bg-muted text-muted-foreground"}`} |
143 | | - title="Left-Click: Mute | Right-Click: Volume" |
| 165 | + onTouchStart={handleTouchStart} |
| 166 | + onTouchEnd={handleTouchEnd} |
| 167 | + onTouchCancel={handleTouchEnd} |
| 168 | + className={`w-8 h-8 rounded-full border border-border flex items-center justify-center transition-all touch-manipulation ${track.muted ? "bg-destructive/20 text-destructive border-destructive/50" : "bg-background hover:bg-muted text-muted-foreground"}`} |
| 169 | + title="Tap: Mute | Hold/Right-Click: Volume" |
144 | 170 | > |
145 | 171 | {track.muted ? <VolumeX size={14} /> : <Volume2 size={14} />} |
146 | 172 | </button> |
|
0 commit comments