浏览代码

restore defaults

romanlesnikov 5 月之前
父节点
当前提交
db436ec01d
共有 51 个文件被更改,包括 382 次插入 和 933 次删除
  1. 1 0
      apps/expo/app.config.js
  2. 1 1
      apps/expo/app/playground.js
  3. 6 17
      apps/next/app/[...path]/page.js
  4. 1 3
      apps/next/app/layout.js
  5. 1 1
      apps/next/app/playground/page.js
  6. 0 24
      apps/next/app/splash-lcp-preload.js
  7. 0 4
      apps/next/app/splash-lcp-preload.native.js
  8. 0 23
      apps/next/app/splash-lcp-utils.js
  9. 1 1
      apps/next/next-env.d.ts
  10. 52 6
      packages/app/components/elements/_map.js
  11. 0 33
      packages/app/components/elements/_map.lazy.js
  12. 5 10
      packages/app/components/elements/browse.js
  13. 5 10
      packages/app/components/elements/browse_list.js
  14. 3 3
      packages/app/components/elements/messenger.js
  15. 14 17
      packages/app/components/elements/messenger/parts/common.js
  16. 23 3
      packages/app/components/form-fields/_map.js
  17. 0 18
      packages/app/components/form-fields/_map.lazy.js
  18. 9 9
      packages/app/components/form-fields/editor.js
  19. 1 3
      packages/app/components/layout.web.js
  20. 2 4
      packages/app/components/layouts.js
  21. 1 3
      packages/app/components/nav/tabs.js
  22. 15 3
      packages/app/components/page-layout/_map.js
  23. 0 17
      packages/app/components/page-layout/_map.lazy.js
  24. 1 1
      packages/app/components/page-layout/messenger.js
  25. 60 42
      packages/app/components/registry-init.js
  26. 0 28
      packages/app/components/registry-lazy.js
  27. 7 30
      packages/app/components/registry.js
  28. 1 1
      packages/app/components/units/content-list/bx_channels.js
  29. 1 1
      packages/app/components/units/content-list/bx_courses.js
  30. 1 1
      packages/app/components/units/content-list/bx_events.js
  31. 1 1
      packages/app/components/units/content-list/bx_organizations.js
  32. 1 1
      packages/app/components/units/content-list/bx_persons.js
  33. 1 1
      packages/app/components/units/content-list/bx_spaces.js
  34. 1 1
      packages/app/components/units/invitations.js
  35. 14 3
      packages/app/default/static.js
  36. 2 2
      packages/app/design/styles/palette.css
  37. 73 77
      packages/app/lib/conductor-helpers.js
  38. 3 3
      packages/app/lib/feed-items_default.js
  39. 0 2
      packages/app/lib/hooks/use-web-scroll-restore.js
  40. 0 112
      packages/app/lib/hooks/use-web-scroll-restore.web.js
  41. 0 18
      packages/app/lib/web-scroll-session.js
  42. 0 102
      packages/app/lib/web-scroll-session.web.js
  43. 8 9
      packages/app/root-client.js
  44. 0 2
      packages/app/settings/browse.js
  45. 1 1
      packages/app/settings/theme/elements.js
  46. 7 23
      packages/app/ui/atoms/link.web.tsx
  47. 3 46
      packages/app/ui/atoms/unilist.web.js
  48. 52 139
      packages/app/ui/molecules/conductor.web.js
  49. 0 15
      packages/app/ui/molecules/splash-illustration.js
  50. 0 40
      packages/app/ui/molecules/splash-illustration.web.js
  51. 3 18
      packages/app/ui/molecules/svg-file.web.js

+ 1 - 0
apps/expo/app.config.js

@@ -61,6 +61,7 @@ const expoConfig = {
             "android.permission.RECORD_AUDIO",
             "android.permission.WAKE_LOCK",
             "android.permission.ACCESS_WIFI_STATE",
+            "android.permission.FOREGROUND_SERVICE",
             "android.permission.ACCESS_FINE_LOCATION",
             "android.permission.ACCESS_COARSE_LOCATION",
             "android.permission.ACCESS_BACKGROUND_LOCATION",

+ 1 - 1
apps/expo/app/playground.js

@@ -6,7 +6,7 @@
  *   - components are registered (`registerAll()`),
  *   - the page-layout is dispatched via the registry by `data.layout`
  *     (we registered `'playground'` in
- *     `packages/app/components/page-layout/_map.lazy.js`, dev only).
+ *     `packages/app/components/page-layout/_map.js`).
  *
  * Lives at the file-based path `/playground` (more specific than
  * `[...path].js`, so it wins). Safe to delete with the page-layout file

+ 6 - 17
apps/next/app/[...path]/page.js

@@ -4,8 +4,6 @@ import Root from 'app/root-client'
 import { Suspense } from 'react'
 import { notFound } from 'next/navigation'
 import { headers } from "next/headers"
-import SplashLcpPreload from '../splash-lcp-preload'
-import { shouldPreloadSplashLcp } from '../splash-lcp-utils'
 
 const SITE_TITLE = 'NEO';
 // Settings use the historical misspelling; keep this key aligned with settings/layout.js.
@@ -250,12 +248,9 @@ export default async function Page(props) {
         }
         
         return (
-            <>
-                <SplashLcpPreload />
-                <Suspense fallback={null}>
-                    <Root settings={remote_config.data} path={'home'} data={fallbackData} uri={'/'} url={'/'} code={200} />
-                </Suspense>
-            </>
+            <Suspense fallback={null}>
+                <Root settings={remote_config.data} path={'home'} data={fallbackData} uri={'/'} url={'/'} code={200} />
+            </Suspense>
         );
     }
     
@@ -270,15 +265,9 @@ export default async function Page(props) {
         notFound(props)
     }
 
-    const pageData = data?.data;
-    const preloadSplashLcp = shouldPreloadSplashLcp(pageData, isHomePage);
-
     return (
-        <>
-            {preloadSplashLcp ? <SplashLcpPreload /> : null}
-            <Suspense fallback={null}>
-                <Root settings={remote_config.data} path={'home'} data={pageData} uri={pageData?.uri} url={pageData?.url} code={data?.code}></Root>
-            </Suspense>
-        </>
+        <Suspense fallback={null}>
+            <Root settings={remote_config.data} path={'home'} data={data?.data} uri={data?.data?.uri} url={data?.data?.url} code={data?.code}></Root>
+        </Suspense>
     )
 }

+ 1 - 3
apps/next/app/layout.js

@@ -7,7 +7,6 @@ import i18n from 'i18next';
 import { initReactI18next } from 'react-i18next';
 import { resources } from 'app/customization/translation';
 import { useEffect, useMemo } from 'react'
-import { useWebScrollRestore } from 'app/lib/hooks/use-web-scroll-restore';
 import Subscriber from 'app/ui/molecules/subscriber';
 import { useLayoutSettings } from 'app/context/layout-settings';
 import { fontVars } from 'app/customization/design/fonts/fonts-web';
