Skip to content

Commit

Permalink
Always show video buttons. Consent dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
nknapp committed Aug 24, 2024
1 parent a517cde commit fc63f7f
Show file tree
Hide file tree
Showing 12 changed files with 143 additions and 24 deletions.
3 changes: 3 additions & 0 deletions scripts/sort-translations.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
/* eslint-disable no-console */
import fs from "node:fs/promises";
import prettier from "prettier";
import { fileURLToPath } from "url";

const urls = import.meta.glob<string>("../src/i18n/**/*.json", { query: "url", eager: true, import: "default" });
console.log("Files found:", Object.keys(urls));
const files = Object.keys(urls).map((file) => fileURLToPath(new URL(file, import.meta.url)));

for (const file of files) {
Expand All @@ -13,5 +15,6 @@ for (const file of files) {
}),
);

console.log(`Sorting ${file}`);
await fs.writeFile(file, await prettier.format(JSON.stringify(sortedTranslations, null, 2), { parser: "json" }));
}
30 changes: 30 additions & 0 deletions src/YoutubePlayer/ConsentDialog.manual-test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ConsentDialog } from "@/YoutubePlayer/ConsentDialog.tsx";
import { createEffect, createSignal, Show } from "solid-js";
import { SimpleButton } from "@/components/solid/atoms/SimpleButton.tsx";
import { Portal } from "solid-js/web";

const LOCAL_STORAGE_KEY = "manual-test-consent-show-dialog";
export const ManualTest = () => {
const [consent, setConsent] = createSignal(false);
const [showDialog, setShowDialog] = createSignal(JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY) || "false"));

createEffect(() => {
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(showDialog()));
});

function onConsentChange(value: boolean) {
setShowDialog(false);
setConsent(value);
}
return (
<div>
<div>Consent: {consent() ? "Yes" : "No"}</div>
<SimpleButton label={"Show consent dialog"} onClick={() => setShowDialog(true)} />
<Portal>
<Show when={showDialog()}>
<ConsentDialog setResult={onConsentChange} />
</Show>
</Portal>
</div>
);
};
64 changes: 64 additions & 0 deletions src/YoutubePlayer/ConsentDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { type Component, onCleanup, onMount } from "solid-js";
import { SimpleButton } from "@/components/solid/atoms/SimpleButton.tsx";
import { t } from "@/i18n";
import { IconYoutubeWhite } from "@/icons";

export const ConsentDialog: Component<{ setResult: (value: boolean) => void }> = (props) => {
let animation: Animation;

function setElement(element: HTMLDivElement | null) {
animation = new Animation(
new KeyframeEffect(element, [{ opacity: 0 }, { opacity: 1 }], { duration: 200, fill: "forwards" }),
document.timeline,
);
}

onMount(() => {
animation?.play();
document.body.style.width = "100vw";
document.body.style.height = "100vh";
document.body.style.overflow = "hidden";
});

onCleanup(() => {
document.body.style.width = "";
document.body.style.height = "";
document.body.style.overflow = "";
});

async function onResult(value: boolean) {
animation?.reverse();
await animation.finished;
props.setResult(value);
}

return (
<div ref={setElement} class={"fixed inset-0 bg-black/25 z-50 opacity-0"}>
<div
class={
"absolute bg-white p-8 rounded-lg top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 max-w-2xl w-3/4 shadow-xl"
}
>
<h1 class={"text-secondary-dark flex flex-wrap mt-0"}>
<IconYoutubeWhite class={"h-8 pe-2"} />
{t("video.consent.title")}
</h1>
<p class={"text-secondary-dark"}>{t("video.consent.text")}</p>
<p class="mb-8">
<a
target="_blank"
href="https://www.youtube.com/howyoutubeworks/our-commitments/protecting-user-data/#privacy-guidelines"
>
{t("video.consent.link-text")}
</a>
</p>

<div class={"grid grid-cols-1 xl:grid-cols-2 gap-4"}>
<SimpleButton label={t("video.consent.yes")} onClick={() => onResult(true)} />
<SimpleButton label={t("video.consent.no")} onClick={() => onResult(false)} />
</div>
<div class={"mt-4 text-secondary"}>{t("video.consent.suffix")}</div>
</div>
</div>
);
};
14 changes: 14 additions & 0 deletions src/YoutubePlayer/askYoutubeConsent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { render } from "solid-js/web";
import { ConsentDialog } from "@/YoutubePlayer/ConsentDialog.tsx";

