play

Seto Elkahfi committed Aug 1, 2024 at 20:17 UTC b68abcdf3d8b343ccb650e061313077738a5b9ea
22 files changed +186 -320
frontend/splitfire-desktop/app/_lib/hooks/use-enter-submit.tsx deleted
-23
@@ -1,23 +0,0 @@
1 -import { useRef, type RefObject } from 'react'
2 -
3 -export function useEnterSubmit(): {
4 - formRef: RefObject<HTMLFormElement>
5 - onKeyDown: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void
6 -} {
7 - const formRef = useRef<HTMLFormElement>(null)
8 -
9 - const handleKeyDown = (
10 - event: React.KeyboardEvent<HTMLTextAreaElement>
11 - ): void => {
12 - if (
13 - event.key === 'Enter' &&
14 - !event.shiftKey &&
15 - !event.nativeEvent.isComposing
16 - ) {
17 - formRef.current?.requestSubmit()
18 - event.preventDefault()
19 - }
20 - }
21 -
22 - return { formRef, onKeyDown: handleKeyDown }
23 -}
frontend/splitfire-desktop/app/_lib/hooks/use-local-storage.ts deleted
-24
@@ -1,24 +0,0 @@
1 -import { useEffect, useState } from 'react'
2 -
3 -export const useLocalStorage = <T>(
4 - key: string,
5 - initialValue: T
6 -): [T, (value: T) => void] => {
7 - const [storedValue, setStoredValue] = useState(initialValue)
8 -
9 - useEffect(() => {
10 - // Retrieve from localStorage
11 - const item = window.localStorage.getItem(key)
12 - if (item) {
13 - setStoredValue(JSON.parse(item))
14 - }
15 - }, [key])
16 -
17 - const setValue = (value: T) => {
18 - // Save state
19 - setStoredValue(value)
20 - // Save to localStorage
21 - window.localStorage.setItem(key, JSON.stringify(value))
22 - }
23 - return [storedValue, setValue]
24 -}
frontend/splitfire-desktop/app/_src/components/player/Player.tsx
+3 -1
@@ -8,11 +8,13 @@ import ErrorView from "../templates/Error"
8 import { LoadingView } from "../templates/LoadingView"
9 import { invoke } from '@tauri-apps/api'
10 import { UserContext } from "../../lib/CurrentUserContext"
11 -import { PlayerPrepareResponse, TAURI_PLAYER_RECORD, TAURI_PLAYER_SET_VOLUME, TAURI_PLAYER_PAUSED, TAURI_PLAYER_UNMOUNT, TAURI_PLAYER_PREPARE, TauriResponse, TAURI_PLAYER_STOP, TAURI_PLAYER_RESUMED, TAURI_PLAYER_PLAY, TAURI_PLAYER_RECORDING_LENGTH, TAURI_PLAYER_RECORD_STOP } from "../../lib/tauriHandler"
11 +import { TAURI_PLAYER_RECORD, TAURI_PLAYER_SET_VOLUME, TAURI_PLAYER_PAUSED, TAURI_PLAYER_UNMOUNT, TAURI_PLAYER_PREPARE, TAURI_PLAYER_STOP, TAURI_PLAYER_RESUMED, TAURI_PLAYER_PLAY, TAURI_PLAYER_RECORDING_LENGTH, TAURI_PLAYER_RECORD_STOP } from "../../lib/tauriHandler"
12 import { ControlButtonsView } from "./ControlButtons"
13 import { VolumeSliderView } from "./VolumeSliderView"
14 import { HideShowToggleView } from "./HideShowToggle"
15 import { RecordingView } from "./RecordingView"
16 +import { PlayerPrepareResponse } from "@/models/content"
17 +import { TauriResponse } from "@/models/shared"
18
19 enum State {
20 LOADING,
frontend/splitfire-desktop/app/_src/lib/tauriHandler.ts
+1 -11
@@ -19,15 +19,5 @@ export const TAURI_ACCOUNT_REGISTER = 'account_register'
19
20 // Contents
21 export const TAURI_CONTENT_CAROUSEL = 'content_carousel'
22 +export const TAURI_CONTENT_READY_TO_PLAY = 'content_ready_to_play'
23
23 -export enum TauriResponse {
24 - ERROR = 0,
25 - SUCCESS = 1
26 -}
27 -
28 -// Response from the Tauri API
29 -export interface PlayerPrepareResponse {
30 - status: TauriResponse,
31 - message: string,
32 - audio_file_name?: string,
33 -}
\ No newline at end of file
frontend/splitfire-desktop/app/_ui/components/chat-panel.tsx deleted
-57
@@ -1,57 +0,0 @@
1 -'use client'
2 -
3 -import * as React from 'react'
4 -import { type UseChatHelpers } from 'ai/react'
5 -
6 -import { PromptForm } from './prompt-form'
7 -
8 -export interface ChatPanelProps
9 - extends Pick<
10 - UseChatHelpers,
11 - | 'append'
12 - | 'isLoading'
13 - | 'reload'
14 - | 'messages'
15 - | 'stop'
16 - | 'input'
17 - | 'setInput'
18 - > {
19 - id?: string
20 - title?: string
21 -}
22 -
23 -export function ChatPanel({
24 - id,
25 - title,
26 - isLoading,
27 - stop,
28 - append,
29 - reload,
30 - input,
31 - setInput,
32 - messages
33 -}: ChatPanelProps) {
34 - const [shareDialogOpen, setShareDialogOpen] = React.useState(false)
35 -
36 - return (
37 - <div className="fixed inset-x-0 bottom-0 w-full bg-gradient-to-b from-muted/30 from-0% to-muted/30 to-50% animate-in duration-300 ease-in-out dark:from-background/10 dark:from-10% dark:to-background/80 peer-[[data-state=open]]:group-[]:lg:pl-[250px] peer-[[data-state=open]]:group-[]:xl:pl-[300px]">
38 - <div className="mx-auto sm:max-w-2xl sm:px-4">
39 - <div className="flex items-center justify-center h-12"></div>
40 - <div className="px-4 py-2 space-y-4 border-t shadow-lg bg-background sm:rounded-t-xl sm:border md:py-4">
41 - <PromptForm
42 - onSubmit={async value => {
43 - await append({
44 - id,
45 - content: value,
46 - role: 'user'
47 - })
48 - }}
49 - input={input}
50 - setInput={setInput}
51 - isLoading={isLoading}
52 - />
53 - </div>
54 - </div>
55 - </div>
56 - )
57 -}
frontend/splitfire-desktop/app/_ui/components/empty-screen.tsx deleted
-56
@@ -1,56 +0,0 @@
1 -import { UseChatHelpers } from 'ai/react'
2 -
3 -import { Button } from './button'
4 -import { ExternalLink } from './external-link'
5 -import { IconArrowRight } from './icons'
6 -
7 -const exampleMessages = [
8 - {
9 - heading: 'Explain technical concepts',
10 - message: `What is a "serverless function"?`
11 - },
12 - {
13 - heading: 'Summarize an article',
14 - message: 'Summarize the following article for a 2nd grader: \n'
15 - },
16 - {
17 - heading: 'Draft an email',
18 - message: `Draft an email to my boss about the following: \n`
19 - }
20 -]
21 -
22 -export function EmptyScreen({ setInput }: Pick<UseChatHelpers, 'setInput'>) {
23 - return (
24 - <div className="mx-auto max-w-2xl px-4">
25 - <div className="rounded-lg border bg-background p-8">
26 - <h1 className="mb-2 text-lg font-semibold">
27 - Welcome to Next.js AI Chatbot!
28 - </h1>
29 - <p className="mb-2 leading-normal text-muted-foreground">
30 - This is an open source AI chatbot app template built with{' '}
31 - <ExternalLink href="https://nextjs.org">Next.js</ExternalLink> and{' '}
32 - <ExternalLink href="https://vercel.com/storage/kv">
33 - Vercel KV
34 - </ExternalLink>
35 - .
36 - </p>
37 - <p className="leading-normal text-muted-foreground">
38 - You can start a conversation here or try the following examples:
39 - </p>
40 - <div className="mt-4 flex flex-col items-start space-y-2">
41 - {exampleMessages.map((message, index) => (
42 - <Button
43 - key={index}
44 - variant="link"
45 - className="h-auto p-0 text-base"
46 - onClick={() => setInput(message.message)}
47 - >
48 - <IconArrowRight className="mr-2 text-muted-foreground" />
49 - {message.heading}
50 - </Button>
51 - ))}
52 - </div>
53 - </div>
54 - </div>
55 - )
56 -}
frontend/splitfire-desktop/app/_ui/components/icons.tsx
+1 -1
@@ -1,6 +1,6 @@
1 'use client'
2
3 -import { cn } from '@/app/_lib/utils'
3 +import { cn } from '@/lib/utils'
4 import * as React from 'react'
5
6 function IconNextChat({
frontend/splitfire-desktop/app/_ui/components/prompt-form.tsx deleted
-99
@@ -1,99 +0,0 @@
1 -'use client';
2 -
3 -import * as React from 'react'
4 -import Textarea from 'react-textarea-autosize'
5 -import { UseChatHelpers } from 'ai/react'
6 -import {
7 - Tooltip,
8 - TooltipContent,
9 - TooltipTrigger
10 -} from './tooltip'
11 -import { IconArrowElbow, IconPlus } from './icons'
12 -import { useRouter } from 'next/navigation'
13 -import { Button, buttonVariants } from './button';
14 -import { useEnterSubmit } from '@/app/_lib/hooks/use-enter-submit';
15 -import { cn } from '@/app/_lib/utils';
16 -
17 -export interface PromptProps
18 - extends Pick<UseChatHelpers, 'input' | 'setInput'> {
19 - onSubmit: (value: string) => void
20 - isLoading: boolean
21 -}
22 -
23 -export function PromptForm({
24 - onSubmit,
25 - input,
26 - setInput,
27 - isLoading
28 -}: PromptProps) {
29 - const { formRef, onKeyDown } = useEnterSubmit()
30 - const inputRef = React.useRef<HTMLTextAreaElement>(null)
31 - const router = useRouter()
32 - React.useEffect(() => {
33 - if (inputRef.current) {
34 - inputRef.current.focus()
35 - }
36 - }, [])
37 -
38 - return (
39 - <form
40 - onSubmit={async e => {
41 - e.preventDefault()
42 - if (!input?.trim()) {
43 - return
44 - }
45 - setInput('')
46 - await onSubmit(input)
47 - }}
48 - ref={formRef}
49 - >
50 - <div className="relative flex flex-col w-full px-8 overflow-hidden max-h-60 grow bg-background sm:rounded-md sm:border sm:px-12">
51 - <Tooltip>
52 - <TooltipTrigger asChild>
53 - <button
54 - onClick={e => {
55 - e.preventDefault()
56 - router.refresh()
57 - router.push('/')
58 - }}
59 - className={cn(
60 - buttonVariants({ size: 'sm', variant: 'outline' }),
61 - 'absolute left-0 top-4 size-8 rounded-full bg-background p-0 sm:left-4'
62 - )}
63 - >
64 - <IconPlus />
65 - <span className="sr-only">New Chat</span>
66 - </button>
67 - </TooltipTrigger>
68 - <TooltipContent>New Chat</TooltipContent>
69 - </Tooltip>
70 - <Textarea
71 - ref={inputRef}
72 - tabIndex={0}
73 - onKeyDown={onKeyDown}
74 - rows={1}
75 - value={input}
76 - onChange={e => setInput(e.target.value)}
77 - placeholder="Send a message."
78 - spellCheck={false}
79 - className="min-h-[60px] w-full resize-none bg-transparent px-4 py-[1.3rem] focus-within:outline-none sm:text-sm"
80 - />
81 - <div className="absolute right-0 top-4 sm:right-4">
82 - <Tooltip>
83 - <TooltipTrigger asChild>
84 - <Button
85 - type="submit"
86 - size="icon"
87 - disabled={isLoading || input === ''}
88 - >
89 - <IconArrowElbow />
90 - <span className="sr-only">Send message</span>
91 - </Button>
92 - </TooltipTrigger>
93 - <TooltipContent>Send message</TooltipContent>
94 - </Tooltip>
95 - </div>
96 - </div>
97 - </form>
98 - )
99 -}
frontend/splitfire-desktop/app/_ui/global-nav.tsx
+2 -1
@@ -1,7 +1,7 @@
1 'use client';
2
3 -import { menus, type Item } from '../_lib/menus';
3 import Link from 'next/link';
4 +import { menus, type Item } from './menus';
5 import { useSelectedLayoutSegment } from 'next/navigation';
6 import { MenuAlt2Icon, XIcon } from '@heroicons/react/solid';
7 import clsx from 'clsx';
@@ -10,6 +10,7 @@ import Image from 'next/image';
10 import { UserContext } from '../_src/lib/CurrentUserContext';
11
12
13 +
14 export function GlobalNav() {
15
16 const [isOpen, setIsOpen] = useState(false);
frontend/splitfire-desktop/app/_ui/menus.ts renamed
+2 -2
@@ -12,8 +12,8 @@ export const menus: { items: Item[] }[] = [
12 slug: '',
13 },
14 {
15 - name: 'Play',
16 - slug: 'play',
15 + name: 'Ready to Play!',
16 + slug: 'ready-to-play',
17 },
18 ],
19 },
frontend/splitfire-desktop/app/_ui/skeleton-card.tsx
+8 -1
@@ -20,11 +20,18 @@ export const SkeletonCard = ({ isLoading }: { isLoading?: boolean }) => (
20 </div>
21 );
22
23 +export enum SongProviderPath {
24 + PLAY = "play",
25 + SPLIT = "split",
26 +}
27 +
28 // duplicate skeleton-card.tsx
29 export const SongProviderCard = ({
30 songProvider,
31 + path,
32 }: {
33 songProvider: SongProvider;
34 + path: SongProviderPath;
35 }) => {
36 const router = useRouter();
37 return (
@@ -33,7 +40,7 @@ export const SongProviderCard = ({
40 "relative overflow-hidden before:absolute before:inset-0 before:-translate-x-full before:animate-[shimmer_1.5s_infinite] before:bg-gradient-to-r before:from-transparent before:via-white/10 before:to-transparent":
41 false,
42 })}
36 - onClick={() => router.push(`/split?songProviderId=${songProvider.id}`)}
43 + onClick={() => router.push(`/${path}?songProviderId=${songProvider.id}`)}
44 >
45 <div className="space-y-3">
46 <Image
frontend/splitfire-desktop/app/login/page.tsx
+2 -1
@@ -2,7 +2,7 @@
2
3 import * as Form from "@radix-ui/react-form";
4 import { invoke } from "@tauri-apps/api/tauri";
5 -import { TAURI_ACCOUNT_LOGIN, TauriResponse } from "../_src/lib/tauriHandler";
5 +import { TAURI_ACCOUNT_LOGIN } from "../_src/lib/tauriHandler";
6 import { Button } from "../_ui/components/button";
7 import { useState } from "react";
8 import { UserContext } from "../_src/lib/CurrentUserContext";
@@ -11,6 +11,7 @@ import { AccountLoginResponse } from "@/models/account";
11 import { Mode } from "../_src/components/player/models/Mode";
12 import { LoadingView } from "../_src/components/templates/LoadingView";
13 import { useRouter } from "next/navigation";
14 +import { TauriResponse } from "@/models/shared";
15
16 enum State {
17 LOADING,
frontend/splitfire-desktop/app/page.tsx
+2 -2
@@ -1,7 +1,7 @@
1 "use client";
2
3 import { useEffect, useState } from "react";
4 -import { SkeletonCard, SongProviderCard } from "./_ui/skeleton-card";
4 +import { SkeletonCard, SongProviderCard, SongProviderPath } from "./_ui/skeleton-card";
5 import { invoke } from "@tauri-apps/api";
6 import { TAURI_CONTENT_CAROUSEL } from "./_src/lib/tauriHandler";
7 import { ContentCarouselResponse } from "@/models/content";
@@ -39,7 +39,7 @@ export default function Page() {
39 <SkeletonCard key={i} isLoading={true} />
40 ))}
41 { songProviders.length > 0 && songProviders.map((SongProvider, i) => (
42 - <SongProviderCard key={i} songProvider={SongProvider} />
42 + <SongProviderCard key={i} songProvider={SongProvider} path={SongProviderPath.SPLIT} />
43 ))}
44 </div>
45 </div>
frontend/splitfire-desktop/app/play/_components/player.tsx new
+10
@@ -0,0 +1,10 @@
1 +
2 +export default async function Page() {
3 + return (
4 + <div className="prose prose-sm prose-invert max-w-none">
5 + <div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
6 + Splitting file
7 + </div>
8 + </div>
9 + );
10 +}
frontend/splitfire-desktop/app/play/layout.tsx
+1 -15
@@ -1,25 +1,11 @@
1 -import { Metadata } from 'next';
1 import React from 'react';
2
4 -export const metadata: Metadata = {
5 - title: "Play",
6 - description: "an intelligent exression engine.",
7 -};
8 -
9 -
3 export default async function Layout({
4 children,
5 }: {
6 children: React.ReactNode;
7 }) {
8 return (
16 - <div className="space-y-9">
17 - <div className="flex justify-between">
18 - <div className="self-start">
19 - <h1 className="text-3xl font-bold">Ready to play!</h1>
20 - </div>
21 - </div>
22 - <div>{children}</div>
23 - </div>
9 + <div className="space-y-9">{children}</div>
10 );
11 }
frontend/splitfire-desktop/app/play/page.tsx
+62 -17
@@ -1,25 +1,70 @@
1 -import { SkeletonCard } from '../_ui/skeleton-card';
1 +"use client";
2
3 -export async function generateStaticParams() {
4 - const posts = [
5 - { slug: "post-1" },
6 - { slug: "post-2" },
7 - { slug: "post-3" },
8 - ];
9 -
10 - return posts.map((post) => ({
11 - slug: post.slug,
12 - }))
3 +import { invoke } from "@tauri-apps/api";
4 +import { useEffect, useState } from "react";
5 +import { TAURI_PLAYER_PREPARE } from "../_src/lib/tauriHandler";
6 +import { useSearchParams } from "next/navigation";
7 +import { useLogger } from "../_src/lib/logger";
8 +import { IconSpinner } from "../_ui/components/icons";
9 +import { PlayerPrepareResponse } from "@/models/content";
10 +import { TauriResponse } from "@/models/shared";
11 +
12 +enum State {
13 + LOADING,
14 + LOADED,
15 + ERROR,
16 }
17
18 export default function Page() {
19 + const log = useLogger("Play");
20 + const searchParams = useSearchParams();
21 + const songProviderId = searchParams.get("songProviderId");
22 + const [title, setTitle] = useState("Loading song...");
23 + const [state, setState] = useState(State.LOADING);
24 +
25 + useEffect(() => {
26 + log.debug("Play page loaded.");
27 + async function fetchData() {
28 + try {
29 + const result: PlayerPrepareResponse = await invoke(
30 + TAURI_PLAYER_PREPARE,
31 + { providerId: songProviderId }
32 + );
33 + log.debug("PreparePlayerResponse", result);
34 + switch (result.status) {
35 + case TauriResponse.SUCCESS:
36 + if (result.audio_file_name) setTitle(result.audio_file_name);
37 +
38 + setState(State.LOADED);
39 + break;
40 + case TauriResponse.ERROR:
41 + setState(State.ERROR);
42 + break;
43 + }
44 + } catch (error) {
45 + log.error(error);
46 + setState(State.ERROR);
47 + }
48 + }
49 + fetchData();
50 + }, []);
51 return (
17 - <div className="prose prose-sm prose-invert max-w-none">
18 - <div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
19 - {Array.from({ length: 6 }).map((_, i) => (
20 - <SkeletonCard key={i} />
21 - ))}
52 + <>
53 + <div className="flex justify-between">
54 + <div className="self-start">
55 + <h1 className="text-3xl font-bold">{title}</h1>
56 + </div>
57 + </div>
58 + <div className="prose prose-sm prose-invert max-w-none">
59 + <div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
60 + {state === State.LOADING && (
61 + <IconSpinner className="w-10 h-10 animate-spin" />
62 + )}
63 + {state === State.ERROR && (
64 + <div className="text-red-500">Failed to load song</div>
65 + )}
66 + </div>
67 </div>
23 - </div>
68 + </>
69 );
70 }
frontend/splitfire-desktop/app/ready-to-play/layout.tsx new
+18
@@ -0,0 +1,18 @@
1 +import React from 'react';
2 +
3 +export default async function Layout({
4 + children,
5 +}: {
6 + children: React.ReactNode;
7 +}) {
8 + return (
9 + <div className="space-y-9">
10 + <div className="flex justify-between">
11 + <div className="self-start">
12 + <h1 className="text-3xl font-bold">Ready to play!</h1>
13 + </div>
14 + </div>
15 + <div>{children}</div>
16 + </div>
17 + );
18 +}
frontend/splitfire-desktop/app/ready-to-play/page.tsx new
+42
@@ -0,0 +1,42 @@
1 +"use client";
2 +
3 +import { ContentCarouselResponse } from "@/models/content";
4 +import { invoke } from "@tauri-apps/api";
5 +import { useEffect, useState } from "react";
6 +import { TAURI_CONTENT_READY_TO_PLAY } from "../_src/lib/tauriHandler";
7 +import { SkeletonCard, SongProviderCard, SongProviderPath } from "../_ui/skeleton-card";
8 +import { SongProvider } from "../_src/models/SongResponse";
9 +
10 +export default function Page() {
11 + const [songProviders, setSongProviders] = useState<SongProvider[]>([]);
12 +
13 + useEffect(() => {
14 + console.log("Discover page loaded");
15 + async function fetchData() {
16 + try {
17 + const response = await invoke<ContentCarouselResponse>(
18 + TAURI_CONTENT_READY_TO_PLAY
19 + );
20 + console.log("Data", response);
21 + setSongProviders(response.audio_files);
22 + } catch (error) {
23 + console.error("Failed to fetch data", error);
24 + }
25 + }
26 + fetchData();
27 + }, []);
28 + return (
29 + <div className="prose prose-sm prose-invert max-w-none">
30 + <div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
31 + {songProviders.length === 0 &&
32 + Array.from({ length: 6 }).map((_, i) => (
33 + <SkeletonCard key={i} isLoading={true} />
34 + ))}
35 + {songProviders.length > 0 &&
36 + songProviders.map((SongProvider, i) => (
37 + <SongProviderCard key={i} songProvider={SongProvider} path={SongProviderPath.PLAY} />
38 + ))}
39 + </div>
40 + </div>
41 + );
42 +}
frontend/splitfire-desktop/models/account.ts
+1 -1
@@ -1,5 +1,5 @@
1 -import { TauriResponse } from "@/app/_src/lib/tauriHandler";
1 import User from "@/app/_src/models/user";
2 +import { TauriResponse } from "./shared";
3
4 export interface AccountLoginResponse {
5 status: TauriResponse,
frontend/splitfire-desktop/models/content.ts
+8 -1
@@ -1,5 +1,5 @@
1 -import { TauriResponse } from "@/app/_src/lib/tauriHandler";
1 import { SongProvider } from "@/app/_src/models/SongResponse";
2 +import { TauriResponse } from "./shared";
3
4 export interface ContentCarouselResponse {
5 status: TauriResponse,
@@ -10,4 +10,11 @@ export interface ContentCarouselResponse {
10 export interface AccountRegisterResponse {
11 status: TauriResponse,
12 message: string,
13 +}
14 +
15 +// Response from the Tauri API
16 +export interface PlayerPrepareResponse {
17 + status: TauriResponse,
18 + message: string,
19 + audio_file_name?: string,
20 }
\ No newline at end of file
frontend/splitfire-desktop/models/shared.ts new
+4
@@ -0,0 +1,4 @@
1 +export enum TauriResponse {
2 + ERROR = 0,
3 + SUCCESS = 1
4 +}
\ No newline at end of file
frontend/splitfire-desktop/src-tauri/src/rest/content.rs
+19 -7
@@ -46,7 +46,7 @@ pub async fn content_carousel() -> ContentCarouselResponse {
46 }
47
48 #[tauri::command]
49 -pub async fn content_ready_to_play() -> Result<(), String> {
49 +pub async fn content_ready_to_play() -> ContentCarouselResponse {
50 let response = Client::new()
51 .get(content_url_builder(PATH_READY_TO_PLAY))
52 .send()
@@ -55,20 +55,32 @@ pub async fn content_ready_to_play() -> Result<(), String> {
55 let response = match response {
56 Ok(response) => response,
57 Err(e) => {
58 - println!("Failed to get response: {:?}", e);
59 - return Err("Failed to get response".to_string());
58 + debug!("Failed to get response: {:?}", e);
59 + return ContentCarouselResponse {
60 + status: TauriResponse::Error,
61 + message: e.to_string(),
62 + audio_files: vec![],
63 + };
64 }
65 };
66 let res: CarouselResponse = match response.json().await {
67 Ok(json) => json,
68 Err(e) => {
65 - println!("Failed to parse response: {:?}", e);
66 - return Err("Failed to parse response".to_string());
69 + debug!("Failed to parse response: {:?}", e);
70 + return ContentCarouselResponse {
71 + status: TauriResponse::Error,
72 + message: e.to_string(),
73 + audio_files: vec![],
74 + };
75 }
76 };
77
70 - println!("Ready to play: {:?}", res);
71 - Ok(())
78 + debug!("Ready to play: {:?}", res);
79 + ContentCarouselResponse {
80 + status: TauriResponse::Success,
81 + message: res.message,
82 + audio_files: res.audio_files,
83 + }
84 }
85
86 fn content_url_builder(path: &str) -> String {