diff --git a/.helix/ignore b/.helix/ignore new file mode 100644 index 0000000..35bf673 --- /dev/null +++ b/.helix/ignore @@ -0,0 +1,3 @@ +*.png +*.ico +*.icns diff --git a/src/App.tsx b/src/App.tsx deleted file mode 100644 index 8843c02..0000000 --- a/src/App.tsx +++ /dev/null @@ -1,565 +0,0 @@ -import React, { MutableRefObject, useEffect, useRef, useState } from "react"; -import { convertFileSrc, invoke } from "@tauri-apps/api/core"; -import "./App.css"; -// import { EventEmitter } from "@tauri-apps/plugin-shell"; -// import { listen } from "@tauri-apps/api/event"; -// import { fetch } from "@tauri-apps/plugin-http"; -import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"; -import { PhysicalPosition } from "@tauri-apps/api/window"; -import { Menu, Submenu, SubmenuOptions } from "@tauri-apps/api/menu"; -import { Config } from "./bindings/Config"; -import { PlaybackInfo } from "./bindings/PlaybackInfo"; -import { PlayerLocation } from "./bindings/PlayerLocation"; -import { URI } from "./bindings/URI"; -import { Song } from "./bindings/Song"; - -const appWindow = getCurrentWebviewWindow(); - -// This needs to be changed to properly reflect cursor position -// this will do for now. -async function contextMenuPosition(event: React.MouseEvent) { - return new PhysicalPosition(event.clientX, event.clientY); -} - -function App() { - const library = useState([]); - const [queue, setQueue] = useState([]); - const [playing, setPlaying] = useState(false); - const [playlists, setPlaylists] = useState([]); - const [viewName, setViewName] = useState("Library"); - const playlistsInfo= useRef([]); - const selectedSongMain = useRef(); - const selectedSongQueue = useRef({uuid: "0", index: 0, location: "Library"}); - const setSelectedSongMain = (props: SongProps) => {selectedSongMain.current = props;} - const setSelectedSongQueue = (song: selectedQueueSong) => {selectedSongQueue.current = song; console.log(selectedSongQueue)} - - const [nowPlaying, setNowPlaying] = useState( - } - /> - ); - - - useEffect(() => { - const unlisten = appWindow.listen("now_playing_change", ({ payload, }) => { - const displayArtwork = () => { - invoke('display_album_art', { uuid: payload.uuid }).then(() => {}) - } - - setNowPlaying( - } - /> - ) - - }) - return () => { unlisten.then((f) => f()) } - }, []); - - useEffect(() => { - const unlisten = appWindow.listen("queue_updated", (_) => { - // console.log(event); - invoke('get_queue').then((_songs) => { - let songs = _songs as [Song, PlayerLocation][]; - setQueue( - songs.filter((_, i) => i != 0).map((song, i) => - - ) - ) - }) - }) - return () => { unlisten.then((f) => f()) } - }, []); - - useEffect(() => { - const unlisten = appWindow.listen("playing", (isPlaying) => { - setPlaying(isPlaying.payload) - }) - return () => { unlisten.then((f) => f()) } - }, []); - - useEffect(() => { - invoke("start_controller").then(() => {}); - }, []) - - - return ( -
-
-
- - -
-
- { nowPlaying } - -
-
-
- -
-
- ); -} - -export default App; - -interface PlaylistInfo { - uuid: string, - name: string, -} - - -interface PlaylistHeadProps { - playlists: JSX.Element[] - setPlaylists: React.Dispatch>, - setViewName: React.Dispatch>, - setLibrary: React.Dispatch>, - playlistsInfo: MutableRefObject, - setSelected: (props: SongProps) => void, - } - -function PlaylistHead({ playlists, setPlaylists, setViewName, setLibrary, playlistsInfo, setSelected }: PlaylistHeadProps) { - function getPlaylist(playlist: PlaylistInfo) { - invoke('get_playlist', { uuid: playlist.uuid }).then((list) => { - setLibrary([...(list as Song[]).map((song, i) => { - // console.log(song); - const reload = () => getPlaylist(playlist) - return ( - - ) - })]) - }) - setViewName( playlist.name ) - } - - useEffect(() => { - const unlisten = appWindow.listen("playlists_gotten", (_res) => { - const res = _res.payload; - // console.log(event); - playlistsInfo.current = [...res]; - // console.log(playlistsInfo, res); - - setPlaylists([ - ...res.map( (list) => { - const _getPlaylist = () => getPlaylist(list) - const deletePlaylist = () => { - invoke('delete_playlist', { uuid: list.uuid }).then(() => {}); - invoke('get_playlists').then(() => {}); - } - async function menuHandler(event: React.MouseEvent) { - event.preventDefault(); - const menu = await Menu.new({ - items: [ - { id: "delete_playlist" + list.uuid, text: "Delete Playlist", action: deletePlaylist } - ] - }); - menu.popup(await contextMenuPosition(event)); - } - - return ( - - ) - }) - ]) - }) - return () => { unlisten.then((f) => f()) } - }, []); - let handle_import = () => { - invoke('import_playlist').then((_res) => { - let res = _res as PlaylistInfo; - - setPlaylists([ - ...playlists, - - ]) - // console.log(res.name); - }) - } - return ( -
- - { playlists } - - -
- ) -} - -interface MainViewProps { - lib_ref: [JSX.Element[], React.Dispatch>], - viewName: string, - playlistsInfo: MutableRefObject, - setSelected: (props: SongProps) => void, - selectedSong: MutableRefObject, -} - -function MainView({ lib_ref, viewName, playlistsInfo, setSelected, selectedSong }: MainViewProps) { - const [library, setLibrary] = lib_ref; - - - const addToQueue = (_: string) => { - invoke('add_song_to_queue', { uuid: selectedSong.current?.uuid, location: selectedSong.current?.playerLocation }).then(() => {}); - } - const playNow = () => invoke("play_now", { uuid: selectedSong.current!.uuid, location: selectedSong.current!.playerLocation }).then(() => {}) - const playNext = () => invoke("play_next_queue", { uuid: selectedSong.current!.uuid, location: selectedSong.current!.playerLocation }).then(() => {}) - const removeLibPlaylist = () => { - invoke("remove_from_lib_playlist", { song: selectedSong.current!.uuid, location: selectedSong.current!.playerLocation }).then(() => { - if (selectedSong.current!.reload !== undefined) { - selectedSong.current!.reload() - } - }) - } - async function clickHandler(event: React.MouseEvent) { - event.preventDefault(); - - await invoke('get_playlists'); - let removeText = "Remove from Library"; - if (selectedSong.current!.playerLocation != "Library") { - removeText = "Remove from Playlist"; - } - const menu = await Menu.new({ - items: [ - { id: "play_now_" + selectedSong.current!.uuid, text: "Play Now", action: playNow }, - { id: "play_next_" + selectedSong.current!.uuid, text: "Play Next", action: playNext }, - { id: "add_song_to_queue" + selectedSong.current!.uuid, text: "Add to Queue", action: addToQueue }, - await Submenu.new( - { - text: "Add to Playlist...", - items: [...selectedSong.current!.playlists.current.map((list) => { - const addToPlaylist = () => { - invoke('add_song_to_playlist', { playlist: list.uuid, song: selectedSong.current!.uuid }).then(() => {}); - } - return { id: "add_song_to_playlists" + selectedSong.current!.uuid + list.uuid, text: list.name, action: addToPlaylist } - })] - } as SubmenuOptions - ), - { id: "remove_from_lib_playlist" + selectedSong.current!.location + selectedSong.current!.uuid, text: removeText, action: removeLibPlaylist }, - ]}); - menu.popup(await contextMenuPosition(event)); - } - - - useEffect(() => { - const unlisten = appWindow.listen("library_loaded", (_) => { - console.log("library_loaded"); - invoke('get_library').then((lib) => { - setLibrary([...(lib as Song[]).map((song, i) => { - console.log("duration", song.duration) - return ( - - ) - })]) - }) - - invoke('get_playlists').then(() => {}) - }) - return () => { unlisten.then((f) => f()) } - }, []); - - - return ( -
-

{ viewName }

-
{ library }
-
- ) -} - - -interface SongProps { - location: URI[], - playerLocation: PlayerLocation, - uuid: string, - plays: number, - format?: string, - duration: number, - last_played?: string, - date_added?: string, - date_modified?: string, - tags: any, - playlists: MutableRefObject, - index: number, - setSelected: (props: SongProps) => void, - reload?: () => void -} - -function MainViewSong(props: SongProps) { - // console.log(props.tags); - // useEffect(() => { - // const unlistenPromise = listen("add_song_to_queue", (event) => { - // switch (event.payload) { - // default: - // console.log("Unimplemented application menu id:", event.payload); - // } - // }); - - // return () => { - // unlistenPromise.then((unlisten) => unlisten()); - // }; - // }, []); - const setSelected = () => { - props.setSelected(props); - console.log(props.tags.Title); - } - return( -
-

{ props.tags.Artist }

-

{ props.tags.Title }

-

{ props.tags.Album }

-

- { Math.round(+props.duration / 60) }: - { (+props.duration % 60).toString().padStart(2, "0") } -

-
- ) -} - - -interface PlayBarProps { - playing: boolean, - setPlaying: React.Dispatch> -} - -function PlayBar({ playing, setPlaying }: PlayBarProps) { - const [position, setPosition] = useState(0); - const [duration, setDuration] = useState(0); - const [seekBarSize, setSeekBarSize] = useState(0); - const seekBarRef = React.createRef(); - const volumeSliderRef = React.createRef(); - - const [lastFmLoggedIn, setLastFmLoggedIn] = useState(false); - - useEffect(() => { - const unlisten = appWindow.listen("playback_info", ({ payload: info, }) => { - const pos_ = Array.isArray(info.position) ? info.position![0] : 0; - const dur_ = Array.isArray(info.duration) ? info.duration![0] : 0; - - setPosition(pos_); - setDuration(dur_); - let progress = ((pos_/dur_) * 100); - setSeekBarSize(progress) - }) - return () => { unlisten.then((f) => f()) } - }, []); - - const seek = (event: React.MouseEvent) => { - event.stopPropagation(); - let rect = seekBarRef.current!.getBoundingClientRect(); - let val = ((event.clientX-rect.left) / (rect.width))*duration; - - invoke('seek', { time: Math.round(val * 1000) }).then() - }; - - const wheelVolume = (event: React.WheelEvent) => { - const n = 5; - if (event.deltaY < 0) { - volumeSliderRef.current!.stepUp(n); - } else { - volumeSliderRef.current!.stepDown(n); - } - invoke('set_volume', { volume: volumeSliderRef.current?.valueAsNumber!.toString() }).then(() => {}) - }; - - return ( -
-
-
-
-
-
- - - - -
-
- - - { - invoke('set_volume', { volume: volume.target.value }).then(() => {}) - }} /> -

- { Math.floor(+position / 60).toString().padStart(2, "0") }: - { (+position % 60).toString().padStart(2, "0") }/ - { Math.floor(+duration / 60).toString().padStart(2, "0") }: - { (+duration % 60).toString().padStart(2, "0") } - -

-
-
-
- ) -} - -interface NowPlayingProps { - title: string | undefined, - artist: string | undefined, - album: string | undefined, - artwork: JSX.Element -} - -function NowPlaying({ title, artist, album, artwork }: NowPlayingProps) { - return ( -
-
- { artwork } -
-

{ title? title : "Unknown Title" }

-

{ artist }

-

{ album }

-
- ) -} - -interface QueueProps { - songs: JSX.Element[], - // song element. put the proper type here :]??? - selectedSong: MutableRefObject, -} - -interface selectedQueueSong { - uuid: string, - index: number - location: PlayerLocation, -} - -function Queue({ songs, selectedSong, }: QueueProps) { - const removeFromQueue = () => { - invoke('remove_from_queue', { index: selectedSong.current.index }).then(() => {}); - } - const playNow = () => { - invoke('queue_move_to', { index: selectedSong.current.index }).then(() => {}); - } - const playNext = () => invoke('play_next_queue', { uuid: selectedSong.current.uuid, location: selectedSong.current.location }).then(() => {}); - const clearQueue = () => invoke('clear_queue').then(); - - async function menuHandler(event: React.MouseEvent) { - event.preventDefault(); - - const menu = await Menu.new({ - items: [ - { id: "play_now" + selectedSong.current.index, text: "Play Now", action: playNow }, - { id: "play_next_" + selectedSong.current.uuid + selectedSong.current.index, text: "Play Next in Queue", action: playNext }, - { id: "remove_queue" + selectedSong.current.uuid + selectedSong.current.index, text: "Remove from Queue", action: removeFromQueue }, - { id: "clear_queue", text: "Clear Queue", action: clearQueue }, - ] - }) - menu.popup(await contextMenuPosition(event)); - } - - return ( -
- { songs } -
- ) -} - -interface QueueSongProps { - song: Song, - location: PlayerLocation, - index: number, - setSelectedSong: (song: selectedQueueSong) => void, -} - -function QueueSong({ song, location, index, setSelectedSong }: QueueSongProps) { - // console.log(song.tags); - - let setSelected = () => setSelectedSong({uuid: song.uuid, index: index, location: location }) - - return ( -
- -
-

{ song.tags.Title }

-

{ song.tags.Artist }

-
-
- ) -} - -function getConfig(): any { - invoke('init_get_config').then( (_config) => { - let config = _config as Config; - if (config.libraries.libraries.length == 0) { - invoke('create_new_library').then(() => {}) - } else { - // console.log("else"); - invoke('lib_already_created').then(() => {}) - } - }) -} - diff --git a/src/App.css b/src/components/App.css similarity index 100% rename from src/App.css rename to src/components/App.css diff --git a/src/components/App.tsx b/src/components/App.tsx new file mode 100644 index 0000000..dcafeae --- /dev/null +++ b/src/components/App.tsx @@ -0,0 +1,130 @@ +import React, { useEffect, useRef, useState } from "react"; +import { convertFileSrc, invoke } from "@tauri-apps/api/core"; +import "./App.css"; +// import { EventEmitter } from "@tauri-apps/plugin-shell"; +// import { listen } from "@tauri-apps/api/event"; +// import { fetch } from "@tauri-apps/plugin-http"; +import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"; +import { PhysicalPosition } from "@tauri-apps/api/window"; +import { Config } from "../bindings/Config"; +import { PlayerLocation } from "../bindings/PlayerLocation"; +import { Song } from "../bindings/Song"; +import { PlaylistHead, PlaylistInfo } from "./PlaylistHead"; +import MainView, { SongProps } from "./MainView"; +import { Queue, QueueSong, selectedQueueSong } from "./Queue"; +import { NowPlaying } from "./NowPlaying"; +import { PlayBar } from "./PlayBar"; + +export const appWindow = getCurrentWebviewWindow(); + +// This needs to be changed to properly reflect cursor position +// this will do for now. +export async function contextMenuPosition(event: React.MouseEvent) { + return new PhysicalPosition(event.clientX, event.clientY); +} + +export default function App() { + const library = useState([]); + const [queue, setQueue] = useState([]); + const [playing, setPlaying] = useState(false); + const [playlists, setPlaylists] = useState([]); + const [viewName, setViewName] = useState("Library"); + const playlistsInfo= useRef([]); + const selectedSongMain = useRef(); + const selectedSongQueue = useRef({uuid: "0", index: 0, location: "Library"}); + const setSelectedSongMain = (props: SongProps) => {selectedSongMain.current = props;} + const setSelectedSongQueue = (song: selectedQueueSong) => {selectedSongQueue.current = song; console.log(selectedSongQueue)} + + const [nowPlaying, setNowPlaying] = useState( + } + /> + ); + + + useEffect(() => { + const unlisten = appWindow.listen("now_playing_change", ({ payload, }) => { + const displayArtwork = () => { + invoke('display_album_art', { uuid: payload.uuid }).then(() => {}) + } + + setNowPlaying( + } + /> + ) + + }) + return () => { unlisten.then((f) => f()) } + }, []); + + useEffect(() => { + const unlisten = appWindow.listen("queue_updated", (_) => { + // console.log(event); + invoke('get_queue').then((_songs) => { + let songs = _songs as [Song, PlayerLocation][]; + setQueue( + songs.filter((_, i) => i != 0).map((song, i) => + + ) + ) + }) + }) + return () => { unlisten.then((f) => f()) } + }, []); + + useEffect(() => { + const unlisten = appWindow.listen("playing", (isPlaying) => { + setPlaying(isPlaying.payload) + }) + return () => { unlisten.then((f) => f()) } + }, []); + + useEffect(() => { + invoke("start_controller").then(() => {}); + }, []) + + + return ( +
+
+
+ + +
+
+ { nowPlaying } + +
+
+
+ +
+
+ ); +} + +function getConfig(): any { + invoke('init_get_config').then( (_config) => { + let config = _config as Config; + if (config.libraries.libraries.length == 0) { + invoke('create_new_library').then(() => {}) + } else { + // console.log("else"); + invoke('lib_already_created').then(() => {}) + } + }) +} + diff --git a/src/components/MainView.tsx b/src/components/MainView.tsx new file mode 100644 index 0000000..623cb1f --- /dev/null +++ b/src/components/MainView.tsx @@ -0,0 +1,154 @@ +import { MutableRefObject, useEffect } from "react"; +import { URI } from "../bindings/URI"; +import { PlayerLocation } from "../bindings/PlayerLocation"; +import { invoke } from "@tauri-apps/api/core"; +import { Menu, Submenu } from "@tauri-apps/api/menu"; +import { appWindow, contextMenuPosition } from "./App"; +import { Song } from "../bindings/Song"; +import { PlaylistInfo } from "./PlaylistHead"; + +export interface MainViewProps { + lib_ref: [JSX.Element[], React.Dispatch>], + viewName: string, + playlistsInfo: MutableRefObject, + setSelected: (props: SongProps) => void, + selectedSong: MutableRefObject, +} + +export default function MainView({ lib_ref, viewName, playlistsInfo, setSelected, selectedSong }: MainViewProps) { + const [library, setLibrary] = lib_ref; + + + const addToQueue = (_: string) => { + invoke('add_song_to_queue', { uuid: selectedSong.current?.uuid, location: selectedSong.current?.playerLocation }).then(() => {}); + } + const playNow = () => invoke("play_now", { uuid: selectedSong.current!.uuid, location: selectedSong.current!.playerLocation }).then(() => {}) + const playNext = () => invoke("play_next_queue", { uuid: selectedSong.current!.uuid, location: selectedSong.current!.playerLocation }).then(() => {}) + const removeLibPlaylist = () => { + invoke("remove_from_lib_playlist", { song: selectedSong.current!.uuid, location: selectedSong.current!.playerLocation }).then(() => { + if (selectedSong.current!.reload !== undefined) { + selectedSong.current!.reload() + } + }) + } + async function clickHandler(event: React.MouseEvent) { + event.preventDefault(); + + await invoke('get_playlists'); + let removeText = "Remove from Library"; + if (selectedSong.current!.playerLocation != "Library") { + removeText = "Remove from Playlist"; + } + const menu = await Menu.new({ + items: [ + { id: "play_now_" + selectedSong.current!.uuid, text: "Play Now", action: playNow }, + { id: "play_next_" + selectedSong.current!.uuid, text: "Play Next", action: playNext }, + { id: "add_song_to_queue" + selectedSong.current!.uuid, text: "Add to Queue", action: addToQueue }, + await Submenu.new( + { + text: "Add to Playlist...", + items: [...selectedSong.current!.playlists.current.map((list) => { + const addToPlaylist = () => { + invoke('add_song_to_playlist', { playlist: list.uuid, song: selectedSong.current!.uuid }).then(() => {}); + } + return { id: "add_song_to_playlists" + selectedSong.current!.uuid + list.uuid, text: list.name, action: addToPlaylist } + })] + } + ), + { id: "remove_from_lib_playlist" + selectedSong.current!.location + selectedSong.current!.uuid, text: removeText, action: removeLibPlaylist }, + ]}); + menu.popup(await contextMenuPosition(event)); + } + + + useEffect(() => { + const unlisten = appWindow.listen("library_loaded", (_) => { + console.log("library_loaded"); + invoke('get_library').then((lib) => { + setLibrary([...(lib as Song[]).map((song, i) => { + console.log("duration", song.duration) + return ( + + ) + })]) + }) + + invoke('get_playlists').then(() => {}) + }) + return () => { unlisten.then((f) => f()) } + }, []); + + + return ( +
+

{ viewName }

+
{ library }
+
+ ) +} + + +export interface SongProps { + location: URI[], + playerLocation: PlayerLocation, + uuid: string, + plays: number, + format?: string, + duration: number, + last_played?: string, + date_added?: string, + date_modified?: string, + tags: any, + playlists: MutableRefObject, + index: number, + setSelected: (props: SongProps) => void, + reload?: () => void +} + +export function MainViewSong(props: SongProps) { + // console.log(props.tags); + // useEffect(() => { + // const unlistenPromise = listen("add_song_to_queue", (event) => { + // switch (event.payload) { + // default: + // console.log("Unimplemented application menu id:", event.payload); + // } + // }); + + // return () => { + // unlistenPromise.then((unlisten) => unlisten()); + // }; + // }, []); + const setSelected = () => { + props.setSelected(props); + console.log(props.tags.Title); + } + return( +
+

{ props.tags.Artist }

+

{ props.tags.Title }

+

{ props.tags.Album }

+

+ { Math.round(+props.duration / 60) }: + { (+props.duration % 60).toString().padStart(2, "0") } +

+
+ ) +} diff --git a/src/components/NowPlaying.tsx b/src/components/NowPlaying.tsx new file mode 100644 index 0000000..e9c1a64 --- /dev/null +++ b/src/components/NowPlaying.tsx @@ -0,0 +1,19 @@ +export interface NowPlayingProps { + title: string | undefined, + artist: string | undefined, + album: string | undefined, + artwork: JSX.Element +} + +export function NowPlaying({ title, artist, album, artwork }: NowPlayingProps) { + return ( +
+
+ { artwork } +
+

{ title? title : "Unknown Title" }

+

{ artist }

+

{ album }

+
+ ) +} diff --git a/src/components/PlayBar.tsx b/src/components/PlayBar.tsx new file mode 100644 index 0000000..ff5b60d --- /dev/null +++ b/src/components/PlayBar.tsx @@ -0,0 +1,83 @@ +import React, { useEffect, useState } from "react"; +import { appWindow } from "./App"; +import { PlaybackInfo } from "../bindings/PlaybackInfo"; +import { invoke } from "@tauri-apps/api/core"; + +export interface PlayBarProps { + playing: boolean, + setPlaying: React.Dispatch> +} + +export function PlayBar({ playing, setPlaying }: PlayBarProps) { + const [position, setPosition] = useState(0); + const [duration, setDuration] = useState(0); + const [seekBarSize, setSeekBarSize] = useState(0); + const seekBarRef = React.createRef(); + const volumeSliderRef = React.createRef(); + + const [lastFmLoggedIn, setLastFmLoggedIn] = useState(false); + + useEffect(() => { + const unlisten = appWindow.listen("playback_info", ({ payload: info, }) => { + const pos_ = Array.isArray(info.position) ? info.position![0] : 0; + const dur_ = Array.isArray(info.duration) ? info.duration![0] : 0; + + setPosition(pos_); + setDuration(dur_); + let progress = ((pos_/dur_) * 100); + setSeekBarSize(progress) + }) + return () => { unlisten.then((f) => f()) } + }, []); + + const seek = (event: React.MouseEvent) => { + event.stopPropagation(); + let rect = seekBarRef.current!.getBoundingClientRect(); + let val = ((event.clientX-rect.left) / (rect.width))*duration; + + invoke('seek', { time: Math.round(val * 1000) }).then() + }; + + const wheelVolume = (event: React.WheelEvent) => { + const n = 5; + if (event.deltaY < 0) { + volumeSliderRef.current!.stepUp(n); + } else { + volumeSliderRef.current!.stepDown(n); + } + invoke('set_volume', { volume: volumeSliderRef.current?.valueAsNumber!.toString() }).then(() => {}) + }; + + return ( +
+
+
+
+
+
+ + + + +
+
+ + + { + invoke('set_volume', { volume: volume.target.value }).then(() => {}) + }} /> +

+ { Math.floor(+position / 60).toString().padStart(2, "0") }: + { (+position % 60).toString().padStart(2, "0") }/ + { Math.floor(+duration / 60).toString().padStart(2, "0") }: + { (+duration % 60).toString().padStart(2, "0") } + +

+
+
+
+ ) +} diff --git a/src/components/PlaylistHead.tsx b/src/components/PlaylistHead.tsx new file mode 100644 index 0000000..c52b100 --- /dev/null +++ b/src/components/PlaylistHead.tsx @@ -0,0 +1,125 @@ +import { MutableRefObject, useEffect } from "react" +import { MainViewSong, SongProps } from "./MainView" +import { invoke } from "@tauri-apps/api/core" +import { Song } from "../bindings/Song" +import { appWindow, contextMenuPosition } from "./App" +import { Menu } from "@tauri-apps/api/menu" + +export interface PlaylistInfo { + uuid: string, + name: string, +} + + +export interface PlaylistHeadProps { + playlists: JSX.Element[] + setPlaylists: React.Dispatch>, + setViewName: React.Dispatch>, + setLibrary: React.Dispatch>, + playlistsInfo: MutableRefObject, + setSelected: (props: SongProps) => void, + } + +export function PlaylistHead({ playlists, setPlaylists, setViewName, setLibrary, playlistsInfo, setSelected }: PlaylistHeadProps) { + function getPlaylist(playlist: PlaylistInfo) { + invoke('get_playlist', { uuid: playlist.uuid }).then((list) => { + setLibrary([...(list as Song[]).map((song, i) => { + // console.log(song); + const reload = () => getPlaylist(playlist) + return ( + + ) + })]) + }) + setViewName( playlist.name ) + } + + useEffect(() => { + const unlisten = appWindow.listen("playlists_gotten", (_res) => { + const res = _res.payload; + // console.log(event); + playlistsInfo.current = [...res]; + // console.log(playlistsInfo, res); + + setPlaylists([ + ...res.map( (list) => { + const _getPlaylist = () => getPlaylist(list) + const deletePlaylist = () => { + invoke('delete_playlist', { uuid: list.uuid }).then(() => {}); + invoke('get_playlists').then(() => {}); + } + async function menuHandler(event: React.MouseEvent) { + event.preventDefault(); + const menu = await Menu.new({ + items: [ + { id: "delete_playlist" + list.uuid, text: "Delete Playlist", action: deletePlaylist } + ] + }); + menu.popup(await contextMenuPosition(event)); + } + + return ( + + ) + }) + ]) + }) + return () => { unlisten.then((f) => f()) } + }, []); + let handle_import = () => { + invoke('import_playlist').then((_res) => { + let res = _res as PlaylistInfo; + + setPlaylists([ + ...playlists, + + ]) + // console.log(res.name); + }) + } + return ( +
+ + { playlists } + + +
+ ) +} diff --git a/src/components/Queue.tsx b/src/components/Queue.tsx new file mode 100644 index 0000000..2bf59c3 --- /dev/null +++ b/src/components/Queue.tsx @@ -0,0 +1,76 @@ +import { MutableRefObject } from "react"; +import { PlayerLocation } from "../bindings/PlayerLocation"; +import { convertFileSrc, invoke } from "@tauri-apps/api/core"; +import { Menu } from "@tauri-apps/api/menu"; +import { contextMenuPosition } from "./App"; +import { Song } from "../bindings/Song"; + +export interface QueueProps { + songs: JSX.Element[], + // song element. put the proper type here :]??? + selectedSong: MutableRefObject, +} + +export interface selectedQueueSong { + uuid: string, + index: number + location: PlayerLocation, +} + +export function Queue({ songs, selectedSong, }: QueueProps) { + const removeFromQueue = () => { + invoke('remove_from_queue', { index: selectedSong.current.index }).then(() => {}); + } + const playNow = () => { + invoke('queue_move_to', { index: selectedSong.current.index }).then(() => {}); + } + const playNext = () => invoke('play_next_queue', { uuid: selectedSong.current.uuid, location: selectedSong.current.location }).then(() => {}); + const clearQueue = () => invoke('clear_queue').then(); + + async function menuHandler(event: React.MouseEvent) { + event.preventDefault(); + + const menu = await Menu.new({ + items: [ + { id: "play_now" + selectedSong.current.index, text: "Play Now", action: playNow }, + { id: "play_next_" + selectedSong.current.uuid + selectedSong.current.index, text: "Play Next in Queue", action: playNext }, + { id: "remove_queue" + selectedSong.current.uuid + selectedSong.current.index, text: "Remove from Queue", action: removeFromQueue }, + { id: "clear_queue", text: "Clear Queue", action: clearQueue }, + ] + }) + menu.popup(await contextMenuPosition(event)); + } + + return ( +
+ { songs } +
+ ) +} + +export interface QueueSongProps { + song: Song, + location: PlayerLocation, + index: number, + setSelectedSong: (song: selectedQueueSong) => void, +} + +export function QueueSong({ song, location, index, setSelectedSong }: QueueSongProps) { + // console.log(song.tags); + + let setSelected = () => setSelectedSong({uuid: song.uuid, index: index, location: location }) + + return ( +
+ +
+

{ song.tags.Title }

+

{ song.tags.Artist }

+
+
+ ) +} diff --git a/src/main.tsx b/src/main.tsx index 09a651b..168ce11 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,6 +1,6 @@ import { StrictMode } from "react"; import ReactDOM from "react-dom/client"; -import App from "./App"; +import App from "./components/App"; ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( //