diff --git a/apps/onelauncher/frontend/src/components/content/ProviderIcon.tsx b/apps/onelauncher/frontend/src/components/content/ProviderIcon.tsx new file mode 100644 index 00000000..d040c07f --- /dev/null +++ b/apps/onelauncher/frontend/src/components/content/ProviderIcon.tsx @@ -0,0 +1,38 @@ +import type { ImgHTMLAttributes, RefAttributes } from 'react'; +import CurseforgeIcon from '@/assets/logos/curseforge.svg'; +import VanillaImage from '@/assets/logos/minecraft.png'; +import ModrinthIcon from '@/assets/logos/modrinth.svg'; +import SkyClientImage from '@/assets/logos/skyclient.png'; + +export function getProviderLogoSrc(provider: string): string { + const providerName = provider.toLowerCase(); + + const mapping: Record = { + modrinth: ModrinthIcon, + curseforge: CurseforgeIcon, + skyclient: SkyClientImage, + }; + + return mapping[providerName]; +} + +type ProviderIconProp = ImgHTMLAttributes & RefAttributes & { + provider: string | undefined; +}; + +function ProviderIcon({ + provider, + src: _src, + alt: _alt, + ...rest +}: ProviderIconProp) { + return ( + {`${provider}'s + ); +} + +export default ProviderIcon; diff --git a/apps/onelauncher/frontend/src/components/overlay/popups/NotificationPopup.tsx b/apps/onelauncher/frontend/src/components/overlay/popups/NotificationPopup.tsx index eeee83c0..35c5927b 100644 --- a/apps/onelauncher/frontend/src/components/overlay/popups/NotificationPopup.tsx +++ b/apps/onelauncher/frontend/src/components/overlay/popups/NotificationPopup.tsx @@ -1,58 +1,11 @@ -import type { NotificationData } from '@/hooks/useNotification'; import useNotifications from '@/hooks/useNotification'; -import { Button, Popup } from '@onelauncher/common/components'; -import { XIcon } from '@untitled-theme/icons-react'; -import { NotificationToast } from '../NotificationToasts'; - -interface NotificationItemProps { - id: string; - data: NotificationData; - onRemove: (id: string) => void; -} - -// eslint-disable-next-line unused-imports/no-unused-vars -- deprecated -function NotificationItem({ id, data, onRemove }: NotificationItemProps) { - return ( -
-
-
-

- {data.title} -

-

- {data.message} -

-
- -
- - {data.fraction !== undefined && ( -
-
-
- )} -
- ); -} +import { Button } from '@onelauncher/common/components'; +import { InfoCircleIcon, Trash01Icon } from '@untitled-theme/icons-react'; function NotificationPopup() { - const { list, clear, remove } = useNotifications(); + const { list, clear } = useNotifications(); const notificationEntries = Object.entries(list); - const removeNotification = (id: string) => { - remove(id); - }; - if (notificationEntries.length === 0) return (
@@ -62,27 +15,57 @@ function NotificationPopup() { return (
-
-

Notifications

+
+
+

Notifications

+
+ +
+ {notificationEntries.map(([id, data]) => ( +
+
+ +
+ +
+

+ {data.title} +

+

+ {data.message} +

+
+ +
+
+
+
+ ))} +
+ +
+ {notificationEntries.length > 0 && ( - +
+ + {/* */} +
)}
- - {notificationEntries.map(([id, data]) => ( -
-
- -
-
- ))}
); } diff --git a/apps/onelauncher/frontend/src/routes/app/browser/route.tsx b/apps/onelauncher/frontend/src/routes/app/browser/route.tsx index 4f5a3972..4cf0fd8a 100644 --- a/apps/onelauncher/frontend/src/routes/app/browser/route.tsx +++ b/apps/onelauncher/frontend/src/routes/app/browser/route.tsx @@ -1,7 +1,10 @@ +import ProviderIcon from '@/components/content/ProviderIcon'; import { PROVIDERS } from '@/utils'; +import { browserCategories } from '@/utils/browser'; import { Button, Dropdown, Show, TextField } from '@onelauncher/common/components'; import { createFileRoute, Outlet } from '@tanstack/react-router'; import { SearchMdIcon } from '@untitled-theme/icons-react'; +import { memo } from 'react'; export const Route = createFileRoute('/app/browser')({ component: RouteComponent, @@ -56,16 +59,13 @@ function BrowserSidebar() {
Provider
- {/* - {provider => ( - - - {provider} - - )} - */} {PROVIDERS.map(provider => ( - {provider} + +
+ + {provider} +
+
))}
@@ -75,6 +75,8 @@ function BrowserSidebar() { } function BrowserCategories() { + const categories = browserCategories.byPackageType('mod', 'Modrinth'); + return (
@@ -82,21 +84,14 @@ function BrowserCategories() {
Categories
- {/* - {category => ( -

toggleCategory(category.id)} - > - {category.display} -

- )} -
*/} -

- Jennie -

+ {categories.map(category => ( +

+ {category.display} +

+ ))}
diff --git a/apps/onelauncher/frontend/src/routes/app/index.tsx b/apps/onelauncher/frontend/src/routes/app/index.tsx index ebf1efc9..b92685ee 100644 --- a/apps/onelauncher/frontend/src/routes/app/index.tsx +++ b/apps/onelauncher/frontend/src/routes/app/index.tsx @@ -5,7 +5,7 @@ import { NewClusterCreate } from '@/components/launcher/cluster/ClusterCreation' import useRecentCluster from '@/hooks/useCluster'; import { bindings } from '@/main'; import { formatAsDuration, upperFirst } from '@/utils'; -import { useCommand } from '@onelauncher/common'; +import { useCommand, useCommandMut, useCommandSuspense } from '@onelauncher/common'; import { Button, Show } from '@onelauncher/common/components'; import { createFileRoute, Link } from '@tanstack/react-router'; import { convertFileSrc } from '@tauri-apps/api/core'; @@ -167,13 +167,10 @@ function ClusterCard({ icon_url, stage, }: ClusterModel) { - const launch = useCommand('launchCluster', () => bindings.core.launchCluster(id, null), { - enabled: false, - subscribed: false, - }); + const launch = useCommandMut(() => bindings.core.launchCluster(id, null)); const handleLaunch = () => { - launch.refetch(); + launch.mutate(); if (launch.error) console.error(launch.error.message); diff --git a/apps/onelauncher/frontend/src/utils/browser.ts b/apps/onelauncher/frontend/src/utils/browser.ts new file mode 100644 index 00000000..375db1d3 --- /dev/null +++ b/apps/onelauncher/frontend/src/utils/browser.ts @@ -0,0 +1,95 @@ +export const BROWSER_VIEWS: Array = ['grid', 'list'] as const; + +export interface CategoryItem { + display: string; + id: string; +} + +export const browserCategories = { + byPackageType(packageType: 'mod' | string, provider: string): Array { + switch (packageType) { + case 'mod': + return this.mod(provider); + } + + return []; + }, + + mod(provider: string = 'Modrinth'): Array { + return Object.values(modMapping[provider]); + }, +}; + +const modMapping: Record> = { + Curseforge: [ + { display: 'Adventure', id: '422' }, + { display: 'API and Library', id: '421' }, + { display: 'Armor and Tools', id: '434' }, + { display: 'Biomes', id: '407' }, + { display: 'Bug Fixes', id: '6821' }, + { display: 'Cosmetic', id: '424' }, + { display: 'Dimensions', id: '410' }, + { display: 'Education', id: '5299' }, + { display: 'Energy', id: '417' }, + { display: 'Farming', id: '416' }, + { display: 'Food', id: '436' }, + { display: 'Forestry', id: '433' }, + { display: 'Magic', id: '419' }, + { display: 'Miscellaneous', id: '425' }, + { display: 'Mobs', id: '411' }, + { display: 'Ores and Resources', id: '408' }, + { display: 'Performance', id: '6814' }, + { display: 'Player Transport', id: '414' }, + { display: 'Redstone', id: '4558' }, + { display: 'Storage', id: '420' }, + { display: 'Structures', id: '409' }, + { display: 'Technology', id: '412' }, + { display: 'Utility & QoL', id: '5191' }, + { display: 'World Generation', id: '406' }, + + // { display: 'Map and Information', id: '423' }, + // { display: 'Server Utility', id: '435' }, + // { display: 'Twitch Integration', id: '4671' }, + // { display: 'Create', id: '6484' }, + // { display: 'Buildcraft', id: '432' }, + // { display: 'Industrial Craft', id: '429' }, + // { display: 'KubeJS', id: '5314' }, + // { display: 'Genetics', id: '418' }, + // { display: 'Blood Magic', id: '4485' }, + // { display: 'Automation', id: '4843' }, + // { display: 'CraftTweaker', id: '4773' }, + // { display: 'Addons', id: '426' }, + // { display: 'Energy, Fluid, and Item Transport', id: '415' }, + // { display: 'Thermal Expansion', id: '427' }, + // { display: 'Thaumcraft', id: '430' }, + // { display: 'Processing', id: '413' }, + // { display: 'Tinker\'s Construct', id: '428' }, + // { display: 'Skyblock', id: '6145' }, + // { display: 'Galacticraft', id: '5232' }, + // { display: 'Applied Energistics 2', id: '4545' }, + // { display: 'Integrated Dynamics', id: '6954' }, + // { display: 'MCreator', id: '4906' }, + ], + Modrinth: [ + { display: 'Adventure', id: 'adventure' }, + { display: 'Cursed', id: 'cursed' }, + { display: 'Decoration', id: 'decoration' }, + { display: 'Economy', id: 'economy' }, + { display: 'Equipment', id: 'equipment' }, + { display: 'Food', id: 'food' }, + { display: 'Game Mechanics', id: 'game-mechanics' }, + { display: 'Library', id: 'library' }, + { display: 'Magic', id: 'magic' }, + { display: 'Management', id: 'management' }, + { display: 'Minigame', id: 'minigame' }, + { display: 'Mobs', id: 'mobs' }, + { display: 'Optimization', id: 'optimization' }, + { display: 'Social', id: 'social' }, + { display: 'Storage', id: 'storage' }, + { display: 'Technology', id: 'technology' }, + { display: 'Transportation', id: 'transportation' }, + { display: 'Utility', id: 'utility' }, + { display: 'World Generation', id: 'worldgen' }, + ], + SkyClient: [], +}; diff --git a/packages/core/src/api/tauri/commands.rs b/packages/core/src/api/tauri/commands.rs index 6ceb336b..d7149bf5 100644 --- a/packages/core/src/api/tauri/commands.rs +++ b/packages/core/src/api/tauri/commands.rs @@ -1,8 +1,8 @@ use interpulse::api::minecraft::Version; -use onelauncher_entity::{clusters, icon::Icon, loader::GameLoader, prelude::entity, resolution::Resolution}; +use onelauncher_entity::{clusters, icon::Icon, loader::GameLoader, package::Provider, prelude::entity, resolution::Resolution}; use sea_orm::ActiveValue::Set; use serde::Serialize; -use crate::store::{Settings, State}; +use crate::{api::packages::data::{ManagedPackage, ManagedUser, ManagedVersion, SearchQuery}, store::{Settings, State}, utils::pagination::Paginated}; use tauri::{AppHandle, Runtime}; use crate::{api::{self, cluster::dao::ClusterId}, error::{LauncherError, LauncherResult}, store::{credentials::MinecraftCredentials, Core}}; @@ -81,6 +81,22 @@ pub trait TauriLauncherApi { #[taurpc(alias = "writeSettings")] async fn write_settings(setting: Settings) -> LauncherResult<()>; + + // Packages + #[taurpc(alias = "searchPackages")] + async fn search_packages(provider: Provider, query: SearchQuery) -> LauncherResult>; + + #[taurpc(alias = "getPackage")] + async fn get_package(provider: Provider, slug: String) -> LauncherResult; + + #[taurpc(alias = "getMultiplePackages")] + async fn get_multiple_packages(provider: Provider, slugs: Vec) -> LauncherResult>; + + #[taurpc(alias = "getPackageVersions")] + async fn get_package_versions(provider: Provider, slug: String, mc_versions: Option>, loaders: Option>, offset: usize, limit: usize) -> LauncherResult>; + + #[taurpc(alias = "getPackageUser")] + async fn get_package_user(provider: Provider, slug: String) -> LauncherResult; } @@ -369,4 +385,6 @@ impl TauriLauncherApi for TauriLauncherApiImpl { Ok(()) } + + // Packages } diff --git a/packages/web_common/src/components/Popup.tsx b/packages/web_common/src/components/Popup.tsx index d5345ffe..0523731c 100644 --- a/packages/web_common/src/components/Popup.tsx +++ b/packages/web_common/src/components/Popup.tsx @@ -7,7 +7,7 @@ interface MyPopoverProps extends Omit { export function Popup({ children, ...props }: MyPopoverProps) { return ( - + {children} diff --git a/packages/web_common/src/theme.css b/packages/web_common/src/theme.css index 6d23eac1..f5fd21f3 100644 --- a/packages/web_common/src/theme.css +++ b/packages/web_common/src/theme.css @@ -118,5 +118,5 @@ h5 { @apply text-[0.875rem] text-fg-secondary font-bold uppercase; } h6 { @apply text-[0.75rem] text-fg-secondary font-medium uppercase; } code { - @apply rounded-md bg-component-bg p-2 text-left; + @apply rounded-md p-2 text-left; } \ No newline at end of file