diff --git a/junior/app/components/device-warning-modal.gjs b/junior/app/components/device-warning-modal.gjs new file mode 100644 index 00000000000..a52e776a976 --- /dev/null +++ b/junior/app/components/device-warning-modal.gjs @@ -0,0 +1,130 @@ +import trapFocus from '@1024pix/pix-ui/app/modifiers/trap-focus'; +import PixButton from '@1024pix/pix-ui/components/pix-button'; +import PixButtonLink from '@1024pix/pix-ui/components/pix-button-link'; +import { action } from '@ember/object'; +import { service } from '@ember/service'; +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { t } from 'ember-intl'; + +import { types } from '../services/device'; +const { MOBILE, TABLET } = types; + +export default class DeviceWarningModal extends Component { + @tracked showModal = false; + @tracked deviceType = ''; + @tracked orientation = ''; + @service device; + @service intl; + @service currentLearner; + + shouldDisplayModal() { + const { type, orientation } = this.device.info; + this.deviceType = type; + this.orientation = orientation; + + if (this.deviceType === MOBILE) { + return true; + } + + const shouldDisplay = type === TABLET && orientation.startsWith('portrait'); + + if (!shouldDisplay && this.showModal) { + this.currentLearner.setHasSeenWarningModal(); + } else if (this.currentLearner.hasSeenWarningModal) { + return false; + } + + return shouldDisplay; + } + + constructor() { + super(...arguments); + if (this.shouldDisplayModal()) { + this.showModal = true; + } + + screen.orientation.addEventListener('change', () => { + this.showModal = this.shouldDisplayModal(); + }); + } + + @action + onCloseModal() { + this.currentLearner.setHasSeenWarningModal(); + this.showModal = false; + } + + get title() { + return this.intl.t(`components.device-warning-modal.${this.deviceType}.title`); + } + + get contentText() { + return this.intl.t(`components.device-warning-modal.${this.deviceType}.subtitle`); + } + + get isTablet() { + return this.deviceType === TABLET; + } + + get isMobile() { + return this.deviceType === MOBILE; + } + + get modalClassName() { + if (this.deviceType === TABLET) { + return 'device-warning-modal is-tablet'; + } + + if (this.deviceType === MOBILE && this.orientation.startsWith('landscape')) { + return 'device-warning-modal is-landscape'; + } + + return 'device-warning-modal'; + } + +} diff --git a/junior/app/services/current-learner.js b/junior/app/services/current-learner.js index de818132de7..49f81fc833e 100644 --- a/junior/app/services/current-learner.js +++ b/junior/app/services/current-learner.js @@ -5,11 +5,20 @@ export default class CurrentLearnerService extends Service { return JSON.parse(localStorage.getItem('learner')); } - async setLearner(learner) { + setLearner(learner) { localStorage.setItem('learner', JSON.stringify(learner)); } - async remove() { + setHasSeenWarningModal() { + localStorage.setItem('learner-has-seen-warning-modal', 'true'); + } + + get hasSeenWarningModal() { + return !!localStorage.getItem('learner-has-seen-warning-modal'); + } + + remove() { localStorage.removeItem('learner'); + localStorage.removeItem('learner-has-seen-warning-modal'); } } diff --git a/junior/app/services/device.js b/junior/app/services/device.js new file mode 100644 index 00000000000..8282c0df11c --- /dev/null +++ b/junior/app/services/device.js @@ -0,0 +1,41 @@ +import Service from '@ember/service'; + +const MOBILE_MAX_HEIGHT = 720; +const MOBILE_MAX_WIDTH = 540; +const TABLET_MAX_HEIGHT = 1366; +const TABLET_MAX_WIDTH = 1024; + +export const types = { + DESKTOP: 'desktop', + MOBILE: 'mobile', + TABLET: 'tablet', +}; + +export default class DeviceService extends Service { + get info() { + return { + orientation: screen.orientation.type, + type: this.getType(screen.orientation.type), + }; + } + + getType(orientation) { + if (orientation.startsWith('landscape')) { + if (screen.width >= TABLET_MAX_HEIGHT) { + return types.DESKTOP; + } + if (screen.width >= MOBILE_MAX_HEIGHT && screen.height >= MOBILE_MAX_WIDTH) { + return types.TABLET; + } + return types.MOBILE; + } else { + if (screen.width >= TABLET_MAX_WIDTH) { + return types.DESKTOP; + } + if (screen.width >= MOBILE_MAX_WIDTH) { + return types.TABLET; + } + return types.MOBILE; + } + } +} diff --git a/junior/app/styles/app.scss b/junior/app/styles/app.scss index a8ce7e1ec32..1325358833f 100644 --- a/junior/app/styles/app.scss +++ b/junior/app/styles/app.scss @@ -18,6 +18,7 @@ @import 'components/challenge/content/challenge-media'; @import 'components/challenge/challenge-layout'; @import 'components/delayed-element'; +@import 'components/device-warning-modal'; @import 'components/mission-card/card'; @import 'components/bubble'; @import 'components/footer'; diff --git a/junior/app/styles/components/device-warning-modal.scss b/junior/app/styles/components/device-warning-modal.scss new file mode 100644 index 00000000000..d019dc3384c --- /dev/null +++ b/junior/app/styles/components/device-warning-modal.scss @@ -0,0 +1,93 @@ +.device-warning-modal-overlay { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1000; + background-color: var(--pix-neutral-0); + + &--hidden { + visibility: hidden; + opacity: 0; + } +} + +.device-warning-modal { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + text-align: initial; + background-image: url('/images/background-blob-with-robot.svg'); + background-repeat: no-repeat; + background-position-x: right; + background-position-y: bottom; + background-size: 90%; + background-attachment: fixed; + + .close-button { + align-self: self-end; + margin: var(--pix-spacing-8x) var(--pix-spacing-8x) 0 0; + } + + section { + display: flex; + flex-direction: column; + gap: var(--pix-spacing-6x); + margin: var(--pix-spacing-12x) var(--pix-spacing-9x); + color: var(--pix-neutral-900); + + h1 { + font-weight: 800; + font-size: 1.2rem; + } + + h2 { + font-weight: 500; + font-size: 1.2rem; + } + + span { + display: flex; + flex-direction: row; + gap: var(--pix-spacing-6x); + align-items: center; + } + + .logo { + width: 182px; + height: 50px; + } + + .button-link { + max-width: 200px; + } + } + + &.is-landscape { + background-size: 45%; + + section { + max-width: 52%; + } + } + + &.is-tablet section { + margin: var(--pix-spacing-6x) 80px; + + h1 { + font-size: 2.5rem; + } + + h2 { + font-size: 1.5rem; + } + + .logo { + width: 291px; + height: 80px; + margin-bottom: var(--pix-spacing-3x); + } + } +} diff --git a/junior/app/templates/application.hbs b/junior/app/templates/application.hbs index 530c579766f..6b977711f85 100644 --- a/junior/app/templates/application.hbs +++ b/junior/app/templates/application.hbs @@ -1,5 +1,6 @@ {{page-title (t "pages.pix-junior")}}
+
{{outlet}} diff --git a/junior/public/images/icons/screen-rotation.svg b/junior/public/images/icons/screen-rotation.svg new file mode 100644 index 00000000000..1250c48f597 --- /dev/null +++ b/junior/public/images/icons/screen-rotation.svg @@ -0,0 +1,5 @@ + + + diff --git a/junior/translations/fr.json b/junior/translations/fr.json index 74b7cbcba5b..6e1ad1ead89 100644 --- a/junior/translations/fr.json +++ b/junior/translations/fr.json @@ -1,4 +1,9 @@ { + "common": { + "actions": { + "close": "Fermer" + } + }, "components": { "challenge": { "embed-simulator": { @@ -7,6 +12,20 @@ } } }, + "device-warning-modal": { + "button": { + "label": "Découvrir Pix Junior" + }, + "mobile": { + "subtitle": "Merci d'utiliser Pix Junior sur une tablette ou un ordinateur.", + "title": "Cette résolution n’est pas prise en compte." + }, + "tablet": { + "subtitle": "Tourne ta tablette pour commencer à utiliser Pix Junior.", + "title": "Oups, ta tablette n'est pas dans le bon sens..." + }, + "title": "Écran dans le mauvais sens" + }, "oralization-button": { "label": "Lire la consigne à haute voix", "play": "J'écoute",