mirror of
https://github.com/MadeBaruna/paimon-moe.git
synced 2024-10-23 17:36:30 +02:00
parent
a4bdd01db8
commit
1f7802d0e0
14 changed files with 512 additions and 22 deletions
|
@ -1,3 +1,9 @@
|
||||||
GOOGLE_DRIVE_CLIENT_ID=
|
GOOGLE_DRIVE_CLIENT_ID=
|
||||||
GOOGLE_DRIVE_API_KEY=
|
GOOGLE_DRIVE_API_KEY=
|
||||||
API_HOST=http://localhost:3001
|
API_HOST=http://localhost:3001
|
||||||
|
FIREBASE_API_KEY=
|
||||||
|
FIREBASE_AUTH_DOMAIN=
|
||||||
|
FIREBASE_PROJECT_ID=
|
||||||
|
FIREBASE_STORAGE_BUCKET=
|
||||||
|
FIREBASE_MESSAGING_SENDER_ID=
|
||||||
|
FIREBASE_APP_ID=
|
||||||
|
|
|
@ -23,6 +23,11 @@ const onwarn = (warning, onwarn) =>
|
||||||
(warning.code === 'CIRCULAR_DEPENDENCY' && /[/\\]@sapper[/\\]/.test(warning.message)) ||
|
(warning.code === 'CIRCULAR_DEPENDENCY' && /[/\\]@sapper[/\\]/.test(warning.message)) ||
|
||||||
onwarn(warning);
|
onwarn(warning);
|
||||||
|
|
||||||
|
const envData = {};
|
||||||
|
Object.entries(envConfig().parsed).forEach(([key, val]) => {
|
||||||
|
envData[`__paimon.env.${key}`] = `'${val}'`;
|
||||||
|
});
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
client: {
|
client: {
|
||||||
input: config.client.input(),
|
input: config.client.input(),
|
||||||
|
@ -34,9 +39,7 @@ export default {
|
||||||
replace({
|
replace({
|
||||||
'process.browser': true,
|
'process.browser': true,
|
||||||
'process.env.NODE_ENV': JSON.stringify(mode),
|
'process.env.NODE_ENV': JSON.stringify(mode),
|
||||||
__paimon: JSON.stringify({
|
...envData,
|
||||||
env: envConfig().parsed,
|
|
||||||
}),
|
|
||||||
}),
|
}),
|
||||||
svelte({
|
svelte({
|
||||||
compilerOptions: {
|
compilerOptions: {
|
||||||
|
@ -98,9 +101,7 @@ export default {
|
||||||
replace({
|
replace({
|
||||||
'process.browser': false,
|
'process.browser': false,
|
||||||
'process.env.NODE_ENV': JSON.stringify(mode),
|
'process.env.NODE_ENV': JSON.stringify(mode),
|
||||||
__paimon: JSON.stringify({
|
...envData,
|
||||||
env: envConfig().parsed,
|
|
||||||
}),
|
|
||||||
}),
|
}),
|
||||||
svelte({
|
svelte({
|
||||||
compilerOptions: {
|
compilerOptions: {
|
||||||
|
@ -127,6 +128,23 @@ export default {
|
||||||
onwarn,
|
onwarn,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
serviceworker: {
|
||||||
|
input: config.serviceworker.input().replace('service-worker', 'firebase-messaging-sw'),
|
||||||
|
output: {
|
||||||
|
...config.serviceworker.output(),
|
||||||
|
file: config.serviceworker.output().file.replace('service-worker', 'firebase-messaging-sw'),
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
replace(envData),
|
||||||
|
resolve(),
|
||||||
|
commonjs(),
|
||||||
|
!dev && terser(),
|
||||||
|
],
|
||||||
|
|
||||||
|
preserveEntrySignatures: false,
|
||||||
|
onwarn,
|
||||||
|
},
|
||||||
|
|
||||||
// serviceworker: {
|
// serviceworker: {
|
||||||
// input: config.serviceworker.input(),
|
// input: config.serviceworker.input(),
|
||||||
// output: config.serviceworker.output(),
|
// output: config.serviceworker.output(),
|
||||||
|
|
|
@ -4,5 +4,11 @@ let envString = '';
|
||||||
envString += `GOOGLE_DRIVE_CLIENT_ID=${process.env.GOOGLE_DRIVE_CLIENT_ID}\n`;
|
envString += `GOOGLE_DRIVE_CLIENT_ID=${process.env.GOOGLE_DRIVE_CLIENT_ID}\n`;
|
||||||
envString += `GOOGLE_DRIVE_API_KEY=${process.env.GOOGLE_DRIVE_API_KEY}\n`;
|
envString += `GOOGLE_DRIVE_API_KEY=${process.env.GOOGLE_DRIVE_API_KEY}\n`;
|
||||||
envString += `API_HOST=${process.env.API_HOST}\n`;
|
envString += `API_HOST=${process.env.API_HOST}\n`;
|
||||||
|
envString += `FIREBASE_API_KEY=${FIREBASE_API_KEY}\n`
|
||||||
|
envString += `FIREBASE_AUTH_DOMAIN=${FIREBASE_AUTH_DOMAIN}\n`
|
||||||
|
envString += `FIREBASE_PROJECT_ID=${FIREBASE_PROJECT_ID}\n`
|
||||||
|
envString += `FIREBASE_STORAGE_BUCKET=${FIREBASE_STORAGE_BUCKET}\n`
|
||||||
|
envString += `FIREBASE_MESSAGING_SENDER_ID=${FIREBASE_MESSAGING_SENDER_ID}\n`
|
||||||
|
envString += `FIREBASE_APP_ID=${FIREBASE_APP_ID}\n`
|
||||||
|
|
||||||
fs.writeFileSync('.env', envString);
|
fs.writeFileSync('.env', envString);
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
export let placeholder = '';
|
export let placeholder = '';
|
||||||
export let step = undefined;
|
export let step = undefined;
|
||||||
export let type = 'text';
|
export let type = 'text';
|
||||||
|
export let pattern = undefined;
|
||||||
export let min = Math.min();
|
export let min = Math.min();
|
||||||
export let max = Math.max();
|
export let max = Math.max();
|
||||||
|
|
||||||
|
@ -32,6 +33,7 @@
|
||||||
{min}
|
{min}
|
||||||
{max}
|
{max}
|
||||||
{step}
|
{step}
|
||||||
|
{pattern}
|
||||||
on:change
|
on:change
|
||||||
on:input={handleInput}
|
on:input={handleInput}
|
||||||
class={`w-full ${icon ? 'pl-12' : 'pl-4'} min-h-full pr-4 text-white placeholder-gray-500 leading-none bg-transparent border-none focus:outline-none`} />
|
class={`w-full ${icon ? 'pl-12' : 'pl-4'} min-h-full pr-4 text-white placeholder-gray-500 leading-none bg-transparent border-none focus:outline-none`} />
|
||||||
|
|
40
src/firebase-messaging-sw.js
Normal file
40
src/firebase-messaging-sw.js
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
importScripts('https://www.gstatic.com/firebasejs/8.3.2/firebase-app.js');
|
||||||
|
importScripts('https://www.gstatic.com/firebasejs/8.3.2/firebase-messaging.js');
|
||||||
|
|
||||||
|
const firebaseConfig = {
|
||||||
|
apiKey: __paimon.env.FIREBASE_API_KEY,
|
||||||
|
authDomain: __paimon.env.FIREBASE_AUTH_DOMAIN,
|
||||||
|
projectId: __paimon.env.FIREBASE_PROJECT_ID,
|
||||||
|
storageBucket: __paimon.env.FIREBASE_STORAGE_BUCKET,
|
||||||
|
messagingSenderId: __paimon.env.FIREBASE_MESSAGING_SENDER_ID,
|
||||||
|
appId: __paimon.env.FIREBASE_APP_ID,
|
||||||
|
};
|
||||||
|
|
||||||
|
firebase.initializeApp(firebaseConfig);
|
||||||
|
|
||||||
|
const messaging = firebase.messaging();
|
||||||
|
|
||||||
|
messaging.onBackgroundMessage((payload) => {
|
||||||
|
console.log('Received background message ', payload);
|
||||||
|
|
||||||
|
const { title, body, url } = payload.data;
|
||||||
|
|
||||||
|
const notificationTitle = title;
|
||||||
|
const notificationOptions = {
|
||||||
|
body,
|
||||||
|
icon: '/favicon.png',
|
||||||
|
data: {
|
||||||
|
url,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
self.registration.showNotification(notificationTitle, notificationOptions);
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener('notificationclick', function (event) {
|
||||||
|
event.notification.close();
|
||||||
|
|
||||||
|
if (clients.openWindow) {
|
||||||
|
clients.openWindow(event.notification.data.url);
|
||||||
|
}
|
||||||
|
});
|
|
@ -469,8 +469,21 @@
|
||||||
"trakteer": "Support me on Trakteer"
|
"trakteer": "Support me on Trakteer"
|
||||||
},
|
},
|
||||||
"reminder": {
|
"reminder": {
|
||||||
"title": "Reminder Notification",
|
|
||||||
"notSupported": "Your browser does not support push notification, please try other browser!",
|
"notSupported": "Your browser does not support push notification, please try other browser!",
|
||||||
"blocked": "Notification is blocked, the reminder notification will not work! Please enable it on your browser."
|
"blocked": "Notification is blocked, the reminder notification will not work! Please enable it on your browser.",
|
||||||
|
"desktop": "Desktop browser cannot receive notification if the browser is not running!",
|
||||||
|
"early": "The notification may arrive earlier (about 1-10 minutes) because of the way we send the notification",
|
||||||
|
"transformer": "Parametric Transformer Reminder",
|
||||||
|
"last": "Enter when you last used the Parametric Transformer",
|
||||||
|
"countdown": "Enter countdown time of the Parametric Transformer (If you don't remember the exact time, you need to approximate it)",
|
||||||
|
"useLast": "Use last used time instead",
|
||||||
|
"useCountdown": "Use countdown time instead",
|
||||||
|
"set": "Set Reminder",
|
||||||
|
"checking": "Checking saved reminder...",
|
||||||
|
"errorSelect": "Please select the day and hour!",
|
||||||
|
"errorSaving": "Something wrong when saving the reminder 🙁",
|
||||||
|
"current": "Current reminder",
|
||||||
|
"hoyolab": "Hoyolab Daily Check-In Reminder",
|
||||||
|
"comingsoon": "Coming Soon!"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -466,5 +466,23 @@
|
||||||
],
|
],
|
||||||
"kofi": "Support me on Ko-fi",
|
"kofi": "Support me on Ko-fi",
|
||||||
"trakteer": "Support me on Trakteer"
|
"trakteer": "Support me on Trakteer"
|
||||||
|
},
|
||||||
|
"reminder": {
|
||||||
|
"notSupported": "Browser mu tidak mensupport notifikasi, silahkan coba browser lain!",
|
||||||
|
"blocked": "Notifikasi di blokir, notifikasi tidak akan bisa terkirim! Silahkan nyalakan notifikasi di browser mu.",
|
||||||
|
"desktop": "Browser dekstop tidak bisa menerima notifikasi jika browser tidak berjalan!",
|
||||||
|
"early": "Notifikasi mungkin akan muncul lebih awal (sekitar 1-10 menit) karena limitasi dalam pengiriman notifikasi.",
|
||||||
|
"transformer": "Reminder Parametric Transformer",
|
||||||
|
"last": "Masukkan kapan kamu terakhir menggunakan Parametric Transformer",
|
||||||
|
"countdown": "Masukkan waktu countdown Parametric Transformer (jika kamu tidak ingat waktu pasti nya, kamu perlu mengira-ngira-kannya)",
|
||||||
|
"useLast": "Gunakan waktu terakhir menggunakan",
|
||||||
|
"useCountdown": "Gunakan waktu countdown",
|
||||||
|
"set": "Set Reminder",
|
||||||
|
"checking": "Mengecek reminder tersimpan...",
|
||||||
|
"errorSelect": "Silahkan pilih sisa hari dan jam nya!",
|
||||||
|
"errorSaving": "Error saat menyimpan reminder 🙁",
|
||||||
|
"current": "Reminder saat ini",
|
||||||
|
"hoyolab": "Reminder Hoyolab Daily Check-In",
|
||||||
|
"comingsoon": "Coming Soon!"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,11 +7,13 @@
|
||||||
|
|
||||||
<div class="bg-item rounded-xl p-4 flex flex-col">
|
<div class="bg-item rounded-xl p-4 flex flex-col">
|
||||||
<p class="text-white">{$t('home.reminder.message')}</p>
|
<p class="text-white">{$t('home.reminder.message')}</p>
|
||||||
<div
|
<a
|
||||||
class="flex justify-end items-center self-end lg:self-start text-white mt-4 bg-background-secondary rounded-xl py-2 px-4"
|
href="/reminder"
|
||||||
|
class="flex justify-end items-center self-end lg:self-start text-white mt-4 bg-background-secondary rounded-xl py-2 px-4
|
||||||
|
hover:bg-background transition-colors duration-100"
|
||||||
>
|
>
|
||||||
<Icon path={mdiAlarm} className="mr-2" />
|
<Icon path={mdiAlarm} className="mr-2" />
|
||||||
{$t('home.reminder.detail')}
|
{$t('home.reminder.detail')}
|
||||||
<Icon path={mdiChevronRight} />
|
<Icon path={mdiChevronRight} />
|
||||||
</div>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
let refreshLayout;
|
let refreshLayout;
|
||||||
|
|
||||||
const onDone = debounce(() => {
|
const onDone = debounce(() => {
|
||||||
console.log('refresh');
|
|
||||||
refreshLayout();
|
refreshLayout();
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
|
@ -42,10 +41,10 @@
|
||||||
<Masonry bind:refreshLayout gridGap="1rem">
|
<Masonry bind:refreshLayout gridGap="1rem">
|
||||||
<Welcome on:done={onDone} />
|
<Welcome on:done={onDone} />
|
||||||
<Wish on:done={onDone} />
|
<Wish on:done={onDone} />
|
||||||
<!-- <Reminder on:done={onDone} /> -->
|
<Reminder on:done={onDone} />
|
||||||
|
<Banner on:done={onDone} featured="venti" bannerId={300010} />
|
||||||
<Event on:done={onDone} />
|
<Event on:done={onDone} />
|
||||||
<Item on:done={onDone} />
|
<Item on:done={onDone} />
|
||||||
<Banner on:done={onDone} featured="venti" bannerId={300010} />
|
|
||||||
<Discord on:done={onDone} />
|
<Discord on:done={onDone} />
|
||||||
<Calculator on:done={onDone} />
|
<Calculator on:done={onDone} />
|
||||||
</Masonry>
|
</Masonry>
|
||||||
|
|
233
src/routes/reminder.svelte
Normal file
233
src/routes/reminder.svelte
Normal file
|
@ -0,0 +1,233 @@
|
||||||
|
<script>
|
||||||
|
import { stores } from '@sapper/app';
|
||||||
|
import { mdiCalendarCheck, mdiLoading } from '@mdi/js';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { t, locale } from 'svelte-i18n';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import relative from 'dayjs/plugin/relativeTime';
|
||||||
|
import 'dayjs/locale/id';
|
||||||
|
import 'dayjs/locale/en';
|
||||||
|
import 'dayjs/locale/ru';
|
||||||
|
dayjs.extend(relative);
|
||||||
|
|
||||||
|
import Button from '../components/Button.svelte';
|
||||||
|
import Input from '../components/Input.svelte';
|
||||||
|
import Select from '../components/Select.svelte';
|
||||||
|
import Icon from '../components/Icon.svelte';
|
||||||
|
import { pushToast } from '../stores/toast';
|
||||||
|
|
||||||
|
import {
|
||||||
|
firebaseToken,
|
||||||
|
firstLoadNotification,
|
||||||
|
loading,
|
||||||
|
loadingFirst,
|
||||||
|
notificationAllowed,
|
||||||
|
notificationSupported,
|
||||||
|
requestPermission,
|
||||||
|
} from '../stores/firebase';
|
||||||
|
|
||||||
|
const daysOptions = [...new Array(7)].map((_, i) => ({
|
||||||
|
label: i,
|
||||||
|
value: i,
|
||||||
|
}));
|
||||||
|
const hourOptions = [...new Array(24)].map((_, i) => ({
|
||||||
|
label: i,
|
||||||
|
value: i,
|
||||||
|
}));
|
||||||
|
|
||||||
|
let useType = 'last';
|
||||||
|
let time = dayjs().format('YYYY-MM-DDTHH:mm');
|
||||||
|
let day = null;
|
||||||
|
let hour = null;
|
||||||
|
|
||||||
|
let currentReminder = null;
|
||||||
|
|
||||||
|
let loadingCurrent = false;
|
||||||
|
let loadingSave = false;
|
||||||
|
|
||||||
|
locale.subscribe((val) => {
|
||||||
|
if (currentReminder === null) return;
|
||||||
|
currentReminder = currentReminder.locale(val.substring(0, 2));
|
||||||
|
});
|
||||||
|
|
||||||
|
async function getCurrentReminder() {
|
||||||
|
if ($firebaseToken === '') return;
|
||||||
|
|
||||||
|
console.log('get reminder');
|
||||||
|
const url = new URL(`${__paimon.env.API_HOST}/reminder`);
|
||||||
|
const query = new URLSearchParams({ token: $firebaseToken, type: 'transformer' });
|
||||||
|
url.search = query.toString();
|
||||||
|
|
||||||
|
loadingCurrent = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(url, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
currentReminder = dayjs(data.time);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadingCurrent = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setReminder() {
|
||||||
|
loadingSave = true;
|
||||||
|
await requestPermission();
|
||||||
|
|
||||||
|
let reminderTime;
|
||||||
|
|
||||||
|
if (useType === 'last') {
|
||||||
|
reminderTime = dayjs(time).add(6, 'day').add(22, 'h').format('YYYY-MM-DD HH:mm:ssZ');
|
||||||
|
} else {
|
||||||
|
if (day === null || hour === null) {
|
||||||
|
pushToast($t('reminder.errorSelect'), 'error');
|
||||||
|
loadingSave = false;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
reminderTime = dayjs().add(day.value, 'day').add(hour.value, 'h').format('YYYY-MM-DD HH:mm:ssZ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${__paimon.env.API_HOST}/reminder`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
time: reminderTime,
|
||||||
|
type: 'transformer',
|
||||||
|
token: $firebaseToken,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
currentReminder = dayjs(data.reminder.time);
|
||||||
|
loadingSave = false;
|
||||||
|
} catch (err) {
|
||||||
|
loadingSave = false;
|
||||||
|
console.error(err);
|
||||||
|
pushToast($t('reminder.errorSaving'), 'error');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('set reminder', useType, 'transformer', reminderTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleUseType() {
|
||||||
|
if (useType === 'last') {
|
||||||
|
useType = 'countdown';
|
||||||
|
time = dayjs().format('HH:mm');
|
||||||
|
} else {
|
||||||
|
useType = 'last';
|
||||||
|
time = dayjs().format('YYYY-MM-DDTHH:mm');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
await firstLoadNotification();
|
||||||
|
await getCurrentReminder();
|
||||||
|
});
|
||||||
|
|
||||||
|
const { page } = stores();
|
||||||
|
$: if (page) {
|
||||||
|
getCurrentReminder();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Paimon.moe</title>
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="Set up a reminder notification for Parametric Transformer and Hoyolab Daily Login here"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property="og:description"
|
||||||
|
content="Set up a reminder notification for Parametric Transformer and Hoyolab Daily Login here"
|
||||||
|
/>
|
||||||
|
</svelte:head>
|
||||||
|
<div class="lg:ml-64 pt-20 lg:pt-8">
|
||||||
|
<div class="px-4 md:px-8 flex flex-col text-white">
|
||||||
|
{#if $loadingFirst}
|
||||||
|
<Icon path={mdiLoading} spin />
|
||||||
|
{:else if !$notificationSupported}
|
||||||
|
<div class="rounded-xl border-2 border-red-600 bg-red-400 bg-opacity-75 p-4 not-supported text-black">
|
||||||
|
{$t('reminder.notSupported')}
|
||||||
|
</div>
|
||||||
|
{:else if !$notificationAllowed}
|
||||||
|
<div class="rounded-xl border-2 border-red-600 bg-red-400 bg-opacity-75 p-4 not-supported text-black">
|
||||||
|
{$t('reminder.blocked')}
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="grid gap-4 grid-cols-1 md:grid-cols-2 xl:grid-cols-3 max-w-screen-xl">
|
||||||
|
<div>
|
||||||
|
<div class="flex items-center mb-2 h-12">
|
||||||
|
<img src="/images/items/parametric_transformer.png" alt="parametric transformer" class="w-12 h-12 mr-2" />
|
||||||
|
<p class="font-display text-xl font-semibold">{$t('reminder.transformer')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="bg-item p-4 rounded-xl text-white flex flex-col">
|
||||||
|
{#if loadingCurrent}
|
||||||
|
<div class="bg-background mb-4 p-4 rounded-xl flex">
|
||||||
|
<Icon path={mdiLoading} size={0.8} spin className="mr-2" />
|
||||||
|
{$t('reminder.checking')}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if currentReminder !== null}
|
||||||
|
<div class="bg-background mb-4 p-4 rounded-xl">
|
||||||
|
<p class="text-gray-400">{$t('reminder.current')}</p>
|
||||||
|
<p class="text-gray-400">{currentReminder.format('YYYY-MM-DD HH:mm')} ({currentReminder.fromNow()})</p>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<p class="mb-1 ml-1">
|
||||||
|
{$t(useType === 'last' ? 'reminder.last' : 'reminder.countdown')}
|
||||||
|
</p>
|
||||||
|
{#if useType === 'last'}
|
||||||
|
<Input type="datetime-local" pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}" bind:value={time} />
|
||||||
|
{:else}
|
||||||
|
<div class="flex">
|
||||||
|
<Select placeholder="Day" className="mr-1 flex-1" options={daysOptions} bind:selected={day} />
|
||||||
|
<Select placeholder="Hour" className="ml-1 flex-1" options={hourOptions} bind:selected={hour} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<Button size="sm" className="my-2" on:click={toggleUseType}>
|
||||||
|
{$t(useType === 'last' ? 'reminder.useCountdown' : 'reminder.useLast')}
|
||||||
|
</Button>
|
||||||
|
<Button on:click={setReminder} disabled={loadingSave}>
|
||||||
|
{#if loadingSave}<Icon path={mdiLoading} size={0.8} spin className="mr-2" />{/if}
|
||||||
|
{$t('reminder.set')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="flex items-center mb-2 h-12">
|
||||||
|
<Icon path={mdiCalendarCheck} size={1.5} className="mr-2" />
|
||||||
|
<p class="font-display text-xl font-semibold">{$t('reminder.hoyolab')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="bg-item p-4 rounded-xl text-white flex flex-col">
|
||||||
|
{$t('reminder.comingsoon')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="rounded-xl border-2 border-green-600 bg-green-400 bg-opacity-75 p-4 not-supported text-black">
|
||||||
|
{$t('reminder.early')}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="rounded-xl border-2 border-orange-600 bg-orange-400 bg-opacity-75 p-4 not-supported text-black mb-4 mt-2 hidden lg:block"
|
||||||
|
>
|
||||||
|
{$t('reminder.desktop')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@screen md {
|
||||||
|
.not-supported {
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -8,6 +8,7 @@
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||||
import 'dayjs/locale/id';
|
import 'dayjs/locale/id';
|
||||||
import 'dayjs/locale/en';
|
import 'dayjs/locale/en';
|
||||||
|
import 'dayjs/locale/ru';
|
||||||
dayjs.extend(duration);
|
dayjs.extend(duration);
|
||||||
dayjs.extend(relativeTime);
|
dayjs.extend(relativeTime);
|
||||||
|
|
||||||
|
|
|
@ -2,19 +2,50 @@ import sirv from 'sirv';
|
||||||
import polka from 'polka';
|
import polka from 'polka';
|
||||||
import compression from 'compression';
|
import compression from 'compression';
|
||||||
import * as sapper from '@sapper/server';
|
import * as sapper from '@sapper/server';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
import { i18nMiddleware } from './i18n.js';
|
import { i18nMiddleware } from './i18n.js';
|
||||||
|
|
||||||
const { PORT, NODE_ENV } = process.env;
|
const { PORT, NODE_ENV } = process.env;
|
||||||
const dev = NODE_ENV === 'development';
|
const dev = NODE_ENV === 'development';
|
||||||
|
|
||||||
|
function serve(pathname) {
|
||||||
|
const filter = (req) => req.path === pathname;
|
||||||
|
const read = (file) => fs.readFileSync(path.join('__sapper__/dev', file));
|
||||||
|
|
||||||
|
return (req, res, next) => {
|
||||||
|
if (filter(req)) {
|
||||||
|
try {
|
||||||
|
const file = path.posix.normalize(decodeURIComponent(req.path));
|
||||||
|
const data = read(file);
|
||||||
|
res.setHeader('Content-Type', 'text/javascript');
|
||||||
|
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
|
||||||
|
res.end(data);
|
||||||
|
} catch (err) {
|
||||||
|
if (err.code === 'ENOENT') {
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
console.error(err);
|
||||||
|
res.statusCode = 500;
|
||||||
|
res.end('an error occurred while reading a static file from disk');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
polka() // You can also use Express
|
polka() // You can also use Express
|
||||||
.use(
|
.use(
|
||||||
compression({ threshold: 0 }),
|
compression({ threshold: 0 }),
|
||||||
sirv('static', { dev }),
|
sirv('static', { dev }),
|
||||||
i18nMiddleware(),
|
i18nMiddleware(),
|
||||||
sapper.middleware()
|
serve('/firebase-messaging-sw.js'),
|
||||||
)
|
serve('/firebase-messaging-sw.js.map'),
|
||||||
.listen(PORT, err => {
|
sapper.middleware(),
|
||||||
if (err) console.log('error', err);
|
)
|
||||||
});
|
.listen(PORT, (err) => {
|
||||||
|
if (err) console.log('error', err);
|
||||||
|
});
|
||||||
|
|
121
src/stores/firebase.js
Normal file
121
src/stores/firebase.js
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
import { writable } from 'svelte/store';
|
||||||
|
|
||||||
|
export const firebaseToken = writable('');
|
||||||
|
export const loadingFirst = writable(true);
|
||||||
|
export const loading = writable(false);
|
||||||
|
export const notificationSupported = writable(false);
|
||||||
|
export const notificationAllowed = writable(true);
|
||||||
|
|
||||||
|
const firebaseConfig = {
|
||||||
|
apiKey: __paimon.env.FIREBASE_API_KEY,
|
||||||
|
authDomain: __paimon.env.FIREBASE_AUTH_DOMAIN,
|
||||||
|
projectId: __paimon.env.FIREBASE_PROJECT_ID,
|
||||||
|
storageBucket: __paimon.env.FIREBASE_STORAGE_BUCKET,
|
||||||
|
messagingSenderId: __paimon.env.FIREBASE_MESSAGING_SENDER_ID,
|
||||||
|
appId: __paimon.env.FIREBASE_APP_ID,
|
||||||
|
};
|
||||||
|
|
||||||
|
let firebase;
|
||||||
|
let messaging;
|
||||||
|
|
||||||
|
export async function firstLoadNotification() {
|
||||||
|
console.log('first load notification');
|
||||||
|
|
||||||
|
const isSupported = 'Notification' in window;
|
||||||
|
notificationSupported.set(isSupported);
|
||||||
|
loadingFirst.set(false);
|
||||||
|
|
||||||
|
if (!isSupported) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Notification.permission === 'granted') {
|
||||||
|
await initFirebase();
|
||||||
|
} else if (Notification.permission === 'denied') {
|
||||||
|
notificationAllowed.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function requestPermission() {
|
||||||
|
console.log('request permission');
|
||||||
|
|
||||||
|
if (Notification.permission === 'granted') {
|
||||||
|
if (messaging) {
|
||||||
|
await getToken();
|
||||||
|
} else {
|
||||||
|
await initFirebase();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loading.set(true);
|
||||||
|
|
||||||
|
const result = await Notification.requestPermission();
|
||||||
|
|
||||||
|
const allowed = result === 'granted';
|
||||||
|
notificationAllowed.set(allowed);
|
||||||
|
|
||||||
|
if (allowed) {
|
||||||
|
await initFirebase();
|
||||||
|
} else {
|
||||||
|
loading.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initFirebase() {
|
||||||
|
console.log('init firebase');
|
||||||
|
|
||||||
|
if (!messaging) {
|
||||||
|
firebase = (await import('firebase/app')).default;
|
||||||
|
await import('firebase/messaging');
|
||||||
|
|
||||||
|
firebase.initializeApp(firebaseConfig);
|
||||||
|
messaging = firebase.messaging();
|
||||||
|
}
|
||||||
|
|
||||||
|
await getToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getToken() {
|
||||||
|
console.log('request token');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const token = await messaging.getToken({
|
||||||
|
vapidKey: 'BA6niiIWa_QP2SXMTjS8gBtM3M7m0q0n0_ZWjECw3Z_iEFujzPG2VdAAvNFJ5btbgpEiRe2B80M4QKxRSxtmvDw',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
firebaseToken.set(token);
|
||||||
|
loading.set(false);
|
||||||
|
console.log(token);
|
||||||
|
handleForegroundNotification();
|
||||||
|
return token;
|
||||||
|
} else {
|
||||||
|
await requestPermission();
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleForegroundNotification() {
|
||||||
|
console.log('handle foreground notification');
|
||||||
|
|
||||||
|
messaging.onMessage((payload) => {
|
||||||
|
console.log('Received foreground message ', payload);
|
||||||
|
|
||||||
|
navigator.serviceWorker.getRegistration('/firebase-cloud-messaging-push-scope').then((registration) => {
|
||||||
|
const { title, body, url } = payload.data;
|
||||||
|
|
||||||
|
const notificationTitle = title;
|
||||||
|
const notificationOptions = {
|
||||||
|
body,
|
||||||
|
icon: '/favicon.png',
|
||||||
|
data: {
|
||||||
|
url,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
registration.showNotification(notificationTitle, notificationOptions);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
BIN
static/images/items/parametric_transformer.png
Normal file
BIN
static/images/items/parametric_transformer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 60 KiB |
Loading…
Reference in a new issue