Skip to content

Commit

Permalink
Redirect potentially non Next Gen users to prototype version (#247)
Browse files Browse the repository at this point in the history
- Shown to users before browser compatibility warning dialog
- Shown only if they are detected to be in the UK (GB), Isle of Man (IM), Jersey (JE), or Guernsey (GG)
- Should only be shown once to the user
- API proxy for calling browser info endpoint for local dev via .env
  • Loading branch information
microbit-grace committed Apr 18, 2024
1 parent fce8fda commit a4c70eb
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 31 deletions.
16 changes: 15 additions & 1 deletion src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
import PageContentView from './views/PageContentView.svelte';
import {
compatibility,
hasSeenAppVersionRedirectDialog,
isCompatibilityWarningDialogOpen,
} from './script/stores/uiStore';
import IncompatiblePlatformView from './views/IncompatiblePlatformView.svelte';
import CompatibilityWarningDialog from './components/CompatibilityWarningDialog.svelte';
import AppVersionRedirectDialog from './components/AppVersionRedirectDialog.svelte';
import Router from './router/Router.svelte';
import ControlBar from './components/control-bar/ControlBar.svelte';
import { t } from './i18n';
Expand All @@ -31,8 +33,17 @@
connectionDialogState,
} from './script/stores/connectDialogStore';
import { isLoading } from 'svelte-i18n';
import { fetchBrowserInfo } from './script/utils/api';
import { get } from 'svelte/store';
let isPotentiallyNonNextGenUser: boolean = false;
onMount(async () => {
if (!get(hasSeenAppVersionRedirectDialog)) {
const { country } = await fetchBrowserInfo();
const nextGenAvailableCountries = ['GB', 'JE', 'IM', 'GG'];
isPotentiallyNonNextGenUser = !nextGenAvailableCountries.includes(country || '');
}
onMount(() => {
const { bluetooth, usb } = $compatibility;
// Value must switch from false to true after mount to trigger dialog transition
isCompatibilityWarningDialogOpen.set(!bluetooth && !usb);
Expand Down Expand Up @@ -65,6 +76,9 @@
{#if $consent}
<CompatibilityWarningDialog />
{/if}
{#if $consent && !$isCompatibilityWarningDialogOpen && isPotentiallyNonNextGenUser}
<AppVersionRedirectDialog />
{/if}
<div class="w-full flex flex-col bg-backgrounddark">
<ControlBar>
<div class="flex items-center divide-x h-full">
Expand Down
52 changes: 52 additions & 0 deletions src/components/AppVersionRedirectDialog.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<!--
(c) 2023, Center for Computational Thinking and Design at Aarhus University and contributors
SPDX-License-Identifier: MIT
-->

<script lang="ts">
import { t } from '../i18n';
import { hasSeenAppVersionRedirectDialog } from '../script/stores/uiStore';
import StandardDialog from './dialogs/StandardDialog.svelte';
import StandardButton from './StandardButton.svelte';
let isOpen = true;
const appVersionRedirect = () => {
hasSeenAppVersionRedirectDialog.set(true);
window.location.href = 'https://ml.microbit.org/v/prototype';
};
const onClose = () => {
hasSeenAppVersionRedirectDialog.set(true);
isOpen = false;
};
</script>

<StandardDialog
{isOpen}
hasCloseButton={false}
closeOnOutsideClick={false}
closeOnEscape={false}
class="w-110 space-y-5"
{onClose}>
<svelte:fragment slot="heading">
{$t('popup.appVersionRedirect.header')}
</svelte:fragment>
<svelte:fragment slot="body">
<div class="space-y-8">
<p>{$t('popup.appVersionRedirect.explain')}</p>
<div class="flex flex-col justify-end space-y-3">
<StandardButton
type="primary"
size="normal"
class="w-sm"
onClick={appVersionRedirect}
>{$t('popup.appVersionRedirect.button.redirect')}</StandardButton>
<StandardButton onClick={onClose} type="secondary" size="normal" class="w-sm"
>{$t('popup.appVersionRedirect.button.stay')}</StandardButton>
</div>
<p class="text-sm">{$t('popup.appVersionRedirect.uk')}</p>
</div>
</svelte:fragment>
</StandardDialog>
6 changes: 6 additions & 0 deletions src/messages/ui.en.json
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,12 @@
"popup.outdatedmicrobit.button.update": "Update now",
"popup.outdatedmicrobit.button.update.mkcd": "Open MakeCode",

"popup.appVersionRedirect.header": "Are you a UK* primary school teacher?",
"popup.appVersionRedirect.explain": "If you are not a UK* primary school teacher, you will be redirected to our prototype version of this tool. This version is only for the UK's BBC micro:bit playground survey.",
"popup.appVersionRedirect.button.redirect": "I'm not a UK primary school teacher",
"popup.appVersionRedirect.button.stay": "I'm a UK primary school teacher",
"popup.appVersionRedirect.uk": "*includes crown dependencies Jersey, Guernsey and the Isle of Man",

"arrowIconRight.altText": "arrow pointing right",
"arrowIconDown.altText": "arrow pointing down",

Expand Down
6 changes: 6 additions & 0 deletions src/script/stores/uiStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import MBSpecs from '../microbit-interfacing/MBSpecs';
import { gestures } from './Stores';
import { HexOrigin } from '../../StaticConfiguration';
import { DeviceRequestStates } from '../microbit-interfacing/MicrobitConnection';
import { persistantWritable } from './storeUtil';
import { logError, logEvent } from '../utils/logging';

// TODO: Rename? Split up further?
Expand Down Expand Up @@ -42,6 +43,11 @@ if (compatibilityResult.bluetooth) {

export const isCompatibilityWarningDialogOpen = writable<boolean>(false);

export const hasSeenAppVersionRedirectDialog = persistantWritable<boolean>(
'hasSeenAppVersionRedirectDialog',
false,
);

export enum ModelView {
TILE,
STACK,
Expand Down
36 changes: 36 additions & 0 deletions src/script/utils/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { logError } from './logging';

/**
* (c) 2023, Center for Computational Thinking and Design at Aarhus University and contributors
*
* SPDX-License-Identifier: MIT
*/
interface BrowserInfo {
country?: string;
region?: string;
}

const isBrowserInfo = (v: unknown): v is BrowserInfo => {
return typeof v === 'object' && v !== null;
};

/**
* Best effort attempt to fetch browser info.
* On error it returns empty browser info.
*/
export const fetchBrowserInfo = async (): Promise<BrowserInfo> => {
try {
// Note this API is not available if you're running locally without configuring API_PROXY in .env
const response = await fetch('/api/v1/browser/info');
if (!response.ok) {
return {};
}
const json = await response.json();
if (isBrowserInfo(json)) {
return json;
}
} catch (e) {
logError('Failed to fetch browser info', e);
}
return {};
};
78 changes: 48 additions & 30 deletions vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,66 @@
*
* SPDX-License-Identifier: MIT
*/
import { defineConfig } from 'vite';
import { defineConfig, loadEnv } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';
import WindiCSS from 'vite-plugin-windicss';
import { preprocessMeltUI, sequence } from '@melt-ui/pp';
import { sveltePreprocess } from 'svelte-preprocess/dist/autoProcess';
import Icons from 'unplugin-icons/vite';

export default defineConfig({
base: process.env.BASE_URL ?? '/',
plugins: [
svelte({
preprocess: sequence([sveltePreprocess({ typescript: true }), preprocessMeltUI()]),
export default defineConfig(({ mode }) => {
const commonEnv = loadEnv(mode, process.cwd(), '');

onwarn(warning, defaultHandler) {
if (warning.code.includes('a11y')) return; // Ignores the a11y warnings when compiling. This does not apply to the editor, see comment at bottom for vscode instructions
return {
base: process.env.BASE_URL ?? '/',
plugins: [
svelte({
preprocess: sequence([
sveltePreprocess({ typescript: true }),
preprocessMeltUI(),
]),

// handle all other warnings normally
defaultHandler!(warning);
onwarn(warning, defaultHandler) {
if (warning.code.includes('a11y')) return; // Ignores the a11y warnings when compiling. This does not apply to the editor, see comment at bottom for vscode instructions

// handle all other warnings normally
defaultHandler!(warning);
},
}),
WindiCSS(),
Icons({ compiler: 'svelte' }),
],
define: {
'import.meta.env.VITE_APP_VERSION': JSON.stringify(process.env.npm_package_version),
},
build: {
target: 'es2017',
rollupOptions: {
input: 'index.html',
},
}),
WindiCSS(),
Icons({ compiler: 'svelte' }),
],
define: {
'import.meta.env.VITE_APP_VERSION': JSON.stringify(process.env.npm_package_version),
},
build: {
target: 'es2017',
rollupOptions: {
input: 'index.html',
},
},
test: {
globals: true,
setupFiles: ['./src/setup_tests.ts'],
poolOptions: {
threads: {
// threads disabled for now due to https://github.com/vitest-dev/vitest/issues/1982
singleThread: true,
server: commonEnv.API_PROXY
? {
port: 5172,
proxy: {
'/api/v1': {
target: commonEnv.API_PROXY,
changeOrigin: true,
},
},
}
: undefined,
test: {
globals: true,
setupFiles: ['./src/setup_tests.ts'],
poolOptions: {
threads: {
// threads disabled for now due to https://github.com/vitest-dev/vitest/issues/1982
singleThread: true,
},
},
},
},
};
});

/**
Expand Down

0 comments on commit a4c70eb

Please sign in to comment.