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,