From 7ef4c4bbb6528bd559c2e6ea3016143fab40a0ce Mon Sep 17 00:00:00 2001 From: LynithDev <61880709+LynithDev@users.noreply.github.com> Date: Wed, 11 Sep 2024 21:01:42 +0200 Subject: [PATCH] feat: Start work on Onboarding experience --- apps/frontend/src/RootLayout.tsx | 46 ++++++++ .../illustrations/onboarding/language.svg | 38 +++++++ apps/frontend/src/index.tsx | 26 +++-- apps/frontend/src/ui/App.tsx | 65 ------------ apps/frontend/src/ui/pages/App.tsx | 31 ++++++ apps/frontend/src/ui/pages/Home.tsx | 14 +-- .../src/ui/pages/browser/BrowserRoot.tsx | 18 ++++ .../src/ui/pages/onboarding/Onboarding.tsx | 100 ++++++++++++++++++ .../pages/onboarding/OnboardingLanguage.tsx | 16 +++ .../ui/pages/onboarding/OnboardingWelcome.tsx | 13 +++ apps/frontend/src/utils/index.ts | 1 + packages/core/src/api/package/content/mod.rs | 34 +++--- .../core/src/api/package/content/modrinth.rs | 1 + packages/core/src/store/package.rs | 4 +- 14 files changed, 310 insertions(+), 97 deletions(-) create mode 100644 apps/frontend/src/RootLayout.tsx create mode 100644 apps/frontend/src/assets/illustrations/onboarding/language.svg delete mode 100644 apps/frontend/src/ui/App.tsx create mode 100644 apps/frontend/src/ui/pages/App.tsx create mode 100644 apps/frontend/src/ui/pages/onboarding/Onboarding.tsx create mode 100644 apps/frontend/src/ui/pages/onboarding/OnboardingLanguage.tsx create mode 100644 apps/frontend/src/ui/pages/onboarding/OnboardingWelcome.tsx diff --git a/apps/frontend/src/RootLayout.tsx b/apps/frontend/src/RootLayout.tsx new file mode 100644 index 00000000..ed95d709 --- /dev/null +++ b/apps/frontend/src/RootLayout.tsx @@ -0,0 +1,46 @@ +import { PROGRAM_INFO } from '@onelauncher/client/bindings'; +import type { ParentProps } from 'solid-js'; +import { MultiProvider } from './ui/components/MultiProvider'; +import { AccountControllerProvider } from './ui/components/overlay/account/AddAccountModal'; +import { ClusterModalControllerProvider } from './ui/components/overlay/cluster/ClusterCreationModal'; +import { ModalProvider, ModalRenderer } from './ui/components/overlay/Modal'; +import NotificationOverlay from './ui/components/overlay/notifications/NotificationOverlay'; +import WindowFrame from './ui/components/WindowFrame'; +import { BrowserProvider } from './ui/hooks/useBrowser'; +import { SettingsProvider } from './ui/hooks/useSettings'; + +function RootLayout(props: ParentProps) { + if (PROGRAM_INFO.dev_build !== true) + document.addEventListener('contextmenu', e => e.preventDefault()); + + return ( + +
+ + + {props.children} + + + +
+
+ ); +} + +export default RootLayout; + +function GlobalContexts(props: ParentProps) { + return ( + + {props.children} + + ); +} diff --git a/apps/frontend/src/assets/illustrations/onboarding/language.svg b/apps/frontend/src/assets/illustrations/onboarding/language.svg new file mode 100644 index 00000000..bc60266c --- /dev/null +++ b/apps/frontend/src/assets/illustrations/onboarding/language.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/frontend/src/index.tsx b/apps/frontend/src/index.tsx index 47f29028..ce656756 100644 --- a/apps/frontend/src/index.tsx +++ b/apps/frontend/src/index.tsx @@ -1,24 +1,34 @@ /* @refresh reload */ import { Route, Router } from '@solidjs/router'; +import App from '~ui/pages/App'; import BrowserRoot from '~ui/pages/browser/BrowserRoot'; -import ClusterRoot from '~ui/pages/cluster/ClusterRoot'; +import ClusterRoot from '~ui/pages/cluster/ClusterRoot'; +import Onboarding from '~ui/pages/onboarding/Onboarding'; import SettingsRoot from '~ui/pages/settings/SettingsRoot'; import { render } from 'solid-js/web'; import { bridge } from './imports'; -import App from './ui/App'; +import RootLayout from './RootLayout'; import HomePage from './ui/pages/Home'; import UpdatesPage from './ui/pages/Updates'; document.body.setAttribute('data-platform', bridge.PROGRAM_INFO.platform); render(() => ( - - - - } component={ClusterRoot} path="/clusters" /> - } component={SettingsRoot} path="/settings" /> - } component={BrowserRoot} path="/browser" /> + + + + + } component={ClusterRoot} path="/clusters" /> + } component={SettingsRoot} path="/settings" /> + } component={BrowserRoot} path="/browser" /> + + + ), document.getElementById('root') as HTMLElement); diff --git a/apps/frontend/src/ui/App.tsx b/apps/frontend/src/ui/App.tsx deleted file mode 100644 index 4205bef4..00000000 --- a/apps/frontend/src/ui/App.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { PROGRAM_INFO } from '@onelauncher/client/bindings'; -import { OverlayScrollbarsComponent } from 'overlayscrollbars-solid'; -import type { ParentProps } from 'solid-js'; -import AnimatedRoutes from './components/AnimatedRoutes'; -import ErrorBoundary from './components/ErrorBoundary'; -import { MultiProvider } from './components/MultiProvider'; -import Navbar from './components/Navbar'; -import { AccountControllerProvider } from './components/overlay/account/AddAccountModal'; -import { ClusterModalControllerProvider } from './components/overlay/cluster/ClusterCreationModal'; -import { ModalProvider, ModalRenderer } from './components/overlay/Modal'; -import NotificationOverlay from './components/overlay/notifications/NotificationOverlay'; -import WindowFrame from './components/WindowFrame'; -import { BrowserProvider } from './hooks/useBrowser'; -import { SettingsProvider } from './hooks/useSettings'; - -function App(props: ParentProps) { - if (PROGRAM_INFO.dev_build !== true) - document.addEventListener('contextmenu', e => e.preventDefault()); - - return ( - -
- -
- -
- -
-
- - -
- - {props.children} - -
-
-
-
-
- - - -
-
- ); -} - -export default App; - -function GlobalContexts(props: ParentProps) { - return ( - - {props.children} - - ); -} diff --git a/apps/frontend/src/ui/pages/App.tsx b/apps/frontend/src/ui/pages/App.tsx new file mode 100644 index 00000000..e09f9b44 --- /dev/null +++ b/apps/frontend/src/ui/pages/App.tsx @@ -0,0 +1,31 @@ +import { OverlayScrollbarsComponent } from 'overlayscrollbars-solid'; +import type { ParentProps } from 'solid-js'; +import AnimatedRoutes from '../components/AnimatedRoutes'; +import ErrorBoundary from '../components/ErrorBoundary'; +import Navbar from '../components/Navbar'; + +function App(props: ParentProps) { + return ( + <> +
+ +
+ +
+
+ + +
+ + {props.children} + +
+
+
+
+
+ + ); +} + +export default App; diff --git a/apps/frontend/src/ui/pages/Home.tsx b/apps/frontend/src/ui/pages/Home.tsx index de228faf..96634ac2 100644 --- a/apps/frontend/src/ui/pages/Home.tsx +++ b/apps/frontend/src/ui/pages/Home.tsx @@ -6,19 +6,19 @@ import { RefreshCw01Icon, SearchMdIcon, } from '@untitled-theme/icons-solid'; -import { For, Show, onMount } from 'solid-js'; -import type { Cluster } from '@onelauncher/client/bindings'; -import BannerBackground from '../../assets/images/header.png'; -import Button from '../components/base/Button'; -import TextField from '../components/base/TextField'; -import ClusterRoot from './cluster/ClusterRoot'; import { bridge } from '~imports'; - import ClusterCover from '~ui/components/game/ClusterCover'; import { useClusterCreator } from '~ui/components/overlay/cluster/ClusterCreationModal'; import { useLaunchCluster, useRecentCluster } from '~ui/hooks/useCluster'; import useCommand from '~ui/hooks/useCommand'; import { formatAsDuration, upperFirst } from '~utils'; +import { For, onMount, Show } from 'solid-js'; + +import type { Cluster } from '@onelauncher/client/bindings'; +import BannerBackground from '../../assets/images/header.png'; +import Button from '../components/base/Button'; +import TextField from '../components/base/TextField'; +import ClusterRoot from './cluster/ClusterRoot'; type GroupedClusters = Record; diff --git a/apps/frontend/src/ui/pages/browser/BrowserRoot.tsx b/apps/frontend/src/ui/pages/browser/BrowserRoot.tsx index d298c7c5..0af47c7d 100644 --- a/apps/frontend/src/ui/pages/browser/BrowserRoot.tsx +++ b/apps/frontend/src/ui/pages/browser/BrowserRoot.tsx @@ -141,6 +141,24 @@ function BrowserSidebar() { + {/*
+
Package Type
+ controller.setSearchQuery(prev => ({ + ...prev, + package_types: [PACKAGE_TYPES[index] || 'mod'], + }))} + selected={() => PACKAGE_TYPES.indexOf(controller.searchQuery().package_types?.[0] || 'mod')} + > + + {provider => ( + + {provider} + + )} + + +
*/} ); diff --git a/apps/frontend/src/ui/pages/onboarding/Onboarding.tsx b/apps/frontend/src/ui/pages/onboarding/Onboarding.tsx new file mode 100644 index 00000000..70e7e440 --- /dev/null +++ b/apps/frontend/src/ui/pages/onboarding/Onboarding.tsx @@ -0,0 +1,100 @@ +import { Route, useLocation, useNavigate } from '@solidjs/router'; +import { ChevronLeftIcon, ChevronRightIcon } from '@untitled-theme/icons-solid'; +import AnimatedRoutes from '~ui/components/AnimatedRoutes'; +import Button from '~ui/components/base/Button'; +import PolyfrostFull from '~ui/components/logos/PolyfrostFull'; +import { type JSX, type ParentProps } from 'solid-js'; +import OnboardingLanguage from './OnboardingLanguage'; +import OnboardingWelcome from './OnboardingWelcome'; + +const basePath = '/onboarding'; +const OnboardingSteps = [ + ['/', OnboardingWelcome], + ['/language', OnboardingLanguage], +] as const; + +function Onboarding(props: ParentProps) { + const navigate = useNavigate(); + const location = useLocation(); + + const step = () => OnboardingSteps.findIndex(([path]) => location.pathname === basePath + path); + + const canGoBack = () => step() > 0; + const canGoForward = () => step() < OnboardingSteps.length - 1; + + const next = () => { + if (canGoForward()) + navigate(basePath + OnboardingSteps[step() + 1]![0]); + }; + + const previous = () => { + if (canGoBack()) + navigate(basePath + OnboardingSteps[step() - 1]![0]); + }; + + return ( +
+
+ +
+ +
+ + {props.children} + +
+ +
+
+
+
+
+ ); +} + +Onboarding.Steps = OnboardingSteps; +Onboarding.Routes = OnboardingSteps.map(([path, component]) => ( + +)); + +export default Onboarding; + +export interface OnboardingStepProps extends ParentProps { + title: string; + paragraph: string; + illustration: JSX.Element; +} + +export function OnboardingStep(props: OnboardingStepProps) { + return ( +
+
+ {props.illustration} +
+ +
+
+

{props.title}

+

{props.paragraph}

+
+ +
+ {props.children} +
+
+
+ ); +} diff --git a/apps/frontend/src/ui/pages/onboarding/OnboardingLanguage.tsx b/apps/frontend/src/ui/pages/onboarding/OnboardingLanguage.tsx new file mode 100644 index 00000000..e85622ac --- /dev/null +++ b/apps/frontend/src/ui/pages/onboarding/OnboardingLanguage.tsx @@ -0,0 +1,16 @@ +import Illustration from '~assets/illustrations/onboarding/language.svg?component-solid'; +import { OnboardingStep } from './Onboarding'; + +function OnboardingLanguage() { + return ( + } + paragraph="Choose your preferred language." + title="Language" + > +

lol

+
+ ); +} + +export default OnboardingLanguage; diff --git a/apps/frontend/src/ui/pages/onboarding/OnboardingWelcome.tsx b/apps/frontend/src/ui/pages/onboarding/OnboardingWelcome.tsx new file mode 100644 index 00000000..c7a69a4f --- /dev/null +++ b/apps/frontend/src/ui/pages/onboarding/OnboardingWelcome.tsx @@ -0,0 +1,13 @@ +import { OnboardingStep } from './Onboarding'; + +function OnboardingWelcome() { + return ( + todo :(} + paragraph="OneLauncher is a powerful and easy-to-use launcher for your applications." + title="Welcome to OneLauncher!" + /> + ); +} + +export default OnboardingWelcome; diff --git a/apps/frontend/src/utils/index.ts b/apps/frontend/src/utils/index.ts index 4fa59fa7..e710f2d6 100644 --- a/apps/frontend/src/utils/index.ts +++ b/apps/frontend/src/utils/index.ts @@ -152,3 +152,4 @@ export function getPackageUrl(provider: Providers, id: string, package_type: Pac export const LOADERS: Loader[] = ['vanilla', 'fabric', 'forge', 'neoforge', 'quilt'] as const; export const PROVIDERS: Providers[] = ['Modrinth'] as const; +export const PACKAGE_TYPES: PackageType[] = ['mod', 'resourcepack', 'datapack', 'shaderpack'] as const; diff --git a/packages/core/src/api/package/content/mod.rs b/packages/core/src/api/package/content/mod.rs index 3f4994e3..155c4e92 100644 --- a/packages/core/src/api/package/content/mod.rs +++ b/packages/core/src/api/package/content/mod.rs @@ -57,12 +57,6 @@ impl Providers { limit, offset, Some(|mut builder: FacetBuilder| { - if let Some(game_versions) = game_versions { - for version in game_versions { - builder.and(Facet("versions".to_string(), FacetOperation::Eq, version)); - } - } - if let Some(categories) = categories { for category in categories { builder.and(Facet( @@ -73,7 +67,25 @@ impl Providers { } } + if let Some(game_versions) = game_versions { + for version in game_versions { + builder.and(Facet("versions".to_string(), FacetOperation::Eq, version)); + } + } + if let Some(package_types) = package_types { + if package_types.contains(&PackageType::Mod) || package_types.contains(&PackageType::ModPack) { + if let Some(loaders) = loaders { + for loader in loaders { + builder.and(Facet( + "categories".to_string(), + FacetOperation::Eq, + loader.to_string(), + )); + } + } + } + for package_type in package_types { builder.and(Facet( "project_types".to_string(), @@ -83,16 +95,6 @@ impl Providers { } } - if let Some(loaders) = loaders { - for loader in loaders { - builder.or(Facet( - "categories".to_string(), - FacetOperation::Eq, - loader.to_string(), - )); - } - } - if let Some(open_source) = open_source { builder.and(Facet( "open_source".to_string(), diff --git a/packages/core/src/api/package/content/modrinth.rs b/packages/core/src/api/package/content/modrinth.rs index 2d9e70fb..6eef038a 100644 --- a/packages/core/src/api/package/content/modrinth.rs +++ b/packages/core/src/api/package/content/modrinth.rs @@ -236,6 +236,7 @@ impl FacetBuilder { self } + #[allow(dead_code)] pub fn or(&mut self, facet: Facet) -> &Self { let mut last_facet = self.facets.pop().unwrap_or_default(); last_facet.push(facet); diff --git a/packages/core/src/store/package.rs b/packages/core/src/store/package.rs index 40b202f5..a2a718d0 100644 --- a/packages/core/src/store/package.rs +++ b/packages/core/src/store/package.rs @@ -27,6 +27,7 @@ pub enum PackageType { /// Represents a resourcepack file ResourcePack, /// Represents a shaderpack file + #[serde(alias = "shader")] ShaderPack, /// Represents a modpack file ModPack, @@ -71,7 +72,7 @@ impl PackageType { PackageType::Mod => "mod", PackageType::DataPack => "datapack", PackageType::ResourcePack => "resourcepack", - PackageType::ShaderPack => "shaderpack", + PackageType::ShaderPack => "shader", PackageType::ModPack => "modpack", } } @@ -118,6 +119,7 @@ impl PackageType { PackageType::DataPack, PackageType::ResourcePack, PackageType::ShaderPack, + PackageType::ModPack, ] .iter() .copied()