Skip to content

Commit ed1d5a1

Browse files
authored
feat: video in iframe (#255)
1 parent 1991d53 commit ed1d5a1

File tree

26 files changed

+794
-42
lines changed

26 files changed

+794
-42
lines changed

apps/atrium-telegram/app/components/flow/ItemCard.vue

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,20 @@
99
/>
1010
<UIcon
1111
v-else
12-
:name="getIconName(item.type)"
12+
:name="getIconNameForFlowItem(item.type)"
1313
class="size-8 text-primary"
1414
/>
1515

16+
<div v-if="isForHub" class="flex flex-row items-center gap-1.5 text-primary">
17+
<UIcon
18+
name="i-lucide-users-round"
19+
class="size-8"
20+
/>
21+
<p class="max-w-22 text-sm/4 font-bold">
22+
В Хабе
23+
</p>
24+
</div>
25+
1626
<div v-if="!isViewed" class="flex flex-row items-center gap-1.5 text-error">
1727
<UIcon
1828
name="i-lucide-pointer"
@@ -28,7 +38,7 @@
2838
{{ item.title }}
2939
</h3>
3040

31-
<div class="w-full text-base/5 font-normal whitespace-pre-wrap break-words line-clamp-8">
41+
<div class="w-full text-base/5 font-normal whitespace-pre-wrap wrap-break-word line-clamp-8">
3242
{{ item.description }}
3343
</div>
3444

@@ -51,6 +61,7 @@
5161
</template>
5262

5363
<script setup lang="ts">
64+
import { getIconNameForFlowItem } from '#shared/utils/helpers'
5465
import { format } from 'date-fns'
5566
import { ru } from 'date-fns/locale/ru'
5667
@@ -62,17 +73,5 @@ const userStore = useUserStore()
6273
const isViewed = computed(() => item.views.some((view) => view.userId === userStore?.id))
6374
const userAvatarUrl = computed(() => userStore.users.find((user) => user.id === item.userId)?.avatarUrl ?? undefined)
6475
65-
function getIconName(type: FlowItemWithData['type']): string {
66-
switch (type) {
67-
case 'user_post':
68-
return 'i-lucide-square-user-round'
69-
case 'partner_maintenance':
70-
return 'i-lucide-user'
71-
case 'daily_task_report':
72-
case 'weekly_task_report':
73-
return 'i-lucide-clipboard-check'
74-
default:
75-
return 'i-lucide-clipboard'
76-
}
77-
}
76+
const isForHub = computed(() => item.type === 'hub_iframe' || item.type === 'hub_post')
7877
</script>

apps/atrium-telegram/app/pages/flow/[itemId]/index.vue

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,27 @@
22
<PageContainer>
33
<Section class="motion-preset-slide-left">
44
<div class="flex flex-row items-start justify-between gap-2.5">
5-
<UAvatar
6-
v-if="item?.userId"
7-
:src="userAvatarUrl"
8-
class="size-10"
9-
/>
105
<UIcon
11-
v-else
12-
name="i-lucide-clipboard-check"
6+
:name="getIconNameForFlowItem(item?.type ?? 'daily_task_report')"
137
class="size-10 text-primary"
148
/>
159
</div>
1610

11+
<iframe
12+
v-if="item?.iframe"
13+
:src="item.iframe"
14+
width="100%"
15+
height="240"
16+
style="background-color: #000"
17+
allow="autoplay; encrypted-media; fullscreen; screen-wake-lock;"
18+
frameborder="0"
19+
allowfullscreen
20+
class="rounded-lg"
21+
/>
22+
1723
<SectionTitle :title="item?.title ?? ''" />
1824

19-
<div class="w-full text-base/5 whitespace-pre-wrap break-words">
25+
<div class="w-full text-base/5 whitespace-pre-wrap wrap-break-word">
2026
{{ item?.description }}
2127
</div>
2228

@@ -87,6 +93,7 @@
8793
</template>
8894

8995
<script setup lang="ts">
96+
import { getIconNameForFlowItem } from '#shared/utils/helpers'
9097
import { format } from 'date-fns'
9198
import { ru } from 'date-fns/locale/ru'
9299
@@ -103,7 +110,6 @@ const isDrawerOpened = ref(false)
103110
const userStore = useUserStore()
104111
const flowStore = useFlowStore()
105112
const item = computed(() => flowStore.items.find((item) => item.id === params.itemId))
106-
const userAvatarUrl = computed(() => userStore.users.find((user) => user.id === item.value?.userId)?.avatarUrl ?? undefined)
107113
108114
const isViewed = computed(() => item.value?.views.some((view) => view.userId === userStore?.id))
109115

apps/atrium-telegram/shared/utils/helpers.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { FlowItemWithData } from '#shared/types'
12
import type { AgreementPatentStatus, UserGender } from '@roll-stack/database'
23
import type { Resolution } from '../services/task'
34

@@ -83,3 +84,21 @@ export function getAgreementProgressPercentLeft(concludedAt?: string | null, wil
8384

8485
return Math.min(100, Math.max(0, res))
8586
}
87+
88+
export function getIconNameForFlowItem(type: FlowItemWithData['type']): string {
89+
switch (type) {
90+
case 'user_post':
91+
return 'i-lucide-square-user-round'
92+
case 'partner_maintenance':
93+
return 'i-lucide-user'
94+
case 'hub_post':
95+
return 'i-lucide-message-circle'
96+
case 'hub_iframe':
97+
return 'i-lucide-video'
98+
case 'daily_task_report':
99+
case 'weekly_task_report':
100+
return 'i-lucide-clipboard-check'
101+
default:
102+
return 'i-lucide-clipboard'
103+
}
104+
}

apps/hub-telegram/app/app.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ watch(colorMode, () => {
5959
6060
// Init Stores
6161
const user = useUserStore()
62+
const flow = useFlowStore()
6263
6364
// Guard
6465
await user.update()
@@ -74,12 +75,14 @@ onMounted(async () => {
7475
await Promise.all([
7576
user.updateOnline(),
7677
user.update(),
78+
flow.update(),
7779
])
7880
7981
interval = setInterval(async () => {
8082
await Promise.all([
8183
user.updateOnline(),
8284
user.update(),
85+
flow.update(),
8386
])
8487
}, 20000)
8588
})
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<template>
2+
<button
3+
class="relative w-full active:scale-95 duration-200 text-left cursor-pointer outline-0"
4+
:class="props.class"
5+
@click="vibrate()"
6+
>
7+
<slot />
8+
</button>
9+
</template>
10+
11+
<script setup lang="ts">
12+
const props = defineProps<{ class?: string }>()
13+
14+
const { vibrate } = useFeedback()
15+
</script>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<template>
2+
<UBadge
3+
v-if="value != null && value !== '0' && value !== 0"
4+
size="lg"
5+
color="secondary"
6+
variant="solid"
7+
:label="value"
8+
class="min-w-8 justify-center text-base/4 font-bold tracking-tight"
9+
/>
10+
</template>
11+
12+
<script setup lang="ts">
13+
defineProps<{ value?: string | number | null }>()
14+
</script>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<template>
2+
<div class="z-50 touch-pan-x sticky inset-0 h-24">
3+
<nav
4+
v-if="isNavigationShown"
5+
class="w-full h-24 tg-bg-bottom-bar border-t border-default rounded-t-lg motion-preset-slide-up"
6+
>
7+
<div
8+
class="mt-3 max-w-[28rem] mx-auto grid"
9+
:class="[
10+
mainRoutes.length > 3 ? 'grid-cols-4' : 'grid-cols-3',
11+
]"
12+
>
13+
<NavigationButton
14+
v-for="route in mainRoutes"
15+
:key="route.path"
16+
:route="route"
17+
/>
18+
</div>
19+
</nav>
20+
</div>
21+
</template>
22+
23+
<script setup lang="ts">
24+
const { isNavigationShown, mainRoutes } = useNavigation()
25+
</script>
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<template>
2+
<button
3+
class="flex flex-col items-center justify-center gap-1 px-4 cursor-pointer tg-text-subtitle"
4+
@click="(isMainPage && canScrollToTop && isThisRoute) ? handleScrollToTop() : handleRedirect(route.path)"
5+
>
6+
<div
7+
class="relative py-1 w-full rounded-2xl flex flex-row items-center justify-center"
8+
:class="[
9+
(isThisRoute || isThisName) && 'tg-bg-button tg-text-button motion-translate-y-in',
10+
]"
11+
>
12+
<UIcon
13+
v-if="isMainPage && canScrollToTop && isThisRoute"
14+
name="i-lucide-arrow-up"
15+
class="size-6 motion-preset-shake"
16+
/>
17+
<UIcon
18+
v-else-if="router.currentRoute.value.meta.canReturn && isThisName"
19+
name="i-lucide-undo-2"
20+
class="size-6 motion-preset-shake"
21+
/>
22+
<UChip
23+
v-else
24+
size="3xl"
25+
color="error"
26+
:show="!!route.badge && route.badge !== '0'"
27+
:text="route.badge"
28+
:ui="{
29+
base: '-right-1 px-1.5 py-2 ring-2 tg-text-button font-bold motion-translate-y-loop-25 motion-duration-3500',
30+
}"
31+
>
32+
<UIcon
33+
:name="route.icon"
34+
class="size-6 motion-preset-shake"
35+
/>
36+
</UChip>
37+
</div>
38+
<p
39+
class="text-xs font-medium"
40+
:class="[
41+
(isThisRoute || isThisName) && 'tg-text',
42+
]"
43+
>
44+
{{ route.title }}
45+
</p>
46+
</button>
47+
</template>
48+
49+
<script setup lang="ts">
50+
const { route } = defineProps<{ route: NavigationRoute }>()
51+
52+
const { vibrate } = useFeedback()
53+
const { canScrollToTop, isMainPage } = useNavigation()
54+
const router = useRouter()
55+
56+
const isThisRoute = computed(() => route.exact ? router.currentRoute.value.path === route.path : router.currentRoute.value.path.startsWith(route.path))
57+
const isThisName = computed(() => route.names.includes(router.currentRoute.value.name))
58+
59+
function handleScrollToTop() {
60+
vibrate()
61+
window.scrollTo({ top: 0, behavior: 'smooth' })
62+
}
63+
64+
function handleRedirect(path: string) {
65+
vibrate()
66+
router.push(path)
67+
}
68+
</script>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<template>
2+
<div class="relative p-4 tg-bg-section group/section space-y-3.5 rounded-lg" :class="props.class">
3+
<slot />
4+
</div>
5+
</template>
6+
7+
<script setup lang="ts">
8+
const props = defineProps<{ class?: string }>()
9+
</script>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<template>
2+
<ActiveCard>
3+
<Section>
4+
<div class="flex flex-row gap-2 items-center">
5+
<UIcon
6+
:name="getIconNameForFlowItem(item.type)"
7+
class="size-8 text-primary"
8+
/>
9+
10+
<div v-if="!isViewed" class="flex flex-row items-center gap-1.5 text-primary">
11+
<UIcon
12+
name="i-lucide-pointer"
13+
class="size-8 motion-translate-y-loop-25 motion-preset-seesaw motion-duration-2000"
14+
/>
15+
<p class="max-w-22 text-sm/4 font-bold">
16+
Не просмотрено
17+
</p>
18+
</div>
19+
</div>
20+
21+
<h3 class="text-xl/5 font-bold">
22+
{{ item.title }}
23+
</h3>
24+
25+
<div class="w-full text-base/5 font-normal whitespace-pre-wrap wrap-break-word line-clamp-8">
26+
{{ item.description }}
27+
</div>
28+
29+
<div class="flex justify-between items-center">
30+
<div class="flex flex-row gap-4">
31+
<div class="flex flex-row gap-1.5 items-center text-sm text-muted">
32+
<UIcon name="i-lucide-message-circle" class="size-5" />
33+
<p>{{ item?.comments.length }}</p>
34+
</div>
35+
</div>
36+
37+
<time
38+
:datetime="item.createdAt"
39+
class="text-sm text-muted"
40+
v-text="format(new Date(item.createdAt), 'd MMMM yyyy в HH:mm', { locale: ru })"
41+
/>
42+
</div>
43+
</Section>
44+
</ActiveCard>
45+
</template>
46+
47+
<script setup lang="ts">
48+
import { getIconNameForFlowItem } from '#shared/utils/helpers'
49+
import { format } from 'date-fns'
50+
import { ru } from 'date-fns/locale/ru'
51+
52+
const { item } = defineProps<{
53+
item: FlowItemWithData
54+
}>()
55+
56+
const userStore = useUserStore()
57+
const isViewed = computed(() => item.views.some((view) => view.userId === userStore?.id))
58+
</script>

0 commit comments

Comments
 (0)