2025-03-12
This commit is contained in:
parent
6d873c8366
commit
3f546c4396
15 changed files with 530 additions and 145 deletions
6
bun.lock
6
bun.lock
|
|
@ -215,11 +215,11 @@
|
||||||
|
|
||||||
"@effect/experimental": ["@effect/experimental@0.41.4", "", { "dependencies": { "msgpackr": "^1.10.2", "uuid": "^11.0.3" }, "peerDependencies": { "@effect/platform": "^0.77.4", "@effect/platform-node": "^0.73.4", "effect": "^3.13.4", "ioredis": "^5", "lmdb": "^3", "ws": "^8" }, "optionalPeers": ["@effect/platform-node", "ioredis", "lmdb", "ws"] }, "sha512-celrEhl/K2Eis906d3/oOv/3/w+M1jcONvrChG5qQgWYo6CJjSJm4xXopcTbiBhxAhvkd06zABkB3tr/lptUCA=="],
|
"@effect/experimental": ["@effect/experimental@0.41.4", "", { "dependencies": { "msgpackr": "^1.10.2", "uuid": "^11.0.3" }, "peerDependencies": { "@effect/platform": "^0.77.4", "@effect/platform-node": "^0.73.4", "effect": "^3.13.4", "ioredis": "^5", "lmdb": "^3", "ws": "^8" }, "optionalPeers": ["@effect/platform-node", "ioredis", "lmdb", "ws"] }, "sha512-celrEhl/K2Eis906d3/oOv/3/w+M1jcONvrChG5qQgWYo6CJjSJm4xXopcTbiBhxAhvkd06zABkB3tr/lptUCA=="],
|
||||||
|
|
||||||
"@effect/platform": ["@effect/platform@0.79.0", "", { "dependencies": { "find-my-way-ts": "^0.1.5", "multipasta": "^0.2.5" }, "peerDependencies": { "effect": "^3.13.9" } }, "sha512-xqnHRsCIvCsGuEIL9Ob4FZsMUU1ziIjZs+2owOGHt3Y3pseBho2A7pIMfUhOPwRrZRRV4/Jp4xpY7Mz5xbQOsw=="],
|
"@effect/platform": ["@effect/platform@0.79.1", "", { "dependencies": { "find-my-way-ts": "^0.1.5", "multipasta": "^0.2.5" }, "peerDependencies": { "effect": "^3.13.10" } }, "sha512-kCoeBuQrdMuEigcuXk9eDoL9WiFIlLlLZeQZj/LapxC3kgnw/6BViJsRQm+Iqei7Z+zRr930suMpdX75CujCXA=="],
|
||||||
|
|
||||||
"@effect/sql": ["@effect/sql@0.30.4", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.25.1", "uuid": "^11.0.3" }, "peerDependencies": { "@effect/experimental": "^0.41.4", "@effect/platform": "^0.77.4", "effect": "^3.13.4" } }, "sha512-hyGOZsNRlw09yqBcXCTr1o2+vVIVxEnBMNZzi0ZX5JbS8JBT05O7xAu3OYEzSU6bC5i+uVpJJUgdx2zDlOLkeA=="],
|
"@effect/sql": ["@effect/sql@0.30.4", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.25.1", "uuid": "^11.0.3" }, "peerDependencies": { "@effect/experimental": "^0.41.4", "@effect/platform": "^0.77.4", "effect": "^3.13.4" } }, "sha512-hyGOZsNRlw09yqBcXCTr1o2+vVIVxEnBMNZzi0ZX5JbS8JBT05O7xAu3OYEzSU6bC5i+uVpJJUgdx2zDlOLkeA=="],
|
||||||
|
|
||||||
"@effect/sql-drizzle": ["@effect/sql-drizzle@0.31.0", "", { "peerDependencies": { "@effect/sql": "^0.32.0", "drizzle-orm": "^0.31", "effect": "^3.13.9" } }, "sha512-PN7BJIX6d/qeBFkXLBJfNjMar2s46AFbzihAtJ7biFDocamq++9SYvpVfUr01go+2MVZ/7CD7kHfOMm1o+YHrg=="],
|
"@effect/sql-drizzle": ["@effect/sql-drizzle@0.31.1", "", { "peerDependencies": { "@effect/sql": "^0.32.1", "drizzle-orm": "^0.31", "effect": "^3.13.10" } }, "sha512-1Ko+BMRwTPxnA/lnVVUFr+4nuCOuZ6WxSRy4rl3wXp9vVbGThFaoKqDl27/eSDrszaLZ/Itlp3ArGlurqV4IpQ=="],
|
||||||
|
|
||||||
"@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="],
|
"@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="],
|
||||||
|
|
||||||
|
|
@ -629,7 +629,7 @@
|
||||||
|
|
||||||
"easy-table": ["easy-table@1.2.0", "", { "dependencies": { "ansi-regex": "^5.0.1" }, "optionalDependencies": { "wcwidth": "^1.0.1" } }, "sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww=="],
|
"easy-table": ["easy-table@1.2.0", "", { "dependencies": { "ansi-regex": "^5.0.1" }, "optionalDependencies": { "wcwidth": "^1.0.1" } }, "sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww=="],
|
||||||
|
|
||||||
"effect": ["effect@3.13.9", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-fcynHENQW6pejcN5thnRyCFCZDleYSkxUTCbQL36AUXGBakbLXERrLHBlwJke3687S3Z4o9MHad7v+pUiDBC4Q=="],
|
"effect": ["effect@3.13.10", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-f2n51BJJ25G9rb/C1ClkgsVFXH6YTkCHmd6ebpu6cAkwQxfhnfbkVWKgkn3nyW9YnC9z4K8bGohRYaZ+HyWtLg=="],
|
||||||
|
|
||||||
"electron-to-chromium": ["electron-to-chromium@1.5.109", "", {}, "sha512-AidaH9JETVRr9DIPGfp1kAarm/W6hRJTPuCnkF+2MqhF4KaAgRIcBc8nvjk+YMXZhwfISof/7WG29eS4iGxQLQ=="],
|
"electron-to-chromium": ["electron-to-chromium@1.5.109", "", {}, "sha512-AidaH9JETVRr9DIPGfp1kAarm/W6hRJTPuCnkF+2MqhF4KaAgRIcBc8nvjk+YMXZhwfISof/7WG29eS4iGxQLQ=="],
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,15 @@
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@effect/platform": "^0.79.0",
|
"@effect/platform": "^0.79.1",
|
||||||
"@effect/sql-drizzle": "^0.31.0",
|
"@effect/sql-drizzle": "^0.31.1",
|
||||||
"@thi.ng/color-palettes": "^1.4.37",
|
"@thi.ng/color-palettes": "^1.4.37",
|
||||||
"@thi.ng/pixel": "^7.3.20",
|
"@thi.ng/pixel": "^7.3.20",
|
||||||
"@thi.ng/pixel-dither": "^1.1.160",
|
"@thi.ng/pixel-dither": "^1.1.160",
|
||||||
"@xstate/vue": "^4.0.2",
|
"@xstate/vue": "^4.0.2",
|
||||||
"a11y-dialog": "^8.1.1",
|
"a11y-dialog": "^8.1.1",
|
||||||
"drizzle-orm": "^0.40.0",
|
"drizzle-orm": "^0.40.0",
|
||||||
"effect": "^3.13.9",
|
"effect": "^3.13.10",
|
||||||
"pinia": "^3.0.1",
|
"pinia": "^3.0.1",
|
||||||
"sqlocal": "^0.14.0",
|
"sqlocal": "^0.14.0",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,34 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { TmdbMovieDetailsQueryParams, TmdbMovieDetailsWithCreditsResponse } from "@/libs/apis/tmdb/schemas";
|
import type { TmdbMovieDetailsQueryParams, TmdbMovieDetailsWithCreditsResponse } from "@/libs/apis/tmdb/schemas";
|
||||||
import type { TmdbCredit } from "@/libs/apis/tmdb/schemas/credits";
|
import type { MovieMainCrew, TmdbCredit } from "@/libs/apis/tmdb/schemas/credits";
|
||||||
|
import type { ArtWorkTitle } from "@/libs/apis/tmdb/schemas/works";
|
||||||
import type { MergedTmdbLocalData } from "@/libs/search/schemas";
|
import type { MergedTmdbLocalData } from "@/libs/search/schemas";
|
||||||
import type { ComputedRef, Ref, ShallowRef } from "vue";
|
import type { Ref, ShallowRef } from "vue";
|
||||||
|
|
||||||
import ImposterBox from "@/components/dialogs/ImposterBox.vue";
|
import ImposterBox from "@/components/dialogs/ImposterBox.vue";
|
||||||
import LoadingBox from "@/components/loading/LoadingBox.vue";
|
import LoadingBox from "@/components/loading/LoadingBox.vue";
|
||||||
import { editEntryDialogMachine } from "@/machines/EditEntryDialog";
|
import { DIRECTING_MAIN_JOBS, EDITING_MAIN_JOBS, WRITING_MAIN_JOBS } from "@/libs/apis/tmdb/schemas/jobs";
|
||||||
import { Images } from "@/services/images.ts";
|
import { Images } from "@/services/images.ts";
|
||||||
import { PrettyLogger } from "@/services/logger";
|
import { PrettyLogger } from "@/services/logger";
|
||||||
import { RuntimeClient } from "@/services/runtime-client";
|
import { RuntimeClient } from "@/services/runtime-client";
|
||||||
import { TmdbApi } from "@/services/tmdb-api";
|
import { TmdbApi } from "@/services/tmdb-api";
|
||||||
import { useEntryStore } from "@/stores/entry.ts";
|
import { useEntryStore } from "@/stores/entry.ts";
|
||||||
import { Url } from "@effect/platform";
|
import { Url } from "@effect/platform";
|
||||||
import { useMachine } from "@xstate/vue";
|
|
||||||
import { Array as Arr, Effect, Match, pipe } from "effect";
|
import { Array as Arr, Effect, Match, pipe } from "effect";
|
||||||
import { isTruthy } from "effect/Predicate";
|
import { isNotNull } from "effect/Predicate";
|
||||||
import { computed, onMounted, ref, useTemplateRef, watch } from "vue";
|
import { ref, useTemplateRef, watch } from "vue";
|
||||||
|
|
||||||
interface Crew {
|
interface EditEntryData {
|
||||||
directors: TmdbCredit[];
|
hasEntry: boolean;
|
||||||
editors: TmdbCredit[];
|
hasUniqueOriginalTitle: boolean;
|
||||||
writers: TmdbCredit[];
|
mainCrew: MovieMainCrew;
|
||||||
|
originalLanguage: string;
|
||||||
|
originalTitle: ArtWorkTitle;
|
||||||
|
overview: string;
|
||||||
|
popularity: number;
|
||||||
|
releaseDate: string;
|
||||||
|
runtime: string;
|
||||||
|
title: ArtWorkTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Émissions et props
|
// Émissions et props
|
||||||
|
|
@ -31,17 +38,13 @@
|
||||||
const tmdbDetails: Ref<TmdbMovieDetailsWithCreditsResponse | undefined> = ref<
|
const tmdbDetails: Ref<TmdbMovieDetailsWithCreditsResponse | undefined> = ref<
|
||||||
TmdbMovieDetailsWithCreditsResponse
|
TmdbMovieDetailsWithCreditsResponse
|
||||||
>();
|
>();
|
||||||
const crew: Ref<Crew> = ref<Crew>({ directors: [], editors: [], writers: [] });
|
const displayData: Ref<EditEntryData | undefined> = ref<EditEntryData>();
|
||||||
|
|
||||||
const hasEntry: ComputedRef<boolean> = computed(() =>
|
|
||||||
isTruthy(entryData.entryId) && isTruthy(entryData.artWorkId)
|
|
||||||
);
|
|
||||||
const hasUniqueOriginalTitle: ComputedRef<boolean> = computed(() =>
|
|
||||||
entryData.originalTitle.toLowerCase() !== entryData.title.toLowerCase()
|
|
||||||
);
|
|
||||||
|
|
||||||
|
/** Conteneur du canvas du poster de l'oeuvre avec tramage. */
|
||||||
const imageContainer: Readonly<ShallowRef<HTMLDivElement | null>> = useTemplateRef("imageContainer");
|
const imageContainer: Readonly<ShallowRef<HTMLDivElement | null>> = useTemplateRef("imageContainer");
|
||||||
|
/** Canvas dans lequel est inséré le poster de l'oeuvre avec tramage. */
|
||||||
const ditheredPoster: Ref<HTMLCanvasElement | undefined> = ref<HTMLCanvasElement>();
|
const ditheredPoster: Ref<HTMLCanvasElement | undefined> = ref<HTMLCanvasElement>();
|
||||||
|
|
||||||
const isLoading: Ref<boolean> = ref<boolean>(true);
|
const isLoading: Ref<boolean> = ref<boolean>(true);
|
||||||
|
|
||||||
// Gestionnaires d'événements
|
// Gestionnaires d'événements
|
||||||
|
|
@ -54,10 +57,9 @@
|
||||||
|
|
||||||
// Cycles
|
// Cycles
|
||||||
watch(() => entryData, async (): Promise<void> => {
|
watch(() => entryData, async (): Promise<void> => {
|
||||||
|
// Récupère le poster avec tramage.
|
||||||
await RuntimeClient.runPromise(Effect.gen(function*() {
|
await RuntimeClient.runPromise(Effect.gen(function*() {
|
||||||
if (!entryData.posterUrl) return undefined;
|
if (!entryData.posterUrl) return undefined;
|
||||||
console.debug("dithering");
|
|
||||||
|
|
||||||
const imageService: Images = yield* Images;
|
const imageService: Images = yield* Images;
|
||||||
|
|
||||||
const canvas = yield* pipe(
|
const canvas = yield* pipe(
|
||||||
|
|
@ -68,11 +70,9 @@
|
||||||
);
|
);
|
||||||
|
|
||||||
ditheredPoster.value = canvas;
|
ditheredPoster.value = canvas;
|
||||||
|
|
||||||
// const base64 = encodeBase64Url(ditheredImage.buffer.data);
|
|
||||||
// console.debug(base64.length);
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Récupère les détails de l'oeuvre auprès de l'API TMDB.
|
||||||
await RuntimeClient.runPromise(Effect.gen(function*() {
|
await RuntimeClient.runPromise(Effect.gen(function*() {
|
||||||
if (!entryData.tmdbId) return;
|
if (!entryData.tmdbId) return;
|
||||||
|
|
||||||
|
|
@ -91,43 +91,44 @@
|
||||||
Effect.catchAll(_ => Effect.succeed(undefined)),
|
Effect.catchAll(_ => Effect.succeed(undefined)),
|
||||||
Effect.tap(details => {
|
Effect.tap(details => {
|
||||||
tmdbDetails.value = details;
|
tmdbDetails.value = details;
|
||||||
|
// Insère les détails TMDB de l'oeuvre courante dans le magasin.
|
||||||
entryStore.setTmdbData(details);
|
entryStore.setTmdbData(details);
|
||||||
isLoading.value = false;
|
|
||||||
}),
|
}),
|
||||||
Effect.tap(Effect.logInfo),
|
Effect.tap(Effect.logInfo),
|
||||||
Effect.provide(PrettyLogger),
|
Effect.provide(PrettyLogger),
|
||||||
);
|
);
|
||||||
|
|
||||||
const newCrew: Crew = { directors: [], editors: [], writers: [] };
|
const newCrew: MovieMainCrew = { directors: [], editors: [], writers: [] };
|
||||||
|
|
||||||
yield* pipe(
|
yield* pipe(
|
||||||
Effect.fromNullable(tmdbDetails.value?.credits.crew),
|
Effect.fromNullable(tmdbDetails.value?.credits.crew),
|
||||||
Effect.tap(Arr.forEach(credit =>
|
Effect.tap(Arr.forEach((credit: TmdbCredit) =>
|
||||||
Match.value(credit).pipe(
|
Match.value(credit).pipe(
|
||||||
Match.when(c => c.job === "Director", c => newCrew.directors.push(c)),
|
Match.when(c => Arr.some(DIRECTING_MAIN_JOBS, job => c.job === job), c => newCrew.directors.push(c)),
|
||||||
Match.when(c => c.job === "Editor", c => newCrew.editors.push(c)),
|
Match.when(c => Arr.some(EDITING_MAIN_JOBS, job => c.job === job), c => newCrew.editors.push(c)),
|
||||||
Match.when(c => c.job === "Novel", c => newCrew.writers.push(c)),
|
Match.when(c => Arr.some(WRITING_MAIN_JOBS, job => c.job === job), c => newCrew.writers.push(c)),
|
||||||
Match.when(c => c.job === "Original Screenplay", c => newCrew.writers.push(c)),
|
|
||||||
Match.when(c => c.job === "Screenplay", c => newCrew.writers.push(c)),
|
|
||||||
Match.when(c => c.job === "Writer", c => newCrew.writers.push(c)),
|
|
||||||
Match.orElse(() => {}),
|
Match.orElse(() => {}),
|
||||||
)
|
)
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
crew.value = newCrew;
|
// Récupère les données utiles pour l'affichage.
|
||||||
|
displayData.value = {
|
||||||
|
hasEntry: isNotNull(entryData.entryId),
|
||||||
|
hasUniqueOriginalTitle: entryData.originalTitle.toLowerCase() !== entryData.title.toLowerCase(),
|
||||||
|
mainCrew: newCrew,
|
||||||
|
originalLanguage: entryData.originalLanguage.toUpperCase(),
|
||||||
|
originalTitle: entryData.originalTitle,
|
||||||
|
overview: entryData.overview,
|
||||||
|
popularity: entryData.popularity,
|
||||||
|
releaseDate: entryData.releaseDate,
|
||||||
|
runtime: String(tmdbDetails.value?.runtime ?? "???"),
|
||||||
|
title: entryData.title,
|
||||||
|
};
|
||||||
|
|
||||||
|
isLoading.value = false;
|
||||||
}));
|
}));
|
||||||
}, { immediate: true });
|
}, { immediate: true });
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
console.debug("EditEntryDialog mounted");
|
|
||||||
});
|
|
||||||
|
|
||||||
const { send, snapshot } = useMachine(editEntryDialogMachine, {
|
|
||||||
input: {
|
|
||||||
entryData: entryData,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -144,37 +145,37 @@
|
||||||
<div class="stack">
|
<div class="stack">
|
||||||
<h3 id="media-title">{{ entryData.originalTitle }}</h3>
|
<h3 id="media-title">{{ entryData.originalTitle }}</h3>
|
||||||
<div class="original-metadata">
|
<div class="original-metadata">
|
||||||
<span v-if="hasUniqueOriginalTitle">{{ entryData.title }} | </span>
|
<span v-if="displayData?.hasUniqueOriginalTitle">{{ displayData?.title }} | </span>
|
||||||
{{ entryData.releaseDate }} | {{ tmdbDetails?.runtime ?? "??" }} min | {{
|
{{ displayData?.releaseDate }} | {{ displayData?.runtime }} min | {{ displayData?.originalLanguage }} | {{
|
||||||
entryData.originalLanguage.toUpperCase()
|
displayData?.popularity
|
||||||
}} | {{ entryData.popularity }}
|
}}
|
||||||
|
|
||||||
<div class="credits cluster">
|
<div class="credits cluster">
|
||||||
<div class="directors crew">
|
<div class="directors crew">
|
||||||
<strong>RÉALISATION </strong>
|
<strong>RÉALISATION </strong>
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="director of crew.directors" :key="director.id">{{ director.name }}</li>
|
<li v-for="director of displayData?.mainCrew.directors" :key="director.id">{{ director.name }}</li>
|
||||||
<li v-if="crew.directors.length === 0">/</li>
|
<li v-if="displayData?.mainCrew.directors.length === 0">/</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="writers crew">
|
<div class="writers crew">
|
||||||
<strong>ÉCRITURE </strong>
|
<strong>ÉCRITURE </strong>
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="writer of crew.writers" :key="writer.id">{{ writer.name }}</li>
|
<li v-for="writer of displayData?.mainCrew.writers" :key="writer.id">{{ writer.name }}</li>
|
||||||
<li v-if="crew.writers.length === 0">/</li>
|
<li v-if="displayData?.mainCrew.writers.length === 0">/</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="editors crew">
|
<div class="editors crew">
|
||||||
<strong>MONTAGE </strong>
|
<strong>MONTAGE </strong>
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="editor of crew.editors" :key="editor.id">{{ editor.name }}</li>
|
<li v-for="editor of displayData?.mainCrew.editors" :key="editor.id">{{ editor.name }}</li>
|
||||||
<li v-if="crew.editors.length === 0">/</li>
|
<li v-if="displayData?.mainCrew.editors.length === 0">/</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="overview">{{ entryData.overview }}</p>
|
<p class="overview">{{ displayData?.overview }}</p>
|
||||||
|
|
||||||
<div class="entry-state">
|
<div class="entry-state">
|
||||||
<template v-if="entryData.entryId">
|
<template v-if="entryData.entryId">
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,260 @@ export const DEFAULT_SEARCH_MOVIE_PARAMS = {
|
||||||
query: "",
|
query: "",
|
||||||
region: "en-US",
|
region: "en-US",
|
||||||
} satisfies TmdbMovieSearchQueryParams;
|
} satisfies TmdbMovieSearchQueryParams;
|
||||||
|
|
||||||
|
interface Iso31661Alpha2 {
|
||||||
|
code: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ISO_3166_1_ALPHA_2: readonly Iso31661Alpha2[] = [
|
||||||
|
{ code: "AF", name: "Afghanistan" },
|
||||||
|
{ code: "AL", name: "Albania" },
|
||||||
|
{ code: "DZ", name: "Algeria" },
|
||||||
|
{ code: "AS", name: "American Samoa" },
|
||||||
|
{ code: "AD", name: "Andorra" },
|
||||||
|
{ code: "AO", name: "Angola" },
|
||||||
|
{ code: "AI", name: "Anguilla" },
|
||||||
|
{ code: "AQ", name: "Antarctica" },
|
||||||
|
{ code: "AG", name: "Antigua and Barbuda" },
|
||||||
|
{ code: "AR", name: "Argentina" },
|
||||||
|
{ code: "AM", name: "Armenia" },
|
||||||
|
{ code: "AW", name: "Aruba" },
|
||||||
|
{ code: "AU", name: "Australia" },
|
||||||
|
{ code: "AT", name: "Austria" },
|
||||||
|
{ code: "AZ", name: "Azerbaijan" },
|
||||||
|
{ code: "BS", name: "Bahamas" },
|
||||||
|
{ code: "BH", name: "Bahrain" },
|
||||||
|
{ code: "BD", name: "Bangladesh" },
|
||||||
|
{ code: "BB", name: "Barbados" },
|
||||||
|
{ code: "BY", name: "Belarus" },
|
||||||
|
{ code: "BE", name: "Belgium" },
|
||||||
|
{ code: "BZ", name: "Belize" },
|
||||||
|
{ code: "BJ", name: "Benin" },
|
||||||
|
{ code: "BM", name: "Bermuda" },
|
||||||
|
{ code: "BT", name: "Bhutan" },
|
||||||
|
{ code: "BO", name: "Bolivia, Plurinational State of" },
|
||||||
|
{ code: "BQ", name: "Bonaire, Sint Eustatius and Saba" },
|
||||||
|
{ code: "BA", name: "Bosnia and Herzegovina" },
|
||||||
|
{ code: "BW", name: "Botswana" },
|
||||||
|
{ code: "BV", name: "Bouvet Island" },
|
||||||
|
{ code: "BR", name: "Brazil" },
|
||||||
|
{ code: "IO", name: "British Indian Ocean Territory" },
|
||||||
|
{ code: "BN", name: "Brunei Darussalam" },
|
||||||
|
{ code: "BG", name: "Bulgaria" },
|
||||||
|
{ code: "BF", name: "Burkina Faso" },
|
||||||
|
{ code: "BI", name: "Burundi" },
|
||||||
|
{ code: "KH", name: "Cambodia" },
|
||||||
|
{ code: "CM", name: "Cameroon" },
|
||||||
|
{ code: "CA", name: "Canada" },
|
||||||
|
{ code: "CV", name: "Cape Verde" },
|
||||||
|
{ code: "KY", name: "Cayman Islands" },
|
||||||
|
{ code: "CF", name: "Central African Republic" },
|
||||||
|
{ code: "TD", name: "Chad" },
|
||||||
|
{ code: "CL", name: "Chile" },
|
||||||
|
{ code: "CN", name: "China" },
|
||||||
|
{ code: "CX", name: "Christmas Island" },
|
||||||
|
{ code: "CC", name: "Cocos (Keeling) Islands" },
|
||||||
|
{ code: "CO", name: "Colombia" },
|
||||||
|
{ code: "KM", name: "Comoros" },
|
||||||
|
{ code: "CG", name: "Congo" },
|
||||||
|
{ code: "CD", name: "Congo, the Democratic Republic of the" },
|
||||||
|
{ code: "CK", name: "Cook Islands" },
|
||||||
|
{ code: "CR", name: "Costa Rica" },
|
||||||
|
{ code: "HR", name: "Croatia" },
|
||||||
|
{ code: "CU", name: "Cuba" },
|
||||||
|
{ code: "CW", name: "Curaçao" },
|
||||||
|
{ code: "CY", name: "Cyprus" },
|
||||||
|
{ code: "CZ", name: "Czech Republic" },
|
||||||
|
{ code: "CI", name: "Côte d'Ivoire" },
|
||||||
|
{ code: "DK", name: "Denmark" },
|
||||||
|
{ code: "DJ", name: "Djibouti" },
|
||||||
|
{ code: "DM", name: "Dominica" },
|
||||||
|
{ code: "DO", name: "Dominican Republic" },
|
||||||
|
{ code: "EC", name: "Ecuador" },
|
||||||
|
{ code: "EG", name: "Egypt" },
|
||||||
|
{ code: "SV", name: "El Salvador" },
|
||||||
|
{ code: "GQ", name: "Equatorial Guinea" },
|
||||||
|
{ code: "ER", name: "Eritrea" },
|
||||||
|
{ code: "EE", name: "Estonia" },
|
||||||
|
{ code: "SZ", name: "Eswatini" },
|
||||||
|
{ code: "ET", name: "Ethiopia" },
|
||||||
|
{ code: "FK", name: "Falkland Islands (Malvinas)" },
|
||||||
|
{ code: "FO", name: "Faroe Islands" },
|
||||||
|
{ code: "FJ", name: "Fiji" },
|
||||||
|
{ code: "FI", name: "Finland" },
|
||||||
|
{ code: "FR", name: "France" },
|
||||||
|
{ code: "GF", name: "French Guiana" },
|
||||||
|
{ code: "PF", name: "French Polynesia" },
|
||||||
|
{ code: "TF", name: "French Southern Territories" },
|
||||||
|
{ code: "GA", name: "Gabon" },
|
||||||
|
{ code: "GM", name: "Gambia" },
|
||||||
|
{ code: "GE", name: "Georgia" },
|
||||||
|
{ code: "DE", name: "Germany" },
|
||||||
|
{ code: "GH", name: "Ghana" },
|
||||||
|
{ code: "GI", name: "Gibraltar" },
|
||||||
|
{ code: "GR", name: "Greece" },
|
||||||
|
{ code: "GL", name: "Greenland" },
|
||||||
|
{ code: "GD", name: "Grenada" },
|
||||||
|
{ code: "GP", name: "Guadeloupe" },
|
||||||
|
{ code: "GU", name: "Guam" },
|
||||||
|
{ code: "GT", name: "Guatemala" },
|
||||||
|
{ code: "GG", name: "Guernsey" },
|
||||||
|
{ code: "GN", name: "Guinea" },
|
||||||
|
{ code: "GW", name: "Guinea-Bissau" },
|
||||||
|
{ code: "GY", name: "Guyana" },
|
||||||
|
{ code: "HT", name: "Haiti" },
|
||||||
|
{ code: "HM", name: "Heard Island and McDonald Islands" },
|
||||||
|
{ code: "VA", name: "Holy See (Vatican City State)" },
|
||||||
|
{ code: "HN", name: "Honduras" },
|
||||||
|
{ code: "HK", name: "Hong Kong" },
|
||||||
|
{ code: "HU", name: "Hungary" },
|
||||||
|
{ code: "IS", name: "Iceland" },
|
||||||
|
{ code: "IN", name: "India" },
|
||||||
|
{ code: "ID", name: "Indonesia" },
|
||||||
|
{ code: "IR", name: "Iran, Islamic Republic of" },
|
||||||
|
{ code: "IQ", name: "Iraq" },
|
||||||
|
{ code: "IE", name: "Ireland" },
|
||||||
|
{ code: "IM", name: "Isle of Man" },
|
||||||
|
{ code: "IL", name: "Israel" },
|
||||||
|
{ code: "IT", name: "Italy" },
|
||||||
|
{ code: "JM", name: "Jamaica" },
|
||||||
|
{ code: "JP", name: "Japan" },
|
||||||
|
{ code: "JE", name: "Jersey" },
|
||||||
|
{ code: "JO", name: "Jordan" },
|
||||||
|
{ code: "KZ", name: "Kazakhstan" },
|
||||||
|
{ code: "KE", name: "Kenya" },
|
||||||
|
{ code: "KI", name: "Kiribati" },
|
||||||
|
{ code: "KP", name: "Korea, Democratic People's Republic of" },
|
||||||
|
{ code: "KR", name: "Korea, Republic of" },
|
||||||
|
{ code: "KW", name: "Kuwait" },
|
||||||
|
{ code: "KG", name: "Kyrgyzstan" },
|
||||||
|
{ code: "LA", name: "Lao People's Democratic Republic" },
|
||||||
|
{ code: "LV", name: "Latvia" },
|
||||||
|
{ code: "LB", name: "Lebanon" },
|
||||||
|
{ code: "LS", name: "Lesotho" },
|
||||||
|
{ code: "LR", name: "Liberia" },
|
||||||
|
{ code: "LY", name: "Libya" },
|
||||||
|
{ code: "LI", name: "Liechtenstein" },
|
||||||
|
{ code: "LT", name: "Lithuania" },
|
||||||
|
{ code: "LU", name: "Luxembourg" },
|
||||||
|
{ code: "MO", name: "Macao" },
|
||||||
|
{ code: "MK", name: "Macedonia, the Former Yugoslav Republic of" },
|
||||||
|
{ code: "MG", name: "Madagascar" },
|
||||||
|
{ code: "MW", name: "Malawi" },
|
||||||
|
{ code: "MY", name: "Malaysia" },
|
||||||
|
{ code: "MV", name: "Maldives" },
|
||||||
|
{ code: "ML", name: "Mali" },
|
||||||
|
{ code: "MT", name: "Malta" },
|
||||||
|
{ code: "MH", name: "Marshall Islands" },
|
||||||
|
{ code: "MQ", name: "Martinique" },
|
||||||
|
{ code: "MR", name: "Mauritania" },
|
||||||
|
{ code: "MU", name: "Mauritius" },
|
||||||
|
{ code: "YT", name: "Mayotte" },
|
||||||
|
{ code: "MX", name: "Mexico" },
|
||||||
|
{ code: "FM", name: "Micronesia, Federated States of" },
|
||||||
|
{ code: "MD", name: "Moldova, Republic of" },
|
||||||
|
{ code: "MC", name: "Monaco" },
|
||||||
|
{ code: "MN", name: "Mongolia" },
|
||||||
|
{ code: "ME", name: "Montenegro" },
|
||||||
|
{ code: "MS", name: "Montserrat" },
|
||||||
|
{ code: "MA", name: "Morocco" },
|
||||||
|
{ code: "MZ", name: "Mozambique" },
|
||||||
|
{ code: "MM", name: "Myanmar" },
|
||||||
|
{ code: "NA", name: "Namibia" },
|
||||||
|
{ code: "NR", name: "Nauru" },
|
||||||
|
{ code: "NP", name: "Nepal" },
|
||||||
|
{ code: "NL", name: "Netherlands" },
|
||||||
|
{ code: "NC", name: "New Caledonia" },
|
||||||
|
{ code: "NZ", name: "New Zealand" },
|
||||||
|
{ code: "NI", name: "Nicaragua" },
|
||||||
|
{ code: "NE", name: "Niger" },
|
||||||
|
{ code: "NG", name: "Nigeria" },
|
||||||
|
{ code: "NU", name: "Niue" },
|
||||||
|
{ code: "NF", name: "Norfolk Island" },
|
||||||
|
{ code: "MP", name: "Northern Mariana Islands" },
|
||||||
|
{ code: "NO", name: "Norway" },
|
||||||
|
{ code: "OM", name: "Oman" },
|
||||||
|
{ code: "PK", name: "Pakistan" },
|
||||||
|
{ code: "PW", name: "Palau" },
|
||||||
|
{ code: "PS", name: "Palestine, State of" },
|
||||||
|
{ code: "PA", name: "Panama" },
|
||||||
|
{ code: "PG", name: "Papua New Guinea" },
|
||||||
|
{ code: "PY", name: "Paraguay" },
|
||||||
|
{ code: "PE", name: "Peru" },
|
||||||
|
{ code: "PH", name: "Philippines" },
|
||||||
|
{ code: "PN", name: "Pitcairn" },
|
||||||
|
{ code: "PL", name: "Poland" },
|
||||||
|
{ code: "PT", name: "Portugal" },
|
||||||
|
{ code: "PR", name: "Puerto Rico" },
|
||||||
|
{ code: "QA", name: "Qatar" },
|
||||||
|
{ code: "RO", name: "Romania" },
|
||||||
|
{ code: "RU", name: "Russian Federation" },
|
||||||
|
{ code: "RW", name: "Rwanda" },
|
||||||
|
{ code: "RE", name: "Réunion" },
|
||||||
|
{ code: "BL", name: "Saint Barthélemy" },
|
||||||
|
{ code: "SH", name: "Saint Helena, Ascension and Tristan da Cunha" },
|
||||||
|
{ code: "KN", name: "Saint Kitts and Nevis" },
|
||||||
|
{ code: "LC", name: "Saint Lucia" },
|
||||||
|
{ code: "MF", name: "Saint Martin (French part)" },
|
||||||
|
{ code: "PM", name: "Saint Pierre and Miquelon" },
|
||||||
|
{ code: "VC", name: "Saint Vincent and the Grenadines" },
|
||||||
|
{ code: "WS", name: "Samoa" },
|
||||||
|
{ code: "SM", name: "San Marino" },
|
||||||
|
{ code: "ST", name: "Sao Tome and Principe" },
|
||||||
|
{ code: "SA", name: "Saudi Arabia" },
|
||||||
|
{ code: "SN", name: "Senegal" },
|
||||||
|
{ code: "RS", name: "Serbia" },
|
||||||
|
{ code: "SC", name: "Seychelles" },
|
||||||
|
{ code: "SL", name: "Sierra Leone" },
|
||||||
|
{ code: "SG", name: "Singapore" },
|
||||||
|
{ code: "SX", name: "Sint Maarten (Dutch part)" },
|
||||||
|
{ code: "SK", name: "Slovakia" },
|
||||||
|
{ code: "SI", name: "Slovenia" },
|
||||||
|
{ code: "SB", name: "Solomon Islands" },
|
||||||
|
{ code: "SO", name: "Somalia" },
|
||||||
|
{ code: "ZA", name: "South Africa" },
|
||||||
|
{ code: "GS", name: "South Georgia and the South Sandwich Islands" },
|
||||||
|
{ code: "SS", name: "South Sudan" },
|
||||||
|
{ code: "ES", name: "Spain" },
|
||||||
|
{ code: "LK", name: "Sri Lanka" },
|
||||||
|
{ code: "SD", name: "Sudan" },
|
||||||
|
{ code: "SR", name: "Suriname" },
|
||||||
|
{ code: "SJ", name: "Svalbard and Jan Mayen" },
|
||||||
|
{ code: "SE", name: "Sweden" },
|
||||||
|
{ code: "CH", name: "Switzerland" },
|
||||||
|
{ code: "SY", name: "Syrian Arab Republic" },
|
||||||
|
{ code: "TW", name: "Taiwan, Province of China" },
|
||||||
|
{ code: "TJ", name: "Tajikistan" },
|
||||||
|
{ code: "TZ", name: "Tanzania, United Republic of" },
|
||||||
|
{ code: "TH", name: "Thailand" },
|
||||||
|
{ code: "TL", name: "Timor-Leste" },
|
||||||
|
{ code: "TG", name: "Togo" },
|
||||||
|
{ code: "TK", name: "Tokelau" },
|
||||||
|
{ code: "TO", name: "Tonga" },
|
||||||
|
{ code: "TT", name: "Trinidad and Tobago" },
|
||||||
|
{ code: "TN", name: "Tunisia" },
|
||||||
|
{ code: "TR", name: "Turkey" },
|
||||||
|
{ code: "TM", name: "Turkmenistan" },
|
||||||
|
{ code: "TC", name: "Turks and Caicos Islands" },
|
||||||
|
{ code: "TV", name: "Tuvalu" },
|
||||||
|
{ code: "UG", name: "Uganda" },
|
||||||
|
{ code: "UA", name: "Ukraine" },
|
||||||
|
{ code: "AE", name: "United Arab Emirates" },
|
||||||
|
{ code: "GB", name: "United Kingdom" },
|
||||||
|
{ code: "US", name: "United States" },
|
||||||
|
{ code: "UM", name: "United States Minor Outlying Islands" },
|
||||||
|
{ code: "UY", name: "Uruguay" },
|
||||||
|
{ code: "UZ", name: "Uzbekistan" },
|
||||||
|
{ code: "VU", name: "Vanuatu" },
|
||||||
|
{ code: "VE", name: "Venezuela, Bolivarian Republic of" },
|
||||||
|
{ code: "VN", name: "Viet Nam" },
|
||||||
|
{ code: "VG", name: "Virgin Islands, British" },
|
||||||
|
{ code: "VI", name: "Virgin Islands, U.S." },
|
||||||
|
{ code: "WF", name: "Wallis and Futuna" },
|
||||||
|
{ code: "EH", name: "Western Sahara" },
|
||||||
|
{ code: "YE", name: "Yemen" },
|
||||||
|
{ code: "ZM", name: "Zambia" },
|
||||||
|
{ code: "ZW", name: "Zimbabwe" },
|
||||||
|
{ code: "AX", name: "Åland Islands" },
|
||||||
|
];
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@ import { Schema } from "effect";
|
||||||
|
|
||||||
import { TmdbCompany } from "./schemas/companies";
|
import { TmdbCompany } from "./schemas/companies";
|
||||||
import { TmdbCredit } from "./schemas/credits";
|
import { TmdbCredit } from "./schemas/credits";
|
||||||
|
import { DateFromTmdbDate } from "./schemas/dates";
|
||||||
import { TmdbLanguage } from "./schemas/languages";
|
import { TmdbLanguage } from "./schemas/languages";
|
||||||
|
import { ArtWorkTitle } from "./schemas/works";
|
||||||
|
|
||||||
// Requête
|
// Requête
|
||||||
|
|
||||||
|
|
@ -45,12 +47,12 @@ export class TmdbMovieSearchResponseResult
|
||||||
genre_ids: Schema.Array(Schema.NonNegativeInt),
|
genre_ids: Schema.Array(Schema.NonNegativeInt),
|
||||||
id: Schema.NonNegativeInt,
|
id: Schema.NonNegativeInt,
|
||||||
original_language: Schema.String,
|
original_language: Schema.String,
|
||||||
original_title: Schema.String,
|
original_title: Schema.String.pipe(Schema.fromBrand(ArtWorkTitle)),
|
||||||
overview: Schema.String,
|
overview: Schema.String,
|
||||||
popularity: Schema.Number,
|
popularity: Schema.Number,
|
||||||
poster_path: Schema.Union(Schema.String, Schema.Null),
|
poster_path: Schema.Union(Schema.String, Schema.Null),
|
||||||
release_date: Schema.String,
|
release_date: DateFromTmdbDate,
|
||||||
title: Schema.String,
|
title: Schema.String.pipe(Schema.fromBrand(ArtWorkTitle)),
|
||||||
video: Schema.Boolean,
|
video: Schema.Boolean,
|
||||||
vote_average: Schema.Number,
|
vote_average: Schema.Number,
|
||||||
vote_count: Schema.NonNegativeInt,
|
vote_count: Schema.NonNegativeInt,
|
||||||
|
|
@ -82,18 +84,18 @@ export class TmdbMovieDetailsWithCreditsResponse
|
||||||
id: Schema.NonNegativeInt,
|
id: Schema.NonNegativeInt,
|
||||||
imdb_id: Schema.Union(Schema.String, Schema.Null),
|
imdb_id: Schema.Union(Schema.String, Schema.Null),
|
||||||
original_language: Schema.String,
|
original_language: Schema.String,
|
||||||
original_title: Schema.String,
|
original_title: Schema.String.pipe(Schema.fromBrand(ArtWorkTitle)),
|
||||||
overview: Schema.String,
|
overview: Schema.String,
|
||||||
popularity: Schema.Number,
|
popularity: Schema.Number,
|
||||||
poster_path: Schema.Union(Schema.String, Schema.Null),
|
poster_path: Schema.Union(Schema.String, Schema.Null),
|
||||||
production_companies: Schema.Array(TmdbCompany),
|
production_companies: Schema.Array(TmdbCompany),
|
||||||
release_date: Schema.String,
|
release_date: DateFromTmdbDate,
|
||||||
revenue: Schema.NonNegativeInt,
|
revenue: Schema.NonNegativeInt,
|
||||||
runtime: Schema.NonNegativeInt,
|
runtime: Schema.NonNegativeInt,
|
||||||
spoken_languages: Schema.Array(TmdbLanguage),
|
spoken_languages: Schema.Array(TmdbLanguage),
|
||||||
status: Schema.NonEmptyString,
|
status: Schema.NonEmptyString,
|
||||||
tagline: Schema.String,
|
tagline: Schema.String,
|
||||||
title: Schema.NonEmptyString,
|
title: Schema.String.pipe(Schema.fromBrand(ArtWorkTitle)),
|
||||||
video: Schema.Boolean,
|
video: Schema.Boolean,
|
||||||
vote_average: Schema.Number,
|
vote_average: Schema.Number,
|
||||||
vote_count: Schema.NonNegativeInt,
|
vote_count: Schema.NonNegativeInt,
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,19 @@
|
||||||
import { Brand, Schema } from "effect";
|
import { Brand, Schema, String as Str } from "effect";
|
||||||
|
|
||||||
import { CountryIsoCode } from "./countries";
|
import { CountryIsoCode } from "./countries";
|
||||||
|
|
||||||
/** L'ID d'une société sous forme de nombre entier positif. */
|
/** L'ID d'une société sous forme de nombre entier positif. */
|
||||||
export type CompanyId = Brand.Brand<"CompanyId"> & number;
|
export type CompanyId = Brand.Brand<"CompanyId"> & number;
|
||||||
export const CompanyId = Brand.refined<CompanyId>(
|
export const CompanyId = Brand.refined<CompanyId>(
|
||||||
n => Number.isInteger(n) && n >= 0,
|
(id: number) => Number.isInteger(id) && id >= 0,
|
||||||
n => Brand.error(`${String(n)} doit être un nombre entier positif.`),
|
(id: number) => Brand.error(`${String(id)} doit être un nombre entier positif.`),
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Le nom d'une société. */
|
||||||
|
export type CompanyName = Brand.Brand<"CompanyName"> & string;
|
||||||
|
export const CompanyName = Brand.refined<CompanyName>(
|
||||||
|
(name: string) => Str.isString(name) && Str.isNonEmpty(name),
|
||||||
|
(name: string) => Brand.error(`${name} n'est pas un nom valide de société.`),
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Entité représentant une société (p. ex. de production). */
|
/** Entité représentant une société (p. ex. de production). */
|
||||||
|
|
@ -16,7 +23,7 @@ export class TmdbCompany extends Schema.Class<TmdbCompany>("TmdbCompany")({
|
||||||
/** Chemin relatif du logo de la société. */
|
/** Chemin relatif du logo de la société. */
|
||||||
logo_path: Schema.Union(Schema.String, Schema.Null),
|
logo_path: Schema.Union(Schema.String, Schema.Null),
|
||||||
/** Nom de la société. */
|
/** Nom de la société. */
|
||||||
name: Schema.String,
|
name: Schema.NonEmptyString.pipe(Schema.fromBrand(CompanyName)),
|
||||||
/** Pays d'origine de la société sous forme de code ISO 3166-1. */
|
/** Pays d'origine de la société sous forme de code ISO 3166-1 Alpha 2. */
|
||||||
origin_country: Schema.Union(Schema.String.pipe(Schema.fromBrand(CountryIsoCode)), Schema.String),
|
origin_country: Schema.Union(Schema.String.pipe(Schema.fromBrand(CountryIsoCode)), Schema.String),
|
||||||
}) {}
|
}) {}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,24 @@
|
||||||
import { Brand, Schema } from "effect";
|
import { Array as Arr, Brand, Schema, String } from "effect";
|
||||||
|
|
||||||
// TODO: Vérifier le code ISO dans une liste.
|
import { ISO_3166_1_ALPHA_2 } from "../constants";
|
||||||
/** Le code ISO 3166-1 (à deux lettres) d'un pays. */
|
|
||||||
|
/** Le code ISO 3166-1 Alpha 2 (à deux lettres) d'un pays. */
|
||||||
export type CountryIsoCode = Brand.Brand<CountryIsoCode> & string;
|
export type CountryIsoCode = Brand.Brand<CountryIsoCode> & string;
|
||||||
export const CountryIsoCode = Brand.refined<CountryIsoCode>(
|
export const CountryIsoCode = Brand.refined<CountryIsoCode>(
|
||||||
s => s.length == 2,
|
(code: string) => code.length == 2 && Arr.some(ISO_3166_1_ALPHA_2, iso => iso.code === code),
|
||||||
s => Brand.error(`${s} ne correspond pas au code ISO 3166-1 connu d'un pays.`),
|
(code: string) => Brand.error(`${code} ne correspond pas au code ISO 3166-1 connu d'un pays.`),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export type CountryName = Brand.Brand<CountryName> & string;
|
||||||
|
export const CountryName = Brand.refined<CountryName>(
|
||||||
|
(name: string) => String.isString(name) && Arr.some(ISO_3166_1_ALPHA_2, iso => iso.name === name),
|
||||||
|
(name: string) => Brand.error(`${name} n'est pas un nom connu de pays.`),
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Un pays selon l'API TMDB. */
|
||||||
export class TmdbCountry extends Schema.Class<TmdbCountry>("TmdbCountry")({
|
export class TmdbCountry extends Schema.Class<TmdbCountry>("TmdbCountry")({
|
||||||
|
/** Code ISO 3166-1 Alpha 2 du pays. */
|
||||||
iso_3166_1: Schema.NonEmptyString.pipe(Schema.fromBrand(CountryIsoCode)),
|
iso_3166_1: Schema.NonEmptyString.pipe(Schema.fromBrand(CountryIsoCode)),
|
||||||
name: Schema.String,
|
/** Nom du pays. */
|
||||||
|
name: Schema.NonEmptyString.pipe(Schema.fromBrand(CountryName)),
|
||||||
}) {}
|
}) {}
|
||||||
|
|
|
||||||
|
|
@ -1,66 +1,61 @@
|
||||||
import type { Values } from "@/libs/utils/types";
|
import { Brand, Schema, String as Str } from "effect";
|
||||||
|
|
||||||
import { Brand, Match, Schema } from "effect";
|
import { GenderStringFromInteger, PersonId, PersonName } from "./persons";
|
||||||
|
|
||||||
export const PERSON_GENDER_VALUES = {
|
/** L'ID de participation au casting sous forme de nombre entier positif. */
|
||||||
FEMALE: "female",
|
export type CastId = Brand.Brand<"CastId"> & number;
|
||||||
MALE: "male",
|
export const CastId = Brand.refined<CastId>(
|
||||||
NON_BINARY: "non-binary",
|
(id: number) => Number.isInteger(id) && id >= 0,
|
||||||
OTHER: "other",
|
(id: number) => Brand.error(`${String(id)} doit être un nombre entier positif.`),
|
||||||
} as const;
|
|
||||||
export type PersonGender = Values<typeof PERSON_GENDER_VALUES>;
|
|
||||||
|
|
||||||
const GenderStringFromInteger = Schema.transform(
|
|
||||||
Schema.Literal(0, 1, 2, 3),
|
|
||||||
Schema.Enums(PERSON_GENDER_VALUES),
|
|
||||||
{
|
|
||||||
decode: (number: 0 | 1 | 2 | 3) =>
|
|
||||||
Match.value(number).pipe(
|
|
||||||
Match.when(0, () => PERSON_GENDER_VALUES.OTHER),
|
|
||||||
Match.when(1, () => PERSON_GENDER_VALUES.FEMALE),
|
|
||||||
Match.when(2, () => PERSON_GENDER_VALUES.MALE),
|
|
||||||
Match.when(3, () => PERSON_GENDER_VALUES.NON_BINARY),
|
|
||||||
Match.exhaustive,
|
|
||||||
),
|
|
||||||
encode: (gender: PersonGender) =>
|
|
||||||
Match.value(gender).pipe(
|
|
||||||
Match.when(PERSON_GENDER_VALUES.OTHER, () => 0 as const),
|
|
||||||
Match.when(PERSON_GENDER_VALUES.FEMALE, () => 1 as const),
|
|
||||||
Match.when(PERSON_GENDER_VALUES.MALE, () => 2 as const),
|
|
||||||
Match.when(PERSON_GENDER_VALUES.NON_BINARY, () => 3 as const),
|
|
||||||
Match.exhaustive,
|
|
||||||
),
|
|
||||||
strict: true,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/** L'ID d'une personne sous forme de nombre entier positif. */
|
/** L'ID d'accréditation sous forme de chaîne alphanumérique. */
|
||||||
export type PersonId = Brand.Brand<"PersonId"> & number;
|
export type CreditId = Brand.Brand<"CreditId"> & string;
|
||||||
export const PersonId = Brand.refined<PersonId>(
|
export const CreditId = Brand.refined<CreditId>(
|
||||||
n => Number.isInteger(n) && n >= 0,
|
(id: string) => Str.isString(id) && Str.length(id) === 24,
|
||||||
n => Brand.error(`${String(n)} doit être un nombre entier positif.`),
|
(id: string) => Brand.error(`${String(id)} doit être une chaîne alphanumérique de 24 caractères.`),
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Le nom d'une personne. */
|
/** Nom du personnage. */
|
||||||
export type PersonName = Brand.Brand<"PersonName"> & string;
|
export type CharacterName = Brand.Brand<"CharacterName"> & string;
|
||||||
export const PersonName = Brand.refined<PersonName>(
|
export const CharacterName = Brand.refined<CharacterName>(
|
||||||
s => s.length > 2,
|
(name: string) => Str.isString(name),
|
||||||
s => Brand.error(`${String(s)} doit contenir au moins 2 lettres.`),
|
(name: string) => Brand.error(`${name} n'est pas un nom de personnage valide`),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export interface MovieMainCrew {
|
||||||
|
directors: TmdbCredit[];
|
||||||
|
editors: TmdbCredit[];
|
||||||
|
writers: TmdbCredit[];
|
||||||
|
}
|
||||||
|
|
||||||
export class TmdbCredit extends Schema.Class<TmdbCredit>("TmdbCredit")({
|
export class TmdbCredit extends Schema.Class<TmdbCredit>("TmdbCredit")({
|
||||||
|
/** Concerne un film pour adultes. */
|
||||||
adult: Schema.Boolean,
|
adult: Schema.Boolean,
|
||||||
cast_id: Schema.NonNegativeInt.pipe(Schema.optional),
|
/** ID de participation au casting (son ordre d'ajout à l'entrée TMDB). */
|
||||||
character: Schema.NonEmptyString.pipe(Schema.optional),
|
cast_id: Schema.NonNegativeInt.pipe(Schema.fromBrand(CastId), Schema.optional),
|
||||||
credit_id: Schema.String,
|
/** Nom du personnage joué par la personne. */
|
||||||
|
character: Schema.String.pipe(Schema.fromBrand(CharacterName), Schema.optional),
|
||||||
|
/** ID unique de l'accréditation. */
|
||||||
|
credit_id: Schema.NonEmptyString.pipe(Schema.fromBrand(CreditId)),
|
||||||
|
/** Département auprès duquel l'accréditation prend place. */
|
||||||
department: Schema.String.pipe(Schema.optional),
|
department: Schema.String.pipe(Schema.optional),
|
||||||
|
/** Genre de la personne. */
|
||||||
gender: GenderStringFromInteger,
|
gender: GenderStringFromInteger,
|
||||||
|
/** ID de la personne. */
|
||||||
id: Schema.NonNegativeInt.pipe(Schema.fromBrand(PersonId)),
|
id: Schema.NonNegativeInt.pipe(Schema.fromBrand(PersonId)),
|
||||||
|
/** Rôle de la personne. */
|
||||||
job: Schema.NonEmptyString.pipe(Schema.optional),
|
job: Schema.NonEmptyString.pipe(Schema.optional),
|
||||||
|
/** Département historiquement associé à la personne. */
|
||||||
known_for_department: Schema.String,
|
known_for_department: Schema.String,
|
||||||
|
/** Nom de la personne. */
|
||||||
name: Schema.NonEmptyString.pipe(Schema.fromBrand(PersonName)),
|
name: Schema.NonEmptyString.pipe(Schema.fromBrand(PersonName)),
|
||||||
|
/** Ordre au sein du casting. */
|
||||||
order: Schema.NonNegativeInt.pipe(Schema.optional),
|
order: Schema.NonNegativeInt.pipe(Schema.optional),
|
||||||
|
/** Nom de naissance de la personne. */
|
||||||
original_name: Schema.NonEmptyString.pipe(Schema.fromBrand(PersonName)),
|
original_name: Schema.NonEmptyString.pipe(Schema.fromBrand(PersonName)),
|
||||||
|
/** Popularité TMDB de la personne. */
|
||||||
popularity: Schema.Number,
|
popularity: Schema.Number,
|
||||||
|
/** Chemin relatif TMDB du profil de la personne. */
|
||||||
profile_path: Schema.Union(Schema.String, Schema.Null),
|
profile_path: Schema.Union(Schema.String, Schema.Null),
|
||||||
}) {}
|
}) {}
|
||||||
|
|
|
||||||
16
src/libs/apis/tmdb/schemas/dates.ts
Normal file
16
src/libs/apis/tmdb/schemas/dates.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import type { Utc } from "effect/DateTime";
|
||||||
|
|
||||||
|
import { DateTime, Schema } from "effect";
|
||||||
|
|
||||||
|
/** Schéma acceptant une chaîne de caractère au format "YYYY-MM-DD" et la convertissant en DateTime. */
|
||||||
|
export const DateFromTmdbDate = Schema.transform(
|
||||||
|
Schema.String.pipe(Schema.length(10)),
|
||||||
|
Schema.DateTimeUtcFromSelf,
|
||||||
|
{
|
||||||
|
decode: (string: string): Utc => DateTime.unsafeMake(string),
|
||||||
|
encode: (date: Utc): string => DateTime.format(date, { dateStyle: "short", locale: "sv-SE" }),
|
||||||
|
strict: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const YearString = Schema.String.pipe(Schema.pattern(/^$|[0-9]{4}/));
|
||||||
32
src/libs/apis/tmdb/schemas/jobs.ts
Normal file
32
src/libs/apis/tmdb/schemas/jobs.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { Brand, String as Str } from "effect";
|
||||||
|
|
||||||
|
/** Rôle dans une oeuvre. */
|
||||||
|
export type Job = Brand.Brand<"Job"> & string;
|
||||||
|
export const Job = Brand.refined<Job>(
|
||||||
|
(name: string) => Str.isString(name) && Str.length(name) >= 2,
|
||||||
|
(name: string) => Brand.error(`${name} n'est pas un rôle connu.`),
|
||||||
|
);
|
||||||
|
|
||||||
|
const JOBS = {
|
||||||
|
DIRECTOR: "Director",
|
||||||
|
EDITOR: "Editor",
|
||||||
|
NOVEL: "Novel",
|
||||||
|
ORIGINAL_SCREENPLAY: "Original Screenplay",
|
||||||
|
SCREENPLAY: "Screenplay",
|
||||||
|
WRITER: "Writer",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DIRECTING_MAIN_JOBS = [
|
||||||
|
JOBS.DIRECTOR,
|
||||||
|
];
|
||||||
|
|
||||||
|
export const EDITING_MAIN_JOBS = [
|
||||||
|
JOBS.EDITOR,
|
||||||
|
];
|
||||||
|
|
||||||
|
export const WRITING_MAIN_JOBS = [
|
||||||
|
JOBS.NOVEL,
|
||||||
|
JOBS.ORIGINAL_SCREENPLAY,
|
||||||
|
JOBS.SCREENPLAY,
|
||||||
|
JOBS.WRITER,
|
||||||
|
];
|
||||||
|
|
@ -2,8 +2,8 @@ import { Brand, Schema } from "effect";
|
||||||
|
|
||||||
export type LanguageIsoCode = Brand.Brand<LanguageIsoCode> & string;
|
export type LanguageIsoCode = Brand.Brand<LanguageIsoCode> & string;
|
||||||
export const LanguageIsoCode = Brand.refined<LanguageIsoCode>(
|
export const LanguageIsoCode = Brand.refined<LanguageIsoCode>(
|
||||||
s => /[a-z]{2}/.test(s),
|
(code: string) => /[a-z]{2}/.test(code),
|
||||||
s => Brand.error(`${s} ne correspond pas au code ISO 639-1 connu d'une langue.`),
|
(code: string) => Brand.error(`${code} ne correspond pas au code ISO 639-1 connu d'une langue.`),
|
||||||
);
|
);
|
||||||
|
|
||||||
export class TmdbLanguage extends Schema.Class<TmdbLanguage>("TmdbLanguage")({
|
export class TmdbLanguage extends Schema.Class<TmdbLanguage>("TmdbLanguage")({
|
||||||
|
|
|
||||||
50
src/libs/apis/tmdb/schemas/persons.ts
Normal file
50
src/libs/apis/tmdb/schemas/persons.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
import type { Values } from "@/libs/utils/types";
|
||||||
|
|
||||||
|
import { Brand, Match, Schema, String as Str } from "effect";
|
||||||
|
|
||||||
|
export const PERSON_GENDER_VALUES = {
|
||||||
|
FEMALE: "female",
|
||||||
|
MALE: "male",
|
||||||
|
NON_BINARY: "non-binary",
|
||||||
|
OTHER: "other",
|
||||||
|
} as const;
|
||||||
|
export type PersonGender = Values<typeof PERSON_GENDER_VALUES>;
|
||||||
|
|
||||||
|
/** Transformation de Schéma d'index numérique vers valeur textuelle de l'énumération de Genre. */
|
||||||
|
export const GenderStringFromInteger = Schema.transform(
|
||||||
|
Schema.Literal(0, 1, 2, 3),
|
||||||
|
Schema.Enums(PERSON_GENDER_VALUES),
|
||||||
|
{
|
||||||
|
decode: (number: 0 | 1 | 2 | 3) =>
|
||||||
|
Match.value(number).pipe(
|
||||||
|
Match.when(0, () => PERSON_GENDER_VALUES.OTHER),
|
||||||
|
Match.when(1, () => PERSON_GENDER_VALUES.FEMALE),
|
||||||
|
Match.when(2, () => PERSON_GENDER_VALUES.MALE),
|
||||||
|
Match.when(3, () => PERSON_GENDER_VALUES.NON_BINARY),
|
||||||
|
Match.exhaustive,
|
||||||
|
),
|
||||||
|
encode: (gender: PersonGender) =>
|
||||||
|
Match.value(gender).pipe(
|
||||||
|
Match.when(PERSON_GENDER_VALUES.OTHER, () => 0 as const),
|
||||||
|
Match.when(PERSON_GENDER_VALUES.FEMALE, () => 1 as const),
|
||||||
|
Match.when(PERSON_GENDER_VALUES.MALE, () => 2 as const),
|
||||||
|
Match.when(PERSON_GENDER_VALUES.NON_BINARY, () => 3 as const),
|
||||||
|
Match.exhaustive,
|
||||||
|
),
|
||||||
|
strict: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/** L'ID d'une personne sous forme de nombre entier positif. */
|
||||||
|
export type PersonId = Brand.Brand<"PersonId"> & number;
|
||||||
|
export const PersonId = Brand.refined<PersonId>(
|
||||||
|
(id: number) => Number.isInteger(id) && id >= 0,
|
||||||
|
(id: number) => Brand.error(`${String(id)} doit être un nombre entier positif.`),
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Le nom d'une personne. */
|
||||||
|
export type PersonName = Brand.Brand<"PersonName"> & string;
|
||||||
|
export const PersonName = Brand.refined<PersonName>(
|
||||||
|
(name: string) => Str.isString(name) && Str.length(name) >= 2,
|
||||||
|
(name: string) => Brand.error(`${String(name)} n'est pas un nom valide'.`),
|
||||||
|
);
|
||||||
8
src/libs/apis/tmdb/schemas/works.ts
Normal file
8
src/libs/apis/tmdb/schemas/works.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { Brand, String as Str } from "effect";
|
||||||
|
|
||||||
|
/** Titre d'une oeuvre. */
|
||||||
|
export type ArtWorkTitle = Brand.Brand<"ArtWorkTitle"> & string;
|
||||||
|
export const ArtWorkTitle = Brand.refined<ArtWorkTitle>(
|
||||||
|
(name: string) => Str.isString(name) && Str.length(name) >= 2,
|
||||||
|
(name: string) => Brand.error(`${name} n'est pas un titre de d'oeuvre valide.`),
|
||||||
|
);
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
import { APPRECIATION_STATES, MEDIA_TYPES } from "@/db/schemas/constants";
|
import { APPRECIATION_STATES, MEDIA_TYPES } from "@/db/schemas/constants";
|
||||||
import { Schema } from "effect";
|
import { Schema } from "effect";
|
||||||
|
|
||||||
|
import { YearString } from "../apis/tmdb/schemas/dates";
|
||||||
|
import { LanguageIsoCode } from "../apis/tmdb/schemas/languages";
|
||||||
|
import { ArtWorkTitle } from "../apis/tmdb/schemas/works";
|
||||||
|
|
||||||
export class SearchPageQueryParams extends Schema.Class<SearchPageQueryParams>("SearchPageQueryParams")({
|
export class SearchPageQueryParams extends Schema.Class<SearchPageQueryParams>("SearchPageQueryParams")({
|
||||||
query: Schema.NonEmptyString,
|
query: Schema.NonEmptyString,
|
||||||
type: Schema.Enums(MEDIA_TYPES),
|
type: Schema.Enums(MEDIA_TYPES),
|
||||||
year: Schema.String,
|
year: YearString,
|
||||||
}) {}
|
}) {}
|
||||||
|
|
||||||
export class MergedTmdbLocalData extends Schema.Class<MergedTmdbLocalData>("MergedTmdbLocalData")({
|
export class MergedTmdbLocalData extends Schema.Class<MergedTmdbLocalData>("MergedTmdbLocalData")({
|
||||||
|
|
@ -18,14 +22,14 @@ export class MergedTmdbLocalData extends Schema.Class<MergedTmdbLocalData>("Merg
|
||||||
entryId: Schema.NonNegativeInt.pipe(Schema.optional),
|
entryId: Schema.NonNegativeInt.pipe(Schema.optional),
|
||||||
entryStateId: Schema.NonNegativeInt.pipe(Schema.optional),
|
entryStateId: Schema.NonNegativeInt.pipe(Schema.optional),
|
||||||
genreIds: Schema.Array(Schema.NonNegativeInt),
|
genreIds: Schema.Array(Schema.NonNegativeInt),
|
||||||
originalLanguage: Schema.String,
|
originalLanguage: Schema.String.pipe(Schema.fromBrand(LanguageIsoCode)),
|
||||||
originalResultIndex: Schema.Int,
|
originalResultIndex: Schema.Int,
|
||||||
originalTitle: Schema.String,
|
originalTitle: Schema.String.pipe(Schema.fromBrand(ArtWorkTitle)),
|
||||||
overview: Schema.String,
|
overview: Schema.String,
|
||||||
popularity: Schema.Number,
|
popularity: Schema.Number,
|
||||||
posterBlob: Schema.Unknown.pipe(Schema.optional),
|
posterBlob: Schema.Unknown.pipe(Schema.optional),
|
||||||
posterUrl: Schema.Union(Schema.String, Schema.Null),
|
posterUrl: Schema.Union(Schema.String, Schema.Null),
|
||||||
releaseDate: Schema.String,
|
releaseDate: Schema.String,
|
||||||
title: Schema.String,
|
title: Schema.String.pipe(Schema.fromBrand(ArtWorkTitle)),
|
||||||
tmdbId: Schema.NonNegativeInt,
|
tmdbId: Schema.NonNegativeInt,
|
||||||
}) {}
|
}) {}
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,10 @@
|
||||||
import { ReadApi } from "@/services/read-api.ts";
|
import { ReadApi } from "@/services/read-api.ts";
|
||||||
import { RuntimeClient } from "@/services/runtime-client.ts";
|
import { RuntimeClient } from "@/services/runtime-client.ts";
|
||||||
import { TmdbApi } from "@/services/tmdb-api.ts";
|
import { TmdbApi } from "@/services/tmdb-api.ts";
|
||||||
import { Effect, pipe, Schema } from "effect";
|
import { useEntryStore } from "@/stores/entry.ts";
|
||||||
|
import { DateTime, Effect, pipe, Schema } from "effect";
|
||||||
import { computed, onMounted, ref, useTemplateRef, watch } from "vue";
|
import { computed, onMounted, ref, useTemplateRef, watch } from "vue";
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { useEntryStore } from "@/stores/entry.ts";
|
|
||||||
|
|
||||||
// États
|
// États
|
||||||
|
|
||||||
|
|
@ -33,18 +33,22 @@
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
/** Effet dérivé des paramètres validés de la route. */
|
// Magasins
|
||||||
const routeQueryParams = computed(() => Schema.decodeUnknown(SearchPageQueryParams)(route.query));
|
const entryStore = useEntryStore();
|
||||||
|
|
||||||
/** L'Élément DOM du formulaire de recherche. */
|
/** L'Élément DOM du formulaire de recherche. */
|
||||||
const form = useTemplateRef("form");
|
const form = useTemplateRef("form");
|
||||||
/** Valeurs du formulaire de recherche. */
|
/** Valeurs du formulaire de recherche. */
|
||||||
const searchFormData: Ref<SearchPageQueryParams | undefined> = ref<SearchPageQueryParams>();
|
const searchFormData: Ref<SearchPageQueryParams | undefined> = ref<SearchPageQueryParams>();
|
||||||
|
/** Effet dérivé des paramètres validés de la route. */
|
||||||
|
const routeQueryParams = computed(() => Schema.decodeUnknown(SearchPageQueryParams)(route.query));
|
||||||
|
|
||||||
/** Retour de la requête de recherche de films auprès de l'API TMDB. */
|
/** Retour de la requête de recherche de films auprès de l'API TMDB. */
|
||||||
const search: Ref<TmdbMovieSearchResponse | undefined> = ref<TmdbMovieSearchResponse>();
|
const search: Ref<TmdbMovieSearchResponse | undefined> = ref<TmdbMovieSearchResponse>();
|
||||||
/** Données complètes de la recherche avec les données TMDB et locales. */
|
/** Données complètes de la recherche avec les données TMDB et locales. */
|
||||||
const searchData: Ref<Map<number, MergedTmdbLocalData>> = ref(new Map<number, MergedTmdbLocalData>());
|
const searchData: Ref<Map<number, MergedTmdbLocalData>> = ref(new Map<number, MergedTmdbLocalData>());
|
||||||
|
/** Données de l'Entrée actuellement éditée. */
|
||||||
|
const currentEditedEntry: Ref<MergedTmdbLocalData | undefined> = ref();
|
||||||
|
|
||||||
/** État du chargement de la requête auprès de l'API TMDB. */
|
/** État du chargement de la requête auprès de l'API TMDB. */
|
||||||
const isLoading: Ref<boolean> = ref(false);
|
const isLoading: Ref<boolean> = ref(false);
|
||||||
|
|
@ -53,11 +57,6 @@
|
||||||
/** Message affiché à l'Utilisateur. */
|
/** Message affiché à l'Utilisateur. */
|
||||||
const message: Ref<string> = ref("");
|
const message: Ref<string> = ref("");
|
||||||
|
|
||||||
const editedEntry: Ref<MergedTmdbLocalData | undefined> = ref();
|
|
||||||
|
|
||||||
// Magasins
|
|
||||||
const entryStore = useEntryStore();
|
|
||||||
|
|
||||||
// Fonctions
|
// Fonctions
|
||||||
|
|
||||||
let updateUrlQuery = updateUrlQueryFromFormData(router, form.value);
|
let updateUrlQuery = updateUrlQueryFromFormData(router, form.value);
|
||||||
|
|
@ -104,7 +103,7 @@
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const updateSearchResults = async (): Promise<void> =>
|
const updateSearchResultsFromUrlQuery = async (): Promise<void> =>
|
||||||
pipe(
|
pipe(
|
||||||
routeQueryParams.value,
|
routeQueryParams.value,
|
||||||
Effect.tap(async (args: SearchPageQueryParams) => {
|
Effect.tap(async (args: SearchPageQueryParams) => {
|
||||||
|
|
@ -123,13 +122,13 @@
|
||||||
Effect.runPromise,
|
Effect.runPromise,
|
||||||
);
|
);
|
||||||
|
|
||||||
const toggleEntryDialog = (tmdbId?: number) => {
|
const toggleEditEntryDialog = (tmdbId?: number) => {
|
||||||
if (!tmdbId) {
|
if (!tmdbId) {
|
||||||
editedEntry.value = undefined;
|
currentEditedEntry.value = undefined;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
editedEntry.value = searchData.value.get(tmdbId);
|
currentEditedEntry.value = searchData.value.get(tmdbId);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Cycles
|
// Cycles
|
||||||
|
|
@ -170,7 +169,7 @@
|
||||||
overview: result.overview,
|
overview: result.overview,
|
||||||
popularity: result.popularity,
|
popularity: result.popularity,
|
||||||
posterUrl: result.poster_path,
|
posterUrl: result.poster_path,
|
||||||
releaseDate: result.release_date,
|
releaseDate: DateTime.format(result.release_date, { dateStyle: "short", locale: "sv-SE" }),
|
||||||
title: result.title,
|
title: result.title,
|
||||||
tmdbId: result.id,
|
tmdbId: result.id,
|
||||||
} satisfies MergedTmdbLocalData,
|
} satisfies MergedTmdbLocalData,
|
||||||
|
|
@ -184,7 +183,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(route, async (): Promise<void> => {
|
watch(route, async (): Promise<void> => {
|
||||||
await updateSearchResults();
|
await updateSearchResultsFromUrlQuery();
|
||||||
}, { immediate: true });
|
}, { immediate: true });
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|
@ -256,11 +255,15 @@
|
||||||
|
|
||||||
<LoadingMessage v-if="isLoading">Récupération des résultats</LoadingMessage>
|
<LoadingMessage v-if="isLoading">Récupération des résultats</LoadingMessage>
|
||||||
<ErrorMessage v-if="isErrored">{{ message }}</ErrorMessage>
|
<ErrorMessage v-if="isErrored">{{ message }}</ErrorMessage>
|
||||||
<TmdbSearchResults v-else :search-data="searchData" @entry-dialog-wanted="toggleEntryDialog"></TmdbSearchResults>
|
<TmdbSearchResults
|
||||||
|
v-else :search-data="searchData" @entry-dialog-wanted="toggleEditEntryDialog"
|
||||||
|
></TmdbSearchResults>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<EditEntryDialog v-if="editedEntry" :entry-data="editedEntry" @dialog-hidden="toggleEntryDialog()"></EditEntryDialog>
|
<EditEntryDialog
|
||||||
|
v-if="currentEditedEntry" :entry-data="currentEditedEntry" @dialog-hidden="toggleEditEntryDialog()"
|
||||||
|
></EditEntryDialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="css">
|
<style scoped lang="css">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue