mirror of
https://github.com/Viren070/AIOStreams.git
synced 2025-12-01 23:14:04 +01:00
feat(frontend): redesign about menu
Release-As: 2.15.0
This commit is contained in:
@@ -17,11 +17,10 @@ import {
|
||||
BiFilterAlt,
|
||||
BiSave,
|
||||
BiSort,
|
||||
BiLogInCircle,
|
||||
BiLogOutCircle,
|
||||
BiCog,
|
||||
BiServer,
|
||||
BiSmile,
|
||||
BiHeart,
|
||||
} from 'react-icons/bi';
|
||||
import { useRouter, usePathname } from 'next/navigation';
|
||||
import { useDisclosure } from '@/hooks/disclosure';
|
||||
@@ -35,6 +34,7 @@ import { toast } from 'sonner';
|
||||
import { Tooltip } from '@/components/ui/tooltip';
|
||||
import { useOptions } from '@/context/options';
|
||||
import { useMode } from '@/context/mode';
|
||||
import { DonationModal } from '@/components/shared/donation-modal';
|
||||
|
||||
type MenuItem = VerticalMenuItem & {
|
||||
id: MenuId;
|
||||
@@ -47,6 +47,7 @@ export function MainSidebar() {
|
||||
const { selectedMenu, setSelectedMenu } = useMenu();
|
||||
const pathname = usePathname();
|
||||
const { isOptionsEnabled, enableOptions } = useOptions();
|
||||
const donationModal = useDisclosure(false);
|
||||
|
||||
const user = useUserData();
|
||||
const signInModal = useDisclosure(false);
|
||||
@@ -229,7 +230,7 @@ export function MainSidebar() {
|
||||
: user.userData.addonLogo || '/logo.png'
|
||||
}
|
||||
alt="logo"
|
||||
className="w-22.5 h-15"
|
||||
className="max-w-[90px] max-h-[60px] object-contain"
|
||||
/>
|
||||
</div>
|
||||
{status?.settings.alternateDesign === false && (
|
||||
@@ -259,35 +260,22 @@ export function MainSidebar() {
|
||||
side="right"
|
||||
trigger={
|
||||
<Button
|
||||
intent="primary-subtle"
|
||||
intent="alert-outline"
|
||||
size="md"
|
||||
rounded
|
||||
iconSpacing="0"
|
||||
className="w-full"
|
||||
iconClass="text-3xl"
|
||||
leftIcon={
|
||||
user.uuid && user.password ? (
|
||||
<BiLogOutCircle />
|
||||
) : (
|
||||
<BiLogInCircle />
|
||||
)
|
||||
}
|
||||
leftIcon={<BiHeart />}
|
||||
hideTextOnLargeScreen
|
||||
onClick={() => {
|
||||
if (user.uuid && user.password) {
|
||||
confirmClearConfig.open();
|
||||
} else {
|
||||
signInModal.open();
|
||||
}
|
||||
donationModal.open();
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-2 ml-2">
|
||||
{user.uuid && user.password ? 'Log Out' : 'Log In'}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 ml-2">Donate</div>
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
{user.uuid && user.password ? 'Log Out' : 'Log In'}
|
||||
Donate
|
||||
</Tooltip>
|
||||
</div>
|
||||
</AppSidebar>
|
||||
@@ -307,6 +295,10 @@ export function MainSidebar() {
|
||||
/>
|
||||
|
||||
<ConfirmationDialog {...confirmClearConfig} />
|
||||
<DonationModal
|
||||
open={donationModal.isOpen}
|
||||
onOpenChange={donationModal.toggle}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,9 +8,16 @@ import React from 'react';
|
||||
import { PageControls } from '@/components/shared/page-controls';
|
||||
import { useMenu } from '@/context/menu';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { HeartIcon } from 'lucide-react';
|
||||
import { useDisclosure } from '@/hooks/disclosure';
|
||||
import { DonationModal } from '@/components/shared/donation-modal';
|
||||
import { BiLogInCircle, BiLogOutCircle } from 'react-icons/bi';
|
||||
import { ConfigModal } from '@/components/config-modal';
|
||||
import { useUserData } from '@/context/userData';
|
||||
import { toast } from 'sonner';
|
||||
import {
|
||||
ConfirmationDialog,
|
||||
useConfirmationDialog,
|
||||
} from '@/components/shared/confirmation-dialog';
|
||||
|
||||
type TopNavbarProps = {
|
||||
children?: React.ReactNode;
|
||||
@@ -20,8 +27,18 @@ export function TopNavbar(props: TopNavbarProps) {
|
||||
const { children, ...rest } = props;
|
||||
const { selectedMenu } = useMenu();
|
||||
const donationModal = useDisclosure(false);
|
||||
const signInModal = useDisclosure(false);
|
||||
const serverStatus = useStatus();
|
||||
const isOffline = !serverStatus.status;
|
||||
const { userData, setUserData, uuid, password } = useUserData();
|
||||
|
||||
const confirmClearConfig = useConfirmationDialog({
|
||||
title: 'Sign Out',
|
||||
description: 'Are you sure you want to sign out?',
|
||||
onConfirm: () => {
|
||||
setUserData(null);
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -52,12 +69,21 @@ export function TopNavbar(props: TopNavbarProps) {
|
||||
) : (
|
||||
<div className="block lg:hidden absolute top-0 right-4">
|
||||
<Button
|
||||
intent="alert-subtle"
|
||||
intent="primary-subtle"
|
||||
size="md"
|
||||
leftIcon={<HeartIcon />}
|
||||
onClick={donationModal.open}
|
||||
iconClass="text-3xl"
|
||||
leftIcon={
|
||||
uuid && password ? <BiLogOutCircle /> : <BiLogInCircle />
|
||||
}
|
||||
onClick={() => {
|
||||
if (uuid && password) {
|
||||
confirmClearConfig.open();
|
||||
} else {
|
||||
signInModal.open();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Support Me
|
||||
{uuid && password ? 'Log Out' : 'Log In'}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
@@ -67,6 +93,19 @@ export function TopNavbar(props: TopNavbarProps) {
|
||||
open={donationModal.isOpen}
|
||||
onOpenChange={donationModal.toggle}
|
||||
/>
|
||||
<ConfigModal
|
||||
open={signInModal.isOpen}
|
||||
onSuccess={() => {
|
||||
signInModal.close();
|
||||
toast.success('Signed in successfully');
|
||||
}}
|
||||
onOpenChange={(v) => {
|
||||
if (!v) {
|
||||
signInModal.close();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<ConfirmationDialog {...confirmClearConfig} />
|
||||
<LayoutHeaderBackground />
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -14,8 +14,8 @@ import {
|
||||
MessageCircleIcon,
|
||||
PencilIcon,
|
||||
} from 'lucide-react';
|
||||
import { FaGithub, FaDiscord } from 'react-icons/fa';
|
||||
import { BiDonateHeart } from 'react-icons/bi';
|
||||
import { FaGithub, FaDiscord, FaChevronRight } from 'react-icons/fa';
|
||||
import { BiDonateHeart, BiLogInCircle, BiLogOutCircle } from 'react-icons/bi';
|
||||
import { AiOutlineDiscord } from 'react-icons/ai';
|
||||
import { FiGithub } from 'react-icons/fi';
|
||||
import Image from 'next/image';
|
||||
@@ -32,6 +32,11 @@ import { useMode } from '@/context/mode';
|
||||
import { DonationModal } from '../shared/donation-modal';
|
||||
import { ModeSwitch } from '../ui/mode-switch/mode-switch';
|
||||
import { ModeSelectModal } from '../shared/mode-select-modal';
|
||||
import { ConfigModal } from '../config-modal';
|
||||
import {
|
||||
ConfirmationDialog,
|
||||
useConfirmationDialog,
|
||||
} from '../shared/confirmation-dialog';
|
||||
import {
|
||||
Card,
|
||||
CardHeader,
|
||||
@@ -43,6 +48,59 @@ import {
|
||||
import { Select } from '@/components/ui/select';
|
||||
import { cn } from '@/components/ui/core/styling';
|
||||
import { Textarea } from '../ui/textarea';
|
||||
import { FaPlay } from 'react-icons/fa6';
|
||||
|
||||
interface QuickLinkProps {
|
||||
href?: string;
|
||||
onClick?: () => void;
|
||||
className?: string;
|
||||
icon: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
function QuickLink({
|
||||
href,
|
||||
onClick,
|
||||
icon,
|
||||
children,
|
||||
className,
|
||||
}: QuickLinkProps) {
|
||||
className = cn(
|
||||
'group relative flex flex-col justify-between p-4 h-32 rounded-lg bg-gray-800/60 hover:bg-gray-800/60 transition-all duration-200 hover:scale-[1.02] hover:shadow-lg border border-transparent hover:border-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:border-white',
|
||||
className
|
||||
);
|
||||
|
||||
const content = (
|
||||
<>
|
||||
<div className="text-gray-400 group-hover:text-white transition-colors">
|
||||
{icon}
|
||||
</div>
|
||||
<span className="font-semibold text-gray-400 text-sm font-bold group-hover:text-white transition-colors text-left">
|
||||
{children}
|
||||
</span>
|
||||
<FaChevronRight className="absolute bottom-4 right-4 w-4 h-4 text-gray-400 group-hover:text-white transition-colors" />
|
||||
</>
|
||||
);
|
||||
|
||||
if (href) {
|
||||
return (
|
||||
<a
|
||||
href={href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={className}
|
||||
>
|
||||
{content}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<button onClick={onClick} className={className}>
|
||||
{content}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export function AboutMenu() {
|
||||
return (
|
||||
@@ -57,17 +115,13 @@ export function AboutMenu() {
|
||||
function Content() {
|
||||
const { status, loading, error } = useStatus();
|
||||
const { nextMenu } = useMenu();
|
||||
const { userData, setUserData } = useUserData();
|
||||
const { userData, setUserData, uuid, password, setPassword } = useUserData();
|
||||
const { mode, setMode, isFirstTime } = useMode();
|
||||
const modeSelectModal = useDisclosure(isFirstTime);
|
||||
const addonName =
|
||||
userData.addonName || status?.settings?.addonName || 'AIOStreams';
|
||||
const defaultDescription = `
|
||||
AIOStreams consolidates multiple Stremio addons and debrid
|
||||
services into a single, easily configurable addon. It allows
|
||||
highly customisable filtering, sorting, and formatting of results
|
||||
and supports proxying all your streams through MediaFlow Proxy or
|
||||
StremThru for improved compatibility and IP restriction bypassing.
|
||||
AIOStreams consolidates multiple Stremio addons and debrid services - including its own suite of exclusive built-in addons - into a single, highly customisable super-addon.
|
||||
`;
|
||||
const addonDescription = userData.addonDescription || defaultDescription;
|
||||
const version = status?.tag || 'Unknown';
|
||||
@@ -78,22 +132,40 @@ function Content() {
|
||||
const discordUrl = 'https://discord.viren070.me';
|
||||
const donationModal = useDisclosure(false);
|
||||
const customizeModal = useDisclosure(false);
|
||||
const signInModal = useDisclosure(false);
|
||||
const customHtml = status?.settings?.customHtml;
|
||||
|
||||
const confirmClearConfig = useConfirmationDialog({
|
||||
title: 'Sign Out',
|
||||
description: 'Are you sure you want to sign out?',
|
||||
onConfirm: () => {
|
||||
setUserData(null);
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col gap-4 w-full">
|
||||
{/* Top section: Responsive logo/name/about layout */}
|
||||
<div className="flex flex-col md:flex-row gap-6 md:gap-8 items-center md:items-start w-full relative">
|
||||
{/* Donate button - visible only on larger screens */}
|
||||
{/* Login/Logout button - visible only on larger screens */}
|
||||
<div className="hidden lg:block absolute top-0 right-0">
|
||||
<Button
|
||||
intent="alert-subtle"
|
||||
intent="primary-outline"
|
||||
size="md"
|
||||
leftIcon={<HeartIcon />}
|
||||
onClick={donationModal.open}
|
||||
iconClass="text-2xl"
|
||||
leftIcon={
|
||||
uuid && password ? <BiLogOutCircle /> : <BiLogInCircle />
|
||||
}
|
||||
onClick={() => {
|
||||
if (uuid && password) {
|
||||
confirmClearConfig.open();
|
||||
} else {
|
||||
signInModal.open();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Support Me
|
||||
{uuid && password ? 'Log Out' : 'Log In'}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -142,6 +214,7 @@ function Content() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Custom HTML section, styled like a card, only if present */}
|
||||
{customHtml && (
|
||||
<SettingsCard>
|
||||
@@ -152,148 +225,139 @@ function Content() {
|
||||
</SettingsCard>
|
||||
)}
|
||||
|
||||
{/* Main content: Getting Started */}
|
||||
<SettingsCard
|
||||
title="Get Started"
|
||||
description="Everything you need to know about AIOStreams"
|
||||
className="mt-4"
|
||||
>
|
||||
<div className="space-y-6">
|
||||
{/* Welcome section */}
|
||||
<div className="text-base text-muted-foreground">
|
||||
<b>Welcome to AIOStreams!</b> <br />
|
||||
<br />
|
||||
<span>
|
||||
Click the Configure button below to start customizing AIOStreams
|
||||
to your preferences. You'll be guided through each section where
|
||||
you can set up your configuration. Once complete, you'll create
|
||||
a password-protected configuration that you can install in
|
||||
Stremio or other compatible apps.
|
||||
</span>
|
||||
<br />
|
||||
<br />
|
||||
<span>
|
||||
Need to make changes later? Simply click configure within your
|
||||
app and enter your password. You can update your settings at any
|
||||
time, and in most cases - you won't need to reinstall
|
||||
AIOStreams!
|
||||
</span>
|
||||
</div>
|
||||
{/* Setup Mode Row */}
|
||||
<div className="flex flex-col md:flex-row items-center gap-4 w-full justify-start md:pl-6">
|
||||
<div className="flex flex-col items-start md:items-start">
|
||||
<span className="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2 self-start">
|
||||
Setup Mode
|
||||
</span>
|
||||
<ModeSwitch
|
||||
value={mode}
|
||||
onChange={setMode}
|
||||
className="w-[280px] h-12 text-base"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-gray-400 md:self-end md:pb-3">
|
||||
<FaChevronRight className="hidden md:block text-2xl" />
|
||||
<FaChevronRight className="md:hidden rotate-90 text-2xl" />
|
||||
</div>
|
||||
<div className="md:self-end">
|
||||
<Button
|
||||
intent="white"
|
||||
rounded
|
||||
leftIcon={<FaPlay />}
|
||||
className="h-12 px-6 text-lg"
|
||||
onClick={() => {
|
||||
nextMenu();
|
||||
}}
|
||||
>
|
||||
START SETUP
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-center mb-6">
|
||||
<div className="flex flex-col gap-4 items-center">
|
||||
<Button
|
||||
intent="white"
|
||||
size="lg"
|
||||
rounded
|
||||
onClick={() => {
|
||||
nextMenu();
|
||||
}}
|
||||
>
|
||||
Configure
|
||||
</Button>
|
||||
<ModeSwitch
|
||||
value={mode}
|
||||
onChange={setMode}
|
||||
size="md"
|
||||
className="w-[280px]"
|
||||
/>
|
||||
{/* Main content: Get Started and What's New sections */}
|
||||
<div className="flex flex-col lg:flex-row gap-8 mt-4">
|
||||
{/* Get Started section */}
|
||||
<div className="flex-1">
|
||||
<div className="p-6 h-full flex flex-col">
|
||||
<div className="mb-4">
|
||||
<h3 className="text-2xl font-semibold text-white mb-1">
|
||||
Welcome to AIOStreams!
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6 flex-1">
|
||||
{/* Welcome section */}
|
||||
<div className="text-base text-muted-foreground">
|
||||
<span>
|
||||
Click the Start Setup button above to start customising
|
||||
AIOStreams to your preferences. You'll be guided through
|
||||
each section where you can set up your configuration. Once
|
||||
complete, you'll create a password-protected configuration
|
||||
that you can install in Stremio or other compatible apps.
|
||||
</span>
|
||||
<br />
|
||||
<br />
|
||||
<span>
|
||||
Need to make changes later? Simply click configure within
|
||||
your app and enter your password. You can update your
|
||||
settings at any time, and in most cases - you won't need to
|
||||
reinstall AIOStreams!
|
||||
</span>
|
||||
<br />
|
||||
<br />
|
||||
<span>
|
||||
Got an existing configuration already? Click the login
|
||||
button in the top right corner to access it.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<div className="absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-gray-700/50 to-transparent" />
|
||||
</div>
|
||||
|
||||
{/* Quick links grid */}
|
||||
<div className="pt-6">
|
||||
<h4 className="text-xl font-semibold text-white mb-4">
|
||||
Resources & Support
|
||||
</h4>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 gap-4">
|
||||
<QuickLink
|
||||
href={configGuideUrl}
|
||||
icon={<BookOpenIcon className="w-8 h-8" />}
|
||||
>
|
||||
Configuration Guide
|
||||
</QuickLink>
|
||||
<QuickLink
|
||||
href="https://github.com/Viren070/AIOStreams/wiki"
|
||||
icon={<BookOpenIcon className="w-8 h-8" />}
|
||||
>
|
||||
Wiki
|
||||
</QuickLink>
|
||||
<QuickLink
|
||||
href={stremioGuideUrl}
|
||||
icon={<InfoIcon className="w-8 h-8" />}
|
||||
>
|
||||
Stremio Guide
|
||||
</QuickLink>
|
||||
<QuickLink
|
||||
href={discordUrl}
|
||||
icon={<AiOutlineDiscord className="w-8 h-8" />}
|
||||
>
|
||||
Discord
|
||||
</QuickLink>
|
||||
<QuickLink
|
||||
href={githubUrl}
|
||||
icon={<FiGithub className="w-8 h-8" />}
|
||||
>
|
||||
GitHub
|
||||
</QuickLink>
|
||||
<QuickLink
|
||||
onClick={donationModal.open}
|
||||
icon={<HeartIcon className="w-8 h-8" />}
|
||||
className="bg-gradient-to-br from-red-500/20 to-pink-500/20 hover:from-red-500/30 hover:to-pink-500/30 border-red-400/30 hover:border-red-400/50"
|
||||
>
|
||||
Donate
|
||||
</QuickLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<div className="absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-gray-700/50 to-transparent" />
|
||||
</div>
|
||||
|
||||
{/* Quick links grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 pt-6">
|
||||
<a
|
||||
href={configGuideUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="group flex flex-col gap-2 p-4 rounded-lg bg-gray-900/40 hover:bg-gray-900/60 transition-all duration-200 hover:scale-[1.02] hover:shadow-lg"
|
||||
>
|
||||
<div className="flex items-center gap-2 text-[--brand] group-hover:underline">
|
||||
<BookOpenIcon className="w-5 h-5" />
|
||||
<span className="font-semibold">Configuration Guide</span>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground group-hover:text-gray-300 transition-colors">
|
||||
Learn how to configure AIOStreams to get the most out of your
|
||||
streaming experience
|
||||
</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://github.com/Viren070/AIOStreams/wiki"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="group flex flex-col gap-2 p-4 rounded-lg bg-gray-900/40 hover:bg-gray-900/60 transition-all duration-200 hover:scale-[1.02] hover:shadow-lg"
|
||||
>
|
||||
<div className="flex items-center gap-2 text-[--brand] group-hover:underline">
|
||||
<BookOpenIcon className="w-5 h-5" />
|
||||
<span className="font-semibold">Wiki</span>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground group-hover:text-gray-300 transition-colors">
|
||||
Browse our comprehensive documentation for advanced features
|
||||
like the Custom Formatter and Group conditions.
|
||||
</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href={stremioGuideUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="group flex flex-col gap-2 p-4 rounded-lg bg-gray-900/40 hover:bg-gray-900/60 transition-all duration-200 hover:scale-[1.02] hover:shadow-lg"
|
||||
>
|
||||
<div className="flex items-center gap-2 text-[--brand] group-hover:underline">
|
||||
<InfoIcon className="w-5 h-5" />
|
||||
<span className="font-semibold">Stremio Guide</span>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground group-hover:text-gray-300 transition-colors">
|
||||
New to Stremio and its addons? Go through my Stremio guide to
|
||||
get started!
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</SettingsCard>
|
||||
|
||||
{/* What's New section in its own row */}
|
||||
<div className="mt-4">
|
||||
<ChangelogBox version={version} />
|
||||
{/* What's New section */}
|
||||
<div className="flex-[1.5]">
|
||||
<ChangelogBox version={version} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Social & donation row */}
|
||||
<div className="flex flex-col items-center mt-4">
|
||||
<div className="flex gap-6 flex-wrap items-center justify-center">
|
||||
<a
|
||||
href={discordUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title="Join Discord"
|
||||
className="text-gray-400 hover:text-[--brand] transition-colors"
|
||||
>
|
||||
<AiOutlineDiscord className="w-7 h-7" />
|
||||
</a>
|
||||
<a
|
||||
href={githubUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title="GitHub"
|
||||
className="text-gray-400 hover:text-[--brand] transition-colors"
|
||||
>
|
||||
<FiGithub className="w-7 h-7" />
|
||||
</a>
|
||||
<a
|
||||
onClick={donationModal.open}
|
||||
title="Support me"
|
||||
className="text-gray-400 hover:text-[--brand] cursor-pointer transition-colors"
|
||||
>
|
||||
<CoffeeIcon className="w-7 h-7" />
|
||||
</a>
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-0.5 mt-4 text-xs text-gray-500">
|
||||
<span>Developed by Viren070.</span>
|
||||
<span>
|
||||
© {new Date().getFullYear()} AIOStreams. Developed by Viren070.
|
||||
</span>
|
||||
<span>
|
||||
This beautiful UI would not be possible without{' '}
|
||||
<a
|
||||
@@ -323,6 +387,19 @@ function Content() {
|
||||
open={modeSelectModal.isOpen}
|
||||
onOpenChange={modeSelectModal.toggle}
|
||||
/>
|
||||
<ConfigModal
|
||||
open={signInModal.isOpen}
|
||||
onSuccess={() => {
|
||||
signInModal.close();
|
||||
toast.success('Signed in successfully');
|
||||
}}
|
||||
onOpenChange={(v) => {
|
||||
if (!v) {
|
||||
signInModal.close();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<ConfirmationDialog {...confirmClearConfig} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -597,23 +674,16 @@ function ChangelogBox({ version }: { version: string }) {
|
||||
);
|
||||
|
||||
return (
|
||||
<SettingsCard
|
||||
title="What's New"
|
||||
description={
|
||||
loading
|
||||
? 'Loading changelogs...'
|
||||
: `View the latest changes for ${currentChannel} releases`
|
||||
}
|
||||
className="h-full flex flex-col"
|
||||
action={
|
||||
newerReleases.length > 0 ? (
|
||||
<span className="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium bg-amber-500/20 text-amber-400 border border-amber-500/30">
|
||||
{newerReleases.length} available update
|
||||
{newerReleases.length > 1 ? 's' : ''}
|
||||
<div className="p-6 h-full flex flex-col">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="text-2xl font-semibold text-white">What's New?</h3>
|
||||
{newerReleases.length > 0 && (
|
||||
<span className="text-[#c8af48] font-bold text-sm">
|
||||
{newerReleases.length} update
|
||||
{newerReleases.length > 1 ? 's' : ''} available
|
||||
</span>
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
)}
|
||||
</div>
|
||||
<div className="relative flex-1" style={{ minHeight: '400px' }}>
|
||||
<div
|
||||
ref={containerRef}
|
||||
@@ -659,22 +729,27 @@ function ChangelogBox({ version }: { version: string }) {
|
||||
{displayReleases.slice(0, visibleCount).map((release, idx) => (
|
||||
<Card
|
||||
key={release.id || release.tag_name}
|
||||
className="bg-gray-900/60 border border-gray-800 relative"
|
||||
className={cn(
|
||||
'border bg-gray-800/60 border-gray-800 relative',
|
||||
isNewerVersion(release.tag_name) && 'border-[#c8af48]/30'
|
||||
)}
|
||||
>
|
||||
<CardHeader className="pb-2">
|
||||
<div className="flex flex-col sm:flex-row sm:items-start justify-between gap-2 sm:gap-4">
|
||||
<div className="flex items-center gap-2 flex-shrink-0">
|
||||
<span className="inline-flex items-center px-2.5 sm:px-3.5 py-1 sm:py-1.5 rounded-full text-xs sm:text-sm font-semibold bg-[--brand]/20 text-[--brand] border border-[--brand]/30 break-all">
|
||||
<span
|
||||
className={cn(
|
||||
'text-sm sm:text-base font-semibold break-all',
|
||||
isNewerVersion(release.tag_name)
|
||||
? 'text-[#c8af48]' // c8af48
|
||||
: 'text-[--brand]'
|
||||
)}
|
||||
>
|
||||
{release.tag_name}
|
||||
</span>
|
||||
{isNewerVersion(release.tag_name) && (
|
||||
<span className="inline-flex items-center px-2 sm:px-2.5 py-0.5 sm:py-1 rounded-full text-xs font-medium bg-amber-500/20 text-amber-400 border border-amber-500/30">
|
||||
Update!
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-shrink-0">
|
||||
<span className="inline-flex items-center px-2 sm:px-2.5 py-0.5 sm:py-1 rounded-full text-xs font-medium bg-gray-700/60 text-gray-300 border border-gray-600/30">
|
||||
<span className="text-xs text-gray-400">
|
||||
{new Date(release.published_at).toLocaleDateString()}
|
||||
</span>
|
||||
</div>
|
||||
@@ -682,7 +757,9 @@ function ChangelogBox({ version }: { version: string }) {
|
||||
</CardHeader>
|
||||
<CardContent className="prose prose-invert prose-sm max-w-none [&_p]:text-sm [&_ul]:text-sm [&_li]:text-sm [&_h1]:text-xl [&_h2]:text-lg [&_h3]:text-base [&_*]:break-all">
|
||||
<ReactMarkdown>
|
||||
{release.body || 'No changelog provided.'}
|
||||
{release.body
|
||||
? release.body.replace(release.tag_name, '')
|
||||
: 'No changelog provided.'}
|
||||
</ReactMarkdown>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
@@ -690,10 +767,13 @@ function ChangelogBox({ version }: { version: string }) {
|
||||
href={release.html_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-[--brand] hover:underline flex items-center gap-2 text-xs"
|
||||
className="text-white hover:underline flex items-center justify-between w-full text-xs"
|
||||
>
|
||||
<GithubIcon className="w-4 h-4" />
|
||||
View on GitHub
|
||||
<span className="flex items-center gap-2">
|
||||
<FaGithub className="w-4 h-4" />
|
||||
View on GitHub
|
||||
</span>
|
||||
<FaChevronRight className="w-4 h-4" />
|
||||
</a>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
@@ -756,7 +836,7 @@ function ChangelogBox({ version }: { version: string }) {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</SettingsCard>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,13 +33,13 @@ export function ModeSelectModal({ open, onOpenChange }: ModeSelectModalProps) {
|
||||
</p>
|
||||
<div className="space-y-4 p-4 rounded-lg bg-gray-900/40 border border-gray-800">
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-lg font-semibold text-[--brand]">Noob</h3>
|
||||
<h3 className="text-lg font-semibold text-[--brand]">Simple</h3>
|
||||
<p className="text-sm text-gray-400">
|
||||
Perfect for beginners! Essential options only.
|
||||
</p>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-lg font-semibold text-[--brand]">Pro</h3>
|
||||
<h3 className="text-lg font-semibold text-[--brand]">Advanced</h3>
|
||||
<p className="text-sm text-gray-400">
|
||||
For advanced users who want full control. Access all
|
||||
configuration options and advanced features for maximum
|
||||
|
||||
@@ -43,14 +43,14 @@ export function ModeSwitch({
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={cn(
|
||||
'relative flex rounded-full bg-gray-900/60 border border-gray-800 overflow-hidden',
|
||||
'relative flex rounded-full bg-gray-800/60 border border-gray-800 overflow-hidden',
|
||||
sizeClasses[size],
|
||||
className
|
||||
)}
|
||||
>
|
||||
{/* Animated highlight */}
|
||||
<div
|
||||
className="absolute top-0 bottom-0 bg-[--brand]/20 border border-[--brand]/30 rounded-full transition-transform duration-300 ease-in-out"
|
||||
className="absolute top-0 bottom-0 bg-white border border-[--brand]/30 rounded-full transition-transform duration-300 ease-in-out"
|
||||
style={highlightStyle}
|
||||
/>
|
||||
|
||||
@@ -59,23 +59,21 @@ export function ModeSwitch({
|
||||
onClick={() => onChange('noob')}
|
||||
className={cn(
|
||||
'relative flex-1 flex items-center justify-center font-medium transition-colors duration-200',
|
||||
value === 'noob'
|
||||
? 'text-[--brand]'
|
||||
: 'text-gray-400 hover:text-gray-300'
|
||||
value === 'noob' ? 'text-black' : 'text-gray-400 hover:text-gray-300'
|
||||
)}
|
||||
>
|
||||
Noob Mode
|
||||
<div className="text-md">
|
||||
<b>SIMPLE</b>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onChange('pro')}
|
||||
className={cn(
|
||||
'relative flex-1 flex items-center justify-center font-medium transition-colors duration-200',
|
||||
value === 'pro'
|
||||
? 'text-[--brand]'
|
||||
: 'text-gray-400 hover:text-gray-300'
|
||||
value === 'pro' ? 'text-black' : 'text-gray-400 hover:text-gray-300'
|
||||
)}
|
||||
>
|
||||
Pro Mode
|
||||
<b>ADVANCED</b>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user