mirror of
https://github.com/zoriya/Kyoo.git
synced 2026-02-14 15:32:13 -05:00
Update settings page
This commit is contained in:
parent
2e1699c75a
commit
e1d1eb3bef
@ -5,7 +5,7 @@ import { supportedLanguages } from "./src/providers/translations.compile.ts";
|
||||
const IS_DEV = process.env.APP_VARIANT === "development";
|
||||
|
||||
export const expo: ExpoConfig = {
|
||||
name: IS_DEV ? "Kyoo Development" : "Kyoo",
|
||||
name: IS_DEV ? "Kyoo Dev" : "Kyoo",
|
||||
slug: "kyoo",
|
||||
scheme: "kyoo",
|
||||
version: "1.0.0",
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 22 KiB |
@ -1,13 +1,14 @@
|
||||
import { getFocusedRouteNameFromRoute } from "@react-navigation/native";
|
||||
import { Stack } from "expo-router";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { useCSSVariable } from "uniwind";
|
||||
import { useCSSVariable, useResolveClassNames } from "uniwind";
|
||||
import { ErrorConsumer } from "~/providers/error-consumer";
|
||||
import { NavbarRight, NavbarTitle } from "~/ui/navbar";
|
||||
|
||||
export default function Layout() {
|
||||
const insets = useSafeAreaInsets();
|
||||
const accent = useCSSVariable("--color-accent");
|
||||
const { color } = useResolveClassNames("text-slate-200");
|
||||
|
||||
return (
|
||||
<ErrorConsumer scope="app">
|
||||
@ -22,6 +23,7 @@ export default function Layout() {
|
||||
headerStyle: {
|
||||
backgroundColor: accent as string,
|
||||
},
|
||||
headerTintColor: color as string,
|
||||
}}
|
||||
>
|
||||
<Stack.Screen
|
||||
|
||||
@ -1,86 +1,66 @@
|
||||
import type { ComponentProps, ComponentType, Ref } from "react";
|
||||
import {
|
||||
type ComponentType,
|
||||
type ForwardedRef,
|
||||
forwardRef,
|
||||
type ReactElement,
|
||||
} from "react";
|
||||
import { type Falsy, type PressableProps, View } from "react-native";
|
||||
import { type Theme, useYoshiki } from "yoshiki/native";
|
||||
type Falsy,
|
||||
type Pressable,
|
||||
type PressableProps,
|
||||
View,
|
||||
} from "react-native";
|
||||
import { cn } from "~/utils";
|
||||
import { Icon } from "./icons";
|
||||
import { PressableFeedback } from "./links";
|
||||
import { P } from "./text";
|
||||
import { ts } from "./utils";
|
||||
|
||||
export const Button = forwardRef(function Button<AsProps = PressableProps>(
|
||||
{
|
||||
children,
|
||||
text,
|
||||
icon,
|
||||
licon,
|
||||
disabled,
|
||||
as,
|
||||
...props
|
||||
}: {
|
||||
children?: ReactElement | ReactElement[] | Falsy;
|
||||
text?: string;
|
||||
licon?: ReactElement | Falsy;
|
||||
icon?: ReactElement | Falsy;
|
||||
disabled?: boolean;
|
||||
as?: ComponentType<AsProps>;
|
||||
} & AsProps,
|
||||
ref: ForwardedRef<unknown>,
|
||||
) {
|
||||
const { css } = useYoshiki("button");
|
||||
|
||||
export const Button = <AsProps = PressableProps>({
|
||||
text,
|
||||
icon,
|
||||
ricon,
|
||||
disabled,
|
||||
as,
|
||||
ref,
|
||||
className,
|
||||
...props
|
||||
}: {
|
||||
disabled?: boolean;
|
||||
text?: string;
|
||||
icon?: ComponentProps<typeof Icon>["icon"] | Falsy;
|
||||
ricon?: ComponentProps<typeof Icon>["icon"] | Falsy;
|
||||
ref?: Ref<typeof Pressable>;
|
||||
className?: string;
|
||||
as?: ComponentType<AsProps>;
|
||||
} & AsProps) => {
|
||||
const Container = as ?? PressableFeedback;
|
||||
return (
|
||||
<Container
|
||||
ref={ref as any}
|
||||
ref={ref}
|
||||
disabled={disabled}
|
||||
{...(css(
|
||||
[
|
||||
{
|
||||
flexGrow: 0,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
overflow: "hidden",
|
||||
p: ts(0.5),
|
||||
borderRadius: ts(5),
|
||||
borderColor: (theme: Theme) => theme.accent,
|
||||
borderWidth: ts(0.5),
|
||||
fover: {
|
||||
self: { bg: (theme: Theme) => theme.accent },
|
||||
text: { color: (theme: Theme) => theme.colors.white },
|
||||
},
|
||||
},
|
||||
disabled && {
|
||||
child: {
|
||||
self: {
|
||||
borderColor: (theme) => theme.overlay1,
|
||||
},
|
||||
text: {
|
||||
color: (theme) => theme.overlay1,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
props as any,
|
||||
) as AsProps)}
|
||||
>
|
||||
{(licon || text || icon) != null && (
|
||||
<View
|
||||
{...css({
|
||||
paddingX: ts(3),
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
})}
|
||||
>
|
||||
{licon}
|
||||
{text && <P {...css({ textAlign: "center" }, "text")}>{text}</P>}
|
||||
{icon}
|
||||
</View>
|
||||
className={cn(
|
||||
"flex-row items-center justify-center overflow-hidden",
|
||||
"rounded-4xl border-3 border-accent p-1",
|
||||
disabled && "border-slate-600",
|
||||
"group focus-within:bg-accent hover:bg-accent",
|
||||
className,
|
||||
)}
|
||||
{children}
|
||||
{...(props as AsProps)}
|
||||
>
|
||||
<View className="flex-row items-center px-6">
|
||||
{icon && (
|
||||
<Icon
|
||||
icon={icon}
|
||||
className="mx-2 group-focus-within:fill-slate-200 group-hover:fill-slate-200"
|
||||
/>
|
||||
)}
|
||||
{text && (
|
||||
<P className="text-center group-focus-within:text-slate-200 group-hover:text-slate-200">
|
||||
{text}
|
||||
</P>
|
||||
)}
|
||||
{ricon && (
|
||||
<Icon
|
||||
icon={ricon}
|
||||
className="mx-2 group-focus-within:fill-slate-200 group-hover:fill-slate-200"
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</Container>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
@ -122,7 +122,7 @@ const MenuItem = ({
|
||||
<Icon
|
||||
icon={icon ?? Check}
|
||||
className={cn(
|
||||
"mx-2",
|
||||
"mx-6",
|
||||
disabled && "fill-slate-600 dark:fill-slate-600",
|
||||
)}
|
||||
/>
|
||||
@ -136,7 +136,7 @@ const MenuItem = ({
|
||||
if (href) router.push(href);
|
||||
}}
|
||||
disabled={disabled}
|
||||
className="h-10 w-full flex-row items-center px-4"
|
||||
className="h-15 w-full flex-row items-center px-4"
|
||||
{...props}
|
||||
>
|
||||
{left && left}
|
||||
|
||||
@ -54,7 +54,7 @@ const Menu = <AsProps extends { onPress: PressableProps["onPress"] }>({
|
||||
}}
|
||||
>
|
||||
<DropdownMenu.Trigger asChild>
|
||||
<InternalTriger Component={Trigger} ComponentProps={props} />
|
||||
<InternalTriger Component={Trigger} {...props} />
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content
|
||||
|
||||
@ -4,14 +4,11 @@ import ExpandLess from "@material-symbols/svg-400/rounded/keyboard_arrow_up-fill
|
||||
import * as RSelect from "@radix-ui/react-select";
|
||||
import { forwardRef } from "react";
|
||||
import { View } from "react-native";
|
||||
import { useYoshiki } from "yoshiki";
|
||||
import { px, type Theme, useYoshiki as useNativeYoshiki } from "yoshiki/native";
|
||||
import { cn } from "~/utils";
|
||||
import { Icon } from "./icons";
|
||||
import { PressableFeedback } from "./links";
|
||||
import { InternalTriger, YoshikiProvider } from "./menu.web";
|
||||
import { InternalTriger } from "./menu.web";
|
||||
import { P } from "./text";
|
||||
import { ContrastArea, SwitchVariant } from "./theme";
|
||||
import { focusReset, ts } from "./utils";
|
||||
|
||||
export const Select = ({
|
||||
label,
|
||||
@ -26,135 +23,79 @@ export const Select = ({
|
||||
values: string[];
|
||||
getLabel: (key: string) => string;
|
||||
}) => {
|
||||
const { css: wCss } = useYoshiki();
|
||||
const { css } = useNativeYoshiki();
|
||||
|
||||
return (
|
||||
<RSelect.Root value={value} onValueChange={onValueChange}>
|
||||
<RSelect.Trigger aria-label={label} asChild>
|
||||
<InternalTriger
|
||||
Component={PressableFeedback}
|
||||
ComponentProps={css({
|
||||
flexGrow: 0,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
overflow: "hidden",
|
||||
p: ts(0.5),
|
||||
borderRadius: ts(5),
|
||||
borderColor: (theme) => theme.accent,
|
||||
borderWidth: ts(0.5),
|
||||
fover: {
|
||||
self: { bg: (theme: Theme) => theme.accent },
|
||||
text: { color: (theme: Theme) => theme.colors.white },
|
||||
},
|
||||
})}
|
||||
className={cn(
|
||||
"group flex-row items-center justify-center overflow-hidden rounded-4xl",
|
||||
"border-2 border-accent p-1 outline-0 focus-within:bg-accent hover:bg-accent",
|
||||
)}
|
||||
>
|
||||
<View
|
||||
{...css({
|
||||
paddingX: ts(3),
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
})}
|
||||
>
|
||||
<P {...css({ textAlign: "center" }, "text")}>{<RSelect.Value />}</P>
|
||||
<RSelect.Icon
|
||||
{...wCss({ display: "flex", justifyContent: "center" })}
|
||||
>
|
||||
<Icon icon={ExpandMore} />
|
||||
<View className="flex-row items-center px-6">
|
||||
<P className="text-center group-focus-within:text-slate-200 group-hover:text-slate-200">
|
||||
{<RSelect.Value />}
|
||||
</P>
|
||||
<RSelect.Icon className="flex justify-center">
|
||||
<Icon
|
||||
icon={ExpandMore}
|
||||
className="group-focus-within:fill-slate-200 group-hover:fill-slate-200"
|
||||
/>
|
||||
</RSelect.Icon>
|
||||
</View>
|
||||
</InternalTriger>
|
||||
</RSelect.Trigger>
|
||||
<ContrastArea mode="user">
|
||||
<SwitchVariant>
|
||||
<YoshikiProvider>
|
||||
{({ css }) => (
|
||||
<RSelect.Portal>
|
||||
<RSelect.Content
|
||||
{...css({
|
||||
bg: (theme) => theme.background,
|
||||
overflow: "auto",
|
||||
minWidth: "220px",
|
||||
borderRadius: "8px",
|
||||
boxShadow:
|
||||
"0px 10px 38px -10px rgba(22, 23, 24, 0.35), 0px 10px 20px -15px rgba(22, 23, 24, 0.2)",
|
||||
zIndex: 2,
|
||||
maxHeight:
|
||||
"calc(var(--radix-dropdown-menu-content-available-height) * 0.8)",
|
||||
})}
|
||||
>
|
||||
<RSelect.ScrollUpButton>
|
||||
<Icon icon={ExpandLess} />
|
||||
</RSelect.ScrollUpButton>
|
||||
<RSelect.Viewport>
|
||||
{values.map((x) => (
|
||||
<Item key={x} label={getLabel(x)} value={x} />
|
||||
))}
|
||||
</RSelect.Viewport>
|
||||
<RSelect.ScrollDownButton>
|
||||
<Icon icon={ExpandMore} />
|
||||
</RSelect.ScrollDownButton>
|
||||
</RSelect.Content>
|
||||
</RSelect.Portal>
|
||||
)}
|
||||
</YoshikiProvider>
|
||||
</SwitchVariant>
|
||||
</ContrastArea>
|
||||
<RSelect.Portal>
|
||||
<RSelect.Content
|
||||
className="z-10 min-w-3xs overflow-auto rounded bg-popover shadow-xl"
|
||||
style={{
|
||||
maxHeight:
|
||||
"calc(var(--radix-dropdown-menu-content-available-height) * 0.8)",
|
||||
}}
|
||||
>
|
||||
<RSelect.ScrollUpButton className="flex justify-center">
|
||||
<Icon icon={ExpandLess} />
|
||||
</RSelect.ScrollUpButton>
|
||||
<RSelect.Viewport>
|
||||
{values.map((x) => (
|
||||
<Item key={x} label={getLabel(x)} value={x} />
|
||||
))}
|
||||
</RSelect.Viewport>
|
||||
<RSelect.ScrollDownButton className="flex justify-center">
|
||||
<Icon icon={ExpandMore} />
|
||||
</RSelect.ScrollDownButton>
|
||||
</RSelect.Content>
|
||||
</RSelect.Portal>
|
||||
</RSelect.Root>
|
||||
);
|
||||
};
|
||||
|
||||
const Item = forwardRef<HTMLDivElement, { label: string; value: string }>(
|
||||
function Item({ label, value, ...props }, ref) {
|
||||
const { css, theme } = useYoshiki();
|
||||
const { css: nCss } = useNativeYoshiki();
|
||||
return (
|
||||
<>
|
||||
{/* <style jsx global>{` */}
|
||||
{/* [data-highlighted] { */}
|
||||
{/* background: ${theme.variant.accent}; */}
|
||||
{/* } */}
|
||||
{/* `}</style> */}
|
||||
<RSelect.Item
|
||||
ref={ref}
|
||||
value={value}
|
||||
{...css(
|
||||
{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
paddingTop: "8px",
|
||||
paddingBottom: "8px",
|
||||
paddingLeft: "35px",
|
||||
paddingRight: "25px",
|
||||
height: "32px",
|
||||
color: (theme) => theme.paragraph,
|
||||
borderRadius: "4px",
|
||||
position: "relative",
|
||||
userSelect: "none",
|
||||
...focusReset,
|
||||
},
|
||||
props as any,
|
||||
)}
|
||||
>
|
||||
<RSelect.ItemText {...css({ color: (theme) => theme.paragraph })}>
|
||||
{label}
|
||||
</RSelect.ItemText>
|
||||
<RSelect.ItemIndicator asChild>
|
||||
<InternalTriger
|
||||
Component={Icon}
|
||||
icon={Check}
|
||||
ComponentProps={nCss({
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
width: px(25),
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
})}
|
||||
/>
|
||||
</RSelect.ItemIndicator>
|
||||
</RSelect.Item>
|
||||
</>
|
||||
<RSelect.Item
|
||||
ref={ref}
|
||||
value={value}
|
||||
className={cn(
|
||||
"flex select-none items-center rounded py-2 pr-6 pl-8 outline-0",
|
||||
"font-sans text-slate-600 dark:text-slate-400",
|
||||
"group data-highlighted:bg-accent data-highlighted:text-slate-200",
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<RSelect.ItemText className={cn()}>{label}</RSelect.ItemText>
|
||||
<RSelect.ItemIndicator asChild>
|
||||
<InternalTriger
|
||||
Component={Icon}
|
||||
icon={Check}
|
||||
className={cn(
|
||||
"absolute left-0 w-6 items-center justify-center",
|
||||
"group-data-highlighted:fill-slate-200",
|
||||
)}
|
||||
/>
|
||||
</RSelect.ItemIndicator>
|
||||
</RSelect.Item>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@ -25,7 +25,6 @@ const styleText = (
|
||||
) => {
|
||||
const Wrapped = ({
|
||||
className,
|
||||
style,
|
||||
...props
|
||||
}: { ref?: Ref<Text> } & TextProps) => {
|
||||
return (
|
||||
@ -128,7 +127,8 @@ export const CroppedText = ({
|
||||
(acc, line) => acc + line.text,
|
||||
"",
|
||||
);
|
||||
setNeedExpand(visible !== children);
|
||||
if (!expended)
|
||||
setNeedExpand(visible !== children && visible?.length > 0);
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
|
||||
@ -5,10 +5,16 @@ import { Svg } from "~/primitives";
|
||||
|
||||
export const KyooLongLogo = (props: ComponentProps<typeof Svg>) => {
|
||||
return (
|
||||
<View className="aspect-[531.15/149] h-full">
|
||||
<View style={{ height: 24, width: (531.15 / 149) * 24 }}>
|
||||
<Svg viewBox="149.28 236.8 531.15 149" {...props}>
|
||||
<Path strokeWidth={3} d="M341.3 328c-3.5-4.2-6.8-7.1-9.7-8.8-3-1.6-6.4-2.5-10.5-2.8v30.9h-23.4V236.8h23.4v59c3.8-.1 7-.9 9.7-2.3s5.6-4 8.9-7.8c3.2-3.8 7.7-9.8 13.4-18.1h27.7c-6.3 9.8-11.5 17-15.6 21.8-4.1 4.7-8 8.1-11.7 10s-8.3 3.1-13.7 3.6v6.8c5.7.4 10.6 1.7 14.6 3.9 4.1 2.2 8.2 5.8 12.5 10.9s9.6 12.6 16.1 22.7h-28c-5.6-8.7-10.2-15.2-13.7-19.4Zm65.1 50.4q-9.9-7.5-12-21.9h22c1.9 6.8 7.1 10.3 15.7 10.3s10.7-1.8 13.7-5.3q4.5-5.25 4.5-16.2v-12l-5.4-1.3c-5.1 10.7-13.2 16.1-24.5 16.1s-15.7-3-20.8-9.1c-5.1-6-7.7-14.3-7.7-24.7v-46.7h23.4v42.6c0 6.1 1.5 10.8 4.4 14 3 3.3 7.1 4.9 12.4 4.9s9.6-1.7 12.7-5.1 4.6-8.2 4.6-14.3v-42.1h23.4v77.3c0 13.4-3.4 23.5-10.1 30.5-6.8 6.9-16.7 10.4-29.8 10.4s-19.9-2.5-26.5-7.5ZM506.9 344q-10.5-5.4-16.5-15c-4-6.4-5.9-13.5-5.9-21.4s2-15 5.9-21.4c4-6.4 9.4-11.4 16.5-15q10.5-5.4 23.7-5.4c13.2 0 16.7 1.8 23.7 5.4q10.5 5.4 16.5 15c4 6.4 5.9 13.5 5.9 21.4s-2 15-5.9 21.4c-4 6.4-9.4 11.4-16.5 15q-10.5 5.4-23.7 5.4c-13.2 0-16.7-1.8-23.7-5.4m40.1-20.5q6.3-6 6.3-15.9c0-9.9-2.1-12-6.3-16s-9.7-6.1-16.5-6.1-12.4 2-16.6 6.1-6.3 9.4-6.3 16 2.1 11.8 6.3 15.9 9.8 6.1 16.6 6.1 12.2-2 16.5-6.1m63.6 20.5q-10.5-5.4-16.5-15c-4-6.4-5.9-13.5-5.9-21.4s2-15 5.9-21.4c4-6.4 9.4-11.4 16.5-15q10.5-5.4 23.7-5.4c13.2 0 16.7 1.8 23.7 5.4q10.5 5.4 16.5 15c4 6.4 5.9 13.5 5.9 21.4s-2 15-5.9 21.4c-4 6.4-9.4 11.4-16.5 15q-10.5 5.4-23.7 5.4c-13.2 0-16.7-1.8-23.7-5.4m40.1-20.5q6.3-6 6.3-15.9c0-9.9-2.1-12-6.3-16s-9.7-6.1-16.5-6.1-12.4 2-16.6 6.1-6.3 9.4-6.3 16 2.1 11.8 6.3 15.9 9.8 6.1 16.6 6.1 12.2-2 16.5-6.1m-411.4-16.4c0 10.2 8.3 18.5 18.5 18.5s18.5-8.3 18.5-18.5-7.5-17.7-17-18.4h-3.2c-9.5.8-17 8.7-17 18.4Zm-90-13c0 10.2 8.3 18.5 18.5 18.5s18.5-8.3 18.5-18.5 8.3-18.5 18.5-18.5h.3c10-.2 18-8.2 18.2-18.2v-.7c-.2-10.1-8.4-18.2-18.5-18.2-10.2 0-18.5 8.3-18.5 18.5 0 1 0 1.9-.2 2.8 0 .3 0 .5-.1.8-.1.7-.3 1.3-.5 1.9s-.4 1.1-.6 1.6-.5 1-.7 1.5c-.1.2-.3.5-.4.7-.2.3-.3.5-.5.8-.1.2-.2.3-.3.5-.4.6-.8 1.1-1.3 1.6-.1.2-.3.3-.4.4l-1.1 1.1c-.2.1-.3.3-.5.4-.2.2-.4.4-.7.5-.7.5-1.4 1-2.2 1.4-.2.1-.5.3-.7.4s-.4.2-.7.3-.6.3-1 .4c-.2 0-.4.2-.6.2-.4.1-.8.2-1.2.4-.2 0-.4 0-.5.1h-.3c-.3 0-.5.1-.8.2-.3 0-.6 0-.9.1H168c-10.2 0-18.5 8.3-18.5 18.5Zm52.9 50.1c0 10.2 8.3 18.5 18.5 18.5 10.1 0 18.4-8.1 18.5-18.2v-2.4c0-.3 0-.6-.1-.9 0-.3-.1-.6-.2-.9v-.2c0-.2 0-.4-.1-.6 0-.4-.2-.7-.3-1.1 0-.2-.2-.5-.2-.7-.1-.3-.2-.5-.3-.8-.2-.5-.5-1.1-.8-1.6-.1-.2-.3-.5-.4-.7s-.3-.4-.4-.6c-.7-1.1-1.6-2-2.5-2.9l-.4-.4c-.5-.5-1-.9-1.6-1.3-.2-.1-.3-.2-.5-.3-1.5-1-3.1-1.7-4.8-2.3-.3 0-.6-.2-.9-.2-.4 0-.7-.2-1.1-.2-.3 0-.5 0-.8-.1-.9-.1-1.8-.2-2.8-.2-10.2 0-18.5 8.3-18.5 18.5Z" />
|
||||
<Path strokeWidth={3} d="M157 337.7c0 10.2 8.3 18.5 18.5 18.5s18.5-8.3 18.5-18.5 8.3-18.5 18.5-18.5c10.1 0 18.4-8.1 18.5-18.2v-.3c0-9.7 7.5-17.7 17-18.4h3.9c8-1 14.4-7.1 15.9-14.9 0-.2 0-.5.1-.7v-3.8c-.5-9.8-8.6-17.6-18.5-17.6s-18 7.8-18.5 17.6v1.1c-.2 10-8.2 18-18.2 18.2h-.3c-10.2 0-18.5 8.3-18.5 18.5s-8.3 18.5-18.5 18.5-18.5 8.3-18.5 18.5Z" />
|
||||
<Path
|
||||
//strokeWidth={3}
|
||||
d="M341.3 328c-3.5-4.2-6.8-7.1-9.7-8.8-3-1.6-6.4-2.5-10.5-2.8v30.9h-23.4V236.8h23.4v59c3.8-.1 7-.9 9.7-2.3s5.6-4 8.9-7.8c3.2-3.8 7.7-9.8 13.4-18.1h27.7c-6.3 9.8-11.5 17-15.6 21.8-4.1 4.7-8 8.1-11.7 10s-8.3 3.1-13.7 3.6v6.8c5.7.4 10.6 1.7 14.6 3.9 4.1 2.2 8.2 5.8 12.5 10.9s9.6 12.6 16.1 22.7h-28c-5.6-8.7-10.2-15.2-13.7-19.4Zm65.1 50.4q-9.9-7.5-12-21.9h22c1.9 6.8 7.1 10.3 15.7 10.3s10.7-1.8 13.7-5.3q4.5-5.25 4.5-16.2v-12l-5.4-1.3c-5.1 10.7-13.2 16.1-24.5 16.1s-15.7-3-20.8-9.1c-5.1-6-7.7-14.3-7.7-24.7v-46.7h23.4v42.6c0 6.1 1.5 10.8 4.4 14 3 3.3 7.1 4.9 12.4 4.9s9.6-1.7 12.7-5.1 4.6-8.2 4.6-14.3v-42.1h23.4v77.3c0 13.4-3.4 23.5-10.1 30.5-6.8 6.9-16.7 10.4-29.8 10.4s-19.9-2.5-26.5-7.5ZM506.9 344q-10.5-5.4-16.5-15c-4-6.4-5.9-13.5-5.9-21.4s2-15 5.9-21.4c4-6.4 9.4-11.4 16.5-15q10.5-5.4 23.7-5.4c13.2 0 16.7 1.8 23.7 5.4q10.5 5.4 16.5 15c4 6.4 5.9 13.5 5.9 21.4s-2 15-5.9 21.4c-4 6.4-9.4 11.4-16.5 15q-10.5 5.4-23.7 5.4c-13.2 0-16.7-1.8-23.7-5.4m40.1-20.5q6.3-6 6.3-15.9c0-9.9-2.1-12-6.3-16s-9.7-6.1-16.5-6.1-12.4 2-16.6 6.1-6.3 9.4-6.3 16 2.1 11.8 6.3 15.9 9.8 6.1 16.6 6.1 12.2-2 16.5-6.1m63.6 20.5q-10.5-5.4-16.5-15c-4-6.4-5.9-13.5-5.9-21.4s2-15 5.9-21.4c4-6.4 9.4-11.4 16.5-15q10.5-5.4 23.7-5.4c13.2 0 16.7 1.8 23.7 5.4q10.5 5.4 16.5 15c4 6.4 5.9 13.5 5.9 21.4s-2 15-5.9 21.4c-4 6.4-9.4 11.4-16.5 15q-10.5 5.4-23.7 5.4c-13.2 0-16.7-1.8-23.7-5.4m40.1-20.5q6.3-6 6.3-15.9c0-9.9-2.1-12-6.3-16s-9.7-6.1-16.5-6.1-12.4 2-16.6 6.1-6.3 9.4-6.3 16 2.1 11.8 6.3 15.9 9.8 6.1 16.6 6.1 12.2-2 16.5-6.1m-411.4-16.4c0 10.2 8.3 18.5 18.5 18.5s18.5-8.3 18.5-18.5-7.5-17.7-17-18.4h-3.2c-9.5.8-17 8.7-17 18.4Zm-90-13c0 10.2 8.3 18.5 18.5 18.5s18.5-8.3 18.5-18.5 8.3-18.5 18.5-18.5h.3c10-.2 18-8.2 18.2-18.2v-.7c-.2-10.1-8.4-18.2-18.5-18.2-10.2 0-18.5 8.3-18.5 18.5 0 1 0 1.9-.2 2.8 0 .3 0 .5-.1.8-.1.7-.3 1.3-.5 1.9s-.4 1.1-.6 1.6-.5 1-.7 1.5c-.1.2-.3.5-.4.7-.2.3-.3.5-.5.8-.1.2-.2.3-.3.5-.4.6-.8 1.1-1.3 1.6-.1.2-.3.3-.4.4l-1.1 1.1c-.2.1-.3.3-.5.4-.2.2-.4.4-.7.5-.7.5-1.4 1-2.2 1.4-.2.1-.5.3-.7.4s-.4.2-.7.3-.6.3-1 .4c-.2 0-.4.2-.6.2-.4.1-.8.2-1.2.4-.2 0-.4 0-.5.1h-.3c-.3 0-.5.1-.8.2-.3 0-.6 0-.9.1H168c-10.2 0-18.5 8.3-18.5 18.5Zm52.9 50.1c0 10.2 8.3 18.5 18.5 18.5 10.1 0 18.4-8.1 18.5-18.2v-2.4c0-.3 0-.6-.1-.9 0-.3-.1-.6-.2-.9v-.2c0-.2 0-.4-.1-.6 0-.4-.2-.7-.3-1.1 0-.2-.2-.5-.2-.7-.1-.3-.2-.5-.3-.8-.2-.5-.5-1.1-.8-1.6-.1-.2-.3-.5-.4-.7s-.3-.4-.4-.6c-.7-1.1-1.6-2-2.5-2.9l-.4-.4c-.5-.5-1-.9-1.6-1.3-.2-.1-.3-.2-.5-.3-1.5-1-3.1-1.7-4.8-2.3-.3 0-.6-.2-.9-.2-.4 0-.7-.2-1.1-.2-.3 0-.5 0-.8-.1-.9-.1-1.8-.2-2.8-.2-10.2 0-18.5 8.3-18.5 18.5Z"
|
||||
/>
|
||||
<Path
|
||||
//strokeWidth={3}
|
||||
d="M157 337.7c0 10.2 8.3 18.5 18.5 18.5s18.5-8.3 18.5-18.5 8.3-18.5 18.5-18.5c10.1 0 18.4-8.1 18.5-18.2v-.3c0-9.7 7.5-17.7 17-18.4h3.9c8-1 14.4-7.1 15.9-14.9 0-.2 0-.5.1-.7v-3.8c-.5-9.8-8.6-17.6-18.5-17.6s-18 7.8-18.5 17.6v1.1c-.2 10-8.2 18-18.2 18.2h-.3c-10.2 0-18.5 8.3-18.5 18.5s-8.3 18.5-18.5 18.5-18.5 8.3-18.5 18.5Z"
|
||||
/>
|
||||
</Svg>
|
||||
</View>
|
||||
);
|
||||
|
||||
@ -46,7 +46,7 @@ export const NavbarTitle = ({
|
||||
<A
|
||||
href="/"
|
||||
aria-label={t("navbar.home")}
|
||||
className={cn("m-4 flex-1", className)}
|
||||
className={cn("m-4 flex flex-1 items-center", className)}
|
||||
{...tooltip(t("navbar.home"))}
|
||||
{...props}
|
||||
>
|
||||
@ -172,7 +172,7 @@ export const NavbarRight = () => {
|
||||
icon={Search}
|
||||
as={Link}
|
||||
href={"/browse"}
|
||||
className="fill-slate-200 dark:fill-slate-200"
|
||||
iconClassName="fill-slate-200 dark:fill-slate-200"
|
||||
{...tooltip(t("navbar.search"))}
|
||||
/>
|
||||
)}
|
||||
@ -181,7 +181,7 @@ export const NavbarRight = () => {
|
||||
icon={Admin}
|
||||
as={Link}
|
||||
href={"/admin"}
|
||||
className="fill-slate-200 dark:fill-slate-200"
|
||||
iconClassName="fill-slate-200 dark:fill-slate-200"
|
||||
{...tooltip(t("navbar.admin"))}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -26,6 +26,7 @@ import { useMutation } from "~/query";
|
||||
import { deleteAccount, logout } from "../login/logic";
|
||||
import { PasswordInput } from "../login/password-input";
|
||||
import { Preference, SettingsContainer } from "./base";
|
||||
import { useUniwind } from "uniwind";
|
||||
|
||||
// function dataURItoBlob(dataURI: string) {
|
||||
// const byteString = atob(dataURI.split(",")[1]);
|
||||
@ -39,7 +40,7 @@ import { Preference, SettingsContainer } from "./base";
|
||||
|
||||
export const AccountSettings = () => {
|
||||
const account = useAccount()!;
|
||||
const { css, theme } = useYoshiki();
|
||||
const { theme } = useUniwind();
|
||||
const [setPopup, close] = usePopup();
|
||||
const { t } = useTranslation();
|
||||
|
||||
@ -68,15 +69,15 @@ export const AccountSettings = () => {
|
||||
<SettingsContainer
|
||||
title={t("settings.account.label")}
|
||||
extra={
|
||||
<View {...css({ marginTop: ts(2), gap: ts(2), flexDirection: "row" })}>
|
||||
<View className="mt-4 flex-row gap-4">
|
||||
<Button
|
||||
licon={<Icon icon={Logout} {...css({ marginX: ts(1) })} />}
|
||||
icon={Logout}
|
||||
text={t("login.logout")}
|
||||
onPress={logout}
|
||||
{...css({ flexGrow: 1, flexShrink: 1, flexBasis: 0 })}
|
||||
className="flex-1"
|
||||
/>
|
||||
<Button
|
||||
licon={<Icon icon={Delete} {...css({ marginX: ts(1) })} />}
|
||||
icon={Delete}
|
||||
text={t("login.delete")}
|
||||
onPress={async () => {
|
||||
Alert.alert(
|
||||
@ -92,13 +93,12 @@ export const AccountSettings = () => {
|
||||
],
|
||||
{
|
||||
cancelable: true,
|
||||
userInterfaceStyle:
|
||||
theme.mode === "auto" ? "light" : theme.mode,
|
||||
userInterfaceStyle: theme as "light" | "dark",
|
||||
icon: "warning",
|
||||
},
|
||||
);
|
||||
}}
|
||||
{...css({ flexGrow: 1, flexShrink: 1, flexBasis: 0 })}
|
||||
className="flex-1"
|
||||
/>
|
||||
</View>
|
||||
}
|
||||
|
||||
@ -1,17 +1,7 @@
|
||||
import { Children, type ReactElement, type ReactNode } from "react";
|
||||
import { Children, Fragment, type ReactElement, type ReactNode } from "react";
|
||||
import { type Falsy, View } from "react-native";
|
||||
import { percent, px, rem, useYoshiki } from "yoshiki/native";
|
||||
import type { User } from "~/models";
|
||||
import {
|
||||
Container,
|
||||
H1,
|
||||
HR,
|
||||
Icon,
|
||||
P,
|
||||
SubP,
|
||||
SwitchVariant,
|
||||
ts,
|
||||
} from "~/primitives";
|
||||
import { Container, H1, HR, Icon, P, SubP } from "~/primitives";
|
||||
import { useAccount } from "~/providers/account-context";
|
||||
import { useMutation } from "~/query";
|
||||
|
||||
@ -29,46 +19,19 @@ export const Preference = ({
|
||||
description: string | ReactElement;
|
||||
children?: ReactNode;
|
||||
}) => {
|
||||
const { css } = useYoshiki();
|
||||
|
||||
return (
|
||||
<View
|
||||
{...css(
|
||||
{
|
||||
margin: ts(1),
|
||||
flexGrow: 1,
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
},
|
||||
props,
|
||||
)}
|
||||
className="m-2 flex-1 flex-row items-center justify-between"
|
||||
{...props}
|
||||
>
|
||||
<View
|
||||
{...css({
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
flexShrink: 1,
|
||||
marginX: ts(2),
|
||||
gap: ts(2),
|
||||
})}
|
||||
>
|
||||
<View className="mx-4 shrink flex-row items-center gap-4">
|
||||
{customIcon ?? <Icon icon={icon} />}
|
||||
<View {...css({ flexShrink: 1 })}>
|
||||
<P {...(css({ marginBottom: 0 }) as any)}>{label}</P>
|
||||
<View className="shrink">
|
||||
<P>{label}</P>
|
||||
<SubP>{description}</SubP>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
{...css({
|
||||
marginX: ts(2),
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end",
|
||||
gap: ts(1),
|
||||
maxWidth: percent(50),
|
||||
flexWrap: "wrap",
|
||||
})}
|
||||
>
|
||||
<View className="mx-4 max-w-1/2 flex-row flex-wrap justify-end gap-2">
|
||||
{children}
|
||||
</View>
|
||||
</View>
|
||||
@ -87,29 +50,18 @@ export const SettingsContainer = ({
|
||||
extra?: ReactElement;
|
||||
extraTop?: ReactElement;
|
||||
}) => {
|
||||
const { css } = useYoshiki();
|
||||
|
||||
return (
|
||||
<Container {...props}>
|
||||
<H1 {...css({ fontSize: rem(2) })}>{title}</H1>
|
||||
<H1 className="my-2 text-4xl">{title}</H1>
|
||||
{extraTop}
|
||||
<SwitchVariant>
|
||||
{({ css }) => (
|
||||
<View
|
||||
{...css({
|
||||
bg: (theme) => theme.background,
|
||||
borderRadius: px(6),
|
||||
})}
|
||||
>
|
||||
{Children.map(children, (x, i) => (
|
||||
<>
|
||||
{i !== 0 && <HR {...css({ marginY: ts(1) })} />}
|
||||
{x}
|
||||
</>
|
||||
))}
|
||||
</View>
|
||||
)}
|
||||
</SwitchVariant>
|
||||
<View className="rounded bg-card">
|
||||
{Children.map(children, (x, i) => (
|
||||
<Fragment key={i}>
|
||||
{i !== 0 && <HR className="my-2" />}
|
||||
{x}
|
||||
</Fragment>
|
||||
))}
|
||||
</View>
|
||||
{extra}
|
||||
</Container>
|
||||
);
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
// import Theme from "@material-symbols/svg-400/outlined/dark_mode.svg";
|
||||
import Theme from "@material-symbols/svg-400/outlined/dark_mode.svg";
|
||||
import Language from "@material-symbols/svg-400/outlined/language.svg";
|
||||
import Android from "@material-symbols/svg-400/rounded/android.svg";
|
||||
import Public from "@material-symbols/svg-400/rounded/public.svg";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Uniwind, useUniwind } from "uniwind";
|
||||
import { Link, Select } from "~/primitives";
|
||||
import { supportedLanguages } from "~/providers/translations.compile";
|
||||
import { useLanguageName } from "~/track-utils";
|
||||
@ -10,24 +11,28 @@ import { Preference, SettingsContainer } from "./base";
|
||||
|
||||
export const GeneralSettings = () => {
|
||||
const { t, i18n } = useTranslation();
|
||||
// const theme = useUserTheme("auto");
|
||||
const theme = useUniwind();
|
||||
const getLanguageName = useLanguageName();
|
||||
|
||||
return (
|
||||
<SettingsContainer title={t("settings.general.label")}>
|
||||
{/* <Preference */}
|
||||
{/* icon={Theme} */}
|
||||
{/* label={t("settings.general.theme.label")} */}
|
||||
{/* description={t("settings.general.theme.description")} */}
|
||||
{/* > */}
|
||||
{/* <Select */}
|
||||
{/* label={t("settings.general.theme.label")} */}
|
||||
{/* value={theme} */}
|
||||
{/* onValueChange={(value) => setUserTheme(value)} */}
|
||||
{/* values={["auto", "light", "dark"]} */}
|
||||
{/* getLabel={(key) => t(`settings.general.theme.${key}`)} */}
|
||||
{/* /> */}
|
||||
{/* </Preference> */}
|
||||
<Preference
|
||||
icon={Theme}
|
||||
label={t("settings.general.theme.label")}
|
||||
description={t("settings.general.theme.description")}
|
||||
>
|
||||
<Select
|
||||
label={t("settings.general.theme.label")}
|
||||
value={
|
||||
theme.hasAdaptiveThemes ? "auto" : (theme.theme as "light" | "dark")
|
||||
}
|
||||
onValueChange={(value) =>
|
||||
Uniwind.setTheme(value === "auto" ? "system" : value)
|
||||
}
|
||||
values={["auto", "light", "dark"]}
|
||||
getLabel={(key) => t(`settings.general.theme.${key}`)}
|
||||
/>
|
||||
</Preference>
|
||||
<Preference
|
||||
icon={Language}
|
||||
label={t("settings.general.language.label")}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { ScrollView } from "react-native";
|
||||
import { ts } from "~/primitives";
|
||||
import { useAccount } from "~/providers/account-context";
|
||||
import { AccountSettings } from "./account";
|
||||
import { About, GeneralSettings } from "./general";
|
||||
@ -9,7 +8,7 @@ import { PlaybackSettings } from "./playback";
|
||||
export const SettingsPage = () => {
|
||||
const account = useAccount();
|
||||
return (
|
||||
<ScrollView contentContainerStyle={{ gap: ts(4), paddingBottom: ts(4) }}>
|
||||
<ScrollView contentContainerClassName="gap-8 pb-8">
|
||||
<GeneralSettings />
|
||||
{account && <PlaybackSettings />}
|
||||
{account && <AccountSettings />}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user