export async function askYoutubeConsent(): Promise<boolean> {
const { promise, resolve } = Promise.withResolvers<boolean>();
const container = document.createElement("div");
document.body.append(container);

const cleanup = render(() => <ConsentDialog setResult={resolve} />, container);
const value = await promise;
cleanup();
container.remove();
return value;
}
6 changes: 5 additions & 1 deletion src/YoutubePlayer/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { renderPlayerContainer } from "@/YoutubePlayer/PlayerContainer.tsx";
import { youtubeEnabled } from "$core/store/youtube.ts";
import { setYoutubeEnabled, youtubeEnabled } from "$core/store/youtube.ts";
import { loadYoutubeAdapter } from "@/YoutubePlayer/adapter.ts";
import type { ResolvedYoutubeLink } from "@/utils/resolveYoutubeLinks.ts";
import { askYoutubeConsent } from "@/YoutubePlayer/askYoutubeConsent.tsx";

export interface YoutubePlayer {
loadVideo(video: ResolvedYoutubeLink): Promise<void>;
Expand All @@ -11,6 +12,9 @@ export interface YoutubePlayer {
}

export async function playVideo(youtubeLink: ResolvedYoutubeLink): Promise<void> {
const consent = youtubeEnabled.get() || (await askYoutubeConsent());
if (!consent) return;
setYoutubeEnabled(consent);
const player = await getOrCreatePlayer();
await player.loadVideo(youtubeLink);
await player.play();
Expand Down
19 changes: 7 additions & 12 deletions src/components/solid/organisms/Reader/Reader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import { createPlayer } from "@/components/solid/organisms/Reader/createPlayer.t
import { CheckButton } from "@/components/solid/atoms/CheckButton.tsx";
import { type Speed, SpeedButton } from "@/components/solid/organisms/Reader/SpeedButton.tsx";
import { type DelayControl, DelayIndicator } from "@/components/solid/atoms/DelayIndicator.tsx";
import { youtubeEnabled } from "$core/store/youtube.ts";
import { usePersistentStore } from "@/components/solid/hooks/usePersistentStore.ts";
import { YoutubePlayButton } from "@/components/solid/atoms/YoutubePlayButton.tsx";
import { type ResolvedYoutubeLink, resolveYoutubeLinks } from "@/utils/resolveYoutubeLinks.ts";

Expand Down Expand Up @@ -83,7 +81,6 @@ const Player: Component<{
youtube: ResolvedYoutubeLink[];
}> = (props) => {
const [speed, setSpeed] = createSignal<Speed>("normal");
const showYoutube = usePersistentStore(youtubeEnabled, false);
return (
<>
<div class={"grid grid-cols-4 gap-4"}>
Expand Down Expand Up @@ -122,15 +119,13 @@ const Player: Component<{
onChange={props.onClickAutoPlay}
disabled={!props.ready}
/>
{showYoutube() && (
<div class={"h-8 w-full flex gap-2"}>
<For each={props.youtube}>
{(link) => {
return <YoutubePlayButton type={"button"} class={"flex-1"} link={link} />;
}}
</For>
</div>
)}
<div class={"h-8 w-full flex gap-2"}>
<For each={props.youtube}>
{(link) => {
return <YoutubePlayButton type={"button"} class={"flex-1"} link={link} />;
}}
</For>
</div>
{/*TODO: Implement speed */}
<SpeedButton
class={"hidden"}
Expand Down
13 changes: 4 additions & 9 deletions src/components/solid/organisms/TechniqueChooser/ExamSheet.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { type Component, createMemo, For, Match, Show, Switch } from "solid-js";
import { type Component, createMemo, For, Match, Switch } from "solid-js";
import { type BaseTechnique, SINGLE_DIRECTION, type Technique, type YoutubeLink } from "$core/model";
import { ForEntries } from "./ForEntries.tsx";
import { t } from "@/i18n";
import { youtubeEnabled } from "$core/store/youtube.ts";
import { usePersistentStore } from "@/components/solid/hooks/usePersistentStore.ts";
import { YoutubePlayButton } from "@/components/solid/atoms/YoutubePlayButton.tsx";
import { resolveYoutubeLinks } from "@/utils/resolveYoutubeLinks.ts";
import { buildTechniqueTree } from "$core/buildExamTable/buildExamTable.ts";
Expand Down Expand Up @@ -89,13 +87,10 @@ const ShowDirections: Component<DirectionsProps> = (props) => {
};

const YoutubeLink: Component<{ technique: BaseTechnique }> = (props) => {
const showYoutube = usePersistentStore(youtubeEnabled, false);
const youtube = resolveYoutubeLinks(props.technique);
return (
<Show when={showYoutube()}>
<span class={"print:hidden"}>
<For each={youtube}>{(video) => <YoutubePlayButton type={"icon"} class={"inline mx-2"} link={video} />}</For>
</span>
</Show>
<span class={"print:hidden"}>
<For each={youtube}>{(video) => <YoutubePlayButton type={"icon"} class={"inline mx-2"} link={video} />}</For>
</span>
);
};
6 changes: 6 additions & 0 deletions src/i18n/common/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@
"meta.description.dojo": "Zufällige Technik-Listen für Aikido-Prüfungen erstellen: {# dojo #}",
"meta.description.home": "Bereite dich auf deine Aikido-Prüfung vor, mit Technik-Listen und Videos",
"reader.upcoming-technique": "Nächste",
"video.consent.link-text": "YouTubes Datenschutzrichtlinien lesen",
"video.consent.no": "Nein, Youtube Videos nicht anzeigen",
"video.consent.suffix": "Deine Auswahl wird gespeichert. Du kannst sie jederzeit ändern. Klicke dafür am Ende dieser Seite auf 'Über diese App'.",
"video.consent.text": "Du bist dabei, ein Video von Youtube abzuspielen. Das Video wird direkt von den Youtube-Servern geladen und Youtube kann dadurch Daten über dich sammeln. Möchtest du weitermachen?",
"video.consent.title": "Anzeige von Youtube Videos erlauben?",
"video.consent.yes": "Ja, Youtube Videos anzeigen",
"video.source": "Quelle:",
"video.stop": "Stop"
}
8 changes: 7 additions & 1 deletion src/i18n/common/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@
"meta.description.dojo": "Create random lists of techniques for Aikido Exams in {# dojo #}",
"meta.description.home": "Learn for your Aikido exam with technique lists and videos",
"reader.upcoming-technique": "Upcoming",
"video.consent.link-text": "Read Youtube's privacy policy",
"video.consent.no": "No, disable YouTube videos",
"video.consent.suffix": "Your choice will be saved. You can change it any time by click 'About this app' at the very bottom of each page.",
"video.consent.text": "You are about to watch a Youtube video. The video will be loaded directly from Youtube's servers and Youtube may collect personal information about you.",
"video.consent.title": "Show Youtube videos?",
"video.consent.yes": "Yes, enable YouTube videos",
"video.source": "Source:",
"video.stop": "Play video"
"video.stop": "Stop"
}
1 change: 1 addition & 0 deletions src/icons/custom/youtube-white-square.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ export { default as IconCheckBoxOutlineBlank } from "@material-design-icons/svg/
export { default as IconNextPlan } from "@material-design-icons/svg/outlined/next_plan.svg?component-solid";
export { default as IconGithub } from "./custom/github-mark.svg?component-solid";
export { default as IconGithubWhite } from "./custom/github-mark-white.svg?component-solid";
export { default as IconYoutubeWhite } from "./custom/youtube-white-square.svg?component-solid";
2 changes: 1 addition & 1 deletion src/pages/[language]/test/manual/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ export async function getStaticPaths() {
---
<DefaultLayout>
<ManualTests client:load />
<ManualTests client:only />
</DefaultLayout>

0 comments on commit fc63f7f

Please sign in to comment.