Skip to content

Commit 8ef100f

Browse files
authored
Merge pull request #4 from MatthewSummers05/feature/update-queue
Changes to the queue page and add history page.
2 parents 6a04c4f + e36625b commit 8ef100f

File tree

9 files changed

+14316
-606
lines changed

9 files changed

+14316
-606
lines changed

components/History.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { getQueueHistory } from "@/lib/history";
2+
import { queueItems } from "@/lib/queue";
3+
import { useAtom, useAtomValue } from "jotai";
4+
import { FlatList, View } from "react-native";
5+
import { UIQueueItem } from "./Queue";
6+
7+
export default function History() {
8+
const history = useAtomValue(getQueueHistory);
9+
const [queue] = useAtom(queueItems);
10+
11+
return (
12+
<View style={{ flex: 1, width: "100%" }}>
13+
<FlatList
14+
data={history}
15+
keyExtractor={(_, idx) => idx.toString()}
16+
renderItem={({ item, index }) => (
17+
<UIQueueItem
18+
item={item}
19+
idx={index}
20+
/>
21+
)}
22+
contentContainerStyle={{ paddingBottom: 32 }}
23+
initialNumToRender={10}
24+
windowSize={5}
25+
getItemLayout={(_, index) => ({
26+
length: 80,
27+
offset: 80 * index,
28+
index,
29+
})}
30+
/>
31+
</View>
32+
);
33+
}

components/NowPlayingView.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useAtomValue } from "jotai";
55
import { useEffect, useState } from "react";
66
import { Dimensions, View } from "react-native";
77
import { SegmentedButtons } from "react-native-paper";
8+
import History from "./History";
89
import Lyrics from "./Lyrics";
910
import { NowPlayingArtwork } from "./NowPlayingArtwork";
1011
import { NowPlayingMetadata } from "./NowPlayingMetadata";
@@ -43,7 +44,7 @@ export function NowPlayingView() {
4344
}, [isPlayingB]);
4445

4546

