better theme for tv
This commit is contained in:
@@ -295,6 +295,11 @@
|
||||
if (!logoUrl) return
|
||||
localStorage.setItem(BOOT_LOGO_KEY, logoUrl)
|
||||
if (logoUrl.indexOf('data:') === 0 || typeof fetch !== 'function') return
|
||||
try {
|
||||
if (new URL(logoUrl, window.location.href).origin !== window.location.origin) return
|
||||
} catch (e) {
|
||||
return
|
||||
}
|
||||
fetch(logoUrl, { mode: 'cors', credentials: 'omit' })
|
||||
.then(function (res) {
|
||||
return res && res.ok ? res.blob() : Promise.reject()
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import type { AppConfig } from '@/config/types'
|
||||
import { getConfig, getRawAppConfig } from '@/config/app-config'
|
||||
import { getConfigUrls, sortConfigUrls } from '@/config/load-config'
|
||||
import {
|
||||
getSuperAdminBase,
|
||||
isSuperAdminUrl,
|
||||
resolveTenantDomain,
|
||||
} from '@/config/superadmin'
|
||||
import {
|
||||
clearCachedPwaManifest,
|
||||
readCachedPwaManifest,
|
||||
@@ -23,7 +28,24 @@ function sleep(ms: number) {
|
||||
}
|
||||
|
||||
function normalizeBaseUrl(url: string) {
|
||||
return url.replace(/\/get_user\/?$/i, '').replace(/\/+$/, '')
|
||||
try {
|
||||
const parsed = new URL(url)
|
||||
parsed.search = ''
|
||||
parsed.hash = ''
|
||||
parsed.pathname = parsed.pathname
|
||||
.replace(/\/get_user\/?$/i, '')
|
||||
.replace(/\/manifest\.json$/i, '')
|
||||
if (!parsed.pathname || parsed.pathname === '/') {
|
||||
return parsed.origin
|
||||
}
|
||||
return `${parsed.origin}${parsed.pathname.replace(/\/+$/, '')}`
|
||||
} catch {
|
||||
return url
|
||||
.replace(/[?#].*$/, '')
|
||||
.replace(/\/get_user\/?$/i, '')
|
||||
.replace(/\/manifest\.json$/i, '')
|
||||
.replace(/\/+$/, '')
|
||||
}
|
||||
}
|
||||
|
||||
/** همان baseای که get_user از آن لود شده (کش شده در localStorage) */
|
||||
@@ -33,15 +55,28 @@ export function getConfigBaseFromStorage() {
|
||||
return normalizeBaseUrl(lastSuccess)
|
||||
}
|
||||
|
||||
function getSuperAdminManifestUrl(): string | null {
|
||||
const saBase = getSuperAdminBase()
|
||||
if (!saBase) return null
|
||||
return `${saBase}/manifest.json?domain=${encodeURIComponent(resolveTenantDomain())}`
|
||||
}
|
||||
|
||||
function configUrlToManifestUrl(configUrl: string) {
|
||||
return `${configUrl.replace(/\/+$/, '')}/manifest.json`
|
||||
if (isSuperAdminUrl(configUrl)) {
|
||||
return getSuperAdminManifestUrl() || `${normalizeBaseUrl(configUrl)}/manifest.json`
|
||||
}
|
||||
return `${normalizeBaseUrl(configUrl)}/manifest.json`
|
||||
}
|
||||
|
||||
/** همان ۵ URL پایهٔ get_user — اولویت با CONFIG_Success_URL */
|
||||
export function resolveManifestUrls(config?: AppConfig | null): string[] {
|
||||
const explicit = getConfig<string>('manifest_url')
|
||||
if (explicit) return [explicit]
|
||||
|
||||
const saManifest = getSuperAdminManifestUrl()
|
||||
if (saManifest) {
|
||||
return [saManifest]
|
||||
}
|
||||
|
||||
return sortConfigUrls(getConfigUrls()).map(configUrlToManifestUrl)
|
||||
}
|
||||
|
||||
|
||||
@@ -86,6 +86,7 @@ import {
|
||||
productsFirstItems,
|
||||
productsItems,
|
||||
productsPageLoading,
|
||||
productsTypeItems,
|
||||
changeProductsTab,
|
||||
initProductsPageData,
|
||||
onProductsPageProviderMounted,
|
||||
|
||||
@@ -73,6 +73,7 @@ import {
|
||||
productsFirstItems,
|
||||
productsItems,
|
||||
productsPageLoading,
|
||||
productsTypeItems,
|
||||
changeProductsTab,
|
||||
} from '@/views/base/products/composables/useProductsPageData'
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { onMounted, onUnmounted, reactive } from 'vue'
|
||||
|
||||
const AUTO_RESUME_MS = 6000
|
||||
const AUTO_PX_PER_FRAME = 0.45
|
||||
const AUTO_PX_PER_FRAME = 0.55
|
||||
|
||||
export function useTvColAutoScroll(keys: string[]) {
|
||||
const paused = reactive(Object.fromEntries(keys.map((key) => [key, false])) as Record<string, boolean>)
|
||||
const listEls: Record<string, HTMLElement | null> = Object.fromEntries(keys.map((key) => [key, null]))
|
||||
const resumeTimers: Record<string, ReturnType<typeof setTimeout> | undefined> = {}
|
||||
let rafId = 0
|
||||
|
||||
function reduceMotion() {
|
||||
@@ -20,20 +18,10 @@ export function useTvColAutoScroll(keys: string[]) {
|
||||
|
||||
function pauseColAuto(key: string) {
|
||||
paused[key] = true
|
||||
if (resumeTimers[key]) {
|
||||
clearTimeout(resumeTimers[key])
|
||||
resumeTimers[key] = undefined
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleColAuto(key: string) {
|
||||
if (resumeTimers[key]) {
|
||||
clearTimeout(resumeTimers[key])
|
||||
}
|
||||
resumeTimers[key] = setTimeout(() => {
|
||||
paused[key] = false
|
||||
resumeTimers[key] = undefined
|
||||
}, AUTO_RESUME_MS)
|
||||
function resumeColAuto(key: string) {
|
||||
paused[key] = false
|
||||
}
|
||||
|
||||
function tick() {
|
||||
@@ -42,11 +30,14 @@ export function useTvColAutoScroll(keys: string[]) {
|
||||
if (paused[key]) continue
|
||||
const el = listEls[key]
|
||||
if (!el) continue
|
||||
const overflow = el.scrollHeight - el.clientHeight
|
||||
if (overflow <= 1) continue
|
||||
const half = el.scrollHeight / 2
|
||||
if (half <= el.clientHeight + 8) continue
|
||||
el.scrollTop += AUTO_PX_PER_FRAME
|
||||
if (el.scrollTop >= half) {
|
||||
if (half > el.clientHeight && el.scrollTop >= half) {
|
||||
el.scrollTop -= half
|
||||
} else if (el.scrollTop >= overflow) {
|
||||
el.scrollTop = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,15 +50,12 @@ export function useTvColAutoScroll(keys: string[]) {
|
||||
|
||||
onUnmounted(() => {
|
||||
cancelAnimationFrame(rafId)
|
||||
for (const key of keys) {
|
||||
if (resumeTimers[key]) clearTimeout(resumeTimers[key])
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
pausedCols: paused,
|
||||
setColListRef,
|
||||
pauseColAuto,
|
||||
scheduleColAuto,
|
||||
resumeColAuto,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,10 @@ function isParsianCoin(product: Product) {
|
||||
function isBankCoin(product: Product) {
|
||||
if (isParsianCoin(product)) return false
|
||||
const group = (product.group_type ?? '').trim()
|
||||
return group === 'سکه' || group === 'سکه بانکی' || (group.includes('سکه') && !group.includes('پارسیان'))
|
||||
const title = (product.title ?? '').trim()
|
||||
if (group === 'سکه' || group === 'سکه بانکی') return true
|
||||
if (group.includes('سکه') && !group.includes('پارسیان')) return true
|
||||
return title.includes('بانکی')
|
||||
}
|
||||
|
||||
function bankCoinRank(product: Product) {
|
||||
|
||||
+22
-7
@@ -90,7 +90,7 @@
|
||||
:ref="(el) => setColListRef(col.key, el)"
|
||||
class="tv-col__list"
|
||||
@mouseenter="pauseColAuto(col.key)"
|
||||
@mouseleave="scheduleColAuto(col.key)"
|
||||
@mouseleave="resumeColAuto(col.key)"
|
||||
>
|
||||
<div
|
||||
v-if="col.items.length"
|
||||
@@ -251,7 +251,7 @@ const {
|
||||
toggleFullscreen,
|
||||
} = useTvMonitorPage()
|
||||
|
||||
const { setColListRef, pauseColAuto, scheduleColAuto } = useTvColAutoScroll(['parsian', 'bank'])
|
||||
const { setColListRef, pauseColAuto, resumeColAuto } = useTvColAutoScroll(['parsian', 'bank'])
|
||||
|
||||
function prefersReducedMotion() {
|
||||
return typeof window !== 'undefined'
|
||||
@@ -259,12 +259,13 @@ function prefersReducedMotion() {
|
||||
}
|
||||
|
||||
function marqueeCopies(count: number) {
|
||||
return count >= 2 && !prefersReducedMotion() ? [0, 1] : [0]
|
||||
return count >= 1 && !prefersReducedMotion() ? [0, 1] : [0]
|
||||
}
|
||||
|
||||
function loopTicker(items: Product[]) {
|
||||
if (!items.length) return []
|
||||
return [0, 1].flatMap((copy) => items.map((product) => ({ product, copy })))
|
||||
const copies = items.length >= 2 && !prefersReducedMotion() ? [0, 1] : [0]
|
||||
return copies.flatMap((copy) => items.map((product) => ({ product, copy })))
|
||||
}
|
||||
|
||||
const shamsTickerLoop = computed(() => loopTicker(tickerProducts.value))
|
||||
@@ -281,7 +282,7 @@ const coinColumns = computed(() => [
|
||||
},
|
||||
{
|
||||
key: 'bank',
|
||||
title: 'سکه بانکی',
|
||||
title: 'سکه',
|
||||
hint: 'دسته سکه',
|
||||
empty: 'سکهای برای نمایش نیست',
|
||||
items: bankCoins.value,
|
||||
@@ -592,10 +593,20 @@ const coinColumns = computed(() => [
|
||||
flex-direction: column;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: color-mix(in srgb, var(--tv-primary) 42%, transparent) transparent;
|
||||
overscroll-behavior: contain;
|
||||
container-type: size;
|
||||
scrollbar-width: none;
|
||||
|
||||
&:hover {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: color-mix(in srgb, var(--tv-primary) 42%, transparent) transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
&:hover::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
@@ -611,13 +622,17 @@ const coinColumns = computed(() => [
|
||||
|
||||
.tv-col__track {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.tv-col__group {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
box-sizing: border-box;
|
||||
min-height: 100cqh;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user