Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tweak(ext/cfx-ui): enforce TOS accepting
- Loading branch information
1 parent
9729577
commit e06177c
Showing
14 changed files
with
330 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 15 additions & 0 deletions
15
ext/cfx-ui/src/cfx/apps/mpMenu/parts/LegalAccepter/LegalAccepter.module.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
.root { | ||
width: min(max(calc(#{ui.viewport-width()} * .5), 500px), #{ui.viewport-width()}); | ||
height: ui.viewport-height(); | ||
|
||
box-shadow: 0 0 0 2px ui.color-token('island-border') inset; | ||
|
||
@include ui.border-radius(); | ||
background-color: ui.color-token('flyout-backdrop-background'); | ||
|
||
.iframe { | ||
width: 100%; | ||
height: 100%; | ||
border: none; | ||
} | ||
} |
71 changes: 71 additions & 0 deletions
71
ext/cfx-ui/src/cfx/apps/mpMenu/parts/LegalAccepter/LegalAccepter.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { observer } from "mobx-react-lite"; | ||
import { mpMenu } from "../../mpMenu"; | ||
import { Pad } from "cfx/ui/Layout/Pad/Pad"; | ||
import { Flex } from "cfx/ui/Layout/Flex/Flex"; | ||
import { TextBlock } from "cfx/ui/Text/Text"; | ||
import { FlexRestricter } from "cfx/ui/Layout/Flex/FlexRestricter"; | ||
import { Button } from "cfx/ui/Button/Button"; | ||
import { Icons } from "cfx/ui/Icons"; | ||
import { Title } from "cfx/ui/Title/Title"; | ||
import { Icon } from "cfx/ui/Icon/Icon"; | ||
import { CurrentGameBrand } from "cfx/base/gameRuntime"; | ||
import { useLegalService } from "cfx/apps/mpMenu/services/legal/legal.service"; | ||
import s from './LegalAccepter.module.scss'; | ||
|
||
export const LegalAccepter = observer(function TOSAccepter() { | ||
const legalService = useLegalService(); | ||
|
||
return ( | ||
<div className={s.root}> | ||
{/* The reason for the reverse order here is so that the "I Accept" button captures focus first when/if user press Tab key */} | ||
<Flex vertical fullHeight fullWidth reverseOrder gap="none"> | ||
<Pad size="large"> | ||
<Flex repell centered reverseOrder> | ||
<Button | ||
tabIndex={0} | ||
theme="primary" | ||
size="large" | ||
text="I Accept" | ||
onClick={legalService.accept} | ||
/> | ||
|
||
<Title delay={1000} title={`Exit ${CurrentGameBrand}`}> | ||
<Button | ||
tabIndex={1} | ||
text="Cancel" | ||
onClick={mpMenu.exit} | ||
/> | ||
</Title> | ||
</Flex> | ||
</Pad> | ||
|
||
<FlexRestricter vertical> | ||
<iframe | ||
src={`${legalService.TOS_URL}#toolbar=0&view=FitH`} | ||
className={s.iframe} | ||
></iframe> | ||
</FlexRestricter> | ||
|
||
<Pad size="large"> | ||
<Flex vertical gap="large"> | ||
<TextBlock family="secondary" weight="bolder" size="xxlarge"> | ||
Terms of Service | ||
</TextBlock> | ||
|
||
<Flex vertical gap="small"> | ||
<TextBlock opacity="75"> | ||
Last modified: {legalService.CURRENT_TOS_VERSION} | ||
</TextBlock> | ||
|
||
<TextBlock typographic opacity="75"> | ||
<Title title={legalService.TOS_URL}> | ||
<a href={legalService.TOS_URL}>Open the Terms of Service in your browser <Icon size="small">{Icons.externalLink}</Icon></a> | ||
</Title> | ||
</TextBlock> | ||
</Flex> | ||
</Flex> | ||
</Pad> | ||
</Flex> | ||
</div> | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
65 changes: 65 additions & 0 deletions
65
ext/cfx-ui/src/cfx/apps/mpMenu/services/legal/legal.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { ServicesContainer, defineService, useService } from "cfx/base/servicesContainer"; | ||
import { ASID } from "cfx/utils/asid"; | ||
import { joaat } from "cfx/utils/hash"; | ||
import { injectable } from "inversify"; | ||
import { makeAutoObservable } from "mobx"; | ||
|
||
export const ILegalService = defineService<LegalService>('legalService'); | ||
|
||
export function registerLegalService(container: ServicesContainer) { | ||
container.registerImpl(ILegalService, LegalService); | ||
} | ||
|
||
export function useLegalService() { | ||
return useService(ILegalService); | ||
} | ||
|
||
const LS_KEY = 'legalAcceptanceData'; | ||
|
||
type TOSVersionHash = number; | ||
type AcceptanceTimestamp = number; | ||
|
||
type LegalAcceptanceData = [AcceptanceTimestamp, TOSVersionHash]; | ||
|
||
@injectable() | ||
export class LegalService { | ||
readonly CURRENT_TOS_VERSION = '2020-09-06'; | ||
readonly TOS_URL = 'https://runtime.fivem.net/fivem-service-agreement-4.pdf'; | ||
|
||
private readonly currentTOSVersionHash = joaat(`${ASID}.${this.CURRENT_TOS_VERSION}`); | ||
|
||
private _hasUserAccepted: boolean; | ||
public get hasUserAccepted(): boolean { return this._hasUserAccepted } | ||
private set hasUserAccepted(hasUserAccepted: boolean) { this._hasUserAccepted = hasUserAccepted } | ||
|
||
constructor() { | ||
this._hasUserAccepted = this.reviveHasUserAccepted(); | ||
|
||
makeAutoObservable(this); | ||
} | ||
|
||
public readonly accept = () => { | ||
try { | ||
const data: LegalAcceptanceData = [ | ||
Date.now(), | ||
this.currentTOSVersionHash, | ||
]; | ||
|
||
window.localStorage.setItem(LS_KEY, JSON.stringify(data)); | ||
|
||
this.hasUserAccepted = true; | ||
} catch (e) { | ||
// no-op | ||
} | ||
}; | ||
|
||
private reviveHasUserAccepted(): boolean { | ||
try { | ||
const [_timestamp, acceptedTOSVersionHash] = JSON.parse(window.localStorage.getItem(LS_KEY) || '[]') as LegalAcceptanceData; | ||
|
||
return acceptedTOSVersionHash === this.currentTOSVersionHash; | ||
} catch (e) { | ||
return false; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { Deferred, idleCallback } from "cfx/utils/async"; | ||
|
||
let loadingSplashShutdownRequested = true; | ||
|
||
export const loadingSplashInactiveDeferred = new Deferred<void>(); | ||
|
||
export function shutdownLoadingSplash() { | ||
if (!loadingSplashShutdownRequested) { | ||
return; | ||
} | ||
|
||
loadingSplashShutdownRequested = false; | ||
|
||
try { | ||
const $loader = document.getElementById('loader'); | ||
if (!$loader) { | ||
throw new Error('No #loader found, did it get deleted from index.html?'); | ||
} | ||
|
||
const $loaderMask = $loader.querySelector('#loader-mask'); | ||
if (!$loaderMask) { | ||
throw new Error('No #loader-mask found, did it get deleted from index.html?'); | ||
} | ||
|
||
requestAnimationFrame(() => { | ||
$loader.classList.add('hide'); | ||
|
||
$loaderMask.addEventListener('animationend', async () => { | ||
await idleCallback(1000); | ||
|
||
$loader.parentNode?.removeChild($loader); | ||
}); | ||
|
||
loadingSplashInactiveDeferred.resolve(); | ||
}); | ||
} catch (e) { | ||
loadingSplashInactiveDeferred.resolve(); | ||
|
||
console.error(e); | ||
} | ||
} |
Oops, something went wrong.