46-
const [playerMode, setPlayerMode] = useState<"player" | "queue" | "lyrics">(
47+
const [playerMode, setPlayerMode] = useState<"player" | "queue" | "history" | "lyrics">(
4748
"player"
4849
);
4950

@@ -95,6 +96,11 @@ export function NowPlayingView() {
9596
label: "Queue",
9697
icon: "playlist-music",
9798
},
99+
{
100+
value: "history",
101+
label: "History",
102+
icon: "playlist-music",
103+
},
98104
{
99105
value: "lyrics",
100106
label: "Lyrics",
@@ -153,6 +159,19 @@ export function NowPlayingView() {
153159
<Queue />
154160
</View>
155161
)}
162+
{playerMode === "history" && (
163+
<View
164+
style={{
165+
alignItems: "center",
166+
justifyContent: "center",
167+
flex: 1,
168+
width: "100%",
169+
display: "flex",
170+
}}
171+
>
172+
<History />
173+
</View>
174+
)}
156175
{playerMode === "lyrics" && (
157176
<View
158177
style={{

components/Queue.tsx

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { changeToIndex, fetchQueue, moveToPosition, queueItems, removeByIndex } from "@/lib/queue";
1+
import { changeToIndex, fetchQueue, modifiedQueueItems, moveToPosition, queueItems, removeByIndex } from "@/lib/queue";
22
import { QueueItem } from "@/types/musickit";
33
import { useIsFocused } from "@react-navigation/native";
44
import { useAtom } from "jotai";
@@ -9,7 +9,7 @@ import { Button, Dialog, IconButton, List, Portal, useTheme } from "react-native
99

1010
export default function Queue() {
1111
const isFocused = useIsFocused();
12-
const [queue, setQueue] = useAtom(queueItems);
12+
const [queue, setQueue] = useAtom(modifiedQueueItems);
1313
const theme = useTheme();
1414
const [draggedIndex, setDraggedIndex] = useState<number | null>(null);
1515

@@ -58,7 +58,7 @@ type UIQueueItemProps = {
5858
onDragEnd?: () => void;
5959
};
6060

61-
function UIQueueItem({ item, idx, isDragged, onDragStart, onDragEnd }: UIQueueItemProps) {
61+
export function UIQueueItem({ item, idx, isDragged, onDragStart, onDragEnd }: UIQueueItemProps) {
6262
const [queue, setQueue] = useAtom(queueItems);
6363
const [showActions, setShowActions] = useState(false);
6464
const [isDragging, setIsDragging] = useState(false);
@@ -85,14 +85,23 @@ function UIQueueItem({ item, idx, isDragged, onDragStart, onDragEnd }: UIQueueIt
8585
toValue: 1.05,
8686
useNativeDriver: true,
8787
}).start();
88-
} else if (event.nativeEvent.state === State.END || event.nativeEvent.state === State.CANCELLED) {
88+
} else if (event.nativeEvent.state === State.END || event.nativeEvent.state === State.CANCELLED) {
8989
const { translationY } = event.nativeEvent;
90-
const itemHeight = 80; // Approximate height of each item
90+
const itemHeight = 80;
9191
const moveDistance = Math.round(translationY / itemHeight);
92-
const newIndex = Math.max(0, Math.min(idx + moveDistance, queue.length - 1));
93-
94-
if (newIndex !== idx && event.nativeEvent.state === State.END) {
95-
moveToPosition(idx, newIndex);
92+
const originalIdx = item.originalIndex;
93+
const newOriginalIndex =
94+
originalIdx !== undefined
95+
? Math.max(0, Math.min(originalIdx + moveDistance, queue.length - 1))
96+
: undefined;
97+
98+
if (
99+
newOriginalIndex !== undefined &&
100+
originalIdx !== undefined &&
101+
newOriginalIndex !== originalIdx &&
102+
event.nativeEvent.state === State.END
103+
) {
104+
moveToPosition(originalIdx, newOriginalIndex);
96105
}
97106

98107
Animated.parallel([
@@ -106,7 +115,6 @@ function UIQueueItem({ item, idx, isDragged, onDragStart, onDragEnd }: UIQueueIt
106115
}),
107116
]).start();
108117

109-
// Delay resetting isDragging to prevent menu from showing
110118
setTimeout(() => {
111119
setIsDragging(false);
112120
}, 100);
@@ -115,7 +123,6 @@ function UIQueueItem({ item, idx, isDragged, onDragStart, onDragEnd }: UIQueueIt
115123
}
116124
};
117125

118-
// Move PanGestureHandler to wrap only the drag handle
119126
return (
120127
<>
121128
<Animated.View
@@ -132,7 +139,7 @@ function UIQueueItem({ item, idx, isDragged, onDragStart, onDragEnd }: UIQueueIt
132139
title={item.attributes.name ?? "Untitled"}
133140
description={item.attributes.artistName ?? ""}
134141
onPress={() => {
135-
changeToIndex(idx);
142+
changeToIndex(item.originalIndex ?? idx);
136143
}}
137144
left={(props) =>
138145
item.attributes.artwork?.url ? (
@@ -179,7 +186,7 @@ function UIQueueItem({ item, idx, isDragged, onDragStart, onDragEnd }: UIQueueIt
179186
<Button
180187
icon="close"
181188
onPress={() => {
182-
removeByIndex(idx);
189+
removeByIndex(item.originalIndex ?? idx);
183190
setShowActions(false);
184191
}}>Remove From Queue</Button>
185192
</Dialog.Content>

lib/history.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { atom } from "jotai";
2+
import { nowPlayingItem } from "./playback-control";
3+
import { queueItems } from "./queue";
4+
5+
export const getQueueHistory = atom((get) => {
6+
const queue = get(queueItems);
7+
const nowPlaying = get(nowPlayingItem);
8+
9+
if (!queue || !nowPlaying) return [];
10+
11+
const nowPlayingId = nowPlaying.playParams?.id;
12+
if (!nowPlayingId) return [];
13+
14+
const nowPlayingIndex = queue.findIndex(item =>
15+
item.id === nowPlayingId ||
16+
item.attributes?.playParams?.id === nowPlayingId ||
17+
item._container?.id === nowPlayingId
18+
);
19+
20+
if (nowPlayingIndex === -1) return [];
21+
return queue.slice(0, nowPlayingIndex);
22+
});

lib/io.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
UpdateNotificationMinimal,
1818
volume
1919
} from "./playback-control";
20-
import { fetchQueue } from "./queue";
20+
import { fetchModifiedQueue, fetchQueue } from "./queue";
2121

2222
export class IOState {
2323
static instance: Socket;
@@ -96,6 +96,7 @@ export class IOState {
9696

9797
await getStorefront();
9898
getNowPlayingItem();
99+
fetchModifiedQueue()
99100
fetchQueue();
100101
}
101102

@@ -135,6 +136,7 @@ export class IOState {
135136
}
136137
getNowPlayingItem();
137138
console.log(msg);
139+
fetchModifiedQueue()
138140
fetchQueue();
139141
try{
140142
UpdateNotification(msg.data);

lib/queue.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,33 @@ const store = getDefaultStore();
66
export const queueItems = atom<QueueItem[]>([]);
77
export const queuePosition = atom(0);
88

9+
export const modifiedQueueItems = atom<QueueItem[]>([]);
10+
11+
export async function fetchModifiedQueue() {
12+
await fetchQueue();
13+
const nowPlayingRes = await CiderFetch<{ info: { playParams: { id: string } } }>("/api/v1/playback/now-playing");
14+
if (!nowPlayingRes?.info?.playParams.id) return;
15+
const nowPlayingId = nowPlayingRes?.info?.playParams.id;
16+
17+
const queueRes = await CiderFetch<QueueItem[]>("/api/v1/playback/queue");
18+
if (!queueRes) return;
19+
20+
const nowPlayingIndex = queueRes.findIndex((item) => {
21+
const id = item._container?.id || item.id || item._songId;
22+
return id === nowPlayingId;
23+
});
24+
const modifiedQueue = (nowPlayingIndex >= 0 ? queueRes.slice(nowPlayingIndex) : queueRes)
25+
.map((item, i) => {
26+
const itemId = item._container?.id || item.id || item._songId;
27+
const originalIndex = queueRes.findIndex((q) => (q._container?.id || q.id || q._songId) === itemId);
28+
if (originalIndex === -1) {
29+
queueRes.forEach(q => console.log(' queueRes id:', q._container?.id || q.id || q._songId));
30+
}
31+
return { ...item, originalIndex };
32+
});
33+
store.set(modifiedQueueItems, modifiedQueue);
34+
}
35+
936
export async function fetchQueue() {
1037
const res = await CiderFetch<QueueItem[]>("/api/v1/playback/queue");
1138
if (!res) return;
@@ -18,6 +45,7 @@ export async function clearQueue() {
1845
});
1946
if (!res) return;
2047
await fetchQueue();
48+
await fetchModifiedQueue();
2149
}
2250

2351
export async function changeToIndex(index: number) {
@@ -26,6 +54,7 @@ export async function changeToIndex(index: number) {
2654
});
2755
if (!res) return;
2856
await fetchQueue();
57+
await fetchModifiedQueue();
2958
}
3059

3160
export async function removeByIndex(index: number) {
@@ -40,6 +69,7 @@ export async function removeByIndex(index: number) {
4069
);
4170
if (!res) return;
4271
await fetchQueue();
72+
await fetchModifiedQueue();
4373
}
4474

4575
export async function moveToPosition(
@@ -55,4 +85,5 @@ export async function moveToPosition(
5585
);
5686
if (!res) return;
5787
await fetchQueue();
88+
await fetchModifiedQueue();
5889
}

0 commit comments

Comments
 (0)