src
lib
Components
Items
Layout
Modals
UI
User
server
routes
[kind=item]
api
following
game
gamesearch
logout
personalfeed
review
user
win
static
···
67
67
"svelte-sonner": "^0.3.28",
68
68
"tailwind-merge": "^2.5.4"
69
69
}
70
70
-
}
70
70
+
}
···
13
13
{#snippet avatar()}
14
14
<div
15
15
class={[
16
16
-
'border-base-400/50 bg-base-100 dark:border-base-700 dark:bg-base-800 overflow-hidden rounded-full border',
16
16
+
'overflow-hidden rounded-full border border-base-400/50 bg-base-100 dark:border-base-700 dark:bg-base-800',
17
17
size,
18
18
className
19
19
]}
···
22
22
<img loading="lazy" class="h-full w-full object-cover" {src} {alt} />
23
23
{:else}
24
24
<svg
25
25
-
class="text-base-300 dark:text-base-600 size-full"
25
25
+
class="size-full text-base-300 dark:text-base-600"
26
26
fill="currentColor"
27
27
viewBox="0 0 24 24"
28
28
>
···
37
37
38
38
{#if postUri}
39
39
<div class="not-prose flex flex-col gap-4">
40
40
-
<div class="text-base-950 dark:text-base-100 text-sm font-semibold">
40
40
+
<div class="text-sm font-semibold text-base-950 dark:text-base-100">
41
41
{postLikesCount} like{postLikesCount === 1 ? '' : 's'}
42
42
</div>
43
43
···
46
46
<a
47
47
href={`https://bsky.app/profile/${user.actor.handle}`}
48
48
class={[
49
49
-
'ring-base-50 dark:ring-base-900 bg-base-950 relative inline-block size-12 overflow-hidden rounded-full ring-2',
49
49
+
'relative inline-block size-12 overflow-hidden rounded-full bg-base-950 ring-2 ring-base-50 dark:ring-base-900',
50
50
index === 0 ? '-ml-2' : ''
51
51
]}
52
52
target="_blank"
···
62
62
63
63
{#if postLikesData.length < postLikesCount}
64
64
<div
65
65
-
class="text-accent-700 dark:text-accent-300 bg-accent-100 dark:bg-accent-950 ring-base-50 dark:ring-base-900 z-10 mb-4 flex size-12 items-center justify-center rounded-full text-sm font-semibold ring-2"
65
65
+
class="z-10 mb-4 flex size-12 items-center justify-center rounded-full bg-accent-100 text-sm font-semibold text-accent-700 ring-2 ring-base-50 dark:bg-accent-950 dark:text-accent-300 dark:ring-base-900"
66
66
>
67
67
+{postLikesCount - postLikesData.length}
68
68
</div>
···
7
7
</script>
8
8
9
9
<article
10
10
-
class="bg-base-900 ring-base-800 relative isolate flex aspect-16/9 h-64 flex-col justify-end rounded-2xl p-4 ring-1"
10
10
+
class="aspect-16/9 relative isolate flex h-64 flex-col justify-end rounded-2xl bg-base-900 p-4 ring-1 ring-base-800"
11
11
>
12
12
{#if data.thumb}
13
13
<img
···
17
17
/>
18
18
{/if}
19
19
<div
20
20
-
class="from-base-950/90 via-base-950/40 absolute inset-0 -z-10 rounded-xl bg-linear-to-t"
20
20
+
class="bg-linear-to-t absolute inset-0 -z-10 rounded-xl from-base-950/90 via-base-950/40"
21
21
></div>
22
22
23
23
<div
24
24
-
class="inset-shadow-md inset-ring-base-950/50 inset-shadow-base-950/40 ring-base-800 absolute inset-0 -z-10 h-full w-full rounded-2xl inset-ring-1 ring-1"
24
24
+
class="inset-shadow-md inset-ring-base-950/50 inset-shadow-base-950/40 inset-ring-1 absolute inset-0 -z-10 h-full w-full rounded-2xl ring-1 ring-base-800"
25
25
></div>
26
26
27
27
-
<div class="text-base-300 flex flex-wrap items-center gap-y-1 overflow-hidden text-sm/6">
27
27
+
<div class="flex flex-wrap items-center gap-y-1 overflow-hidden text-sm/6 text-base-300">
28
28
<div>{domain}</div>
29
29
</div>
30
30
<h3 class="mt-1 text-lg/6 font-semibold text-white">
···
3
3
</script>
4
4
5
5
{#each data as { fullsize: string; alt: string; thumb: string; aspectRatio: { width: number; height: number } }[] as image}
6
6
-
<div class="ring-base-800 relative h-fit w-fit rounded-2xl ring-1">
6
6
+
<div class="relative h-fit w-fit rounded-2xl ring-1 ring-base-800">
7
7
<img
8
8
loading="lazy"
9
9
src={image.thumb}
···
14
14
/>
15
15
16
16
<div
17
17
-
class="inset-shadow-md inset-shadow-base-950/30 inset-ring-base-950/10 absolute inset-0 h-full w-full rounded-2xl inset-ring-2"
17
17
+
class="inset-shadow-md inset-shadow-base-950/30 inset-ring-base-950/10 inset-ring-2 absolute inset-0 h-full w-full rounded-2xl"
18
18
></div>
19
19
</div>
20
20
{/each}
···
1
1
<script lang="ts">
2
2
-
import { onMount } from "svelte";
3
3
-
import Hls from "hls.js";
2
2
+
import { onMount } from 'svelte';
3
3
+
import Hls from 'hls.js';
4
4
5
5
-
const { data } = $props();
5
5
+
const { data } = $props();
6
6
7
7
-
import Plyr from "plyr";
7
7
+
import Plyr from 'plyr';
8
8
9
9
-
onMount(() => {
10
10
-
if (Hls.isSupported()) {
11
11
-
var hls = new Hls();
12
12
-
hls.loadSource(data.playlist);
13
13
-
hls.attachMedia(element);
14
14
-
}
9
9
+
onMount(() => {
10
10
+
if (Hls.isSupported()) {
11
11
+
var hls = new Hls();
12
12
+
hls.loadSource(data.playlist);
13
13
+
hls.attachMedia(element);
14
14
+
}
15
15
16
16
-
new Plyr(element, {
17
17
-
controls: ["play-large", "play", "progress", "current-time", "mute", "volume", "fullscreen", "settings"],
18
18
-
hideControls: true
19
19
-
});
20
20
-
});
16
16
+
new Plyr(element, {
17
17
+
controls: [
18
18
+
'play-large',
19
19
+
'play',
20
20
+
'progress',
21
21
+
'current-time',
22
22
+
'mute',
23
23
+
'volume',
24
24
+
'fullscreen',
25
25
+
'settings'
26
26
+
],
27
27
+
hideControls: true
28
28
+
});
29
29
+
});
21
30
22
22
-
let element: HTMLMediaElement;
31
31
+
let element: HTMLMediaElement;
23
32
</script>
24
33
25
34
<svelte:head>
26
26
-
<link rel="stylesheet" href="https://cdn.plyr.io/3.7.8/plyr.css" />
35
35
+
<link rel="stylesheet" href="https://cdn.plyr.io/3.7.8/plyr.css" />
27
36
</svelte:head>
28
37
29
29
-
<div class="max-w-full w-full aspect-video rounded-2xl overflow-hidden border border-base-700">
30
30
-
<video bind:this={element}>
31
31
-
<track kind="captions" />
32
32
-
</video>
38
38
+
<div class="aspect-video w-full max-w-full overflow-hidden rounded-2xl border border-base-700">
39
39
+
<video bind:this={element}>
40
40
+
<track kind="captions" />
41
41
+
</video>
33
42
</div>
34
43
35
44
<style>
···
1
1
<script lang="ts">
2
2
-
import { onDestroy } from 'svelte'
3
3
-
import { register, unregister } from './state'
2
2
+
import { onDestroy } from 'svelte';
3
3
+
import { register, unregister } from './state';
4
4
5
5
-
export let date: Date | number
6
6
-
export let locale: string
7
7
-
export let live = true
5
5
+
export let date: Date | number;
6
6
+
export let locale: string;
7
7
+
export let live = true;
8
8
9
9
-
let instance = new Object()
10
10
-
let text = ''
9
9
+
let instance = new Object();
10
10
+
let text = '';
11
11
12
12
-
register(instance, date, locale, live, value => ({ text } = value))
12
12
+
register(instance, date, locale, live, (value) => ({ text } = value));
13
13
14
14
-
onDestroy(() => unregister(instance))
14
14
+
onDestroy(() => unregister(instance));
15
15
</script>
16
16
17
17
<span class={$$props.class}>{text}</span>
···
1
1
-
import type { Callback } from './render'
2
2
-
import { register, unregister } from './state'
1
1
+
import type { Callback } from './render';
2
2
+
import { register, unregister } from './state';
3
3
4
4
export interface Options {
5
5
-
date: Date | number
6
6
-
locale?: string
7
7
-
live?: boolean
5
5
+
date: Date | number;
6
6
+
locale?: string;
7
7
+
live?: boolean;
8
8
}
9
9
10
10
export function relativeTime(node: HTMLElement, options: Options) {
11
11
-
const callback: Callback = ({ text }) => (node.textContent = text)
11
11
+
const callback: Callback = ({ text }) => (node.textContent = text);
12
12
13
13
function init(options: Options) {
14
14
-
const date = options.date
15
15
-
const locale = options.locale || navigator.language
16
16
-
const live = (options.live = true)
14
14
+
const date = options.date;
15
15
+
const locale = options.locale || navigator.language;
16
16
+
const live = (options.live = true);
17
17
18
18
-
register(node, date, locale, live, callback)
18
18
+
register(node, date, locale, live, callback);
19
19
}
20
20
21
21
-
init(options)
21
21
+
init(options);
22
22
23
23
return {
24
24
update(options: Options) {
25
25
-
init(options)
25
25
+
init(options);
26
26
},
27
27
destroy() {
28
28
-
unregister(node)
29
29
-
},
30
30
-
}
28
28
+
unregister(node);
29
29
+
}
30
30
+
};
31
31
}
···
1
1
// keep a cache of formatter per locale, to avoid re-creating them (GC)
2
2
-
const formatters = new Map<string, Intl.RelativeTimeFormat>()
2
2
+
const formatters = new Map<string, Intl.RelativeTimeFormat>();
3
3
4
4
// get the Intl.RelativeTimeFormat formatter for the given locale
5
5
export function getFormatter(locale: string) {
6
6
if (formatters.has(locale)) {
7
7
-
return formatters.get(locale)!
7
7
+
return formatters.get(locale)!;
8
8
}
9
9
-
const formatter = new Intl.RelativeTimeFormat(locale, { numeric: 'always', style: 'narrow' })
10
10
-
formatters.set(locale, formatter)
11
11
-
return formatter
9
9
+
const formatter = new Intl.RelativeTimeFormat(locale, { numeric: 'always', style: 'narrow' });
10
10
+
formatters.set(locale, formatter);
11
11
+
return formatter;
12
12
}
···
1
1
-
export * from './action'
2
2
-
export type { Callback } from './render'
3
3
-
export { register, unregister } from './state'
4
4
-
export { default as default } from './RelativeTime.svelte'
1
1
+
export * from './action';
2
2
+
export type { Callback } from './render';
3
3
+
export { register, unregister } from './state';
4
4
+
export { default as default } from './RelativeTime.svelte';
···
1
1
-
export type Callback = (result: { seconds: number; count: number; units: Intl.RelativeTimeFormatUnit; text: string }) => void
1
1
+
export type Callback = (result: {
2
2
+
seconds: number;
3
3
+
count: number;
4
4
+
units: Intl.RelativeTimeFormatUnit;
5
5
+
text: string;
6
6
+
}) => void;
2
7
3
8
export interface RenderState {
4
4
-
date: Date | number
5
5
-
callback: Callback
6
6
-
formatter: Intl.RelativeTimeFormat
9
9
+
date: Date | number;
10
10
+
callback: Callback;
11
11
+
formatter: Intl.RelativeTimeFormat;
7
12
}
8
13
9
14
// Array reprsenting one minute, hour, day, week, month, etc in seconds
10
10
-
const cutoffs = [60, 3600, 86400, 86400 * 7, 86400 * 30, 86400 * 365, Infinity]
15
15
+
const cutoffs = [60, 3600, 86400, 86400 * 7, 86400 * 30, 86400 * 365, Infinity];
11
16
12
17
// Array equivalent to the above but in the string representation of the units
13
13
-
const formatUnits: Intl.RelativeTimeFormatUnit[] = ['seconds', 'minutes', 'hours', 'days', 'weeks', 'months', 'years']
18
18
+
const formatUnits: Intl.RelativeTimeFormatUnit[] = [
19
19
+
'seconds',
20
20
+
'minutes',
21
21
+
'hours',
22
22
+
'days',
23
23
+
'weeks',
24
24
+
'months',
25
25
+
'years'
26
26
+
];
14
27
15
28
// function to render relative time into
16
29
export function render(state: RenderState, now: number) {
17
17
-
const { date, callback, formatter } = state
30
30
+
const { date, callback, formatter } = state;
18
31
19
32
// Allow dates or times to be passed
20
20
-
const timeMs = typeof date === 'number' ? date : date.getTime()
33
33
+
const timeMs = typeof date === 'number' ? date : date.getTime();
21
34
22
35
// Get the amount of seconds between the given date and now
23
23
-
const delta = timeMs - now
24
24
-
const seconds = Math.round(delta / 1000)
36
36
+
const delta = timeMs - now;
37
37
+
const seconds = Math.round(delta / 1000);
25
38
26
39
// Grab the ideal cutoff unit
27
27
-
const unitIndex = cutoffs.findIndex(cutoff => cutoff > Math.abs(seconds))
40
40
+
const unitIndex = cutoffs.findIndex((cutoff) => cutoff > Math.abs(seconds));
28
41
29
42
// units
30
30
-
const units = formatUnits[unitIndex]
43
43
+
const units = formatUnits[unitIndex];
31
44
32
45
// Get the divisor to divide from the seconds. E.g. if our unit is 'day' our divisor
33
46
// is one day in seconds, so we can divide our seconds by this to get the # of days
34
34
-
const divisor = unitIndex ? cutoffs[unitIndex - 1] : 1
47
47
+
const divisor = unitIndex ? cutoffs[unitIndex - 1] : 1;
35
48
36
49
// count of units
37
37
-
const count = Math.round(seconds / divisor)
50
50
+
const count = Math.round(seconds / divisor);
38
51
39
52
// Intl.RelativeTimeFormat do its magic
40
40
-
callback({ seconds: seconds, count, units, text: formatter.format(count, units) })
53
53
+
callback({ seconds: seconds, count, units, text: formatter.format(count, units) });
41
54
42
55
// calculate time to next update, taking account rounding (e.g. it goes from 2 minutes to 1 minute at 90 seconds)
43
56
// and also the changeover from units (59 seconds shouldn't show as 1 minute, so at 61 seconds we schedule the next
44
57
// update for 1 second time)
45
58
46
46
-
const divisorMs = divisor * 1000
59
59
+
const divisorMs = divisor * 1000;
47
60
48
48
-
let updateIn
61
61
+
let updateIn;
49
62
50
63
if (unitIndex) {
51
51
-
updateIn = divisorMs / 2 - (Math.abs(delta) % divisorMs)
64
64
+
updateIn = divisorMs / 2 - (Math.abs(delta) % divisorMs);
52
65
if (updateIn < 0) {
53
53
-
updateIn += divisorMs
66
66
+
updateIn += divisorMs;
54
67
}
55
68
} else {
56
56
-
updateIn = divisorMs - (Math.abs(delta) % divisorMs)
69
69
+
updateIn = divisorMs - (Math.abs(delta) % divisorMs);
57
70
}
58
71
59
59
-
const updateAt = now + updateIn
72
72
+
const updateAt = now + updateIn;
60
73
61
61
-
return updateAt
74
74
+
return updateAt;
62
75
}
···
1
1
-
import { getFormatter } from './formatter'
2
2
-
import { render } from './render'
3
3
-
import type { Callback, RenderState } from './render'
1
1
+
import { getFormatter } from './formatter';
2
2
+
import { render } from './render';
3
3
+
import type { Callback, RenderState } from './render';
4
4
5
5
interface UpdateState extends RenderState {
6
6
-
update: number
6
6
+
update: number;
7
7
}
8
8
9
9
// keep track of each instance
10
10
-
const instances = new Map<Object, UpdateState>()
10
10
+
const instances = new Map<Object, UpdateState>();
11
11
12
12
// we use a single timer for efficiency and to keep updates in sync
13
13
-
let updateInterval: number | NodeJS.Timeout
13
13
+
let updateInterval: number | NodeJS.Timeout;
14
14
15
15
// register or update instance
16
16
-
export function register(instance: Object, date: Date | number, locale: string, live: boolean, callback: Callback) {
16
16
+
export function register(
17
17
+
instance: Object,
18
18
+
date: Date | number,
19
19
+
locale: string,
20
20
+
live: boolean,
21
21
+
callback: Callback
22
22
+
) {
17
23
// get the formatter for the given locale, we do this here so we don't keep having to look it up on each tick
18
18
-
const formatter = getFormatter(locale)
24
24
+
const formatter = getFormatter(locale);
19
25
20
26
// create state to render
21
21
-
const state = { date, callback, formatter }
27
27
+
const state = { date, callback, formatter };
22
28
23
29
// initial render is immediate, so works for SSR
24
24
-
const update = render(state, Date.now())
30
30
+
const update = render(state, Date.now());
25
31
26
32
// if it's to update live, we keep a track and schedule the next update
27
33
if (live) {
28
28
-
instances.set(instance, { ...state, update })
34
34
+
instances.set(instance, { ...state, update });
29
35
} else {
30
30
-
instances.delete(instance)
36
36
+
instances.delete(instance);
31
37
}
32
38
33
39
// start the clock ticking if there are any live instances
···
35
41
updateInterval =
36
42
updateInterval ||
37
43
setInterval(() => {
38
38
-
const now = Date.now()
44
44
+
const now = Date.now();
39
45
for (const state of instances.values()) {
40
46
if (state.update <= now) {
41
41
-
state.update = render(state, now)
47
47
+
state.update = render(state, now);
42
48
}
43
49
}
44
44
-
}, 1000)
50
50
+
}, 1000);
45
51
}
46
52
}
47
53
48
54
export function unregister(instance: Object) {
49
49
-
instances.delete(instance)
55
55
+
instances.delete(instance);
50
56
if (instances.size === 0) {
51
51
-
clearInterval(updateInterval)
52
52
-
updateInterval = 0
57
57
+
clearInterval(updateInterval);
58
58
+
updateInterval = 0;
53
59
}
54
60
}
···
5
5
AppBskyEmbedExternal,
6
6
AppBskyEmbedImages,
7
7
AppBskyEmbedRecordWithMedia,
8
8
-
AppBskyEmbedVideo,
8
8
+
AppBskyEmbedVideo
9
9
} from '@atproto/api';
10
10
11
11
export interface Post extends AppBskyFeedDefs.PostView {
···
70
70
</div>
71
71
<div class="mt-2 flex justify-between">
72
72
<h3 class="sm:text-md text-sm font-medium text-base-50">
73
73
-
<a
74
74
-
href="/{item.media_type}/{item.id}-{nameToId(
75
75
-
item.title ?? ''
76
76
-
)}"
77
77
-
>
73
73
+
<a href="/{item.media_type}/{item.id}-{nameToId(item.title ?? '')}">
78
74
<span aria-hidden="true" class="absolute inset-0"></span>
79
75
<div class="line-clamp-2 max-w-full">
80
76
{item.title}
···
8
8
9
9
import { cn, nameToId } from '$lib/utils';
10
10
11
11
-
let { data, showMovieDetails = true, bigText = false }: { data: MainRecord; showMovieDetails?: boolean; bigText?: boolean } =
12
12
-
$props();
11
11
+
let {
12
12
+
data,
13
13
+
showMovieDetails = true,
14
14
+
bigText = false
15
15
+
}: { data: MainRecord; showMovieDetails?: boolean; bigText?: boolean } = $props();
13
16
14
17
let isLiked = $state(false);
15
18
</script>
···
54
57
{data.author.displayName || data.author.handle}
55
58
</div>
56
59
</div>
57
57
-
<div class="shrink-0 pr-2 text-sm text-base-400 font-normal">
60
60
+
<div class="shrink-0 pr-2 text-sm font-normal text-base-400">
58
61
<RelativeTime date={new Date(data.updatedAt)} locale="en-US" />
59
62
</div>
60
63
</a>
···
95
98
</div>
96
99
97
100
{#if data.record.note?.value}
98
98
-
<div class={cn("mt-4 text-sm text-base-300", bigText ? 'text-xl text-base-100' : '')}>
101
101
+
<div class={cn('mt-4 text-sm text-base-300', bigText ? 'text-xl text-base-100' : '')}>
99
102
{@html data.record.note?.value?.replace('\n', '<br /><br />')}
100
103
</div>
101
104
{/if}
···
176
179
{(data.record.likes ?? 0) + (isLiked ? 1 : 0)}
177
180
</button>
178
181
179
179
-
<OptionButton data={data} />
182
182
+
<OptionButton {data} />
180
183
</div>
181
184
182
185
<!-- <a href={`/review/${encodeURIComponent(data.uri)}`}>
···
87
87
{#if rateMovieModal.showModal}
88
88
<div class="relative z-50" aria-labelledby="modal-title" role="dialog" aria-modal="true">
89
89
<div
90
90
-
class="bg-base-950/90 fixed inset-0 backdrop-blur-sm transition-opacity"
90
90
+
class="fixed inset-0 bg-base-950/90 backdrop-blur-sm transition-opacity"
91
91
onclick={() => (rateMovieModal.showModal = false)}
92
92
aria-hidden="true"
93
93
></div>
94
94
<div class="pointer-events-none fixed inset-0 z-50 h-[100dvh] w-screen overflow-y-auto">
95
95
<div class="flex h-[100dvh] items-end justify-center p-4 text-center sm:items-center sm:p-0">
96
96
<div
97
97
-
class="border-base-800 bg-base-900 pointer-events-auto relative w-full transform overflow-hidden rounded-lg border px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:max-w-sm sm:p-6"
97
97
+
class="pointer-events-auto relative w-full transform overflow-hidden rounded-lg border border-base-800 bg-base-900 px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:max-w-sm sm:p-6"
98
98
>
99
99
<button
100
100
-
class="bg-base-800/50 hover:bg-base-800/80 absolute right-2 top-2 rounded-full p-1"
100
100
+
class="absolute right-2 top-2 rounded-full bg-base-800/50 p-1 hover:bg-base-800/80"
101
101
onclick={() => (rateMovieModal.showModal = false)}
102
102
>
103
103
<svg
···
115
115
</button>
116
116
117
117
<div>
118
118
-
<h3 class="text-md text-base-50 mb-4 font-semibold" id="modal-title">
118
118
+
<h3 class="text-md mb-4 font-semibold text-base-50" id="modal-title">
119
119
{#if rateMovieModal.selectedItem?.editUri}
120
120
Edit review
121
121
{:else}
···
126
126
{#if rateMovieModal.selectedItem?.title}
127
127
<div class="relative flex items-center gap-4">
128
128
<div
129
129
-
class="border-base-800 bg-base-900/50 relative z-20 aspect-[2/3] h-32 w-auto shrink-0 overflow-hidden rounded-md border"
129
129
+
class="relative z-20 aspect-[2/3] h-32 w-auto shrink-0 overflow-hidden rounded-md border border-base-800 bg-base-900/50"
130
130
>
131
131
{#if rateMovieModal.selectedItem?.poster_path}
132
132
<img
···
137
137
{/if}
138
138
</div>
139
139
<h3
140
140
-
class="text-base-50 mb-4 flex flex-col gap-2 text-xl font-semibold"
140
140
+
class="mb-4 flex flex-col gap-2 text-xl font-semibold text-base-50"
141
141
id="modal-title"
142
142
>
143
143
{rateMovieModal.selectedItem.title}
···
158
158
{/if}
159
159
160
160
<div class="mt-4">
161
161
-
<label for="comment" class="text-base-50 block text-xs font-medium">review</label>
161
161
+
<label for="comment" class="block text-xs font-medium text-base-50">review</label>
162
162
<div class="mt-2">
163
163
<textarea
164
164
rows="4"
···
166
166
id="comment"
167
167
bind:value={review}
168
168
placeholder="write a review"
169
169
-
class="outline-nonse border-base-800 bg-base-950 text-base-50 placeholder:text-base-400 focus:outline-accent-400 block w-full rounded-lg border px-3 py-1.5 text-base -outline-offset-1 focus:outline focus:outline-2 focus:-outline-offset-2 sm:text-sm/6"
169
169
+
class="outline-nonse block w-full rounded-lg border border-base-800 bg-base-950 px-3 py-1.5 text-base text-base-50 -outline-offset-1 placeholder:text-base-400 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-accent-400 sm:text-sm/6"
170
170
></textarea>
171
171
</div>
172
172
</div>
···
182
182
}}
183
183
type="button"
184
184
disabled={sending || !rateMovieModal.selectedItem?.title}
185
185
-
class="border-accent-900 bg-accent-950/80 text-accent-300 hover:bg-accent-950 focus-visible:outline-accent-600 inline-flex w-full justify-center rounded-md border px-3 py-2 text-sm font-semibold shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
185
185
+
class="inline-flex w-full justify-center rounded-md border border-accent-900 bg-accent-950/80 px-3 py-2 text-sm font-semibold text-accent-300 shadow-sm hover:bg-accent-950 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent-600 disabled:cursor-not-allowed disabled:opacity-50"
186
186
>{sending
187
187
? 'Sending...'
188
188
: rateMovieModal.selectedItem?.editUri
···
111
111
rateMovieModal.selectedItem = {
112
112
...item,
113
113
currentRating: watchedItems.getRating(item)?.rating ?? undefined,
114
114
-
currentReview: watchedItems.getRating(item)?.ratingText ?? undefined,
114
114
+
currentReview: watchedItems.getRating(item)?.ratingText ?? undefined
115
115
};
116
116
rateMovieModal.showModal = true;
117
117
}}
···
44
44
@{profile.handle}
45
45
</div>
46
46
</div>
47
47
-
<div class="mt-6 hidden flex-row justify-stretch space-x-4 space-y-0">
48
48
-
<button
49
49
-
type="button"
50
50
-
class="inline-flex justify-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-base-900 shadow-sm ring-1 ring-inset ring-base-300 hover:bg-base-50"
51
51
-
>
52
52
-
<span>Call</span>
53
53
-
</button>
54
54
-
</div>
55
47
</div>
56
48
</div>
57
49
</div>
···
164
164
return data;
165
165
}
166
166
export async function getPopularMovies() {
167
167
-
const i=Math.floor(Math.random() * 6);
168
168
-
const url = `https://api.themoviedb.org/3/movie/top_rated?language=en-US&page=`+i;
167
167
+
const i = Math.floor(Math.random() * 6);
168
168
+
const url = `https://api.themoviedb.org/3/movie/popular?language=en-US&page=` + i;
169
169
const options = {
170
170
method: 'GET',
171
171
headers: {
···
176
176
177
177
const response = await fetch(url, options);
178
178
const data = await response.json();
179
179
-
//console.log(data.results[0]);
180
179
181
180
return data;
182
181
}
183
183
-
···
36
36
showLoginModal.toggle();
37
37
}}
38
38
type="button"
39
39
-
class="mt-8 inline-flex w-fit items-center gap-x-1.5 rounded-md border border-accent-500/30 bg-accent-700/20 px-3 py-2 text-sm font-semibold text-accent-400 shadow-sm transition-all duration-100 hover:bg-accent-700/30 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent-600"
39
39
+
class="mt-8 inline-flex w-fit items-center gap-x-1.5 rounded-xl border border-accent-500/30 bg-accent-700/20 px-3 py-2 text-sm font-semibold text-accent-400 shadow-sm transition-all duration-100 hover:bg-accent-700/30 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent-600"
40
40
>
41
41
Sign in with Bluesky
42
42
</button>
···
46
46
rateMovieModal.showEmpty();
47
47
}}
48
48
type="button"
49
49
-
class="mt-8 inline-flex w-fit items-center gap-x-1.5 rounded-md border border-accent-500/30 bg-accent-700/20 px-3 py-2 text-sm font-semibold text-accent-400 shadow-sm transition-all duration-100 hover:bg-accent-700/30 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent-600"
49
49
+
class="mt-8 inline-flex w-fit items-center gap-x-1.5 rounded-xl border border-accent-500/30 bg-accent-700/20 px-3 py-2 text-sm font-semibold text-accent-400 shadow-sm transition-all duration-100 hover:bg-accent-700/30 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent-600"
50
50
>
51
51
New review
52
52
</button>
···
17
17
<svelte:head>
18
18
<title>{data.item.title} | skywatched</title>
19
19
20
20
-
<meta
21
21
-
name="description"
22
22
-
content={`Rate and review "${data.item.title}" on skywatched`}
23
23
-
/>
20
20
+
<meta name="description" content={`Rate and review "${data.item.title}" on skywatched`} />
24
21
25
22
<meta property="og:url" content={$page.url.href} />
26
23
<meta property="og:type" content="website" />
27
27
-
<meta
28
28
-
property="og:title"
29
29
-
content="{data.item.title} | skywatched.app"
30
30
-
/>
31
31
-
<meta
32
32
-
property="og:description"
33
33
-
content={`Rate and review "${data.item.title}" on skywatched`}
34
34
-
/>
24
24
+
<meta property="og:title" content="{data.item.title} | skywatched.app" />
25
25
+
<meta property="og:description" content={`Rate and review "${data.item.title}" on skywatched`} />
35
26
<meta property="og:image" content="{$page.url.href}/og.png" />
36
27
37
28
<meta name="twitter:card" content="summary_large_image" />
38
29
<meta property="twitter:domain" content="skywatched.app" />
39
30
<meta property="twitter:url" content={$page.url.href} />
40
40
-
<meta
41
41
-
name="twitter:title"
42
42
-
content="{data.item.title} | skywatched.app"
43
43
-
/>
44
44
-
<meta
45
45
-
name="twitter:description"
46
46
-
content={`Rate and review "${data.item.title}" on skywatched`}
47
47
-
/>
31
31
+
<meta name="twitter:title" content="{data.item.title} | skywatched.app" />
32
32
+
<meta name="twitter:description" content={`Rate and review "${data.item.title}" on skywatched`} />
48
33
<meta name="twitter:image" content="{$page.url.href}/og.png" />
49
34
</svelte:head>
50
35
···
59
44
)}
60
45
onclick={() => {
61
46
rateMovieModal.show({
62
62
-
...data.item,
47
47
+
...data.item
63
48
});
64
49
}}
65
50
>
···
21
21
collection: string;
22
22
rkey: string;
23
23
record: {
24
24
-
subject: string;
24
24
+
subject: string;
25
25
createdAt: string;
26
26
};
27
27
} = {
···
29
29
collection: 'app.bsky.graph.follow',
30
30
rkey,
31
31
record: {
32
32
-
subject: did ,
32
32
+
subject: did,
33
33
createdAt: new Date().toISOString()
34
34
}
35
35
};
···
1
1
+
import { getFollows } from '$lib/bluesky.js';
2
2
+
import { getRecentRecords } from '$lib/db';
3
3
+
4
4
+
/** @type {import('./$types').PageServerLoad} */
5
5
+
export async function load(event) {
6
6
+
const feed = await getRecentRecords();
7
7
+
const did = event.locals.user.did;
8
8
+
const follows = await getFollows({ did });
9
9
+
const filt = feed.filter((review) =>
10
10
+
follows.follows.some((person) => person.did == review.author.did)
11
11
+
);
12
12
+
13
13
+
return { truefeed: filt };
14
14
+
}
···
15
15
const id = parseInt(event.params.id.split('-')[0]);
16
16
const id2 = parseInt(event.params.id2.split('-')[0]);
17
17
const kind = event.params.kind;
18
18
-
const kind2= event.params.kind2;
19
19
-
const ids=id;
20
20
-
const ids2=id2;
21
21
-
const couter=event.params.counter;
22
22
-
if(kind==kind2&&id==id2){
23
23
-
redirect(302,"/win/"+couter);
18
18
+
const kind2 = event.params.kind2;
19
19
+
const ids = id;
20
20
+
const ids2 = id2;
21
21
+
const couter = event.params.counter;
22
22
+
if (kind == kind2 && id == id2) {
23
23
+
redirect(302, '/win/' + couter);
24
24
}
25
25
-
26
26
-
if (kind !== 'movie' && kind !== 'tv' && kind!=='cast') {
25
25
+
26
26
+
if (kind !== 'movie' && kind !== 'tv' && kind !== 'cast') {
27
27
return error(404, 'Not found');
28
28
}
29
29
-
if (kind2 !== 'movie' && kind2 !== 'tv' && kind2!=='cast') {
29
29
+
if (kind2 !== 'movie' && kind2 !== 'tv' && kind2 !== 'cast') {
30
30
return error(404, 'Not found');
31
31
}
32
32
···
44
44
var person2;
45
45
var credits;
46
46
var credits2;
47
47
-
if(kind==="cast"){
48
48
-
temp = getDetails(68730,"movie");
49
49
-
casting=getCast(68730,"movie");
50
50
-
person=getPersonDetails(id);
51
51
-
person2=getPersonDetails(id2);
52
52
-
credits=id;
53
53
-
credits2=id2;
54
54
-
}
55
55
-
else{
47
47
+
if (kind === 'cast') {
48
48
+
temp = getDetails(68730, 'movie');
49
49
+
casting = getCast(68730, 'movie');
50
50
+
person = getPersonDetails(id);
51
51
+
person2 = getPersonDetails(id2);
52
52
+
credits = id;
53
53
+
credits2 = id2;
54
54
+
} else {
56
55
temp = getDetails(id, kind);
57
57
-
casting=getCast(id,kind);
58
58
-
person=getPersonDetails(37625);
59
59
-
person2=getPersonDetails(37625);
60
60
-
credits=37625;
61
61
-
credits2=37625;
62
62
-
56
56
+
casting = getCast(id, kind);
57
57
+
person = getPersonDetails(37625);
58
58
+
person2 = getPersonDetails(37625);
59
59
+
credits = 37625;
60
60
+
credits2 = 37625;
63
61
}
64
64
-
if(kind2==="cast"){
65
65
-
temp2 = getDetails(68730,"movie");
66
66
-
casting2=getCast(68730,"movie");
67
67
-
person2=getPersonDetails(id2);
68
68
-
credits2=id2;
69
69
-
}
70
70
-
else{
62
62
+
if (kind2 === 'cast') {
63
63
+
temp2 = getDetails(68730, 'movie');
64
64
+
casting2 = getCast(68730, 'movie');
65
65
+
person2 = getPersonDetails(id2);
66
66
+
credits2 = id2;
67
67
+
} else {
71
68
temp2 = getDetails(id2, kind2);
72
72
-
casting2=getCast(id2,kind2);
73
73
-
person2=getPersonDetails(37625);
74
74
-
credits2=37625;
75
75
-
69
69
+
casting2 = getCast(id2, kind2);
70
70
+
person2 = getPersonDetails(37625);
71
71
+
credits2 = 37625;
76
72
}
77
73
const resultPromise = temp;
78
78
-
const personPromise=person;
79
79
-
const person2Promise=person2;
74
74
+
const personPromise = person;
75
75
+
const person2Promise = person2;
80
76
//if(kind==="cast"){
81
77
// const resultPromise = getDetails(68730,"movie")
82
78
//}
···
87
83
88
84
const castPromise = casting;
89
85
const castPromise2 = casting2;
90
90
-
const creditsPromise= credits;
91
91
-
const creditsPromise2= credits2;
86
86
+
const creditsPromise = credits;
87
87
+
const creditsPromise2 = credits2;
92
88
const combinedCredits = await getCombinedCredits(credits);
93
89
const creditsSet = new Set<string>();
94
90
const combinedCredits2 = await getCombinedCredits(credits2);
···
126
122
};
127
123
});
128
124
129
129
-
const [result,result2, cast,cast2,personDetails,person2Details,creditDetails,creditDetails2] = await Promise.all([
125
125
+
const [
126
126
+
result,
127
127
+
result2,
128
128
+
cast,
129
129
+
cast2,
130
130
+
personDetails,
131
131
+
person2Details,
132
132
+
creditDetails,
133
133
+
creditDetails2
134
134
+
] = await Promise.all([
130
135
resultPromise,
131
136
resultPromise2,
132
137
castPromise,
···
1
1
<script lang="ts">
2
2
-
import { type PageData } from '../../../../../$types';
2
2
+
import { type PageData } from './$types';
3
3
import { cn, nameToId } from '$lib/utils';
4
4
-
import { rateMovieModal, videoPlayer, watchedItems } from '$lib/state.svelte';
5
4
6
6
-
import Container from '$lib/Components/Container.svelte';
7
7
-
import Avatar from '$lib/Components/Avatar.svelte';
5
5
+
import Container from '$lib/Components/Layout/Container.svelte';
6
6
+
import Avatar from '$lib/Components/User/Avatar.svelte';
8
7
import { page } from '$app/stores';
8
8
+
import { watchedItems } from '$lib/state/user.svelte';
9
9
+
import { rateMovieModal } from '$lib/state/modals.svelte';
9
10
10
11
let { data }: { data: PageData } = $props();
11
12
</script>
···
70
71
{/if}
71
72
{/snippet}
72
73
73
73
-
74
74
<div class="fixed inset-0 h-full w-full bg-black/50"></div>
75
75
76
76
<Container class="relative z-10 pb-8 pt-4">
77
77
<div class="flex justify-center gap-4 px-4 pt-8">
78
78
<div class="flex-column">
79
79
-
{#if data.kind === "cast"}
80
80
-
<div class="text-center text-sm font-medium">Cast1</div>
81
81
-
<Avatar
82
82
-
src={data.personDetails.profile_path
83
83
-
? 'https://image.tmdb.org/t/p/w500' + data.personDetails.profile_path
84
84
-
: undefined}
85
85
-
size="size-44"
86
86
-
/>
87
87
-
{:else}
88
88
-
<div class="text-center text-sm font-medium">Movie1</div>
89
89
-
<img
90
90
-
src="https://image.tmdb.org/t/p/w500{data.result.poster_path}"
91
91
-
alt=""
92
92
-
class="poster h-36 w-24 shrink-0 rounded-lg border border-white/10 sm:h-64 sm:w-44"
93
93
-
style:--name={`poster-${data.result.id}`}
94
94
-
/>
95
95
-
{/if}
79
79
+
{#if data.kind === 'cast'}
80
80
+
<div class="text-center text-sm font-medium">Cast1</div>
81
81
+
<Avatar
82
82
+
src={data.personDetails.profile_path
83
83
+
? 'https://image.tmdb.org/t/p/w500' + data.personDetails.profile_path
84
84
+
: undefined}
85
85
+
size="size-44"
86
86
+
/>
87
87
+
{:else}
88
88
+
<div class="text-center text-sm font-medium">Movie1</div>
89
89
+
<img
90
90
+
src="https://image.tmdb.org/t/p/w500{data.result.poster_path}"
91
91
+
alt=""
92
92
+
class="poster h-36 w-24 shrink-0 rounded-lg border border-white/10 sm:h-64 sm:w-44"
93
93
+
style:--name={`poster-${data.result.id}`}
94
94
+
/>
95
95
+
{/if}
96
96
</div>
97
97
<div class="flex-column">
98
98
-
{#if data.kind2 === "cast"}
99
99
-
<div class="text-center text-sm font-medium">Cast2</div>
100
100
-
<Avatar
101
101
-
src={data.person2Details.profile_path
102
102
-
? 'https://image.tmdb.org/t/p/w500' + data.person2Details.profile_path
103
103
-
: undefined}
104
104
-
size="size-44"
105
105
-
/>
106
106
-
{:else}
107
107
-
<div class="text-center text-sm font-medium">Movie2</div>
108
108
-
<img
109
109
-
src="https://image.tmdb.org/t/p/w500{data.result2.poster_path}"
110
110
-
alt=""
111
111
-
class="poster h-36 w-24 shrink-0 rounded-lg border border-white/10 sm:h-64 sm:w-44"
112
112
-
style:--name={`poster-${data.result2.id}`}
113
113
-
/>
114
114
-
{/if}
98
98
+
{#if data.kind2 === 'cast'}
99
99
+
<div class="text-center text-sm font-medium">Cast2</div>
100
100
+
<Avatar
101
101
+
src={data.person2Details.profile_path
102
102
+
? 'https://image.tmdb.org/t/p/w500' + data.person2Details.profile_path
103
103
+
: undefined}
104
104
+
size="size-44"
105
105
+
/>
106
106
+
{:else}
107
107
+
<div class="text-center text-sm font-medium">Movie2</div>
108
108
+
<img
109
109
+
src="https://image.tmdb.org/t/p/w500{data.result2.poster_path}"
110
110
+
alt=""
111
111
+
class="poster h-36 w-24 shrink-0 rounded-lg border border-white/10 sm:h-64 sm:w-44"
112
112
+
style:--name={`poster-${data.result2.id}`}
113
113
+
/>
114
114
+
{/if}
115
115
</div>
116
116
-
117
116
</div>
118
117
119
118
<div class="px-4 pt-4 text-sm text-white">
···
121
120
{@render buttons()}
122
121
</div>
123
122
</div>
124
124
-
123
123
+
125
124
{#if data.cast.length > 0}
126
125
<div class="flex flex-col gap-x-6 px-4 pb-8 pt-4 text-sm text-white">
127
127
-
{#if data.kind==="cast" }
128
128
-
<div class="text-center text-sm font-medium">Movies of {data.personDetails.name}</div>
129
129
-
<div class={cn('flex gap-x-6 justify-start overflow-x-auto')}>
130
130
-
{#each data.combinedCredits as castMember}
131
131
-
<a
132
132
-
href={`/game/movie/${castMember.id}-${castMember.title ?? castMember.name}/${data.kind2}/${data.ids2}-/${parseInt(data.couter)+1}`}
133
133
-
class="flex flex-col items-center gap-1"
134
134
-
>
135
135
-
<img
136
136
-
src="https://image.tmdb.org/t/p/w342{castMember.poster_path}"
137
137
-
alt="movie poster for {castMember.title ?? castMember.name}"
138
138
-
class="poster size-32 object-center object-cover lg:size-full w-32"
139
139
-
/>
140
140
-
<div class="text-center size-32 text-sm font-medium">{castMember.title ?? castMember.name}</div>
141
141
-
</a>
142
142
-
143
143
-
{/each}
144
144
-
</div>
126
126
+
{#if data.kind === 'cast'}
127
127
+
<div class="text-center text-sm font-medium">Movies of {data.personDetails.name}</div>
128
128
+
<div class={cn('flex justify-start gap-x-6 overflow-x-auto')}>
129
129
+
{#each data.combinedCredits as castMember}
130
130
+
<a
131
131
+
href={`/game/movie/${castMember.id}-${castMember.title ?? castMember.name}/${data.kind2}/${data.ids2}-/${parseInt(data.couter) + 1}`}
132
132
+
class="flex flex-col items-center gap-1"
133
133
+
>
134
134
+
<img
135
135
+
src="https://image.tmdb.org/t/p/w342{castMember.poster_path}"
136
136
+
alt="movie poster for {castMember.title ?? castMember.name}"
137
137
+
class="poster size-32 w-32 object-cover object-center lg:size-full"
138
138
+
/>
139
139
+
<div class="size-32 text-center text-sm font-medium">
140
140
+
{castMember.title ?? castMember.name}
141
141
+
</div>
142
142
+
</a>
143
143
+
{/each}
144
144
+
</div>
145
145
{:else}
146
146
-
<div class="text-center text-sm font-medium">cast of {data.result.title}</div>
147
147
-
<div class={'flex gap-x-6 justify-left overflow-x-auto'}>
148
148
-
{#each data.cast as castMember}
149
149
-
<a
150
150
-
href={`/game/cast/${castMember.id}-${nameToId(castMember.name)}/${data.kind2}/${data.ids2}-/${parseInt(data.couter)+1}`}
151
151
-
class="flex flex-col items-center gap-1"
152
152
-
>
153
153
-
<Avatar
154
154
-
src={castMember.profile_path
155
155
-
? 'https://image.tmdb.org/t/p/w500' + castMember.profile_path
156
156
-
: undefined}
157
157
-
size="size-32"
158
158
-
/>
159
159
-
<div class="text-center text-xs font-medium">{castMember.name}</div>
160
160
-
<div class="text-center text-xs text-base-400">{castMember.character}</div>
161
161
-
</a>
162
162
-
{/each}
163
163
-
</div>
146
146
+
<div class="text-center text-sm font-medium">cast of {data.result.title}</div>
147
147
+
<div class={'justify-left flex gap-x-6 overflow-x-auto'}>
148
148
+
{#each data.cast as castMember}
149
149
+
<a
150
150
+
href={`/game/cast/${castMember.id}-${nameToId(castMember.name)}/${data.kind2}/${data.ids2}-/${parseInt(data.couter) + 1}`}
151
151
+
class="flex flex-col items-center gap-1"
152
152
+
>
153
153
+
<Avatar
154
154
+
src={castMember.profile_path
155
155
+
? 'https://image.tmdb.org/t/p/w500' + castMember.profile_path
156
156
+
: undefined}
157
157
+
size="size-32"
158
158
+
/>
159
159
+
<div class="text-center text-xs font-medium">{castMember.name}</div>
160
160
+
<div class="text-center text-xs text-base-400">{castMember.character}</div>
161
161
+
</a>
162
162
+
{/each}
163
163
+
</div>
164
164
{/if}
165
165
-
{#if data.kind2==="cast" }
166
166
-
<div class="text-center text-sm font-medium">Movies of {data.person2Details.name}</div>
167
167
-
<div class={cn('flex gap-x-6 overflow-x-auto')}>
168
168
-
{#each data.combinedCredits2 as castMember}
169
169
-
<a
170
170
-
href={`/game/${data.kind}/${data.ids}-/movie/${castMember.id}-${castMember.title ?? castMember.name}/${parseInt(data.couter)+1}`}
171
171
-
class="flex flex-col items-center gap-1"
172
172
-
>
173
173
-
<img
174
174
-
src="https://image.tmdb.org/t/p/w342{castMember.poster_path}"
175
175
-
alt="movie poster for {castMember.title ?? castMember.name}"
176
176
-
class="poster object-center object-cover lg:size-full w-500"
177
177
-
/>
178
178
-
<div class="text-center size-32 text-sm font-medium">{castMember.title ?? castMember.name}</div>
179
179
-
</a>
180
180
-
181
181
-
{/each}
182
182
-
</div>
165
165
+
{#if data.kind2 === 'cast'}
166
166
+
<div class="text-center text-sm font-medium">Movies of {data.person2Details.name}</div>
167
167
+
<div class={cn('flex gap-x-6 overflow-x-auto')}>
168
168
+
{#each data.combinedCredits2 as castMember}
169
169
+
<a
170
170
+
href={`/game/${data.kind}/${data.ids}-/movie/${castMember.id}-${castMember.title ?? castMember.name}/${parseInt(data.couter) + 1}`}
171
171
+
class="flex flex-col items-center gap-1"
172
172
+
>
173
173
+
<img
174
174
+
src="https://image.tmdb.org/t/p/w342{castMember.poster_path}"
175
175
+
alt="movie poster for {castMember.title ?? castMember.name}"
176
176
+
class="poster w-500 object-cover object-center lg:size-full"
177
177
+
/>
178
178
+
<div class="size-32 text-center text-sm font-medium">
179
179
+
{castMember.title ?? castMember.name}
180
180
+
</div>
181
181
+
</a>
182
182
+
{/each}
183
183
+
</div>
183
184
{:else}
184
184
-
<div class="text-center text-sm font-medium">Cast of {data.result2.title}</div>
185
185
-
<div class={'flex gap-x-6 justify-right overflow-x-auto'}>
186
186
-
{#each data.cast2 as castMember}
187
187
-
<a
188
188
-
href={`/game/${data.kind}/${data.ids}-/cast/${castMember.id}-${castMember.title ?? castMember.name}/${parseInt(data.couter)+1}`}
189
189
-
class="flex flex-col items-center gap-1"
190
190
-
>
191
191
-
<Avatar
192
192
-
src={castMember.profile_path
193
193
-
? 'https://image.tmdb.org/t/p/w500' + castMember.profile_path
194
194
-
: undefined}
195
195
-
size="size-32"
196
196
-
/>
197
197
-
<div class="text-center text-xs font-medium">{castMember.name}</div>
198
198
-
<div class="text-center text-xs text-base-400">{castMember.character}</div>
199
199
-
</a>
200
200
-
{/each}
201
201
-
</div>
185
185
+
<div class="text-center text-sm font-medium">Cast of {data.result2.title}</div>
186
186
+
<div class={'justify-right flex gap-x-6 overflow-x-auto'}>
187
187
+
{#each data.cast2 as castMember}
188
188
+
<a
189
189
+
href={`/game/${data.kind}/${data.ids}-/cast/${castMember.id}-${castMember.title ?? castMember.name}/${parseInt(data.couter) + 1}`}
190
190
+
class="flex flex-col items-center gap-1"
191
191
+
>
192
192
+
<Avatar
193
193
+
src={castMember.profile_path
194
194
+
? 'https://image.tmdb.org/t/p/w500' + castMember.profile_path
195
195
+
: undefined}
196
196
+
size="size-32"
197
197
+
/>
198
198
+
<div class="text-center text-xs font-medium">{castMember.name}</div>
199
199
+
<div class="text-center text-xs text-base-400">{castMember.character}</div>
200
200
+
</a>
201
201
+
{/each}
202
202
+
</div>
202
203
{/if}
203
204
</div>
204
205
{/if}
···
1
1
-
// src/routes/og/+server.ts
2
2
-
import { getDetails } from '$lib/server/movies';
3
3
-
import { ImageResponse } from '@ethercorps/sveltekit-og';
4
4
-
import { error, type RequestHandler } from '@sveltejs/kit';
5
5
-
6
6
-
const template = (data: {
7
7
-
backdrop_path: string;
8
8
-
poster_path: string;
9
9
-
title?: string;
10
10
-
name?: string;
11
11
-
}) => {
12
12
-
return `
13
13
-
<div tw="bg-zinc-900 flex flex-col w-full h-full items-center justify-center">
14
14
-
15
15
-
<div tw="flex absolute bottom-0 left-0 right-0 top-0 bg-sky-400">
16
16
-
<img src="https://image.tmdb.org/t/p/w780${data.backdrop_path}" alt="" class="flex h-full w-full rounded-xl opacity-50" />
17
17
-
18
18
-
<div tw="flex absolute h-full w-full bg-black/80">
19
19
-
</div>
20
20
-
</div>
21
21
-
22
22
-
<div tw="flex flex-row w-full py-8 px-16 items-center justify-start">
23
23
-
<div tw="flex h-auto aspect-[3/2] w-72">
24
24
-
<img src="https://image.tmdb.org/t/p/w500${data.poster_path}" alt=""
25
25
-
class="flex h-full w-full rounded-xl border-2 border-zinc-800" style="border-radius: 16px; border-width: 1px; border-color: #3f3f46;" />
26
26
-
</div>
27
27
-
28
28
-
<h2 tw="flex flex-col text-7xl font-bold text-zinc-100 text-left px-12 max-w-3xl">
29
29
-
<span tw="flex tracking-tight">${data.title ?? data.name}</span>
30
30
-
</h2>
31
31
-
</div>
32
32
-
33
33
-
<div tw="flex text-4xl text-sky-400 items-end justify-end w-full px-8">
34
34
-
<div tw="flex">
35
35
-
rate on skywatched.app
36
36
-
</div>
37
37
-
</div>
38
38
-
</div>`;
39
39
-
};
40
40
-
41
41
-
const host = import.meta.env.DEV ? 'http://localhost:5173' : 'https://skywatched.app';
42
42
-
const fontPath = `fonts/inter-latin-ext-400-normal.woff`;
43
43
-
const fontFile = await fetch(`${host}/${fontPath}`);
44
44
-
const fontData: ArrayBuffer = await fontFile.arrayBuffer();
45
45
-
46
46
-
export const GET: RequestHandler = async ({ params }) => {
47
47
-
const id = parseInt(params.id?.split('-')[0] ?? '');
48
48
-
const kind = params.kind;
49
49
-
50
50
-
if (kind !== 'movie' && kind !== 'tv') {
51
51
-
return error(404, 'Not found');
52
52
-
}
53
53
-
54
54
-
if (!id) {
55
55
-
return error(404, 'Not found');
56
56
-
}
57
57
-
const result = await getDetails(id, kind);
58
58
-
59
59
-
return new ImageResponse(
60
60
-
template(result),
61
61
-
{
62
62
-
fonts: [
63
63
-
{
64
64
-
name: 'Inter Latin',
65
65
-
data: fontData,
66
66
-
weight: 400
67
67
-
}
68
68
-
]
69
69
-
},
70
70
-
{}
71
71
-
);
72
72
-
};
···
1
1
-
import { searchMulti,getPopularMovies } from '$lib/server/movies';
1
1
+
import { searchMulti, getPopularMovies } from '$lib/server/movies';
2
2
import { redirect } from '@sveltejs/kit';
3
3
-
import type { Actions, PageServerLoad } from './$types';
3
3
+
import type { Actions } from './$types';
4
4
5
5
/** @type {import('./$types').PageServerLoad} */
6
6
export async function load(event) {
7
7
const query = event.url.searchParams.get('query');
8
8
-
const query2=event.url.searchParams.get('query2');
8
8
+
const query2 = event.url.searchParams.get('query2');
9
9
10
10
-
if (query) {
10
10
+
if (query && query2) {
11
11
const results = (await searchMulti(query))
12
12
.map((result) => {
13
13
if (result.media_type === 'movie') {
···
17
17
}
18
18
return null;
19
19
})
20
20
-
.filter((result) => result !== null && result.poster_path !== null);
20
20
+
.filter((result) => result?.poster_path !== null);
21
21
const results2 = (await searchMulti(query2))
22
22
.map((result2) => {
23
23
if (result2.media_type === 'movie') {
···
27
27
}
28
28
return null;
29
29
})
30
30
-
.filter((result2) => result2 !== null && result2.poster_path !== null);
31
31
-
return { results,results2, query,query2 };
30
30
+
.filter((result2) => result2?.poster_path !== null);
31
31
+
return { results, results2, query, query2 };
32
32
}
33
33
34
34
-
return { results: [],results2: [], query,query2 };
34
34
+
return { results: [], results2: [], query, query2 };
35
35
}
36
36
export const actions: Actions = {
37
37
-
randomize: async (event) => {
38
38
-
const randmovies=await getPopularMovies();
39
39
-
const i=Math.floor(Math.random() * randmovies.results.length);
40
40
-
const j=Math.floor(Math.random() * randmovies.results.length);
41
41
-
//console.log(randmovies.results.length);
42
42
-
return redirect(302, '/game/movie/'+randmovies.results[i]?.id+"-/movie/"+randmovies.results[j]?.id+"-/0");}
43
43
-
};
37
37
+
randomize: async () => {
38
38
+
const randmovies = await getPopularMovies();
39
39
+
const i = Math.floor(Math.random() * randmovies.results.length);
40
40
+
const j = Math.floor(Math.random() * randmovies.results.length);
41
41
+
42
42
+
return redirect(
43
43
+
302,
44
44
+
'/game/movie/' + randmovies.results[i]?.id + '-/movie/' + randmovies.results[j]?.id + '-/0'
45
45
+
);
46
46
+
}
47
47
+
};
···
1
1
<script lang="ts">
2
2
-
import ItemsGrid from '$lib/Components/ItemsGrid.svelte';
3
3
-
import Container from '$lib/Components/Container.svelte';
4
4
-
import BaseHeadTags from '$lib/Components/BaseHeadTags.svelte';
2
2
+
import ItemsGrid from '$lib/Components/Items/ItemsGrid.svelte';
3
3
+
import Container from '$lib/Components/Layout/Container.svelte';
4
4
+
import BaseHeadTags from '$lib/Components/Layout/BaseHeadTags.svelte';
5
5
6
6
import { type PageData } from './$types';
7
7
let { data }: { data: PageData } = $props();
···
18
18
<h1 class="pb-8 text-4xl font-bold tracking-tight text-base-50">search for a movie or show</h1>
19
19
<div class="mt-10 w-full sm:mx-auto sm:max-w-sm">
20
20
<form class="space-y-6" action="?/randomize" method="POST">
21
21
-
22
21
<div>
23
22
<button
24
23
type="submit"
···
76
75
77
76
{#if data.results.length > 0}
78
77
<div class="flex-row">
79
79
-
<div class="flex-col">
80
80
-
<h2 class="mt-8 text-2xl font-bold tracking-tight text-base-50">results</h2>
81
81
-
78
78
+
<div class="flex-col">
79
79
+
<h2 class="mt-8 text-2xl font-bold tracking-tight text-base-50">results</h2>
82
80
83
83
-
<ItemsGrid items={data.results} showMark={!!data.user} />
81
81
+
<ItemsGrid items={data.results} showMark={!!data.user} />
84
82
</div>
85
85
-
<div class="flex-col">
86
86
-
<h2 class="mt-8 text-2xl font-bold tracking-tight text-base-50">results</h2>
83
83
+
<div class="flex-col">
84
84
+
<h2 class="mt-8 text-2xl font-bold tracking-tight text-base-50">results</h2>
87
85
88
88
-
<ItemsGrid items={data.results2} showMark={!!data.user} />
86
86
+
<ItemsGrid items={data.results2} showMark={!!data.user} />
89
87
</div>
90
88
</div>
91
89
{:else if data.query}
92
90
<p class="text-md mt-8 text-base-50">no results found</p>
93
91
{/if}
94
94
-
95
92
</div>
96
93
</Container>
···
1
1
-
import { redirect } from '@sveltejs/kit';
2
2
-
3
3
-
import type { Actions, PageServerLoad } from './$types';
4
4
-
import { register } from '$lib/Components/relative-time';
5
5
-
6
6
-
export const load: PageServerLoad = async (event) => {
7
7
-
if (event.cookies.get('sid')) {
8
8
-
const sid = event.cookies.get('sid');
9
9
-
10
10
-
11
11
-
}
12
12
-
return {};
13
13
-
};
14
14
-
15
15
-
export const actions: Actions = {
16
16
-
login: async (event) =>
17
17
-
{event.cookies.delete('sid', { path: '/' });
18
18
-
return redirect(302, '/');},
19
19
-
register: async (event) =>
20
20
-
{event.cookies.delete('sid', { path: '/' });
21
21
-
return redirect(302, '/');}
22
22
-
23
23
-
};
···
1
1
-
<script lang="ts">
2
2
-
import type { ActionData } from './$types';
3
3
-
import Logo from '$lib/Components/Logo.svelte';
4
4
-
5
5
-
let { form }: { form: ActionData } = $props();
6
6
-
</script>
7
7
-
8
8
-
<div class="flex min-h-screen flex-col items-center justify-center px-6 py-12 lg:px-8">
9
9
-
<div class="sm:mx-auto sm:w-full sm:max-w-sm">
10
10
-
<Logo class="mx-auto size-10" />
11
11
-
<h2 class="mt-10 text-center text-2xl/9 font-bold tracking-tight text-white">Are you sure you want to logout</h2>
12
12
-
</div>
13
13
-
14
14
-
<div class="mt-10 w-full sm:mx-auto sm:max-w-sm">
15
15
-
<form class="space-y-6" action="?/login" method="POST">
16
16
-
17
17
-
<div>
18
18
-
<button
19
19
-
type="submit"
20
20
-
class="flex w-full justify-center rounded-xl bg-accent-500 px-3 py-1.5 text-sm/6 font-semibold text-white shadow-sm hover:bg-accent-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent-500"
21
21
-
>logout</button
22
22
-
>
23
23
-
</div>
24
24
-
</form>
25
25
-
</div>
26
26
-
</div>
···
1
1
-
import { getProfile, resolveHandle,getFollows } from '$lib/bluesky.js';
2
2
-
import {getRecentRecords,getRecentRecordOfUser,getAuthorDids } from '$lib/db';
3
3
-
import { settings, user } from '$lib/state.svelte';
4
4
-
5
5
-
/** @type {import('./$types').PageServerLoad} */
6
6
-
export async function load(event) {
7
7
-
const feed = await getRecentRecords();
8
8
-
const did = event.locals.user.did;
9
9
-
var filt=feed;
10
10
-
const followsPromise = getFollows({ did });
11
11
-
const authors=await getAuthorDids();
12
12
-
const [follows] = await Promise.all([followsPromise]);
13
13
-
filt=feed.filter((review)=>(follows.follows.some((person)=>(person.did==review.author.did))));
14
14
-
console.log(filt);
15
15
-
16
16
-
const truefeed=filt;
17
17
-
return { truefeed };
18
18
-
}
···
1
1
<script lang="ts">
2
2
-
import Container from '$lib/Components/Container.svelte';
3
3
-
import { onMount } from 'svelte';
2
2
+
import Container from '$lib/Components/Layout/Container.svelte';
4
3
import { type PageData } from './$types';
5
5
-
import ReviewList from '$lib/Components/ReviewList.svelte';
6
6
-
import BaseHeadTags from '$lib/Components/BaseHeadTags.svelte';
4
4
+
import ReviewList from '$lib/Components/Items/ReviewList.svelte';
5
5
+
import BaseHeadTags from '$lib/Components/Layout/BaseHeadTags.svelte';
7
6
8
7
let { data }: { data: PageData } = $props();
9
8
</script>
···
58
58
<ReviewCard data={data.record} bigText={true} />
59
59
60
60
{#if data.record.record.crosspost?.uri}
61
61
-
<div class="relative w-full max-w-2xl backdrop-blur-sm mb-8">
61
61
+
<div class="relative mb-8 w-full max-w-2xl backdrop-blur-sm">
62
62
<div class="flex max-w-full items-center gap-4 overflow-hidden px-4">
63
63
<Comments uri={data.record.record.crosspost.uri} user={''} comments={[]} url={''} />
64
64
</div>
···
66
66
{:else}
67
67
<div class="flex flex-wrap items-center justify-center gap-4">
68
68
<div
69
69
-
class="border-base-700 bg-base-50/5 text-base-200 hover:bg-base-50/10 z-10 rounded-lg border px-3 py-2 text-xs font-medium transition-all duration-100"
69
69
+
class="z-10 rounded-lg border border-base-700 bg-base-50/5 px-3 py-2 text-xs font-medium text-base-200 transition-all duration-100 hover:bg-base-50/10"
70
70
>
71
71
<a
72
72
href={`/${data.record.record.item.ref === 'tmdb:m' ? 'movie' : 'tv'}/${data.record.record.item.value}`}
···
75
75
</a>
76
76
</div>
77
77
<div
78
78
-
class="border-base-700 bg-base-50/5 text-base-200 hover:bg-base-50/10 z-10 rounded-lg border px-3 py-2 text-xs font-medium transition-all duration-100"
78
78
+
class="z-10 rounded-lg border border-base-700 bg-base-50/5 px-3 py-2 text-xs font-medium text-base-200 transition-all duration-100 hover:bg-base-50/10"
79
79
>
80
80
<a href={`/user/${data.record.author.handle}`}>
81
81
all reviews by {data.record.author.displayName || data.record.author.handle}
···
1
1
-
import { getProfile, resolveHandle,getFollows } from '$lib/bluesky.js';
2
2
-
import { getRecentRecordOfUser,getAuthorDids } from '$lib/db';
3
3
-
import type { Actions, PageServerLoad } from './$types';
1
1
+
import { getProfile, resolveHandle, getFollows } from '$lib/bluesky.js';
2
2
+
import { getRecentRecordOfUser, getAuthorDids } from '$lib/db';
3
3
+
4
4
/** @type {import('./$types').PageServerLoad} */
5
5
export async function load(event) {
6
6
const handle = event.params.handle;
7
7
8
8
const did = await resolveHandle({ handle: handle });
9
9
-
const userdid=event.locals.user?.did;
10
9
11
10
const profilePromise = getProfile({ did });
12
12
-
const itemsPromise = getRecentRecordOfUser({ did });
13
13
-
const followsPromise = getFollows({ did });
14
14
-
const userfollowsPromise=getFollows({ did });
15
15
-
const authors=await getAuthorDids();
16
16
-
const [profile, items,follows,userfollows] = await Promise.all([profilePromise, itemsPromise,followsPromise,userfollowsPromise]);
17
17
-
var i=follows.follows.length-1;
18
18
-
const isfollowed=userfollows.follows.some((word)=>(word.did==did));
19
19
-
while(i>=0){
20
20
-
if(authors.includes(follows.follows[i].did)){
21
21
-
}
22
22
-
else{
23
23
-
follows.follows.splice(i,1);
24
24
-
}
25
25
-
i--;
26
26
-
11
11
+
const reviewsPromise = getRecentRecordOfUser({ did });
12
12
+
13
13
+
const authorsPromise = getAuthorDids();
14
14
+
15
15
+
const [profile, reviews, authors] = await Promise.all([
16
16
+
profilePromise,
17
17
+
reviewsPromise,
18
18
+
authorsPromise
19
19
+
]);
20
20
+
21
21
+
if (event.locals.user) {
22
22
+
const follows = await getFollows({ did: event.locals.user.did });
23
23
+
24
24
+
return {
25
25
+
isUser: event.locals.user?.did === did,
26
26
+
username: event.params.handle,
27
27
+
profile,
28
28
+
items: reviews,
29
29
+
follows: follows.follows.filter((profile) => authors.includes(profile.did)),
30
30
+
following: follows.follows.some((profile) => profile.did == did),
31
31
+
did
32
32
+
};
27
33
}
28
34
29
29
-
return { isUser: event.locals.user?.did === did, username: event.params.handle, profile, items,follows,isfollowed,did };
35
35
+
return {
36
36
+
isUser: false,
37
37
+
profile,
38
38
+
items: reviews,
39
39
+
did
40
40
+
};
30
41
}
···
1
1
<script lang="ts">
2
2
-
import { getFollows } from '$lib/bluesky.js';
3
2
import Avatar from '$lib/Components/User/Avatar.svelte';
4
3
import BaseHeadTags from '$lib/Components/Layout/BaseHeadTags.svelte';
5
4
import Container from '$lib/Components/Layout/Container.svelte';
6
5
import Profile from '$lib/Components/User/Profile.svelte';
7
6
import ReviewList from '$lib/Components/Items/ReviewList.svelte';
8
7
import { toast } from 'svelte-sonner';
8
8
+
import { cn } from '$lib/utils.js';
9
9
10
10
let { data } = $props();
11
11
</script>
···
21
21
<Profile profile={data.profile} />
22
22
</div>
23
23
{#if data.isUser}
24
24
-
<div class="text-center">
25
25
-
<a
26
26
-
href={'/logout/'}
27
27
-
class="button position-relative align-items-center w-full flex-row-reverse justify-center rounded-xl bg-accent-500 px-3 py-1.5 text-center text-sm/6 font-semibold text-white shadow-sm hover:bg-accent-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent-500"
28
28
-
>logout</a
29
29
-
>
30
30
-
</div>
31
31
-
{:else if data.isfollowed}
32
32
-
<div class="text-center">
33
33
-
<a
34
34
-
href={'/logout/'}
35
35
-
class="button w-full flex-row-reverse justify-end rounded-xl bg-accent-500 px-3 py-1.5 text-center text-sm/6 font-semibold text-white shadow-sm hover:bg-accent-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent-500"
36
36
-
>logout</a
37
37
-
>
24
24
+
<div class="mx-auto mt-2 flex max-w-2xl px-4 sm:-mt-24 sm:justify-end sm:px-0">
25
25
+
<form class="space-y-6" action="/?/logout" method="POST">
26
26
+
<button
27
27
+
type="submit"
28
28
+
class={cn(
29
29
+
'inline-flex justify-center rounded-xl bg-white/10 px-3 py-2 text-sm font-semibold text-base-50 shadow-sm ring-1 ring-inset ring-base-300/20 hover:bg-base-50/20',
30
30
+
data.profile.banner ? 'mt-6' : 'mt-3'
31
31
+
)}
32
32
+
>
33
33
+
<span>Logout</span>
34
34
+
</button>
35
35
+
</form>
38
36
</div>
39
39
-
{:else}
40
40
-
<div class="text-center">
37
37
+
{:else if data.user}
38
38
+
<div class="mx-auto mt-2 flex max-w-2xl px-4 sm:-mt-24 sm:justify-end sm:px-0">
41
39
<button
42
42
-
class="danger lg justify-center rounded-xl bg-accent-500 px-3 py-1.5 text-sm/6 font-semibold text-white shadow-sm hover:bg-accent-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent-500"
40
40
+
type="button"
43
41
onclick={async () => {
44
44
-
if (data.isfollowed) return;
42
42
+
if (data.following) return;
45
43
46
46
-
data.isfollowed = true;
44
44
+
data.following = true;
47
45
48
46
try {
49
47
const response = await fetch('/api/follow', {
···
51
49
body: JSON.stringify({ did: data.did })
52
50
});
53
51
if (response.ok) {
54
54
-
toast.success('Liked');
52
52
+
toast.success('Followed');
55
53
} else {
56
56
-
toast.error('must be logged in to like');
57
57
-
data.isfollowed = false;
54
54
+
toast.error('You must be logged in to follow');
55
55
+
data.following = false;
58
56
}
59
57
} catch (e) {
60
58
console.error(e);
61
61
-
toast.error('must be logged in to like');
62
62
-
data.isfollowed = false;
59
59
+
toast.error('Must be logged in to follow');
60
60
+
61
61
+
data.following = false;
63
62
}
64
63
}}
64
64
+
class={cn(
65
65
+
'inline-flex justify-center rounded-xl bg-accent-500/10 px-3 py-2 text-sm font-semibold text-accent-400 shadow-sm ring-1 ring-inset ring-accent-300/20 hover:bg-accent-400/20',
66
66
+
data.profile.banner ? 'mt-6' : 'mt-3',
67
67
+
data.following ? 'bg-accent-500/10' : ''
68
68
+
)}
65
69
>
66
66
-
Follow
70
70
+
<span>{data.following ? 'Following' : 'Follow'}</span>
67
71
</button>
68
72
</div>
69
73
{/if}
70
70
-
{#if data.items.length > 0}
74
74
+
75
75
+
{#if data.isUser && data.follows}
71
76
<p
72
72
-
class="max-w-full truncate text-center text-lg font-bold text-base-900 dark:text-base-100 sm:text-xl"
77
77
+
class="mx-auto mt-16 max-w-2xl truncate px-4 text-lg font-bold text-base-900 dark:text-base-100 sm:text-xl"
73
78
>
74
74
-
Follows
79
79
+
People you follow
75
80
</p>
76
76
-
<div class={'mx-auto mt-8 flex max-w-2xl gap-x-6 overflow-x-auto'}>
77
77
-
{#each data.follows.follows as castMember}
78
78
-
{#if castMember.did}
79
79
-
<a href={`/user/` + castMember.handle} class="flex flex-col items-center gap-1">
80
80
-
<Avatar src={castMember.avatar ? castMember.avatar : undefined} size="size-16" />
81
81
-
<div class="text-center text-xs font-medium">{castMember.displayName}</div>
82
82
-
<div class="text-center text-xs text-base-400">{castMember.handle}</div>
81
81
+
<div class="mx-auto mt-4 flex max-w-2xl gap-x-6 overflow-x-auto px-4">
82
82
+
{#each data.follows as profile}
83
83
+
{#if profile.did}
84
84
+
<a href={`/user/` + profile.handle} class="flex flex-col items-center gap-1">
85
85
+
<Avatar src={profile.avatar ? profile.avatar : undefined} size="size-16" />
86
86
+
<div class="text-center text-xs font-medium">{profile.displayName}</div>
87
87
+
<div class="text-center text-xs text-base-400">{profile.handle}</div>
83
88
</a>
84
89
{/if}
85
90
{/each}
86
91
</div>
92
92
+
{/if}
93
93
+
{#if data.items.length > 0}
87
94
<p
88
88
-
class="max-w-full truncate text-center text-lg font-bold text-base-900 dark:text-base-100 sm:text-xl"
95
95
+
class="mx-auto mt-16 max-w-2xl truncate px-4 text-lg font-bold text-base-900 dark:text-base-100 sm:text-xl"
89
96
>
90
97
Reviews
91
98
</p>
92
92
-
<div><ReviewList reviews={data.items} class="mx-auto mt-8 max-w-2xl" /></div>
99
99
+
<div><ReviewList reviews={data.items} class="mx-auto mt-2 max-w-2xl" /></div>
93
100
{:else}
94
101
<p class="py-8 text-center text-base-500">No movies or tv shows rated yet.</p>
95
102
{/if}
···
1
1
-
import { redirect } from '@sveltejs/kit';
2
2
-
3
3
-
import type { Actions, PageServerLoad } from './$types';
4
4
-
import { register } from '$lib/Components/relative-time';
5
5
-
6
6
-
export const load: PageServerLoad = async (event) => {
7
7
-
if (event.cookies.get('sid')) {
8
8
-
const sid = event.cookies.get('sid');
9
9
-
10
10
-
11
11
-
}
12
12
-
return {};
13
13
-
};
14
14
-
···
1
1
<script lang="ts">
2
2
-
import type { ActionData } from './$types';
3
3
-
import Logo from '$lib/Components/Logo.svelte';
4
4
-
5
5
-
let { form }: { form: ActionData } = $props();
2
2
+
import Logo from '$lib/Components/UI/Logo.svelte';
6
3
</script>
7
4
8
5
<div class="flex min-h-screen flex-col items-center justify-center px-6 py-12 lg:px-8">
9
6
<div class="sm:mx-auto sm:w-full sm:max-w-sm">
10
7
<Logo class="mx-auto size-10" />
11
11
-
<h2 class="mt-10 text-center text-2xl/9 font-bold tracking-tight text-white">You have Won. Congratulations</h2>
8
8
+
<h2 class="mt-10 text-center text-2xl/9 font-bold tracking-tight text-white">
9
9
+
You have Won. Congratulations
10
10
+
</h2>
12
11
</div>
13
12
</div>
···
306
306
font-size: 13px;
307
307
font-size: var(--plyr-font-size-menu, var(--plyr-font-size-small, 13px));
308
308
padding: 4.66667px 10.5px;
309
309
-
padding: calc(var(--plyr-control-spacing, 10px) * 0.7/1.5)
309
309
+
padding: calc(var(--plyr-control-spacing, 10px) * 0.7 / 1.5)
310
310
calc(var(--plyr-control-spacing, 10px) * 0.7 * 1.5);
311
311
-webkit-user-select: none;
312
312
user-select: none;
···
347
347
margin: 7px;
348
348
margin: calc(var(--plyr-control-spacing, 10px) * 0.7);
349
349
margin-bottom: 3.5px;
350
350
-
margin-bottom: calc(var(--plyr-control-spacing, 10px) * 0.7/2);
350
350
+
margin-bottom: calc(var(--plyr-control-spacing, 10px) * 0.7 / 2);
351
351
padding-left: 28px;
352
352
padding-left: calc(var(--plyr-control-spacing, 10px) * 0.7 * 4);
353
353
position: relative;
···
369
369
height: 1px;
370
370
left: 0;
371
371
margin-top: 3.5px;
372
372
-
margin-top: calc(var(--plyr-control-spacing, 10px) * 0.7/2);
372
372
+
margin-top: calc(var(--plyr-control-spacing, 10px) * 0.7 / 2);
373
373
overflow: hidden;
374
374
position: absolute;
375
375
right: 0;