split page

Seto Elkahfi committed Aug 1, 2024 at 18:52 UTC 7a970032573fc069e43f2d1d5d8425f099b82871
13 files changed +127 -114
backend/musik88-web/app/controllers/api/v1/song_bridge_controller.rb
+1 -1
@@ -71,7 +71,7 @@ module Api
71 end
72
73 def carousel
74 - @songs = SongProvider.where(provider_type: :youtube).order(Arel.sql('random()')).limit(5)
74 + @songs = SongProvider.where(provider_type: :youtube).order(Arel.sql('random()')).limit(15)
75 render json: {
76 code: 200,
77 message: 'OK',
frontend/splitfire-desktop/.vscode/launch.json deleted
-32
@@ -1,32 +0,0 @@
1 -{
2 - // Use IntelliSense to learn about possible attributes.
3 - // Hover to view descriptions of existing attributes.
4 - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 - "version": "0.2.0",
6 - "configurations": [
7 - {
8 - "type": "lldb",
9 - "request": "launch",
10 - "name": "Tauri Development Debug",
11 - "cargo": {
12 - "args": [
13 - "build",
14 - "--manifest-path=./src-tauri/Cargo.toml",
15 - "--no-default-features"
16 - ]
17 - },
18 - // task for the `beforeDevCommand` if used, must be configured in `.vscode/tasks.json`
19 - "preLaunchTask": "ui:dev"
20 - },
21 - {
22 - "type": "lldb",
23 - "request": "launch",
24 - "name": "Tauri Production Debug",
25 - "cargo": {
26 - "args": ["build", "--release", "--manifest-path=./src-tauri/Cargo.toml"]
27 - },
28 - // task for the `beforeBuildCommand` if used, must be configured in `.vscode/tasks.json`
29 - "preLaunchTask": "ui:build"
30 - }
31 - ]
32 -}
\ No newline at end of file
frontend/splitfire-desktop/.vscode/tasks.json deleted
-33
@@ -1,33 +0,0 @@
1 -{
2 - // See https://go.microsoft.com/fwlink/?LinkId=733558
3 - // for the documentation about the tasks.json format
4 - "version": "2.0.0",
5 - "tasks": [
6 - {
7 - "label": "nvm:use",
8 - "type": "shell",
9 - "command": "nvm",
10 - "args": ["use"]
11 - },
12 - {
13 - "label": "ui:dev",
14 - "type": "shell",
15 - // `dev` keeps running in the background
16 - // ideally you should also configure a `problemMatcher`
17 - // see https://code.visualstudio.com/docs/editor/tasks#_can-a-background-task-be-used-as-a-prelaunchtask-in-launchjson
18 - "isBackground": true,
19 - // change this to your `beforeDevCommand`:
20 - "command": "bun",
21 - "args": ["start"],
22 - "dependsOn": "nvm:use"
23 - },
24 - {
25 - "label": "ui:build",
26 - "type": "shell",
27 - // change this to your `beforeBuildCommand`:
28 - "command": "bun",
29 - "args": ["build"],
30 - "dependsOn": "nvm:use"
31 - }
32 - ]
33 -}
\ No newline at end of file
frontend/splitfire-desktop/app/_src/lib/tauriHandler.ts
+3
@@ -17,6 +17,9 @@ export const TAURI_ACCOUNT_LOGIN = 'account_login'
17 export const TAURI_ACCOUNT_LOGOUT = 'account_logout'
18 export const TAURI_ACCOUNT_REGISTER = 'account_register'
19
20 +// Contents
21 +export const TAURI_CONTENT_CAROUSEL = 'content_carousel'
22 +
23 export enum TauriResponse {
24 ERROR = 0,
25 SUCCESS = 1
frontend/splitfire-desktop/app/_src/models/SongResponse.tsx
+1 -18
@@ -8,24 +8,7 @@ export interface SongResponse {
8 images: ProviderImage[],
9 items: SongProvider[]
10 }
11 -/*
12 -{
13 - "id": 1571,
14 - "user_id": 1,
15 - "song_id": 641,
16 - "provider_id": "EYACy7jELw4",
17 - "provider_type": "youtube",
18 - "name": "Arjuna",
19 - "preview_url": null,
20 - "created_at": "2022-11-23T12:56:56.623Z",
21 - "updated_at": "2022-11-23T12:56:56.623Z",
22 - "album_provider_id": 1,
23 - "artist_provider_id": null,
24 - "path": "/songs-bridge/arjuna-1571",
25 - "type": "Song bridge",
26 - "created": "6 months ago"
27 -}
28 -*/
11 +
12 export interface SongProvider {
13 id: string,
14 name: string,
frontend/splitfire-desktop/app/_ui/address-bar.tsx
+3 -1
@@ -3,10 +3,12 @@
3 import React from 'react';
4 import { usePathname } from 'next/navigation';
5 import { HomeIcon } from '@heroicons/react/solid';
6 +import { useLogger } from '../_src/lib/logger';
7
8 export function AddressBar() {
9 + const log = useLogger('AddressBar');
10 const pathname = usePathname();
9 - console.log('pathname', pathname);
11 + log.debug("pathname", pathname);
12 return (
13 <div className="flex items-center gap-x-2 p-3.5 lg:px-5 lg:py-3">
14 <div className="text-gray-600">
frontend/splitfire-desktop/app/_ui/skeleton-card.tsx
+38 -3
@@ -1,9 +1,14 @@
1 -import clsx from 'clsx';
1 +"use client";
2 +
3 +import clsx from "clsx";
4 +import { SongProvider } from "../_src/models/SongResponse";
5 +import Image from "next/image";
6 +import { useRouter } from "next/navigation";
7
8 export const SkeletonCard = ({ isLoading }: { isLoading?: boolean }) => (
9 <div
5 - className={clsx('rounded-2xl bg-gray-900/80 p-4', {
6 - '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':
10 + className={clsx("rounded-2xl bg-gray-900/80 p-4", {
11 + "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":
12 isLoading,
13 })}
14 >
@@ -14,3 +19,33 @@ export const SkeletonCard = ({ isLoading }: { isLoading?: boolean }) => (
19 </div>
20 </div>
21 );
22 +
23 +// duplicate skeleton-card.tsx
24 +export const SongProviderCard = ({
25 + songProvider,
26 +}: {
27 + songProvider: SongProvider;
28 +}) => {
29 + const router = useRouter();
30 + return (
31 + <button
32 + className={clsx("rounded-2xl bg-gray-900/80 p-4", {
33 + "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":
34 + false,
35 + })}
36 + onClick={() => router.push(`/split?songProviderId=${songProvider.id}`)}
37 + >
38 + <div className="space-y-3">
39 + <Image
40 + src={songProvider.image_url}
41 + alt={songProvider.name}
42 + width="0"
43 + height="0"
44 + sizes="100vw"
45 + className="w-full h-auto aspect-video"
46 + />
47 + <h3 className="w-11/12">{songProvider.name}</h3>
48 + </div>
49 + </button>
50 + );
51 +};
frontend/splitfire-desktop/app/login/layout.tsx
-7
@@ -1,12 +1,5 @@
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 }: {
frontend/splitfire-desktop/app/page.tsx
+29 -14
@@ -1,18 +1,30 @@
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 - }))
13 -}
3 +import { useEffect, useState } from "react";
4 +import { SkeletonCard, SongProviderCard } 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";
8 +import { SongProvider } from "./_src/models/SongResponse";
9
10 export default function Page() {
11 +
12 + const [songProviders, setSongProviders] = useState<SongProvider[]>([]);
13 +
14 + useEffect(() => {
15 + console.log("Discover page loaded");
16 + async function fetchData() {
17 + try {
18 + const response = await invoke<ContentCarouselResponse>(TAURI_CONTENT_CAROUSEL);
19 + console.log("Data", response);
20 + setSongProviders(response.audio_files);
21 + } catch (error) {
22 + console.error("Failed to fetch data", error);
23 + }
24 + }
25 + fetchData();
26 + }, []);
27 +
28 return (
29 <div className="space-y-9">
30 <div className="flex justify-between">
@@ -23,8 +35,11 @@ export default function Page() {
35 <div>
36 <div className="prose prose-sm prose-invert max-w-none">
37 <div className="grid grid-cols-3 gap-6">
26 - {Array.from({ length: 20 }).map((_, i) => (
27 - <SkeletonCard key={i} />
38 + { songProviders.length === 0 && Array.from({ length: 6 }).map((_, i) => (
39 + <SkeletonCard key={i} isLoading={true} />
40 + ))}
41 + { songProviders.length > 0 && songProviders.map((SongProvider, i) => (
42 + <SongProviderCard key={i} songProvider={SongProvider} />
43 ))}
44 </div>
45 </div>
frontend/splitfire-desktop/app/split/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">Split</h1>
13 + </div>
14 + </div>
15 + <div>{children}</div>
16 + </div>
17 + );
18 +}
frontend/splitfire-desktop/app/split/page.tsx new
+15
@@ -0,0 +1,15 @@
1 +"use client";
2 +
3 +import { useSearchParams } from "next/navigation";
4 +
5 +export default function Page() {
6 + const searchParams = useSearchParams();
7 + const songProviderId = searchParams.get("songProviderId");
8 + return (
9 + <div className="prose prose-sm prose-invert max-w-none">
10 + <div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
11 + Splitting file {songProviderId}
12 + </div>
13 + </div>
14 + );
15 +}
frontend/splitfire-desktop/models/content.ts new
+13
@@ -0,0 +1,13 @@
1 +import { TauriResponse } from "@/app/_src/lib/tauriHandler";
2 +import { SongProvider } from "@/app/_src/models/SongResponse";
3 +
4 +export interface ContentCarouselResponse {
5 + status: TauriResponse,
6 + message: string,
7 + audio_files: SongProvider[], // This is missleading, it should be song_providers
8 +}
9 +
10 +export interface AccountRegisterResponse {
11 + status: TauriResponse,
12 + message: string,
13 +}
\ No newline at end of file
frontend/splitfire-desktop/src-tauri/src/rest/content.rs
+6 -5
@@ -5,6 +5,7 @@ use crate::{
5 player::TauriResponse,
6 },
7 };
8 +use log::{debug, error};
9 use reqwest::Client;
10
11 #[tauri::command]
@@ -17,10 +18,10 @@ pub async fn content_carousel() -> ContentCarouselResponse {
18 let response = match response {
19 Ok(response) => response,
20 Err(e) => {
20 - println!("Failed to get response: {:?}", e);
21 + error!("Failed to get response: {:?}", e);
22 return ContentCarouselResponse {
23 status: TauriResponse::Error,
23 - message: "Failed to get response".to_string(),
24 + message: e.to_string(),
25 audio_files: vec![],
26 };
27 }
@@ -28,15 +29,15 @@ pub async fn content_carousel() -> ContentCarouselResponse {
29 let res: CarouselResponse = match response.json().await {
30 Ok(json) => json,
31 Err(e) => {
31 - println!("Failed to parse response: {:?}", e);
32 + error!("Failed to parse response: {:?}", e);
33 return ContentCarouselResponse {
34 status: TauriResponse::Error,
34 - message: "Failed to parse response".to_string(),
35 + message: e.to_string(),
36 audio_files: vec![],
37 };
38 }
39 };
39 -
40 + debug!("Carousel response: {:?}", res);
41 ContentCarouselResponse {
42 status: TauriResponse::Success,
43 message: res.message,