diff --git a/demo/public/assets/apps/file-explorer/icons/file-audio.svg b/demo/public/assets/apps/file-explorer/icons/file-audio.svg new file mode 100644 index 00000000..0c5a66d0 --- /dev/null +++ b/demo/public/assets/apps/file-explorer/icons/file-audio.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/public/assets/apps/file-explorer/icons/file-video.svg b/demo/public/assets/apps/file-explorer/icons/file-video.svg new file mode 100644 index 00000000..752527bd --- /dev/null +++ b/demo/public/assets/apps/file-explorer/icons/file-video.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/public/assets/apps/file-explorer/icons/folder-audio.svg b/demo/public/assets/apps/file-explorer/icons/folder-audio.svg new file mode 100644 index 00000000..974dd72d --- /dev/null +++ b/demo/public/assets/apps/file-explorer/icons/folder-audio.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/public/assets/apps/file-explorer/icons/folder-images.svg b/demo/public/assets/apps/file-explorer/icons/folder-images.svg index 32e14dc3..fe83dc63 100644 --- a/demo/public/assets/apps/file-explorer/icons/folder-images.svg +++ b/demo/public/assets/apps/file-explorer/icons/folder-images.svg @@ -1,17 +1,17 @@ - - - + + + - + - + - - + + - + diff --git a/demo/public/assets/apps/file-explorer/icons/folder-text.svg b/demo/public/assets/apps/file-explorer/icons/folder-text.svg index 98f8960b..0b5779f8 100644 --- a/demo/public/assets/apps/file-explorer/icons/folder-text.svg +++ b/demo/public/assets/apps/file-explorer/icons/folder-text.svg @@ -1,16 +1,16 @@ - - - + + + - + - + - + - + diff --git a/demo/public/assets/apps/file-explorer/icons/folder-video.svg b/demo/public/assets/apps/file-explorer/icons/folder-video.svg new file mode 100644 index 00000000..e146508e --- /dev/null +++ b/demo/public/assets/apps/file-explorer/icons/folder-video.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/public/assets/videos/recording1.webm b/demo/public/assets/videos/recording1.webm new file mode 100644 index 00000000..97e038cf Binary files /dev/null and b/demo/public/assets/videos/recording1.webm differ diff --git a/demo/public/assets/videos/recording2.webm b/demo/public/assets/videos/recording2.webm new file mode 100644 index 00000000..728e624d Binary files /dev/null and b/demo/public/assets/videos/recording2.webm differ diff --git a/docs/src/public/assets/file-icons.png b/docs/src/public/assets/file-icons.png index 9578317f..4802fa70 100644 Binary files a/docs/src/public/assets/file-icons.png and b/docs/src/public/assets/file-icons.png differ diff --git a/docs/src/public/assets/folder-icons.png b/docs/src/public/assets/folder-icons.png index 72b95cab..3ae18836 100644 Binary files a/docs/src/public/assets/folder-icons.png and b/docs/src/public/assets/folder-icons.png differ diff --git a/docs/src/reference/skins/classes/skin.md b/docs/src/reference/skins/classes/skin.md index 525b1d64..bc09413a 100644 --- a/docs/src/reference/skins/classes/skin.md +++ b/docs/src/reference/skins/classes/skin.md @@ -29,12 +29,16 @@ interface SkinOptions { info?: string; text?: string; code?: string; + video?: string; + audio?: string; }; folderIcons?: { generic: string; images?: string; text?: string; link?: string; + video?: string; + audio?: string; }; loadStyleSheet?: () => void; } @@ -58,13 +62,13 @@ SVG icon for the system Replacements for app icons based on app id -- **Type:** `{ [key: string]: string }` +- **Type:** `Record` ### appNames Replacements for app names based on app id -- **Type:** `{ [key: string]: string }` +- **Type:** `Record` ### wallpapers @@ -96,6 +100,8 @@ interface FileIcons { info?: string; text?: string; code?: string; + video?: string; + audio?: string; } ``` @@ -115,6 +121,8 @@ interface FolderIcons { images?: string; text?: string; link?: string; + video?: string; + audio?: string; } ``` diff --git a/packages/apps/media-viewer/src/components/MediaViewer.module.css b/packages/apps/media-viewer/src/components/MediaViewer.module.css index 96bf398a..cf8ec3b0 100644 --- a/packages/apps/media-viewer/src/components/MediaViewer.module.css +++ b/packages/apps/media-viewer/src/components/MediaViewer.module.css @@ -5,10 +5,59 @@ width: 100%; height: 100%; padding: 2rem; -} +} .MediaViewer img { width: 100%; height: 100%; object-fit: contain; +} + +.AudioViewer { + width: 70%; + margin: 0.5rem auto 0; +} + +.AudioViewer .AudioControls { + margin-top: 20px; + display: flex; + justify-content: center; + align-items: center; +} + +.AudioControls button { + margin: 1rem; + padding: 0.5rem 0; + width: 20%; + border-radius: var(--border-radius-1); +} + +.AudioControls> .Playing { + background-color: var(--purple-0); + color: var(--white-0); +} + +.AudioControls> :not(.Playing) { + background-color: var(--red-0); + color: var(--white-0); +} + +.VideoViewer { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; +} + +.VideoViewer h3 { + margin: 0; +} + +.VideoViewer> * { + width: 100%; + height: 100%; + border: none; + background: transparent; } \ No newline at end of file diff --git a/packages/apps/media-viewer/src/components/MediaViewer.tsx b/packages/apps/media-viewer/src/components/MediaViewer.tsx index cdf734f1..18b4d816 100644 --- a/packages/apps/media-viewer/src/components/MediaViewer.tsx +++ b/packages/apps/media-viewer/src/components/MediaViewer.tsx @@ -1,6 +1,6 @@ -import { useEffect } from "react"; +import { useEffect, useState, useRef } from "react"; import styles from "./MediaViewer.module.css"; -import { AppsConfig, IMAGE_EXTENSIONS, useSystemManager, useWindowsManager, VirtualFile, WindowProps } from "@prozilla-os/core"; +import { AppsConfig, IMAGE_EXTENSIONS, VIDEO_EXTENSIONS, AUDIO_EXTENSIONS, useSystemManager, useWindowsManager, VirtualFile, WindowProps, MEDIA_EXTENSIONS } from "@prozilla-os/core"; export interface MediaViewerProps extends WindowProps { file?: VirtualFile; @@ -9,11 +9,79 @@ export interface MediaViewerProps extends WindowProps { export function MediaViewer({ file, close, setTitle }: MediaViewerProps) { const { appsConfig } = useSystemManager(); const windowsManager = useWindowsManager(); + const [isPlaying, setIsPlaying] = useState(false); + const audioRef = useRef(null); + const videoRef = useRef(null); useEffect(() => { - if (file != null) setTitle?.(file.id); + if (file != null) + setTitle?.(file.id); }, [file, setTitle]); + useEffect(() => { + if (file == null || file.source == null) + return; + + if (file.extension && AUDIO_EXTENSIONS.includes(file.extension)) { + if (audioRef.current) { + audioRef.current.src = file.source; + void audioRef.current.play(); + setIsPlaying(true); + } + } + + if (file.extension && VIDEO_EXTENSIONS.includes(file.extension)) { + if (videoRef.current) { + videoRef.current.src = file.source; + void videoRef.current.play(); + setIsPlaying(true); + } + } + + return () => { + if (audioRef.current) { + audioRef.current.pause(); + audioRef.current.currentTime = 0; + } + if (videoRef.current) { + videoRef.current.pause(); + videoRef.current.currentTime = 0; + } + }; + }, [file]); + + const handlePlay = () => { + if (audioRef.current) { + void audioRef.current.play(); + } + if (videoRef.current) { + void videoRef.current.play(); + } + setIsPlaying(true); + }; + + const handlePause = () => { + if (audioRef.current) { + audioRef.current.pause(); + } + if (videoRef.current) { + videoRef.current.pause(); + } + setIsPlaying(false); + }; + + const handleStop = () => { + if (audioRef.current) { + audioRef.current.pause(); + audioRef.current.currentTime = 0; + } + if (videoRef.current) { + videoRef.current.pause(); + videoRef.current.currentTime = 0; + } + setIsPlaying(false); + }; + if (file == null) { const fileExplorerApp = appsConfig.getAppByRole(AppsConfig.APP_ROLES.fileExplorer); @@ -22,12 +90,56 @@ export function MediaViewer({ file, close, setTitle }: MediaViewerProps) { windowsManager?.open(fileExplorerApp.id, { path: "~/Pictures" }); close?.(); }, 10); - return; + return null; } - if (file.extension == null || !IMAGE_EXTENSIONS.includes(file.extension)) return

Invalid file format.

; + if (file.extension == null || !MEDIA_EXTENSIONS.includes(file.extension)) { + return

Invalid file format.

; + } - if (file.source == null) return

File failed to load.

; + if (file.source == null) + return

File failed to load.

; + + if (IMAGE_EXTENSIONS.includes(file.extension)) { + return
+ {file.id} +
; + } else if (AUDIO_EXTENSIONS.includes(file.extension)) { + return
+

Playing audio: {file.id}

+
; + } else if (VIDEO_EXTENSIONS.includes(file.extension)) { + if (file.extension === "yt") { + return
+