diff --git a/packages/frontend/src/app/main-sidebar.tsx b/packages/frontend/src/app/main-sidebar.tsx
index fa50c343..22e0bd6d 100644
--- a/packages/frontend/src/app/main-sidebar.tsx
+++ b/packages/frontend/src/app/main-sidebar.tsx
@@ -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"
/>
{status?.settings.alternateDesign === false && (
@@ -259,35 +260,22 @@ export function MainSidebar() {
side="right"
trigger={
- ) : (
-
- )
- }
+ leftIcon={ }
hideTextOnLargeScreen
onClick={() => {
- if (user.uuid && user.password) {
- confirmClearConfig.open();
- } else {
- signInModal.open();
- }
+ donationModal.open();
}}
>
-
- {user.uuid && user.password ? 'Log Out' : 'Log In'}
-
+ Donate
}
>
- {user.uuid && user.password ? 'Log Out' : 'Log In'}
+ Donate
@@ -307,6 +295,10 @@ export function MainSidebar() {
/>
+
>
);
}
diff --git a/packages/frontend/src/app/top-navbar.tsx b/packages/frontend/src/app/top-navbar.tsx
index 642a993f..5ffbd81d 100644
--- a/packages/frontend/src/app/top-navbar.tsx
+++ b/packages/frontend/src/app/top-navbar.tsx
@@ -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) {
) : (
}
- onClick={donationModal.open}
+ iconClass="text-3xl"
+ leftIcon={
+ uuid && password ? :
+ }
+ onClick={() => {
+ if (uuid && password) {
+ confirmClearConfig.open();
+ } else {
+ signInModal.open();
+ }
+ }}
>
- Support Me
+ {uuid && password ? 'Log Out' : 'Log In'}
)}
@@ -67,6 +93,19 @@ export function TopNavbar(props: TopNavbarProps) {
open={donationModal.isOpen}
onOpenChange={donationModal.toggle}
/>
+ {
+ signInModal.close();
+ toast.success('Signed in successfully');
+ }}
+ onOpenChange={(v) => {
+ if (!v) {
+ signInModal.close();
+ }
+ }}
+ />
+
>
diff --git a/packages/frontend/src/components/menu/about.tsx b/packages/frontend/src/components/menu/about.tsx
index 726cfd9b..f56401dd 100644
--- a/packages/frontend/src/components/menu/about.tsx
+++ b/packages/frontend/src/components/menu/about.tsx
@@ -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 = (
+ <>
+
+ {icon}
+
+
+ {children}
+
+
+ >
+ );
+
+ if (href) {
+ return (
+
+ {content}
+
+ );
+ }
+
+ return (
+
+ {content}
+
+ );
+}
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 (
<>
{/* Top section: Responsive logo/name/about layout */}
- {/* Donate button - visible only on larger screens */}
+ {/* Login/Logout button - visible only on larger screens */}
}
- onClick={donationModal.open}
+ iconClass="text-2xl"
+ leftIcon={
+ uuid && password ? :
+ }
+ onClick={() => {
+ if (uuid && password) {
+ confirmClearConfig.open();
+ } else {
+ signInModal.open();
+ }
+ }}
>
- Support Me
+ {uuid && password ? 'Log Out' : 'Log In'}
@@ -142,6 +214,7 @@ function Content() {
+
{/* Custom HTML section, styled like a card, only if present */}
{customHtml && (
@@ -152,148 +225,139 @@ function Content() {
)}
- {/* Main content: Getting Started */}
-
-
- {/* Welcome section */}
-
- Welcome to AIOStreams!
-
-
- 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.
-
-
-
-
- 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!
-
-
+ {/* Setup Mode Row */}
+
+
+
+ Setup Mode
+
+
+
+
+
+
+
+
+ }
+ className="h-12 px-6 text-lg"
+ onClick={() => {
+ nextMenu();
+ }}
+ >
+ START SETUP
+
+
+
-
-
-
{
- nextMenu();
- }}
- >
- Configure
-
-
+ {/* Main content: Get Started and What's New sections */}
+
+ {/* Get Started section */}
+
+
+
+
+ Welcome to AIOStreams!
+
+
+
+
+ {/* Welcome section */}
+
+
+ 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.
+
+
+
+
+ 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!
+
+
+
+
+ Got an existing configuration already? Click the login
+ button in the top right corner to access it.
+
+
+
+
+
+ {/* Quick links grid */}
+
+
+ Resources & Support
+
+
+ }
+ >
+ Configuration Guide
+
+ }
+ >
+ Wiki
+
+ }
+ >
+ Stremio Guide
+
+ }
+ >
+ Discord
+
+ }
+ >
+ GitHub
+
+ }
+ 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
+
+
+
-
-
-
- {/* Quick links grid */}
-
-
- {/* What's New section in its own row */}
-
-
+ {/* What's New section */}
+
+
+
{/* Social & donation row */}
-
-
Developed by Viren070.
+
+ © {new Date().getFullYear()} AIOStreams. Developed by Viren070.
+
This beautiful UI would not be possible without{' '}
+ {
+ signInModal.close();
+ toast.success('Signed in successfully');
+ }}
+ onOpenChange={(v) => {
+ if (!v) {
+ signInModal.close();
+ }
+ }}
+ />
+
>
);
}
@@ -597,23 +674,16 @@ function ChangelogBox({ version }: { version: string }) {
);
return (
- 0 ? (
-
- {newerReleases.length} available update
- {newerReleases.length > 1 ? 's' : ''}
+
+
+
What's New?
+ {newerReleases.length > 0 && (
+
+ {newerReleases.length} update
+ {newerReleases.length > 1 ? 's' : ''} available
- ) : undefined
- }
- >
+ )}
+
(
-
+
{release.tag_name}
- {isNewerVersion(release.tag_name) && (
-
- Update!
-
- )}
-
+
{new Date(release.published_at).toLocaleDateString()}
@@ -682,7 +757,9 @@ function ChangelogBox({ version }: { version: string }) {
- {release.body || 'No changelog provided.'}
+ {release.body
+ ? release.body.replace(release.tag_name, '')
+ : 'No changelog provided.'}
@@ -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"
>
-
- View on GitHub
+
+
+ View on GitHub
+
+
@@ -756,7 +836,7 @@ function ChangelogBox({ version }: { version: string }) {
)}
-
+
);
}
diff --git a/packages/frontend/src/components/shared/mode-select-modal.tsx b/packages/frontend/src/components/shared/mode-select-modal.tsx
index ef92e6e2..ec1b5809 100644
--- a/packages/frontend/src/components/shared/mode-select-modal.tsx
+++ b/packages/frontend/src/components/shared/mode-select-modal.tsx
@@ -33,13 +33,13 @@ export function ModeSelectModal({ open, onOpenChange }: ModeSelectModalProps) {
-
Noob
+
Simple
Perfect for beginners! Essential options only.
-
Pro
+
Advanced
For advanced users who want full control. Access all
configuration options and advanced features for maximum
diff --git a/packages/frontend/src/components/ui/mode-switch/mode-switch.tsx b/packages/frontend/src/components/ui/mode-switch/mode-switch.tsx
index 367fb9c5..9163dcd8 100644
--- a/packages/frontend/src/components/ui/mode-switch/mode-switch.tsx
+++ b/packages/frontend/src/components/ui/mode-switch/mode-switch.tsx
@@ -43,14 +43,14 @@ export function ModeSwitch({
{/* Animated highlight */}
@@ -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
+
+ SIMPLE
+
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
+ ADVANCED
);