Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package hu.bme.sch.cmsch.component.app

import hu.bme.sch.cmsch.component.ComponentBase
import hu.bme.sch.cmsch.model.RoleType
import hu.bme.sch.cmsch.service.ControlPermissions
import hu.bme.sch.cmsch.setting.BooleanSettingRef
import hu.bme.sch.cmsch.setting.ComponentSettingService
import hu.bme.sch.cmsch.setting.MinRoleSettingRef
import hu.bme.sch.cmsch.setting.NumberSettingRef
import hu.bme.sch.cmsch.setting.SettingGroup
import hu.bme.sch.cmsch.setting.SettingType
import hu.bme.sch.cmsch.setting.StringSettingRef
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
import org.springframework.context.annotation.Conditional
import org.springframework.core.env.Environment
import org.springframework.stereotype.Service

@Service
@ConditionalOnBean(ApplicationComponent::class)
class PopupComponent(
componentSettingService: ComponentSettingService,
env: Environment
) : ComponentBase(
componentSettingService,
"popup",
"/",
"Popup",
ControlPermissions.PERMISSION_CONTROL_APP,
listOf(),
env
) {

final override val menuDisplayName = null

final override var minRole by MinRoleSettingRef(MinRoleSettingRef.ALL_ROLES, minRoleToEdit = RoleType.NOBODY,
fieldName = "Jogosultságok", description = "Melyik roleokkal nyitható meg az oldal")

/// -------------------------------------------------------------------------------------------------------------------

val jumpscareGroup by SettingGroup(fieldName = "Jumpscare beállítások")

val popupEnabled by BooleanSettingRef(false, fieldName = "Felugró média engedélyezése", description = "Engedélyezi a felugró média megjelenítését az oldalon")

val popupUrls by StringSettingRef(
fieldName = "Felugró média", type = SettingType.LONG_TEXT,
description = "A felugró médiák elérési útjai, vesszővel elválasztva"
)

val popupBackground by StringSettingRef(
defaultValue = "#000000", type = SettingType.COLOR,
fieldName = "Felugró média háttérszíne",
description = "A felugró média háttérszíne hexadecimális formátumban (pl. #000000)"
)

val popupOpacity by NumberSettingRef(
defaultValue = 80,
fieldName = "Felugró média átlátszósága",
description = "A felugró média átlátszósága 0 és 100 között (0 = teljesen átlátszó, 100 = teljesen átlátszatlan)"
)

val intervalLowBound by NumberSettingRef(
defaultValue = 60,
fieldName = "Felugró média alsó intervallum",
description = "Legutóbbi megjelenésétől számítva legalább ennyi másodpercnek kell eltelnie a következő felugró média megjelenéséig",
)

val intervalHighBound by NumberSettingRef(
defaultValue = 60,
fieldName = "Felugró média felső intervallum",
description = "Legutóbbi megjelenésétől számítva legfeljebb ennyi másodpercnek kell eltelnie a következő felugró média megjelenéséig"
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package hu.bme.sch.cmsch.component.app

import hu.bme.sch.cmsch.component.ComponentApiBase
import hu.bme.sch.cmsch.service.AdminMenuService
import hu.bme.sch.cmsch.service.AuditLogService
import hu.bme.sch.cmsch.service.ControlPermissions
import hu.bme.sch.cmsch.service.StorageService
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.RequestMapping


@Controller
@RequestMapping("/admin/control/component/popup")
class PopupComponentController(
adminMenuService: AdminMenuService,
component: PopupComponent,
menuService: MenuService,
auditLogService: AuditLogService,
storageService: StorageService
): ComponentApiBase(
adminMenuService,
PopupComponent::class.java,
component,
ControlPermissions.PERMISSION_CONTROL_APP,
componentMenuName = "Popup beállítások",
componentMenuIcon = "flash_on",
insertComponentCategory = false,
componentCategory = ApplicationComponent.STYLING_CATEGORY,
componentMenuPriority = 6,
menuService = menuService,
auditLogService = auditLogService,
storageService = storageService
)
10 changes: 10 additions & 0 deletions frontend/src/api/contexts/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export interface Components {
communities?: Communities
footer?: Footer
support?: Support
popup?: Popup
}

export interface App {
Expand Down Expand Up @@ -449,3 +450,12 @@ export interface Communities {
searchEnabledResort: boolean
tinderEnabled: boolean
}

export interface Popup {
popupEnabled: boolean
popupUrls: string
popupBackground: string
popupOpacity: number
intervalLowBound: number
intervalHighBound: number
}
27 changes: 15 additions & 12 deletions frontend/src/common-components/layout/CmschLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { EnableNotifications } from '../EnableNotifications'
import { Footer } from '../footer/Footer'
import { Navbar } from '../navigation/Navbar'
import { Warning } from '../Warning'
import { Popup } from './Popup'
import { ScrollToTop } from './ScrollToTop'

export const CmschLayout = ({ children }: PropsWithChildren) => {
Expand All @@ -25,17 +26,19 @@ export const CmschLayout = ({ children }: PropsWithChildren) => {
}

return (
<div className="flex flex-col min-h-screen">
<ScrollToTop />
<Navbar />
<main className="flex-1 pb-20">
<ErrorBoundary>
<Warning />
<EnableNotifications />
<ErrorBoundary>{children}</ErrorBoundary>
</ErrorBoundary>
</main>
<Footer />
</div>
<Popup>
<div className="flex flex-col min-h-screen">
<ScrollToTop />
<Navbar />
<main className="flex-1 pb-20">
<ErrorBoundary>
<Warning />
<EnableNotifications />
<ErrorBoundary>{children}</ErrorBoundary>
</ErrorBoundary>
</main>
<Footer />
</div>
</Popup>
)
}
61 changes: 61 additions & 0 deletions frontend/src/common-components/layout/Popup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { useConfigContext } from '@/api/contexts/config/ConfigContext.tsx'
import { type PropsWithChildren, useEffect, useMemo, useState } from 'react'

export const Popup = ({ children }: PropsWithChildren) => {
const component = useConfigContext()?.components?.popup
const popupUrls = useMemo(
() =>
component?.popupUrls
.split(',')
.map((url) => url.trim())
.filter((url) => url.length > 0) ?? [],
[component?.popupUrls]
)
const popupConfigKey = [
component?.popupEnabled,
component?.intervalLowBound,
component?.intervalHighBound,
component?.popupBackground,
component?.popupOpacity,
popupUrls.join(',')
].join('|')
const [popup, setPopup] = useState<{ url: string; configKey: string }>()
//const [timerKey, setTimerKey] = useState(0)

useEffect(() => {
if (!component?.popupEnabled || popupUrls.length === 0) return

const lowBound = Math.max(0, component.intervalLowBound)
const highBound = Math.max(lowBound, component.intervalHighBound)
const intervalSeconds = Math.random() * (highBound - lowBound) + lowBound

Check warning on line 30 in frontend/src/common-components/layout/Popup.tsx

View check run for this annotation

SonarQubeCloud / [cmsch-frontend] SonarCloud Code Analysis

Make sure that using this pseudorandom number generator is safe here.

See more on https://sonarcloud.io/project/issues?id=kir-dev_cmsch-frontend&issues=AaFkWS37R0vdp3bntVnW&open=AaFkWS37R0vdp3bntVnW&pullRequest=1083
const timer = window.setTimeout(() => {
const randomUrl = popupUrls[Math.floor(Math.random() * popupUrls.length)]

Check warning on line 32 in frontend/src/common-components/layout/Popup.tsx

View check run for this annotation

SonarQubeCloud / [cmsch-frontend] SonarCloud Code Analysis

Make sure that using this pseudorandom number generator is safe here.

See more on https://sonarcloud.io/project/issues?id=kir-dev_cmsch-frontend&issues=AaFkWS37R0vdp3bntVnX&open=AaFkWS37R0vdp3bntVnX&pullRequest=1083
if (randomUrl) setPopup({ url: randomUrl, configKey: popupConfigKey })
}, intervalSeconds * 1000)

return () => window.clearTimeout(timer)
}, [component?.intervalHighBound, component?.intervalLowBound, component?.popupEnabled, popupConfigKey, popupUrls /*, timerKey*/])

const dismissPopup = () => {
setPopup(undefined)
//setTimerKey((key) => key + 1)
}

return (
<>
{children}
{popup?.configKey === popupConfigKey && component?.popupEnabled && (
<div className="fixed inset-0 z-50 flex h-screen w-screen items-center justify-center" onClick={dismissPopup} role="presentation">

Check warning on line 48 in frontend/src/common-components/layout/Popup.tsx

View check run for this annotation

SonarQubeCloud / [cmsch-frontend] SonarCloud Code Analysis

Use <img alt=...> instead of the "presentation" role to ensure accessibility across all devices.

See more on https://sonarcloud.io/project/issues?id=kir-dev_cmsch-frontend&issues=AaFkWS37R0vdp3bntVnY&open=AaFkWS37R0vdp3bntVnY&pullRequest=1083
<div
className="absolute inset-0"
style={{
backgroundColor: component?.popupBackground,
opacity: Math.min(100, Math.max(0, component?.popupOpacity ?? 100)) / 100
}}
/>
<img alt="" className="relative max-h-full max-w-full object-contain" src={popup.url} />
</div>
)}
</>
)
}
Loading