@@ -22,7 +21,7 @@ const queryClient = new QueryClient({
     defaultOptions: {
         queries: {
             staleTime: 60 * 1000,
-            cacheTime: 3 * 60 * 1000,
+            gcTime: 3 * 60 * 1000, // ✅ Уменьшить до 3 минут
             refetchOnWindowFocus: false,
         },
     },
@@ -30,7 +29,6 @@ const queryClient = new QueryClient({
 
 export default function RootLayout({ children }) {
     const { langCode } = useLayoutSettings();
-    useWebScrollRestore();
 
     // Initialize i18n in useEffect to avoid setState during render
     useEffect(() => {

+ 1 - 1
apps/next/app/playground/page.js

@@ -7,7 +7,7 @@
  *   - components are registered (`registerAll()`),
  *   - the page-layout is dispatched via the registry by `data.layout`
  *     (we registered `'playground'` in
- *     `packages/app/components/page-layout/_map.lazy.js`, dev only).
+ *     `packages/app/components/page-layout/_map.js`).
  *
  * Safe to delete with the page-layout file once NeoButton is approved.
  */

+ 0 - 24
apps/next/app/splash-lcp-preload.js

@@ -1,24 +0,0 @@
-/**
- * Guest splash LCP images live in /public/svg/. Render inside `<head>` so Next.js
- * hoists preload hints into the document head (body `<link>` tags are ignored).
- */
-export default function SplashLcpPreload() {
-    return (
-        <head>
-            <link
-                rel="preload"
-                as="image"
-                href="/svg/splash-light.svg"
-                media="(prefers-color-scheme: light)"
-                fetchPriority="high"
-            />
-            <link
-                rel="preload"
-                as="image"
-                href="/svg/splash-dark.svg"
-                media="(prefers-color-scheme: dark)"
-                fetchPriority="high"
-            />
-        </head>
-    );
-}

+ 0 - 4
apps/next/app/splash-lcp-preload.native.js

@@ -1,4 +0,0 @@
-/** Native has no HTML preload; splash SVGs load via SvgFile. */
-export default function SplashLcpPreload() {
-    return null;
-}

+ 0 - 23
apps/next/app/splash-lcp-utils.js

@@ -1,23 +0,0 @@
-import { appSetting } from 'app/config';
-
-/** Guest home / splash routes: preload illustration for LCP. */
-export function shouldPreloadSplashLcp(pageData, isHomePage) {
-    if (!isHomePage || !pageData) {
-        return false;
-    }
-
-    if (pageData.user?.id || pageData.logged === 1 || pageData.logged === '1') {
-        return false;
-    }
-
-    if (pageData.layout === 'splash') {
-        return true;
-    }
-
-    if (appSetting('layout', 'use_splash_page')) {
-        return true;
-    }
-
-    const uri = pageData.uri || pageData.url || '';
-    return uri === 'home' || uri === '/' || uri === '';
-}

+ 1 - 1
apps/next/next-env.d.ts

@@ -1,6 +1,6 @@
 /// <reference types="next" />
 /// <reference types="next/image-types/global" />
-import "./.next/types/routes.d.ts";
+import "./.next/dev/types/routes.d.ts";
 
 // NOTE: This file should not be edited
 // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

+ 52 - 6
packages/app/components/elements/_map.js

@@ -1,3 +1,6 @@
+import Browse from './browse';
+import BrowseSimple from './browse_simple';
+import BrowseList from './browse_list';
 import Form from './form';
 import Msg from './msg';
 import Login from './login';
@@ -14,22 +17,59 @@ import CategoriesList from './categories_list';
 import FeedItem from './feed_item';
 import ProfileSwitcher from './profile_switcher';
 import DashboardStat from './dashboard_stat';
+import SmartGrid from './smart_grid';
 import ProfileContacts from './contacts';
 import SimpleList from './simple_list';
 import Membership from './membership';
 import Lang from './lang';
+import Invite from './invite';
+import Map from './map';
 import Menu from './menu';
+import Deploy from './deploy';
+import Calendar from './calendar';
+import Grid from './grid';
+import Pricing from './pricing';
+import Chart from './chart';
+import MultiPostForm from './multi_post_form';
 import InviteInContext from './invite_in_context';
+import Comments from './comments';
 import CommentContent from './comment_content';
 import NotificationsSettings from './notifications_settings';
+import CourseStructure from './course_structure';
+import ModuleStructure from './module_structure';
+import EditCourseContent from './edit_course_content';
+import Messenger from './messenger';
+import ProfileList from './profiles_list';
+import { ReputationSummary, ReputationWidget, ReputationLeaderboard, ReputationLevels, ReputationHistory, ReputationActions } from './reputation';
+import SearchSections from './search_sections';
+import StripeConnect from './stripe_connect';
+import Bundles from './bundles';
 
-export { elementLazyLoaders } from './_map.lazy';
-
-/** Sync element registry — keep feed, forms, entity blocks, and other hot paths here. */
 export const componentsMapDefault = {
+    messenger_main_page: Messenger,
     invite_in_context: InviteInContext,
+    search_sections: SearchSections,
+    reputation_summary: ReputationSummary,
+    reputation_leaderboard: ReputationLeaderboard,
+    reputation_levels: ReputationLevels,
+    reputation_history: ReputationHistory,
+    reputation_actions: ReputationActions,
+    reputation_widget: ReputationWidget,
+    chart: Chart,
+    bundles: Bundles,
     membership: Membership,
-    comment_content: CommentContent,
+    comment_content:CommentContent,
+    browse: Browse,
+    browse_simple: BrowseSimple,
+    browse_list: BrowseList,
+    grid: Grid,
+    pricing: Pricing,
+    invite: Invite,
+    map: Map,
+    menu: Menu,
+    deploy: Deploy,
+    calendar: Calendar,
+    comments: Comments,
     form: Form,
     msg: Msg,
     login: Login,
@@ -43,15 +83,21 @@ export const componentsMapDefault = {
     entity_cover: EntityCover,
     profile_menu: ProfileMenu,
     profile_switcher: ProfileSwitcher,
+    bento_grid: SmartGrid,
     feed_item: FeedItem,
     notifications_settings: NotificationsSettings,
     dashboard_stat: DashboardStat,
     categories_list: CategoriesList,
+    course_structure: CourseStructure,
+    edit_course_content: EditCourseContent,
+    module_structure: ModuleStructure,
     lang: Lang,
+    profiles_list: ProfileList,
     raw: Lang,
     html: Lang,
     custom: Lang,
     get_block_contacts_messenger: ProfileContacts,
+    get_create_post_form: MultiPostForm,
     simple_list: SimpleList,
-    menu: Menu,
-};
+    stripe_connect: StripeConnect
+};

+ 0 - 33
packages/app/components/elements/_map.lazy.js

@@ -1,33 +0,0 @@
-/**
- * Heavy or rare element blocks — loaded on first use via registry lazy wrappers.
- * Platform files (.web.js / .native.js) resolve automatically in each bundler.
- */
-export const elementLazyLoaders = {
-    browse: () => import('./browse'),
-    browse_simple: () => import('./browse_simple'),
-    browse_list: () => import('./browse_list'),
-    bento_grid: () => import('./smart_grid'),
-    map: () => import('./map'),
-    chart: () => import('./chart'),
-    grid: () => import('./grid'),
-    pricing: () => import('./pricing'),
-    messenger_main_page: () => import('./messenger'),
-    calendar: () => import('./calendar'),
-    deploy: () => import('./deploy'),
-    get_create_post_form: () => import('./multi_post_form'),
-    course_structure: () => import('./course_structure'),
-    module_structure: () => import('./module_structure'),
-    edit_course_content: () => import('./edit_course_content'),
-    stripe_connect: () => import('./stripe_connect'),
-    bundles: () => import('./bundles'),
-    comments: () => import('./comments'),
-    invite: () => import('./invite'),
-    search_sections: () => import('./search_sections'),
-    profiles_list: () => import('./profiles_list'),
-    reputation_summary: () => import('./reputation').then((m) => ({ default: m.ReputationSummary })),
-    reputation_leaderboard: () => import('./reputation').then((m) => ({ default: m.ReputationLeaderboard })),
-    reputation_levels: () => import('./reputation').then((m) => ({ default: m.ReputationLevels })),
-    reputation_history: () => import('./reputation').then((m) => ({ default: m.ReputationHistory })),
-    reputation_actions: () => import('./reputation').then((m) => ({ default: m.ReputationActions })),
-    reputation_widget: () => import('./reputation').then((m) => ({ default: m.ReputationWidget })),
-};

+ 5 - 10
packages/app/components/elements/browse.js

@@ -143,8 +143,7 @@ export default function Browse(props) {
         hasNextPage,
         isFetchingNextPage,
         refetch,
-        isRefetching,
-        isLoading,
+        isRefetching
     } = useInfiniteQuery({
         queryKey: qKey,
         //queryFn: fetchData,
@@ -159,7 +158,6 @@ export default function Browse(props) {
             return (lastPage?.data.length > 0 && lastPage?.cursor) ? { ...lastPage?.params, start: lastPage?.cursor } : undefined
         },
         staleTime: appSetting('browse', 'stale_time'),
-        cacheTime: appSetting('browse', 'gc_time'),
         refetchOnWindowFocus: updateMode != 'none',
         refetchOnReconnect: updateMode != 'none',
     })
@@ -378,13 +376,10 @@ export default function Browse(props) {
 
     const NoContent = getComponent('molecule', 'no_content')
 
-    const PreloadComponent = !data?.hide_empty_msg && dataItems.length === 0
-        ? (hasNextPage === false && !isLoading
-            ? <NoContent endpoint={{ request_url: data.request_url, params: {} }} />
-            : (isLoading || hasNextPage === undefined)
-                ? Preload
-                : null)
-        : null;
+    const PreloadComponent = !data?.hide_empty_msg && dataItems.length === 0 ? (hasNextPage === false
+        ? <NoContent endpoint={{ request_url: data.request_url, params: {} }} />
+        : (!dataItems.params?.loaded ? Preload : null)
+    ) : null;
 
 
     const handleOpenChange = (open) => {

+ 5 - 10
packages/app/components/elements/browse_list.js

@@ -111,8 +111,7 @@ export default function Browse(props) {
         hasNextPage,
         isFetchingNextPage,
         refetch,
-        isRefetching,
-        isLoading,
+        isRefetching
     } = useInfiniteQuery({
         queryKey: qKey,
         //queryFn: fetchData,
@@ -124,7 +123,6 @@ export default function Browse(props) {
 
         getNextPageParam: (lastPage) => lastPage?.data.length > 0 ? { ...lastPage?.params, start: lastPage?.params.start + lastPage?.params.per_page } : undefined,
         staleTime: appSetting('browse', 'stale_time'),
-        cacheTime: appSetting('browse', 'gc_time'),
         refetchOnWindowFocus: updateMode != 'none',
         refetchOnReconnect: updateMode != 'none',
     })
@@ -270,13 +268,10 @@ export default function Browse(props) {
 
     const NoContent = getComponent('molecule', 'no_content')
 
-    const PreloadComponent = !data?.hide_empty_msg && dataItems.length === 0
-        ? (hasNextPage === false && !isLoading
-            ? <NoContent endpoint={{ request_url: data.request_url, params: {} }} />
-            : (isLoading || hasNextPage === undefined)
-                ? Preload
-                : null)
-        : null;
+    const PreloadComponent = !data?.hide_empty_msg && dataItems.length === 0 ? (hasNextPage === false
+        ? <NoContent endpoint={{request_url: data.request_url, params: {} }}/>
+        : (!dataItems.params?.loaded ? Preload : null)
+    ) : null;
 
     const uniListProps = {
         preloadComponent: PreloadComponent,

+ 3 - 3
packages/app/components/elements/messenger.js

@@ -67,7 +67,7 @@ export default function MessengerEl(props) {
     const addButtons = useMemo(() => {
         if (!props.data?.config?.permissions || props.data?.config?.permissions?.create_talk == 1){
         return [
-            <CreateConvoButton key="a" onSave={onSave} variant='text' />
+            <CreateConvoButton key="a" onSave={onSave} variant='secondary' />
         ]
         }
         return null
@@ -88,7 +88,7 @@ export default function MessengerEl(props) {
     return messengerContainer;
 }
 
-const MessengerContainer = memo(({ convos, selectedMenu, url, data, pageData, windowWHeight, layout, fetchConvos, defaultConvoId, onSave, addButtons }) => {
+const MessengerContainer = memo(({ convos, selectedMenu, url, data, windowWHeight, layout, fetchConvos, defaultConvoId, onSave, addButtons }) => {
     const isWeb = Platform.OS == 'web'
     const isDesktop = useIsDesktop();
     const layoutHeaderHeight = 64;
@@ -118,7 +118,7 @@ const MessengerContainer = memo(({ convos, selectedMenu, url, data, pageData, wi
 
     return (
         <BlockWrapper >
-            <Messenger pageData={pageData} addButtons={addButtons} onSave={onSave} fetchConvos={fetchConvos} layoutHeight={height} {...data} url={url} selectedMenu={selectedMenu} convos={convos} defaultConvoId={defaultConvoId} />
+            <Messenger addButtons={addButtons} onSave={onSave} fetchConvos={fetchConvos} layoutHeight={height} {...data} url={url} selectedMenu={selectedMenu} convos={convos} defaultConvoId={defaultConvoId} />
         </BlockWrapper>
     );
 });

+ 14 - 17
packages/app/components/elements/messenger/parts/common.js

@@ -32,10 +32,9 @@ import {
 } from 'app/ui/molecules/resizable-panels'
 import { useIsDesktop } from 'app/context/measure';
 import { useSetHeader, defaultHeader } from 'app/context/jotai/layout';
-import { getComponent } from 'app/components/registry';
 import { useSafeAreaInsets } from 'app/lib/hooks/router'
 
-export default function ({ defaultConvoId, selectedMenu, convos, layoutHeight, fetchConvos, data, pageData, onSave, addButtons }) {
+export default function ({ defaultConvoId, selectedMenu, convos, layoutHeight, fetchConvos, data, onSave, addButtons }) {
     const isWeb = Platform.OS == 'web'
     const { setBottomSheetData } = useBottomSheetData();
     const [convoId, setConvoId] = useState(defaultConvoId);
@@ -387,7 +386,6 @@ export default function ({ defaultConvoId, selectedMenu, convos, layoutHeight, f
 
     const convosComponent = useMemo(() => {
         return <Convos
-            pageData={pageData}
             isSmallScreen={isSmallScreen}
             layoutHeightLeft={layoutHeightLeft}
             data={convosData}
@@ -536,7 +534,7 @@ export default function ({ defaultConvoId, selectedMenu, convos, layoutHeight, f
     )
 }
 
-const Convos = memo(({ layoutHeightLeft, data, pageData, selectedConvoIndex, changeConvo, onSave, searchValue, handleSearch, addButtons, isSmallScreen }) => {
+const Convos = memo(({ layoutHeightLeft, data, selectedConvoIndex, changeConvo, onSave, searchValue, handleSearch, addButtons, isSmallScreen }) => {
     const isWeb = Platform.OS == 'web'
     const [showSearch, setShowSearch] = useState(false);
 
@@ -550,26 +548,25 @@ const Convos = memo(({ layoutHeightLeft, data, pageData, selectedConvoIndex, cha
         handleSearch('')
     }
 
-    console.log("pageData", pageData)
     const srch = <Input  size="small" name="search" placeholder={("Search") + '...'} value={searchValue} onChangeText={(value) => handleSearch(value)} />;
-    const ContextSelector = getComponent('molecule', 'context_selector');
+
     const header = useMemo(() => (
-        <Row className={appSetting('layout', 'header', 'content')}>
+        <Row className=' bg-card px-3 gap-2 web:border-b border-border/60 gap-x-3 h-16'>
             <View className='flex-1 hidden lg:flex justify-center items-center'>
                 <View className=' w-full h-12 items-start justify-center'>
                 {srch}
                 </View>
             </View>
-            {!showSearch && <Row className={appSetting('layout', 'header', 'content_left')+' lg:hidden'}>
+            {!showSearch && <Row className='lg:hidden flex-auto  items-center '>
                 {appSetting('messenger', 'back_button') && getBackButtonWeb()}
-                { appSetting('context_selector', 'show_always') ? <><ContextSelector url={pageData?.url} uri={pageData?.uri} data={pageData?.context} /></>:  <Text className={`lg:hidden font-bold truncate flex-1  leading-12 lg:px-2 text-card-foreground text-2xl tracking-tight font-main`}>Messenger</Text>}
+                <Text className={`lg:hidden font-bold truncate flex-1  leading-12 lg:px-2 text-card-foreground text-2xl tracking-tight font-main`}>Messenger</Text>
             </Row>}
-            {showSearch && <Row className={appSetting('layout', 'header', 'content_left')+' lg:hidden'}>
+            {showSearch && <Row className='lg:hidden flex-auto  items-center '>
                 {srch}
             </Row>}
             <Row className='my-auto'>
                 <View className='lg:hidden mr-1 lg:mr-0 '>
-                    <Button size="base" startDecorator="Search" variant="text" rounded onPress={() => handleSearch2()} />
+                    <Button size="base" startDecorator="Search" variant="secondary" rounded onPress={() => handleSearch2()} />
                 </View>
                 {addButtons}
             </Row>
@@ -643,10 +640,10 @@ const Jots = memo(({ isSmallScreen, title, layoutHeightRight, data, refListJots,
 
 
     const header = useMemo(() => (
-
-            <Row className={appSetting('layout', 'header', 'content')}>
-                <Row className={appSetting('layout', 'header', 'content_left')+' lg:hidden'}>
-                    {isSmallScreen && <BackButton buttonProps={{ variant: "text", startDecorator: 'ArrowLeft', rounded: 'rounded' }} callback={showConvo} />}
+        <View className='md:px-0 w-full border-border/60 bg-card border-b h-16 justify-center'>
+            <Row className='px-2 lg:px-4 items-center justify-between w-full '>
+                <Row className='items-center justify-start gap-3 flex-1 overflow-hidden'>
+                    {isSmallScreen && <BackButton buttonProps={{ variant: "secondary", startDecorator: 'ArrowLeft', rounded: 'rounded' }} callback={showConvo} />}
                     <View className="overflow-hidden flex-1">
                     <Text numberOfLines={1} className="font-bold text-card-foreground text-2xl tracking-tight overflow-hidden text-ellipsis">{title}</Text>
                     </View>
@@ -655,7 +652,7 @@ const Jots = memo(({ isSmallScreen, title, layoutHeightRight, data, refListJots,
                     <View>
                         <DropdownMenu onSelect={(oItem) => { handleManage(oItem) }} items={menuItems}>
                             <Button
-                                variant="text"
+                                variant="secondary"
                                 size="base"
                                 rounded
 
@@ -665,7 +662,7 @@ const Jots = memo(({ isSmallScreen, title, layoutHeightRight, data, refListJots,
                     </View>
                 </Row>
             </Row>
-        
+        </View>
     ), [isSmallScreen, title, showConvo]);
 
     useEffect(() => {

+ 23 - 3
packages/app/components/form-fields/_map.js

@@ -5,25 +5,37 @@ import Password from './password';
 import Submit from './submit';
 import Switcher from './switcher';
 import TextField from './text';
+import Editor from './editor';
 import Select from './select';
+import Files from './files';
+import Location from './location';
+import Datetime from './datetime';
+import Selector from './selector';
 import BlockHeader from './block_header';
 import BlockEnd from './block_end';
 import Suggestion from './suggestion';
 import InitialMembers from './initial_members';
 import Labels from './labels';
 import InputSet from './input_set';
+import LocationRadius from './location_radius';
 import DoubleRange from './doublerange';
 import CheckboxSet from './checkbox_set';
 import Visibility from './visibility';
 import Stars from './stars';
+import MultiField from './multi_field';
+import Embed from './embed';
+import Polls from './polls';
+import StripeConnect from './stripe_connect';
 import List from './list';
 
-export { formFieldLazyLoaders } from './_map.lazy';
-
-/** Sync form fields — text, auth, and simple controls stay eager. */
 export const componentsMapDefault = {
     input_set: InputSet,
+    stripe_connect: StripeConnect,
     visibility: Visibility,
+    multi_field: MultiField,
+    embed: Embed,
+    editor: Editor,
+    polls: Polls,
     initial_members: InitialMembers,
     captcha: Captcha,
     custom: Custom,
@@ -38,8 +50,16 @@ export const componentsMapDefault = {
     phone: TextField,
     value: TextField,
     price: TextField,
+    textarea: Editor,
     select: Select,
+    select_multiple: Selector,
     radio_set: Select,
+    files: Files,
+    location: Location,
+    selector: Selector,
+    location_radius: LocationRadius,
+    datetime: Datetime,
+    datepicker: Datetime,
     suggestion: Suggestion,
     block_header: BlockHeader,
     block_end: BlockEnd,

+ 0 - 18
packages/app/components/form-fields/_map.lazy.js

@@ -1,18 +0,0 @@
-/**
- * Form fields that pull large dependencies (maps, files, rich text, payments).
- */
-export const formFieldLazyLoaders = {
-    editor: () => import('./editor'),
-    textarea: () => import('./editor'),
-    files: () => import('./files'),
-    location: () => import('./location'),
-    location_radius: () => import('./location_radius'),
-    embed: () => import('./embed'),
-    polls: () => import('./polls'),
-    stripe_connect: () => import('./stripe_connect'),
-    multi_field: () => import('./multi_field'),
-    datetime: () => import('./datetime'),
-    datepicker: () => import('./datetime'),
-    select_multiple: () => import('./selector'),
-    selector: () => import('./selector'),
-};

+ 9 - 9
packages/app/components/form-fields/editor.js

@@ -2,21 +2,21 @@
 import Field, { getValidationRules } from './_field';
 import { useController, useFormContext } from 'react-hook-form';
 import { InputMulti, TextInputClear } from 'app/design/controls'
-import { useState, useEffect, lazy, Suspense } from 'react';
-import { Loading } from 'app/customization/loading'
+import { useState, useEffect } from 'react';
+import  RftText from 'app/components/form-fields/editor-inner';
+import { Platform } from 'react-native'
 
-const RftText = lazy(() => import('app/components/form-fields/editor-inner'));
+const isWeb = Platform.OS === 'web';
 
 export default function FormFieldText(props) {
     const formContext = useFormContext();      
 
     return (
         <Field {...props} error2={formContext.formState.errors[props.name]}>
-            {props.html == 1 || props.html == 2 || props.html == 3 ? (
-                <Suspense fallback={<Loading />}>
-                    <RftText {...props} />
-                </Suspense>
-            ) : <PlainText {...props} />}
+            {props.html == 1 || props.html == 2 || props.html == 3 ? <>
+                <RftText {...props} />
+
+            </> : <PlainText {...props} />}
         </Field>
     );
 }
@@ -78,4 +78,4 @@ function PlainText(props) {
     }, [props.name, props.value]);
 
     return input
-}
+}

+ 1 - 3
packages/app/components/layout.web.js

@@ -1,11 +1,9 @@
 import React, { useEffect, useCallback, useState } from 'react';
-import dynamic from 'next/dynamic';
 import Footer from 'app/customization/nav/footer';
 import { Modal } from 'app/design/controls'
 import Informer from 'app/components/elements/informer';
+import Suggestions from 'app/ui/molecules/suggestions';
 import AsyncWorker from 'app/ui/molecules/async_worker';
-
-const Suggestions = dynamic(() => import('app/ui/molecules/suggestions'), { ssr: false });
 import { View } from 'app/design/view';
 import { useCurrentUser } from 'app/context/user'
 import BottomSheet from 'app/ui/molecules/bottomsheet_content';

+ 2 - 4
packages/app/components/layouts.js

@@ -33,11 +33,9 @@ function WindowSizeSync() {
 export default function Layouts({ path, data }) {
     const uri = data?.uri
     const url = data?.url
-
-    // Run once per mount; sync maps only — lazy wrappers materialize on first getComponent.
-    useMemo(() => {
+    const isWeb = Platform.OS === 'web';
+    if (isWeb)
         registerAll();
-    }, []);
 
     const { currentUser } = useCurrentUser();
 

+ 1 - 3
packages/app/components/nav/tabs.js

@@ -121,9 +121,7 @@ export default function Tabs() {
     const router = useRouter();
     const pathname = usePathname();
 
-    useMemo(() => {
-        registerAll();
-    }, []);
+    registerAll();
 
     const playClick = useSound('click');
     // useTranslation должен вызываться после всех других хуков, чтобы избежать проблем с порядком

+ 15 - 3
packages/app/components/page-layout/_map.js

@@ -1,22 +1,34 @@
 import PageLayoutDefault from 'app/components/page-layout/default';
 import PageCustomPost from 'app/components/page-layout/post';
 import PageCustomPostWithoutComments from 'app/components/page-layout/post-without-comments';
+import PageCustomMessenger from 'app/components/page-layout/messenger';
+import PageCustomNavigator from 'app/components/page-layout/navigator';
+import PageCustomNavigatorSearch from 'app/components/page-layout/navigator_search';
+import PageCustomProfile from 'app/components/page-layout/profile';
 import PageCustomHome from 'app/components/page-layout/home';
+import PageCustomNotif from 'app/components/page-layout/notif';
 import PageCustomCreateAccount from 'app/components/page-layout/create-account';
 import PageCustomLogin from 'app/components/page-layout/login';
 import PageUniversal from 'app/components/page-layout/universal';
+import PagePlayground from 'app/components/page-layout/playground';
+import PageWiki from 'app/components/page-layout/wiki';
 
-export { layoutLazyLoaders } from './_map.lazy';
-
-/** Sync layouts — home, auth, post, and universal CMS layouts stay on the critical path. */
 export const componentsMapDefault = {
     'default': PageLayoutDefault,
     'post': PageCustomPost,
+    'playground': PagePlayground,
     'discussion': PageCustomPost,
     'post-without-comments': PageCustomPostWithoutComments,
+    'navigator': PageCustomNavigator,
+    'navigator_search': PageCustomNavigatorSearch,
+    'messenger': PageCustomMessenger,
+    'profile': PageCustomProfile,
+    'profile-alt': PageCustomProfile,
     'home': PageCustomHome,
+    'notif': PageCustomNotif,
     'create-account': PageCustomCreateAccount,
     'login': PageCustomLogin,
+    'layout_1_column_wiki': PageWiki,
     'layout_top_area_bar_right': PageUniversal,
     'layout_topbottom_area_bar_left': PageUniversal,
     'layout_topbottom_area_bar_right': PageUniversal,

+ 0 - 17
packages/app/components/page-layout/_map.lazy.js

@@ -1,17 +0,0 @@
-/**
- * Page layouts that are not needed for the initial guest/login/home shell.
- */
-export const layoutLazyLoaders = {
-    messenger: () => import('./messenger'),
-    navigator: () => import('./navigator'),
-    navigator_search: () => import('./navigator_search'),
-    profile: () => import('./profile'),
-    'profile-alt': () => import('./profile'),
-    notif: () => import('./notif'),
-    layout_1_column_wiki: () => import('./wiki'),
-};
-
-// Dev-only layout; `import()` + NODE_ENV lets webpack/Metro drop this in production.
-if (process.env.NODE_ENV === 'development') {
-    layoutLazyLoaders.playground = () => import('./playground');
-}

+ 1 - 1
packages/app/components/page-layout/messenger.js

@@ -20,7 +20,7 @@ export default function PageLayout({ data }) {
     }, []);*/
 
     if (blockData?.content[0]?.data)
-        return <Messenger pageData={data} data={blockData.content[0].data} url={data.url} />
+        return <Messenger data={blockData.content[0].data} url={data.url} />
     else {
         const cells = Object.keys(data.elements).map((key) => (
             <Cell key={key} uri={data?.uri} url={data.url} blocks={data.elements[key]} />

+ 60 - 42
packages/app/components/registry-init.js

@@ -1,58 +1,76 @@
-import {
-    registerComponent,
-    registerLazyLoader,
-    isInited,
-    markRegistryInitialized,
-} from 'app/components/registry';
+import { registerComponent, isInited } from 'app/components/registry';
 
 import { componentsMap as FormFields } from 'app/customization/form-fields/_map';
-import { formFieldLazyLoaders } from 'app/components/form-fields/_map.lazy';
-import { componentsMap as Elements } from 'app/customization/elements/_map';
-import { elementLazyLoaders } from 'app/components/elements/_map.lazy';
-import { componentsMap as Molecules } from 'app/customization/molecules/_map';
+import { componentsMap as Elements  } from 'app/customization/elements/_map';
+import { componentsMap as Molecules} from 'app/customization/molecules/_map'
 import { componentsMap as Forms } from 'app/customization/forms/_map';
 import { componentsMap as Layouts } from 'app/customization/page-layout/_map';
-import { layoutLazyLoaders } from 'app/components/page-layout/_map.lazy';
 import { componentsMap as MenuItems } from 'app/customization/menu-items/_map';
-import { componentsMap as Units } from 'app/customization/units/_map';
-import { componentsMap as ContentList } from 'app/customization/units/content-list/_map';
-import { componentsMap as ProfileList } from 'app/customization/units/profile-list/_map';
-import { skeletonsMap as Skeletons } from 'app/customization/skeletons/_map';
+import { componentsMap as Units} from 'app/customization/units/_map';
+import { componentsMap as ContentList } from "app/customization/units/content-list/_map";
+import { componentsMap as ProfileList} from "app/customization/units/profile-list/_map";
+import { skeletonsMap as Skeletons} from 'app/customization/skeletons/_map'
 
-function registerSyncMap(type, map) {
-    for (const [name, Component] of Object.entries(map)) {
-        if (Component) {
-            registerComponent(type, name, Component);
+export function registerAll() {
+    if (!isInited()){
+        for (const [name, Component] of Object.entries(FormFields)) {
+            if (Component) {
+                registerComponent('form-field', name, Component);
+            }
+        }
+        for (const [name, Component] of Object.entries(Elements)) {
+            if (Component) {
+                registerComponent('element', name, Component);
+            }
         }
-    }
-}
 
-function registerLazyLoaders(type, loaders) {
-    for (const [name, loader] of Object.entries(loaders)) {
-        registerLazyLoader(type, name, loader);
-    }
-}
+        for (const [name, Component] of Object.entries(Molecules)) {
+            if (Component) {
+                registerComponent('molecule', name, Component);
+            }
+        }
 
-export function registerAll() {
-    if (!isInited()) {
-        registerSyncMap('form-field', FormFields);
-        registerLazyLoaders('form-field', formFieldLazyLoaders);
+        for (const [name, Component] of Object.entries(Forms)) {
+            if (Component) {
+                registerComponent('form', name, Component);
+            }
+        }
 
-        registerSyncMap('element', Elements);
-        registerLazyLoaders('element', elementLazyLoaders);
+        for (const [name, Component] of Object.entries(Layouts)) {
+            if (Component) {
+                registerComponent('layout', name, Component);
+            }
+        }
+
+        for (const [name, Component] of Object.entries(MenuItems)) {
+            if (Component) {
+                registerComponent('menu-item', name, Component);
+            }
+        }
 
-        registerSyncMap('molecule', Molecules);
-        registerSyncMap('form', Forms);
+        for (const [name, Component] of Object.entries(Units)) {
+            if (Component) {
+                registerComponent('unit', name, Component);
+            }
+        }
 
-        registerSyncMap('layout', Layouts);
-        registerLazyLoaders('layout', layoutLazyLoaders);
+        for (const [name, Component] of Object.entries(ContentList)) {
+            if (Component) {
+                registerComponent('content-list', name, Component);
+            }
+        }
 
-        registerSyncMap('menu-item', MenuItems);
-        registerSyncMap('unit', Units);
-        registerSyncMap('content-list', ContentList);
-        registerSyncMap('profile-list', ProfileList);
-        registerSyncMap('skeleton', Skeletons);
+        for (const [name, Component] of Object.entries(ProfileList)) {
+            if (Component) {
+                registerComponent('profile-list', name, Component);
+            }
+        }
 
-        markRegistryInitialized();
+        for (const [name, Component] of Object.entries(Skeletons)) {
+            if (Component) {
+                registerComponent('skeleton', name, Component);
+            }
+        }
     }
 }
+

+ 0 - 28
packages/app/components/registry-lazy.js

@@ -1,28 +0,0 @@
-'use client';
-
-import React, { Suspense, lazy } from 'react';
-
-/**
- * Wraps a dynamic import for the component registry. Registered synchronously so
- * getComponent / isComponent stay sync; the module loads on first render (Suspense).
- * Works on web (webpack) and native (Metro).
- */
-/** Minimal fallback keeps TBT lower than mounting full Loading animation trees. */
-function LazyFallback() {
-    return null;
-}
-
-export function createLazyComponent(loader) {
-    const Lazy = lazy(loader);
-
-    function LazyRegistryComponent(props) {
-        return (
-            <Suspense fallback={<LazyFallback />}>
-                <Lazy {...props} />
-            </Suspense>
-        );
-    }
-
-    LazyRegistryComponent.displayName = 'LazyRegistryComponent';
-    return LazyRegistryComponent;
-}

+ 7 - 30
packages/app/components/registry.js

@@ -1,41 +1,22 @@
-import { createLazyComponent } from 'app/components/registry-lazy';
-
 const registry = {};
-const lazyLoaders = {};
-let registryInitialized = false;
 
 export function registerComponent(type, name, component) {
+
     if (!registry[type]) {
         registry[type] = {};
     }
     registry[type][name] = component;
 }
 
-/** Store loader only — React.lazy wrapper is created on first getComponent (reduces TBT). */
-export function registerLazyLoader(type, name, loader) {
-    if (!loader) {
-        return;
-    }
-    if (!lazyLoaders[type]) {
-        lazyLoaders[type] = {};
-    }
-    lazyLoaders[type][name] = loader;
-}
-
-function ensureLazyComponent(type, name) {
-    if (registry[type]?.[name] || !lazyLoaders[type]?.[name]) {
-        return;
-    }
-    registerComponent(type, name, createLazyComponent(lazyLoaders[type][name]));
-}
-
 export function getComponent(type, name) {
-    ensureLazyComponent(type, name);
-    return registry[type]?.[name];
+    const a = registry[type][name];
+    if (!a){
+    }
+    return a;
 }
 
 export function isComponent(type, name) {
-    return !!(registry[type]?.[name] || lazyLoaders[type]?.[name]);
+    return !!registry[type][name];
 }
 
 export function getRegisteredComponents() {
@@ -43,9 +24,5 @@ export function getRegisteredComponents() {
 }
 
 export function isInited() {
-    return registryInitialized;
-}
-
-export function markRegistryInitialized() {
-    registryInitialized = true;
+    return Object.keys(registry).length !== 0;
 }

+ 1 - 1
packages/app/components/units/content-list/bx_channels.js

@@ -47,7 +47,7 @@ export default function Unit(props) {
                                 {...data.cover}
                                 alt={data.title}
                                 view="cover"
-                                className="absolute u-cover rounded-lg"
+                                className="absolute u-cover rounded-xl"
                                 sizes='auto'
                             />
                         </View>

+ 1 - 1
packages/app/components/units/content-list/bx_courses.js

@@ -50,7 +50,7 @@ export default function Unit(props) {
                                     {...data.cover}
                                     alt={data.title}
                                     view="cover"
-                                    className="absolute u-cover rounded-lg"
+                                    className="absolute u-cover rounded-xl"
                                     sizes='auto'
                                 />
 

+ 1 - 1
packages/app/components/units/content-list/bx_events.js

@@ -42,7 +42,7 @@ export default function Unit(props) {
                                 {...data.cover}
                                 alt={data.title}
                                 view="cover"
-                                className="absolute u-cover rounded-lg"
+                                className="absolute u-cover rounded-xl"
                                 sizes='auto'
                             />
                         </Skeleton>

+ 1 - 1
packages/app/components/units/content-list/bx_organizations.js

@@ -28,7 +28,7 @@ function ImageSection({ data }) {
                 src={data?.image?.src}
                 alt={data.title}
                 view="cover"
-                className="absolute u-cover rounded-lg"
+                className="absolute u-cover rounded-xl"
                 sizes='auto'
             />
             {!data?.image?.src && (

+ 1 - 1
packages/app/components/units/content-list/bx_persons.js

@@ -28,7 +28,7 @@ function ImageSection({ data }) {
                 src={data?.image?.src}
                 alt={data.title}
                 view="cover"
-                className="absolute u-cover rounded-lg"
+                className="absolute u-cover rounded-xl"
                 sizes='auto'
             />
             {!data?.image?.src && (

+ 1 - 1
packages/app/components/units/content-list/bx_spaces.js

@@ -50,7 +50,7 @@ export default function Unit(props) {
                                     {...data.cover}
                                     alt={data.title}
                                     view="cover"
-                                    className="absolute u-cover rounded-lg"
+                                    className="absolute u-cover rounded-xl"
                                     sizes='auto'
                                 />
 

+ 1 - 1
packages/app/components/units/invitations.js

@@ -36,7 +36,7 @@ export default function Unit(props) {
                                 {...data.cover}
                                 alt={data.title}
                                 view="cover"
-                                className="absolute u-cover rounded-lg"
+                                className="absolute u-cover rounded-xl"
                                 sizes='auto'
                             />
 

+ 14 - 3
packages/app/default/static.js

@@ -15,7 +15,6 @@ import ProfilesList from 'app/ui/molecules/profile_list'
 import { useThemeName } from 'app/design/theme'
 import { Platform } from 'react-native'
 import SvgFile from 'app/ui/molecules/svg-file'
-import SplashIllustration from 'app/ui/molecules/splash-illustration'
 import MenuFooter from 'app/components/nav/menu-footer'
 import { appSetting } from 'app/lib/util'
 const isWeb = Platform.OS === 'web'
@@ -124,7 +123,13 @@ const SplashTextComponent = (props) => {
 
             <View className="relative flex-auto h-48 w-48">
 
-                <SplashIllustration />
+                <SvgFile
+                    src_dark="splash-dark.svg"
+                    src_default="splash-light.svg"
+                    colorize
+                    className="text-secondary-foreground"
+                    alt="Splash screen illustration"
+                />
 
             </View>
             <View className="flex-auto w-full items-center lg:items-start">
@@ -134,7 +139,13 @@ const SplashTextComponent = (props) => {
     ) : (
         <View className=" items-center lg:items-start flex-auto gap-6 w-full mx-auto">
             <View className="flex-auto w-64 h-64">
-                <SplashIllustration />
+                <SvgFile
+                    src_dark="splash-dark.svg"
+                    src_default="splash-light.svg"
+                    colorize
+                    className="text-secondary-foreground"
+                    alt="Splash screen illustration"
+                />
             </View>
             <View className="flex-auto w-full">
                 <SplashTextInner />

+ 2 - 2
packages/app/design/styles/palette.css

@@ -51,7 +51,7 @@
 
         /* Semantic — explicit light/dark variants for Uniwind parity */
         @variant light {
-            --default: var(--neutral-100);
+            --default: var(--white);
             --default-foreground: var(--neutral-950);
             --background: var(--neutral-100);
             --foreground: var(--neutral-950);
@@ -89,7 +89,7 @@
 
         /* Semantic — dark overrides */
         @variant dark {
-            --default: var(--neutral-950);
+            --default: var(--neutral-900);
             --default-foreground: var(--neutral-50);
             --background: var(--neutral-950);
             --foreground: var(--neutral-50);

+ 73 - 77
packages/app/lib/conductor-helpers.js

@@ -88,62 +88,39 @@ export async function fetchUniListData({ pageParam, requestUrl, defaultParams })
 }
 
 export const refetchUniListReducer = (state, action) => {
-    const items = state?.visibleItems ?? []
     switch (action.type) {
         case 'SET_ITEMS':
             return {
-                visibleItems: action.items ?? [],
+                visibleItems: action.items,
                 hasNewData: false
             }
         case 'PREPEND_ITEM':
             return {
-                visibleItems: [action.item, ...items],
-                hasNewData: state?.hasNewData ?? false
+                ...state,
+                visibleItems: [action.item, ...state.visibleItems]
             }
         case 'APPEND_ITEM':
             return {
-                visibleItems: [...items, action.item],
-                hasNewData: state?.hasNewData ?? false
+                ...state,
+                visibleItems: [...state.visibleItems, action.item]
             }
         case 'REMOVE_ITEM':
             return {
-                visibleItems: items.filter((item) => item.id != action.id),
-                hasNewData: state?.hasNewData ?? false
+                ...state,
+                visibleItems: state.visibleItems.filter(item => item.id != action.id)
             }
         case 'SHOW_NEW_DATA':
             return {
-                visibleItems: items,
+                ...state,
                 hasNewData: true
             }
         default:
-            return {
-                visibleItems: items,
-                hasNewData: state?.hasNewData ?? false
-            }
+            return state
     }
 }
 
 export const flattenPagesForUniList = (pagesData) => (pagesData?.pages ?? []).flatMap((p) => p.data ?? [])
 
-export function getBrowseInfiniteQueryKey(pageRoute, { keyword = '', ts, timestamp } = {}) {
-    return [
-        pageRoute?.endpoint?.request_url,
-        pageRoute?.link,
-        keyword,
-        JSON.stringify(pageRoute?.endpoint?.params?.filters),
-        ts,
-        timestamp,
-    ]
-}
-
-export function getCachedUniListItems(queryClient, queryKey) {
-    if (!queryClient || !queryKey?.[0]) {
-        return []
-    }
-    const cached = flattenPagesForUniList(queryClient.getQueryData(queryKey))
-    return Array.isArray(cached) ? cached : []
-}
-
 export const isSameItemsForUniList = (a, b) => {
     if (a.length !== b.length) return false
     for (let i = 0; i < a.length; i++) {
@@ -318,46 +295,78 @@ export async function parseData(routes, index, setRoutes, newData) {
 
 export async function fetchAndUpdateData(routes, index, setRoutes) {
     const currentRoute = routes.find((item) => item.index === index)
-    if (!currentRoute || currentRoute.inited) {
-        return
-    }
-    let link = currentRoute.link
-    if (currentRoute.link.includes('?')) {
-        const urlObj = parseUrl(currentRoute.link)
-
-        let obj = parseQueryString(urlObj.queryString)
-        link =
-            urlObj.path.replace('/', '') +
-            '&params[]=&params[]=' +
-            JSON.stringify(obj)
-    }
+    if (!currentRoute.inited) {
+        let link = currentRoute.link
+        if (currentRoute.link.includes('?')) {
+            const urlObj = parseUrl(currentRoute.link) 
+
+            let obj = parseQueryString(urlObj.queryString)
+            link =
+                urlObj.path.replace('/', '') +
+                '&params[]=&params[]=' +
+                JSON.stringify(obj)
+        }
 
-    const sResponse = await fetcher(
-        '/api.php?r=system/get_page_by_request/TemplServicePages&params[]=' +
-        link
-    )
+        const sResponse = await fetcher(
+            '/api.php?r=system/get_page_by_request/TemplServicePages&params[]=' +
+            link
+        )
 
-    const pageData = sResponse?.data
-    const settings = getPageSettings(pageData?.config, getURI(currentRoute.link))
+        const settings = getPageSettings(sResponse.data?.config, getURI(currentRoute.link));
 
-    const blocks = settings?.blocks || getBlocksFromData(pageData)
+        const blocks = settings?.blocks || getBlocksFromData(sResponse.data)
 
-    const contentAndEndpoint = processUrl(pageData, settings?.blocks)
+        const contentAndEndpoint = processUrl(sResponse.data, settings?.blocks)
 
-    addMoreData(
-        contentAndEndpoint.content,
-        contentAndEndpoint.endpoint,
-        setRoutes,
-        index,
-        blocks,
-        contentAndEndpoint.sidebar,
-        contentAndEndpoint.leftbar,
-        pageData
-    )
+        addMoreData(
+            contentAndEndpoint.content,
+            contentAndEndpoint.endpoint,
+            setRoutes,
+            index,
+            blocks,
+            contentAndEndpoint.sidebar,
+            contentAndEndpoint.leftbar,
+            sResponse.data
+        )
+    }
 }
 /* new logic */
 export async function getDataForRoute(routes, index, setRoutes) {
-    return fetchAndUpdateData(routes, index, setRoutes)
+    const currentRoute = routes.find((item) => item.index === index)
+    if (!currentRoute.inited) {
+        let link = currentRoute.link
+        if (currentRoute.link.includes('?')) {
+            const urlObj = parseUrl(currentRoute.link) 
+
+            let obj = parseQueryString(urlObj.queryString)
+            link =
+                urlObj.path.replace('/', '') +
+                '&params[]=&params[]=' +
+                JSON.stringify(obj)
+        }
+
+        const sResponse = await fetcher(
+            '/api.php?r=system/get_page_by_request/TemplServicePages&params[]=' +
+            link
+        )
+
+        const settings = getPageSettings(sResponse.data?.config, getURI(currentRoute.link));
+
+        const blocks = settings?.blocks || getBlocksFromData(sResponse.data)
+
+        const contentAndEndpoint = processUrl(sResponse.data, settings?.blocks)
+
+        addMoreData(
+            contentAndEndpoint.content,
+            contentAndEndpoint.endpoint,
+            setRoutes,
+            index,
+            blocks,
+            contentAndEndpoint.sidebar,
+            contentAndEndpoint.leftbar,
+            sResponse.data
+        )
+    }
 }
 
 export function addMoreData(
@@ -472,17 +481,7 @@ function mapLayoutBlocks(items = []) {
     return { content: content, endpoint: endpoint?.content?.[0].data }
 }
 
-const EMPTY_PROCESS_URL_RESULT = {
-    content: [],
-    endpoint: null,
-    sidebar: { endpoint: null, content: [] },
-    leftbar: { endpoint: null, content: [] },
-}
-
 function processParsedUrl(data, blocks) {
-    if (!data?.elements) {
-        return EMPTY_PROCESS_URL_RESULT
-    }
     const cellCenter = mapLayoutBlocks(data.elements.cell_center);
     const cellRight = mapLayoutBlocks(data.elements.cell_right);
     const cellLeft = mapLayoutBlocks(data.elements.cell_left);
@@ -495,9 +494,6 @@ function processParsedUrl(data, blocks) {
 }
 
 export function processUrl(data, blocks) {
-    if (!data) {
-        return EMPTY_PROCESS_URL_RESULT
-    }
     if (data.layout_parsed){
         const a = processParsedUrl(data, blocks);
         return a;

+ 3 - 3
packages/app/lib/feed-items_default.js

@@ -260,7 +260,7 @@ export const PostsView = memo(({ data, styles, url, isCompact, content_attach, f
                             {...data.mainImage}
                             alt={data.title}
                             view="cover"
-                            className="u-cover rounded-lg"
+                            className="u-cover rounded-xl"
                             sizes={LAYOUT_BREAKPOINTS.md}
                         />
                     </View>
@@ -336,7 +336,7 @@ export const DefaultView = memo(
                                 {...data.mainImage}
                                 alt={data.title}
                                 view="cover"
-                                className=" u-cover rounded-lg "
+                                className=" u-cover rounded-xl "
                                 sizes={LAYOUT_BREAKPOINTS.md}
                             />
                         </View>
@@ -472,7 +472,7 @@ export const PollView = memo(
                                     {...data.mainImage}
                                     alt={data.title}
                                     view="cover"
-                                    className=" u-cover rounded-lg "
+                                    className=" u-cover rounded-xl "
                                     sizes={LAYOUT_BREAKPOINTS.md}
                                 />
                             </View>

+ 0 - 2
packages/app/lib/hooks/use-web-scroll-restore.js

@@ -1,2 +0,0 @@
-/** Native: window scroll restore is web-only. */
-export function useWebScrollRestore() {}

+ 0 - 112
packages/app/lib/hooks/use-web-scroll-restore.web.js

@@ -1,112 +0,0 @@
-import { useEffect, useRef } from 'react';
-import { usePathname } from 'next/navigation';
-import {
-    getWebScrollKey,
-    readWebScroll,
-    writeWebScroll,
-    restoreWebScroll,
-} from 'app/lib/web-scroll-session.web';
-
-function isInternalHref(href) {
-    if (!href || href.startsWith('#') || href.startsWith('mailto:') || href.startsWith('tel:')) {
-        return false;
-    }
-    if (href.startsWith('/')) {
-        return true;
-    }
-    try {
-        const url = new URL(href, window.location.origin);
-        return url.origin === window.location.origin;
-    } catch {
-        return false;
-    }
-}
-
-/**
- * Lives in the root Next layout (survives page navigations). Saves window scroll per URL,
- * restores on return. Works with scroll={false} on Link so Next does not zero scroll before save.
- */
-export function useWebScrollRestore() {
-    const pathname = usePathname();
-    const pathnameRef = useRef(pathname);
-    const cancelRestoreRef = useRef(null);
-    const saveRafRef = useRef(0);
-
-    useEffect(() => {
-        if (typeof window === 'undefined') {
-            return;
-        }
-        history.scrollRestoration = 'manual';
-    }, []);
-
-    useEffect(() => {
-        pathnameRef.current = pathname;
-
-        const key = getWebScrollKey(pathname, window.location.search);
-        const savedY = readWebScroll(key);
-
-        if (cancelRestoreRef.current) {
-            cancelRestoreRef.current();
-        }
-
-        if (savedY != null) {
-            cancelRestoreRef.current = restoreWebScroll(key);
-        } else {
-            window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
-        }
-
-        const onScroll = () => {
-            if (saveRafRef.current) {
-                return;
-            }
-            saveRafRef.current = requestAnimationFrame(() => {
-                saveRafRef.current = 0;
-                writeWebScroll(
-                    getWebScrollKey(pathnameRef.current, window.location.search),
-                    window.scrollY || document.documentElement.scrollTop || 0
-                );
-            });
-        };
-
-        const onPointerDown = (event) => {
-            const anchor = event.target?.closest?.('a[href]');
-            if (!anchor || anchor.target === '_blank') {
-                return;
-            }
-            const href = anchor.getAttribute('href');
-            if (!isInternalHref(href)) {
-                return;
-            }
-            writeWebScroll(
-                getWebScrollKey(pathname, window.location.search),
-                window.scrollY || document.documentElement.scrollTop || 0
-            );
-        };
-
-        const onPageHide = () => {
-            writeWebScroll(
-                getWebScrollKey(pathname, window.location.search),
-                window.scrollY || document.documentElement.scrollTop || 0
-            );
-        };
-
-        window.addEventListener('scroll', onScroll, { passive: true });
-        document.addEventListener('pointerdown', onPointerDown, true);
-        window.addEventListener('pagehide', onPageHide);
-
-        return () => {
-            if (cancelRestoreRef.current) {
-                cancelRestoreRef.current();
-                cancelRestoreRef.current = null;
-            }
-            cancelAnimationFrame(saveRafRef.current);
-            window.removeEventListener('scroll', onScroll);
-            document.removeEventListener('pointerdown', onPointerDown, true);
-            window.removeEventListener('pagehide', onPageHide);
-            writeWebScroll(
-                getWebScrollKey(pathname, window.location.search),
-                window.scrollY || document.documentElement.scrollTop || 0
-            );
-        };
-    }, [pathname]);
-}

+ 0 - 18
packages/app/lib/web-scroll-session.js

@@ -1,18 +0,0 @@
-/** Web scroll session helpers — native no-op stubs. */
-export function getWebScrollKey() {
-    return '';
-}
-
-export function readWebScroll() {
-    return null;
-}
-
-export function writeWebScroll() {}
-
-export function restoreWebScroll() {
-    return () => {};
-}
-
-export function scheduleWebScrollRestore() {
-    return () => {};
-}

+ 0 - 102
packages/app/lib/web-scroll-session.web.js

@@ -1,102 +0,0 @@
-const SCROLL_PREFIX = 'neo-scroll:';
-const VIRTUOSO_PREFIX = 'neo-virtuoso:';
-
-const DEFAULT_RESTORE_DELAYS_MS = [0, 50, 150, 300, 500, 800, 1200, 1800, 2500];
-
-export function getWebScrollKey(pathname, search) {
-    if (typeof window === 'undefined') {
-        return '';
-    }
-    const path = pathname ?? window.location.pathname ?? '/';
-    const query = search ?? window.location.search ?? '';
-    return `${path}${query}`;
-}
-
-export function readWebScroll(key) {
-    if (!key || typeof sessionStorage === 'undefined') {
-        return null;
-    }
-    const raw = sessionStorage.getItem(SCROLL_PREFIX + key);
-    if (raw == null) {
-        return null;
-    }
-    const y = Number(raw);
-    return Number.isFinite(y) && y > 0 ? y : null;
-}
-
-/** Never overwrite a saved position with 0 unless allowClear (explicit reset). */
-export function writeWebScroll(key, y, { allowClear = false } = {}) {
-    if (!key || typeof sessionStorage === 'undefined') {
-        return;
-    }
-    const rounded = Math.max(0, Math.round(y));
-    if (rounded === 0 && !allowClear) {
-        const prev = readWebScroll(key);
-        if (prev != null && prev > 0) {
-            return;
-        }
-    }
-    sessionStorage.setItem(SCROLL_PREFIX + key, String(rounded));
-}
-
-export function readVirtuosoState(storageKey) {
-    if (!storageKey || typeof sessionStorage === 'undefined') {
-        return null;
-    }
-    try {
-        const raw = sessionStorage.getItem(VIRTUOSO_PREFIX + storageKey);
-        return raw ? JSON.parse(raw) : null;
-    } catch {
-        return null;
-    }
-}
-
-export function writeVirtuosoState(storageKey, state) {
-    if (!storageKey || !state?.ranges?.length || typeof sessionStorage === 'undefined') {
-        return;
-    }
-    try {
-        sessionStorage.setItem(VIRTUOSO_PREFIX + storageKey, JSON.stringify(state));
-    } catch {
-        // Quota or serialization — ignore.
-    }
-}
-
-export function restoreWebScroll(key, { delays = DEFAULT_RESTORE_DELAYS_MS } = {}) {
-    const y = readWebScroll(key);
-    if (y == null || typeof window === 'undefined') {
-        return () => {};
-    }
-
-    const apply = () => {
-        window.scrollTo({ top: y, left: 0, behavior: 'auto' });
-    };
-
-    const timers = delays.map((delay) =>
-        setTimeout(() => {
-            const tallEnough =
-                document.documentElement.scrollHeight >= y + window.innerHeight * 0.25;
-            if (tallEnough || delay >= 500) {
-                apply();
-            }
-        }, delay)
-    );
-
-    return () => {
-        timers.forEach(clearTimeout);
-    };
-}
-
-let activeRestoreCancel = null;
-
-export function scheduleWebScrollRestore(key) {
-    if (typeof window === 'undefined') {
-        return () => {};
-    }
-    const scrollKey = key ?? getWebScrollKey();
-    if (activeRestoreCancel) {
-        activeRestoreCancel();
-    }
-    activeRestoreCancel = restoreWebScroll(scrollKey);
-    return activeRestoreCancel;
-}

+ 8 - 9
packages/app/root-client.js

@@ -1,15 +1,14 @@
 'use client'
 import dynamic from 'next/dynamic'
 import { Loading } from 'app/customization/loading'
-
-// ssr: false — Root intentionally renders null until useEffect syncs Zustand user from
-// page props; SSR would only emit an empty shell. Keeps the app/root chunk client-only
-// (code-split) without server-running the full registry tree.
-const RootDyn = dynamic(() => import('app/root').then((m) => m.Root), {
+import { Root } from 'app/root'
+// SSR enabled for faster initial page load - server pre-renders the HTML
+// The loading fallback only shows during client-side navigation while chunk loads
+/*const RootDyn = dynamic(() => import('app/root').then(m => m.Root), {
     ssr: false,
     loading: () => <Loading />,
-})
-
+})*/
 export default function RootClient(props) {
-    return <RootDyn {...props} />
-}
+    //return <RootDyn {...props} />
+    return <Root {...props} />
+}

+ 0 - 2
packages/app/settings/browse.js

@@ -4,8 +4,6 @@ export const settingsBrowse = {
     browse: {
         new_skeletons : true,
         stale_time: 30000,
-        /** Keep inactive browse-tab query data warm while switching conductor modules */
-        gc_time: 30 * 60 * 1000,
         per_line: [/* only for images for now*/ 
             { width: 1280, count: 4 },
             { width: 1024, count: 4 },

+ 1 - 1
packages/app/settings/theme/elements.js

@@ -3,7 +3,7 @@
 export const settingsElements = {
     conductor: {
         menu: ' w-full items-left justify-center ',
-        menu_max_width: ' w-full max-w-7xl mx-auto',
+        menu_max_width: ' w-full max-w-7xl px-3 lg:px-4 mx-auto',
         
         content_max_width: ' w-full max-w-7xl mx-auto ',
         content_max_width_nav: ' w-full max-w-screen-2xl  ',

+ 7 - 23
packages/app/ui/atoms/link.web.tsx

@@ -2,9 +2,7 @@ import Link from 'next/link'
 import { Pressable } from 'app/design/view'
 import { useRouter } from 'app/lib/hooks/router'
 import { useCallback, useMemo } from 'react';
-import { usePathname } from 'next/navigation';
 import { appSetting, cn, sanitazeUrl } from 'app/lib/util'
-import { getWebScrollKey, writeWebScroll } from 'app/lib/web-scroll-session.web'
 
 
 // Variants and sizes from theme
@@ -31,36 +29,22 @@ export default function ElementLink({
 }) {
 
     const router = useRouter();
-    const pathname = usePathname();
     const href = sanitazeUrl(hrefProp);
 
     // Convert alt to aria-label (alt is not valid for <a> elements)
     const accessibleLabel = alt || (typeof children === 'string' ? children : undefined);
 
-    /** Persist scroll for the page being left (current URL), restored when user returns. */
-    const saveScrollForCurrentPage = useCallback(() => {
-        if (typeof window === 'undefined') {
-            return;
-        }
-        writeWebScroll(
-            getWebScrollKey(pathname, window.location.search),
-            window.scrollY || document.documentElement.scrollTop || 0
-        );
-    }, [pathname]);
-
     const handlePress = useCallback((event) => {
         if (onPress) onPress(event);
         if (href) {
-            saveScrollForCurrentPage();
-            router.push(href, { scroll: false });
+            router.push(href);
             event?.preventDefault?.();
         }
-    }, [href, router, onPress, saveScrollForCurrentPage]);
+    }, [href, router, onPress]);
 
-    const handleLinkClick = useCallback((event) => {
-        saveScrollForCurrentPage();
-        if (onClick) onClick(event);
-    }, [onClick, saveScrollForCurrentPage]);
+    const handleLinkClick = useCallback((event) => {  
+        if (onClick) onClick();
+    }, [onClick, target, href]);
 
     const sizeClass = (() => {
         if (!size) return '';
@@ -100,7 +84,8 @@ export default function ElementLink({
                 href={href}
                 className={className}
                 prefetch={isPrefetch}
-                scroll={false}
+               
+              
             >
                 {children}
             </Link>
@@ -113,7 +98,6 @@ export default function ElementLink({
             {...rest}
             className={composedClassName}
             prefetch={isPrefetch}
-            scroll={false}
             onClick={handleLinkClick}
             aria-label={accessibleLabel}
         >

+ 3 - 46
packages/app/ui/atoms/unilist.web.js

@@ -7,42 +7,15 @@ import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd";
 import { useCallback, forwardRef, useMemo } from 'react';
 import { useBreakpoint } from 'app/context/measure'
 import { paddingForList } from 'app/customization/functions';
-import {
-    readVirtuosoState,
-    writeVirtuosoState,
-    scheduleWebScrollRestore,
-} from 'app/lib/web-scroll-session.web';
 
 export default function UniList(props) {
     let { useCustomScrollHandler, preloadComponent, isModal, sortable, data: rawData, renderItem, onEndReached, onStartReached, maxToRenderPerBatch, initialNumToRender, contentContainerStyle, initialScrollIndex, ListHeaderComponent, ListFooterComponent, refer, onScrollToIndex,
-        onSort, mode, layout, numColumns, keyboardShouldPersistTaps, keyExtractor, useWindowScroll: useWindowScrollProp, height, listState, endpoint, viewParams, topItemCount, scrollToLastItem, refreshing, onRefresh, isInPanel, paddingTop, storagekey, rangeChanged: rangeChangedProp, stateChanged: _stateChangedProp, ...rest } = props
+        onSort, mode, layout, numColumns, keyboardShouldPersistTaps, keyExtractor, useWindowScroll: useWindowScrollProp, height, listState, endpoint, viewParams, topItemCount, scrollToLastItem, refreshing, onRefresh, isInPanel, paddingTop, ...rest } = props
 
     const uniRef = useRef();
     const currentBreakpoint = useBreakpoint();
 
     const data = useMemo(() => rawData.filter((v, i, a) => a.findIndex(t => (t.id === v.id)) === i), [rawData]);
-
-    const virtuosoRestoreState = useMemo(() => {
-        if (listState?.ranges) {
-            return listState;
-        }
-        if (storagekey) {
-            return readVirtuosoState(storagekey);
-        }
-        return null;
-    }, [listState, storagekey]);
-
-    const listRef = refer ?? uniRef;
-
-    const persistVirtuosoState = useCallback(() => {
-        if (!storagekey || !listRef?.current?.getState) {
-            return;
-        }
-        listRef.current.getState((state) => {
-            writeVirtuosoState(storagekey, state);
-        });
-    }, [storagekey, listRef]);
-
     const isGridMode = mode != 'simple' && !sortable;
     const [isGridReady, setIsGridReady] = useState(!preloadComponent);
     const [showContent, setShowContent] = useState(!preloadComponent);
@@ -174,18 +147,6 @@ export default function UniList(props) {
     // Always use window scroll if no height is resolved to prevent zero-sized element errors
     const isWindowScroll = hasResolvedHeight ? shouldUseWindowScroll : true;
 
-    useEffect(() => {
-        if (showContent && isWindowScroll) {
-            scheduleWebScrollRestore();
-        }
-    }, [showContent, isWindowScroll]);
-
-    useEffect(() => {
-        return () => {
-            persistVirtuosoState();
-        };
-    }, [persistVirtuosoState]);
-
     // Only set height style if we have a valid height AND we're not using window scroll
     // This prevents virtuoso from receiving conflicting signals
     let style = (normalizedHeight && !isWindowScroll) ? { height: normalizedHeight } : {};
@@ -219,11 +180,7 @@ export default function UniList(props) {
         data,
         useWindowScroll: isWindowScroll,
         style: virtuosoStyle,
-        ref: listRef,
-        rangeChanged: (range) => {
-            persistVirtuosoState();
-            rangeChangedProp?.(range);
-        },
+        ref: refer ? refer : uniRef,
         startReached: onStartReached,
         endReached: onEndReached,
         overscan: props.unit == 'notifications' ? 100 : 900,
@@ -345,7 +302,7 @@ export default function UniList(props) {
                             <Virtuoso
                                 itemContent={itemContent}
                                 {...commonVirtuosoProps}
-                                {...(virtuosoRestoreState?.ranges && { restoreStateFrom: virtuosoRestoreState })}
+                                {...(listState?.ranges && { restoreStateFrom: listState })}
                                 {...(scrollToLastItem && { initialTopMostItemIndex: data.length })}
                                 endReached={onEndReached}
                             />

+ 52 - 139
packages/app/ui/molecules/conductor.web.js

@@ -19,15 +19,13 @@ import {
     isSameItemsForUniList,
     flattenPagesForUniList,
     fetchUniListData,
-    getBrowseInfiniteQueryKey,
-    getCachedUniListItems,
     Addon,
     getAddon
 } from 'app/lib/conductor-helpers'
 import { ItemRenderer } from 'app/components/item-renderer'
 import { Button } from 'app/design/controls'
 import Link from 'app/ui/atoms/link'
-import { useInfiniteQuery, useQueryClient } from '@tanstack/react-query'
+import { useInfiniteQuery } from '@tanstack/react-query'
 import { getSkeletonForList } from 'app/lib/skeleton-helpers'
 import { BlockByName } from 'app/components/block'
 import { useTranslation } from 'react-i18next'
@@ -121,20 +119,6 @@ export function Conductor({
     const playClick = useSound('click');
     const [index, _setIndex] = useState(initialIndex)
     const [prevIndex, setPrevIndex] = useState(initialIndex)
-    const [mountedTabIndices, setMountedTabIndices] = useState(
-        () => new Set([initialIndex])
-    )
-
-    useEffect(() => {
-        setMountedTabIndices((prev) => {
-            if (prev.has(index)) {
-                return prev
-            }
-            const next = new Set(prev)
-            next.add(index)
-            return next
-        })
-    }, [index])
 
     const setIndex = (newIndex) => {
         playClick();
@@ -395,42 +379,23 @@ export function Conductor({
         </View>
     </View> : null
 
-    const CenterColumnContent = (
-        <View className="w-full">
-            {routes.map((route) => {
-                if (!mountedTabIndices.has(route.index)) {
-                    return null
-                }
-                const isActive = route.index === index
-                return (
-                    <View
-                        key={route.link ?? route.key ?? route.index}
-                        className={isActive ? 'w-full' : 'hidden'}
-                        aria-hidden={!isActive}
-                    >
-                        <TabSceneMainContent
-                            pageRoute={route}
-                            isInited={route.inited}
-                            isActive={isActive}
-                            isCover={isCover}
-                            headerHeight={
-                                showFiltersBtn && routes.length > 1
-                                    ? defaultHeaderHeight + 52
-                                    : defaultHeaderHeight
-                            }
-                            header={isUseCurrentHeader ? null : headerComponent}
-                            isUseCurrentHeader={isUseCurrentHeader}
-                            onFormChangedValues={onFormChangedValues}
-                            keyword={keyword}
-                            ts={ts}
-                            timestamp={timestamp}
-                            skeleton={skeleton}
-                        />
-                    </View>
-                )
-            })}
-        </View>
-    )
+    const CenterColumnContent = <TabSceneMainContent
+        pageRoute={tabRoute}
+        isInited={currentRoute.inited}
+        isCover={isCover}
+        headerHeight={
+            showFiltersBtn && routes.length > 1
+                ? defaultHeaderHeight + 52
+                : defaultHeaderHeight
+        }
+        header={isUseCurrentHeader ? null : headerComponent}
+        isUseCurrentHeader={isUseCurrentHeader}
+        onFormChangedValues={onFormChangedValues}
+        keyword={keyword}
+        ts={ts}
+        timestamp={timestamp}
+        skeleton={skeleton}
+    />
 
     return (
         <View className="w-full h-full" scrollEnabled={false}>
@@ -461,7 +426,6 @@ const TabSceneMainContent = ({
     pageRoute,
     header,
     isInited,
-    isActive = true,
     skeleton,
     keyword,
     ts,
@@ -473,7 +437,6 @@ const TabSceneMainContent = ({
     const pageData = pageRoute.pageData
     const uniRef = useRef()
     const dataItemsPage = pageRoute?.data;
-    const queryClient = useQueryClient()
 
     const setHeader = useSetHeader();
 
@@ -482,37 +445,24 @@ const TabSceneMainContent = ({
         return type === 'default' ? getUnitType(pageRoute) : type
     }, [pageRoute?.endpoint, pageRoute?.blocks])
 
-    const qKey = useMemo(
-        () => getBrowseInfiniteQueryKey(pageRoute, { keyword, ts, timestamp }),
-        [
-            pageRoute?.endpoint?.request_url,
-            pageRoute?.link,
-            keyword,
-            pageRoute?.endpoint?.params?.filters,
-            ts,
-            timestamp,
-        ]
-    )
-
-    const [refetchState, dispatch] = useReducer(refetchUniListReducer, () => {
-        const items = getCachedUniListItems(queryClient, qKey)
-        return {
-            visibleItems: items,
-            hasNewData: false,
-        }
+    const [refetchState, dispatch] = useReducer(refetchUniListReducer, {
+        visibleItems: [],
+        hasNewData: false
+    })
+    const refetchRef = useRef({
+        skipToast: false,
+        isFirstLoad: true,
+        prevItems: []
     })
-    const refetchRef = useRef(null)
-    if (!refetchRef.current) {
-        const items = getCachedUniListItems(queryClient, qKey)
-        refetchRef.current = {
-            skipToast: false,
-            isFirstLoad: items.length === 0,
-            prevItems: items,
-        }
-    }
 
-    const browseStaleTime = appSetting('browse', 'stale_time')
-    const browseGcTime = appSetting('browse', 'gc_time')
+    const qKey = [
+        pageRoute?.endpoint?.request_url,
+        pageRoute.link,
+        keyword,
+        JSON.stringify(pageRoute?.endpoint?.params?.filters),
+        ts,
+        timestamp
+    ]
 
     const {
         data: pagesData,
@@ -520,8 +470,7 @@ const TabSceneMainContent = ({
         hasNextPage,
         isFetchingNextPage,
         refetch,
-        isRefetching,
-        isLoading,
+        isRefetching
     } = useInfiniteQuery({
         queryKey: qKey,
         queryFn: ({ pageParam }) => fetchUniListData({
@@ -533,11 +482,10 @@ const TabSceneMainContent = ({
         getNextPageParam: (lastPage) => {
             return (lastPage?.data.length > 0 && lastPage?.cursor) ? { ...lastPage?.params, start: lastPage?.cursor } : undefined
         },
-        staleTime: browseStaleTime,
-        cacheTime: browseGcTime,
-        refetchOnWindowFocus: isActive,
-        refetchOnReconnect: isActive,
-        enabled: isActive && !!pageRoute?.endpoint?.request_url,
+        staleTime: appSetting('browse', 'stale_time'),
+        refetchOnWindowFocus: true,
+        refetchOnReconnect: true,
+        enabled: !!pageRoute?.endpoint?.request_url
     })
 
     const test = () => {
@@ -565,17 +513,13 @@ const TabSceneMainContent = ({
 
     useEffect(() => {
         const subscription = emitter.addListener(`page`, (data) => {
-            if (!isActive || data.action != 'reload') {
-                return
+            if (data.action == 'reload') {
+                refetchRef.current.skipToast = true
+                refetch();
             }
-            refetchRef.current.skipToast = true
-            refetch();
         })
 
         const subscription2 = emitter.addListener('feed', (data) => {
-            if (!isActive) {
-                return
-            }
             if (data.action == 'remove_content') {
                 dispatch({ type: 'REMOVE_ITEM', id: data.id })
                 if (refetchRef.current?.prevItems) {
@@ -596,16 +540,11 @@ const TabSceneMainContent = ({
             subscription.remove();
             subscription2.remove()
         }
-    }, [isActive, pageRoute?.endpoint?.params?.owner_id, refetch])
+    }, [])
 
     useEffect(() => {
-        const items = getCachedUniListItems(queryClient, qKey)
-        refetchRef.current = {
-            skipToast: true,
-            isFirstLoad: items.length === 0,
-            prevItems: items,
-        }
-    }, [qKey, queryClient])
+        refetchRef.current.skipToast = true
+    }, [qKey])
 
 
     const handleEndReached = useCallback(
@@ -723,14 +662,8 @@ const TabSceneMainContent = ({
 
     const Form = getComponent('element', 'form');
     const formProps = pageRoute?.endpoint?.filters;
-    const hasVisibleListItems = (refetchState?.visibleItems?.length ?? 0) > 0
-    const isAwaitingFirstListPage =
-        !!pageRoute?.endpoint?.request_url && hasNextPage === undefined
-    const isInitialLoading =
-        !hasVisibleListItems &&
-        !!pageRoute?.endpoint?.request_url &&
-        (isAwaitingFirstListPage || isLoading || !isInited)
-    const [showContent, setShowContent] = useState(!isInitialLoading)
+    const isInitialLoading = (pageRoute?.endpoint?.request_url && hasNextPage === undefined) || !isInited;
+    const [showContent, setShowContent] = useState(!isInitialLoading);
     const revealTimerRef = useRef(null);
 
     useEffect(() => {
@@ -782,7 +715,7 @@ const TabSceneMainContent = ({
                 {(pageRoute?.endpoint?.request_url && hasNextPage) && PreloadShort}
                 {(pageRoute?.endpoint?.request_url && hasNextPage === false && dataItems.filter((item) => item.type != 'block').length == 0) && <NoContent endpoint={pageRoute?.endpoint} />}
                 <Snackbar
-                    visible={refetchState?.hasNewData ?? false}
+                    visible={refetchState.hasNewData}
                     onPress={() => {
                         const latestItems = flattenPagesForUniList(pagesData)
                         dispatch({ type: 'SET_ITEMS', items: latestItems })
@@ -800,9 +733,7 @@ const TabSceneMainContent = ({
                     size="sm"
                 />
             </View>
-            <View
-                className={`absolute inset-0 z-10 min-h-[50vh] pointer-events-none transition-opacity duration-500 ease-out ${showContent ? 'opacity-0' : 'opacity-100'}`}
-            >
+            <View className={`absolute inset-0 z-10 pointer-events-none transition-opacity duration-500 ease-out ${showContent ? 'opacity-0' : 'opacity-100'}`}>
                 {Preload}
             </View>
         </View>
@@ -920,26 +851,9 @@ const TabScene = ({
         }
     }, [currentBreakpointName, pageRoute])
 
-    const pageRouteKey = pageRoute?.key ?? pageRoute?.link ?? '';
-    const prevPageRouteKeyRef = useRef(null);
-    const skipNextScrollToTopRef = useRef(true);
-
     useEffect(() => {
-        if (!pageRouteKey) {
-            return;
-        }
-        // Skip first run per mount so back/forward and session restore keep feed scroll.
-        if (skipNextScrollToTopRef.current) {
-            skipNextScrollToTopRef.current = false;
-            prevPageRouteKeyRef.current = pageRouteKey;
-            return;
-        }
-        if (prevPageRouteKeyRef.current === pageRouteKey) {
-            return;
-        }
-        prevPageRouteKeyRef.current = pageRouteKey;
         window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
-    }, [pageRouteKey]);
+    }, [pageRoute])
 
     return (
         <PanelGroup
@@ -1420,7 +1334,6 @@ const TabBar = ({
 
     if (routes.length > 0) {
         const addButtons = <AddMenu menu={menu} filter="hideInTopBar" />
-        const isShowSecondLine = (routes.length > 1 || !!pageData.cover_block?.actions_menu)
         return (
             <TopSidebar
                 layoutName={layoutName}
@@ -1429,7 +1342,7 @@ const TabBar = ({
                 layout={layout}
                 title={t(menuSettings?.name)}
             >
-                {isShowSecondLine && <Row className="px-2 lg:px-0 w-full">
+                <Row className="px-2 lg:px-0 w-full">
                     <View className="flex-1 h-14">
                         {routes.length > 1 && <ConductorMenu
                             routes={routes}
@@ -1462,7 +1375,7 @@ const TabBar = ({
                                 )}
                         </Row>
                     )}
-                </Row>}
+                </Row>
             </TopSidebar>
         )
     }

+ 0 - 15
packages/app/ui/molecules/splash-illustration.js

@@ -1,15 +0,0 @@
-import SvgFile from 'app/ui/molecules/svg-file';
-
-/** Native splash illustration — themed tint via SvgFile mask. */
-export default function SplashIllustration(props) {
-    return (
-        <SvgFile
-            src_dark="splash-dark.svg"
-            src_default="splash-light.svg"
-            colorize
-            className="text-secondary-foreground"
-            alt="Splash screen illustration"
-            {...props}
-        />
-    );
-}

+ 0 - 40
packages/app/ui/molecules/splash-illustration.web.js

@@ -1,40 +0,0 @@
-import { useThemeName } from 'app/design/theme';
-import { getBaseUrl } from 'app/config';
-
-/**
- * Splash hero for web: <img> with CSS mask so LCP gets fetchpriority=high + eager load,
- * while keeping secondary-foreground tint (same technique as SvgFile colorize).
- */
-export default function SplashIllustration({
-    className = 'text-secondary-foreground',
-    alt = 'Splash screen illustration',
-}) {
-    const theme = useThemeName();
-    const file = theme === 'dark' ? 'splash-dark.svg' : 'splash-light.svg';
-    const baseUrl = typeof window !== 'undefined' ? getBaseUrl() : '';
-    const url = `${baseUrl}/svg/${file}`;
-
-    return (
-        <img
-            src={url}
-            alt={alt}
-            width={192}
-            height={192}
-            fetchPriority="high"
-            loading="eager"
-            decoding="async"
-            className={`block h-full w-full ${className}`}
-            style={{
-                backgroundColor: 'currentColor',
-                maskImage: `url("${url}")`,
-                maskPosition: 'center',
-                maskRepeat: 'no-repeat',
-                maskSize: 'contain',
-                WebkitMaskImage: `url("${url}")`,
-                WebkitMaskPosition: 'center',
-                WebkitMaskRepeat: 'no-repeat',
-                WebkitMaskSize: 'contain',
-            }}
-        />
-    );
-}

+ 3 - 18
packages/app/ui/molecules/svg-file.web.js

@@ -2,19 +2,9 @@ import { appSetting } from 'app/lib/util';
 import { useThemeName } from 'app/design/theme';
 import { getBaseUrl } from 'app/config';
 
-export default function ({
-    src_web,
-    src_dark,
-    src_default,
-    colorize = false,
-    className = '',
-    style,
-    alt = '',
-    priority = false,
-    ...props
-}) {
+export default function ({src_web, src_dark, src_default, colorize = false, className = '', style, alt = '', ...props}) {
     const theme = useThemeName();
-    const src = theme === 'dark' && src_dark ? src_dark : src_default;
+    const src = theme === 'dark' && src_dark ? src_dark : src_default; 
 
     if (!src) {
         console.warn('SvgFile: No valid src provided');
@@ -22,15 +12,11 @@ export default function ({
     }
 
     // For web, always use the current domain to support preview deployments
-    const baseUrl = typeof window !== 'undefined'
+    const baseUrl = typeof window !== 'undefined' 
         ? getBaseUrl()
         : appSetting('config', 'native_app_images_url');
 
     const url = baseUrl + '/svg/' + src;
-    const lcpImageProps = priority
-        ? { fetchPriority: 'high', loading: 'eager', decoding: 'async' }
-        : {};
-
     if (colorize) {
         return (
             <span
@@ -62,7 +48,6 @@ export default function ({
         <img
             key={src}
             {...props}
-            {...lcpImageProps}
             className={className}
             src={url}
             alt={alt}