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