mirror of
https://github.com/MadeBaruna/paimon-moe.git
synced 2025-03-14 11:43:52 +01:00
Add homepage
This commit is contained in:
parent
b7f2084f3d
commit
1857fee004
17 changed files with 626 additions and 4 deletions
|
@ -60,6 +60,7 @@
|
|||
<Icon path={mdiCloseCircle} size={2} color="white" className="mb-8 mt-4 opacity-75" />
|
||||
</div>
|
||||
{/if}
|
||||
<SidebarItem on:clicked={close} active={segment === undefined} image="/images/home.png" label="Home" href="/" />
|
||||
<SidebarItem
|
||||
on:clicked={close}
|
||||
active={segment === 'characters'}
|
||||
|
|
|
@ -9,6 +9,7 @@ export const eventsData = [
|
|||
color: '#FAE2B4',
|
||||
url:
|
||||
'https://webstatic-sea.mihoyo.com/ys/event/e20210316cooking-sea/index.html?lang=en-us?utm_source=hoyolab&utm_medium=banner',
|
||||
showOnHome: true,
|
||||
},
|
||||
{
|
||||
name: 'Contending Tides Event',
|
||||
|
@ -19,6 +20,7 @@ export const eventsData = [
|
|||
color: '#6C99F7',
|
||||
zoom: '180%',
|
||||
url: 'https://www.hoyolab.com/genshin/article/275307',
|
||||
showOnHome: true,
|
||||
},
|
||||
],
|
||||
{
|
||||
|
@ -30,6 +32,7 @@ export const eventsData = [
|
|||
color: '#DDD7E8',
|
||||
zoom: '180%',
|
||||
url: 'https://genshin.mihoyo.com/en/news/detail/9262',
|
||||
showOnHome: true,
|
||||
},
|
||||
{
|
||||
name: 'Invitation of Windblume - 1.4 Event',
|
||||
|
@ -40,6 +43,7 @@ export const eventsData = [
|
|||
color: '#79D2EB',
|
||||
zoom: '120%',
|
||||
url: 'https://genshin.mihoyo.com/en/news/detail/9407',
|
||||
showOnHome: true,
|
||||
},
|
||||
[
|
||||
{
|
||||
|
@ -49,6 +53,7 @@ export const eventsData = [
|
|||
start: '2021-03-05 04:00:00',
|
||||
end: '2021-03-12 04:00:00',
|
||||
color: '#F6AD55',
|
||||
showOnHome: true,
|
||||
},
|
||||
{
|
||||
name: 'Act I',
|
||||
|
@ -102,6 +107,7 @@ export const eventsData = [
|
|||
start: '2021-03-02 18:00:00',
|
||||
end: '2021-03-16 15:00:00',
|
||||
color: '#FC8181',
|
||||
showOnHome: true,
|
||||
},
|
||||
{
|
||||
name: 'Ballad in Goblets - Venti Banner',
|
||||
|
@ -111,6 +117,7 @@ export const eventsData = [
|
|||
end: '2021-04-06 16:00:00',
|
||||
color: '#6EDDCA',
|
||||
url: 'https://genshin.mihoyo.com/en/news/detail/9269',
|
||||
showOnHome: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
|
@ -121,6 +128,7 @@ export const eventsData = [
|
|||
start: '2021-02-23 18:00:00',
|
||||
end: '2021-03-16 15:00:00',
|
||||
color: '#F56565',
|
||||
showOnHome: true,
|
||||
},
|
||||
{
|
||||
name: 'Epitome Invocation - Weapon Banner',
|
||||
|
@ -130,6 +138,7 @@ export const eventsData = [
|
|||
end: '2021-04-06 16:00:00',
|
||||
color: '#FFAA4B',
|
||||
url: 'https://genshin.mihoyo.com/en/news/detail/9278',
|
||||
showOnHome: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
|
|
|
@ -1,4 +1,47 @@
|
|||
{
|
||||
"home": {
|
||||
"welcome": "Welcome to Paimon.moe! 👋",
|
||||
"message": "Your best Genshin Impact companion! Help you plan what to farm with ascension calculator, also track your progress with todo and wish counter.",
|
||||
"banner": {
|
||||
"featured": "Venti",
|
||||
"summoned": "Summoned",
|
||||
"percentage": "from all 5",
|
||||
"avg": "Pity average",
|
||||
"subtitle": "Calculated from data submitted by paimon.moe users",
|
||||
"detail": "Global With Tally"
|
||||
},
|
||||
"wish": {
|
||||
"message": "Import your wish history to keep it more than 6 months! Also automatically count your pity and statistic about your wishes with fancy charts 📊",
|
||||
"latest": "Your Last Wish",
|
||||
"banner": "Banner",
|
||||
"time": "Time",
|
||||
"name": "Name",
|
||||
"pity": "Pity",
|
||||
"detail": "Wish Counter"
|
||||
},
|
||||
"reminder": {
|
||||
"message": "You can set up a reminder notification 🔔 for Parametric Transformer and Hoyolab Daily Login here! Click the reminder button below to start!",
|
||||
"detail": "Reminder"
|
||||
},
|
||||
"event": {
|
||||
"upcoming": "Upcoming Event",
|
||||
"current": "Current Event",
|
||||
"detail": "Timeline"
|
||||
},
|
||||
"discord": {
|
||||
"online": "Members Online",
|
||||
"message": "Join our Discord server for latest update announcement! Also discuss about Genshin Impact and feedback for paimon.moe.",
|
||||
"join": "Join Our Discord"
|
||||
},
|
||||
"items": {
|
||||
"title": "Farmable Today",
|
||||
"detail": "Items"
|
||||
},
|
||||
"calculator": {
|
||||
"title": "🧮 Calculate Character and Weapons ascension material and talent book! All the calculations can be added to the Todo list, it will show you how much resin you need too!",
|
||||
"detail": "Calculator"
|
||||
}
|
||||
},
|
||||
"characters": {
|
||||
"title": "Characters",
|
||||
"subtitle": "Stat numbers are at level 80 Ascension 6. You can also click the table header to sort!",
|
||||
|
|
|
@ -1,4 +1,47 @@
|
|||
{
|
||||
"home": {
|
||||
"welcome": "Selamat Datang di Paimon.moe! 👋",
|
||||
"message": "Your best Genshin Impact companion! Membantu kamu merencanakan apa yang harus di farm dengan kalkulator ascension, juga catat progress mu dengan todo dan wish counter.",
|
||||
"banner": {
|
||||
"featured": "Venti",
|
||||
"summoned": "Pulang",
|
||||
"percentage": "dari semua 5",
|
||||
"avg": "Pity rata-rata",
|
||||
"subtitle": "Dihitung dari data yang dikirim oleh pengguna paimon.moe",
|
||||
"detail": "Perhitungan Wish Pity Global"
|
||||
},
|
||||
"wish": {
|
||||
"message": "Import riwayat wish mu dan datanya bisa disimpan lebih dari 6 bulan! Juga otomatis menghitung pity-mu dan ada statistik tentang wish-mu dengan grafik 📊",
|
||||
"latest": "Wish Terakhir",
|
||||
"banner": "Banner",
|
||||
"time": "Waktu",
|
||||
"name": "Nama",
|
||||
"pity": "Pity",
|
||||
"detail": "Wish Counter"
|
||||
},
|
||||
"reminder": {
|
||||
"message": "Kamu bisa mengatur notifikasi 🔔 pengigat untuk Parametric Transformer dan Hoyolab Login Harian disini! Klik tombol reminder dibawah untuk mulai!",
|
||||
"detail": "Reminder"
|
||||
},
|
||||
"event": {
|
||||
"upcoming": "Event Akan Datang",
|
||||
"current": "Event Sekarang",
|
||||
"detail": "Timeline"
|
||||
},
|
||||
"discord": {
|
||||
"online": "Member Online",
|
||||
"message": "Gabung server Discord untuk informasi mengenai update terbaru! Juga diskusi tentang Genshin Impact dan feedback untuk paimon.moe.",
|
||||
"join": "Gabung Discord"
|
||||
},
|
||||
"items": {
|
||||
"title": "Bisa di Farm Hari Ini",
|
||||
"detail": "Items"
|
||||
},
|
||||
"calculator": {
|
||||
"title": "🧮 Hitung Ascension dan Talent Book Karakter dan Senjata! Semua hasil perhitungan bisa ditambahkan ke daftar todo, dan juga akan menampilkan berapa resin yang kamu perlukan!",
|
||||
"detail": "Kalkulator"
|
||||
}
|
||||
},
|
||||
"characters": {
|
||||
"title": "Karakter",
|
||||
"subtitle": "Angka stat adalah saat level 80 Ascension 6. Kamu juga dapat mengklik judul tabel untuk mengurutkan!",
|
||||
|
|
91
src/routes/_index/banner.svelte
Normal file
91
src/routes/_index/banner.svelte
Normal file
|
@ -0,0 +1,91 @@
|
|||
<script>
|
||||
import { mdiChevronRight, mdiEarth, mdiLoading, mdiStar } from '@mdi/js';
|
||||
import { onMount, createEventDispatcher, tick } from 'svelte';
|
||||
|
||||
import { t } from 'svelte-i18n';
|
||||
import Icon from '../../components/Icon.svelte';
|
||||
|
||||
const numberFormat = Intl.NumberFormat('en', {
|
||||
maximumFractionDigits: 0,
|
||||
minimumFractionDigits: 0,
|
||||
});
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let featured;
|
||||
export let bannerId;
|
||||
|
||||
let loading = true;
|
||||
let featuredPull = 0;
|
||||
let percentage = '...';
|
||||
let average = '...';
|
||||
|
||||
async function getData() {
|
||||
const url = new URL(`${__paimon.env.API_HOST}/wish`);
|
||||
const query = new URLSearchParams({ banner: bannerId });
|
||||
url.search = query.toString();
|
||||
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
const item = data.list.find((e) => e.name === featured);
|
||||
featuredPull = item.count;
|
||||
percentage = numberFormat.format((item.count / data.total.legendary) * 100);
|
||||
average = numberFormat.format(data.pityAverage.legendary);
|
||||
|
||||
loading = false;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
getData();
|
||||
await tick();
|
||||
dispatch('done');
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="bg-item rounded-xl p-4 flex flex-col">
|
||||
<div class="relative">
|
||||
<img src="/images/home/venti.png" alt="venti" style="min-height: 150px;" />
|
||||
<div
|
||||
class="flex text-white items-center absolute bottom-0 pb-1"
|
||||
style="background: linear-gradient(90deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0.75) 27%, rgba(0,0,0,0.75) 70%, rgba(0,0,0,0) 100%);"
|
||||
>
|
||||
<h3 class="text-4xl ml-4 font-black leading-10" style="margin-top: 8px;">
|
||||
{#if loading}
|
||||
<Icon path={mdiLoading} spin />
|
||||
{:else}
|
||||
{featuredPull}
|
||||
{/if}
|
||||
</h3>
|
||||
<div class="flex flex-col ml-2 pr-2">
|
||||
<p class="font-semibold">{$t('home.banner.featured')}</p>
|
||||
<p class="text-gray-200 leading-3">{$t('home.banner.summoned')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-start pl-2 mt-1">
|
||||
<p class="text-white mr-4">
|
||||
<span class="font-semibold">{percentage}%</span>
|
||||
{$t('home.banner.percentage')}<Icon className="mb-1" path={mdiStar} size={0.8} />
|
||||
</p>
|
||||
<p class="text-white">{$t('home.banner.avg')} <span class="font-semibold">{average}</span></p>
|
||||
</div>
|
||||
<p class="text-gray-400 pl-2">※ {$t('home.banner.subtitle')}</p>
|
||||
<a
|
||||
href="/wish/tally"
|
||||
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={mdiEarth} className="mr-2" />
|
||||
{$t('home.banner.detail')}
|
||||
<Icon path={mdiChevronRight} />
|
||||
</a>
|
||||
</div>
|
19
src/routes/_index/calculator.svelte
Normal file
19
src/routes/_index/calculator.svelte
Normal file
|
@ -0,0 +1,19 @@
|
|||
<script>
|
||||
import { mdiChevronRight } from '@mdi/js';
|
||||
|
||||
import { t } from 'svelte-i18n';
|
||||
import Icon from '../../components/Icon.svelte';
|
||||
</script>
|
||||
|
||||
<div class="bg-item rounded-xl p-4 flex flex-col">
|
||||
<p class="text-white">{$t('home.calculator.title')}</p>
|
||||
<a
|
||||
href="/calculator"
|
||||
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"
|
||||
>
|
||||
<img src="/images/calculator.png" alt="wish" class="mr-2 h-6 w-6" />
|
||||
{$t('home.calculator.detail')}
|
||||
<Icon path={mdiChevronRight} />
|
||||
</a>
|
||||
</div>
|
49
src/routes/_index/discord.svelte
Normal file
49
src/routes/_index/discord.svelte
Normal file
|
@ -0,0 +1,49 @@
|
|||
<script>
|
||||
import { mdiChevronRight, mdiCircle } from '@mdi/js';
|
||||
import { onMount } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import Icon from '../../components/Icon.svelte';
|
||||
|
||||
const url = 'https://discord.com/api/guilds/820601523125747712/widget.json';
|
||||
let online = '...';
|
||||
|
||||
async function getData() {
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
online = data.presence_count;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
getData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="bg-item rounded-xl p-4 flex flex-col items-start">
|
||||
<img class="h-16" src="/images/home/discord.svg" alt="discord" />
|
||||
<p class="text-white ml-2">
|
||||
<span class="text-green-400 mr-1">⬤</span>
|
||||
{online}
|
||||
{$t('home.discord.online')}
|
||||
</p>
|
||||
<p class="text-white ml-2">
|
||||
{$t('home.discord.message')}
|
||||
</p>
|
||||
<a
|
||||
href="https://discord.gg/4nbWsCGjjE"
|
||||
target="_blank"
|
||||
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"
|
||||
>
|
||||
{$t('home.discord.join')}
|
||||
<Icon path={mdiChevronRight} />
|
||||
</a>
|
||||
</div>
|
91
src/routes/_index/event.svelte
Normal file
91
src/routes/_index/event.svelte
Normal file
|
@ -0,0 +1,91 @@
|
|||
<script>
|
||||
import { t } from 'svelte-i18n';
|
||||
import { createEventDispatcher, onMount, tick } from 'svelte';
|
||||
import { mdiChevronRight } from '@mdi/js';
|
||||
import dayjs from 'dayjs';
|
||||
import duration from 'dayjs/plugin/duration';
|
||||
dayjs.extend(duration);
|
||||
|
||||
import Icon from '../../components/Icon.svelte';
|
||||
import { eventsData } from '../../data/timeline';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const now = dayjs();
|
||||
let upcoming = [];
|
||||
let current = [];
|
||||
|
||||
function checkEvent(event) {
|
||||
if (!event.showOnHome) return;
|
||||
|
||||
const start = dayjs(event.start);
|
||||
const end = dayjs(event.end);
|
||||
if (start.isBefore(now) && end.isAfter(now)) {
|
||||
const diff = end.diff(now);
|
||||
const timeLeft = dayjs.duration(diff);
|
||||
event.time = timeLeft.format(diff > 86400000 ? 'D[d] H[h]' : 'H[h]');
|
||||
current = [...current, event];
|
||||
} else if (start.isAfter(now)) {
|
||||
const diff = start.diff(now);
|
||||
const timeUpcoming = dayjs.duration(diff);
|
||||
event.time = timeUpcoming.format(diff > 86400000 ? 'D[d] H[h]' : 'H[h]');
|
||||
upcoming = [...upcoming, event];
|
||||
}
|
||||
}
|
||||
|
||||
function parseEvents() {
|
||||
for (const event of eventsData) {
|
||||
if (Array.isArray(event)) {
|
||||
for (const ev of event) {
|
||||
checkEvent(ev);
|
||||
}
|
||||
} else {
|
||||
checkEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
parseEvents();
|
||||
await tick();
|
||||
dispatch('done');
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="bg-item rounded-xl p-4 flex flex-col">
|
||||
{#if upcoming.length > 0}
|
||||
<p class="text-white mb-1">{$t('home.event.upcoming')}</p>
|
||||
<div class="flex flex-col">
|
||||
{#each upcoming as item}
|
||||
<div class="pl-2 pr-1 py-1 rounded-xl mb-1 flex" style="background: {item.color};">
|
||||
<span class="whitespace-no-wrap overflow-x-hidden flex-1 mr-1 text-sm" style="text-overflow: ellipsis;">
|
||||
{item.name}
|
||||
</span>
|
||||
<span class="bg-black bg-opacity-50 rounded-xl px-2 text-white text-sm">{item.time}</span>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#if current.length > 0}
|
||||
<p class="text-white mb-1">{$t('home.event.current')}</p>
|
||||
<div class="flex flex-col">
|
||||
{#each current as item}
|
||||
<div class="pl-2 pr-1 py-1 rounded-xl mb-1 flex" style="background: {item.color};">
|
||||
<span class="whitespace-no-wrap overflow-x-hidden flex-1 mr-1 text-sm" style="text-overflow: ellipsis;">
|
||||
{item.name}
|
||||
</span>
|
||||
<span class="bg-black bg-opacity-50 rounded-xl px-2 text-white text-sm">{item.time}</span>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
<a
|
||||
href="/timeline"
|
||||
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"
|
||||
>
|
||||
<img src="/images/timeline.png" alt="wish" class="mr-2 h-6 w-6" />
|
||||
{$t('home.event.detail')}
|
||||
<Icon path={mdiChevronRight} />
|
||||
</a>
|
||||
</div>
|
96
src/routes/_index/item.svelte
Normal file
96
src/routes/_index/item.svelte
Normal file
|
@ -0,0 +1,96 @@
|
|||
<script>
|
||||
import { mdiChevronRight } from '@mdi/js';
|
||||
|
||||
import { onMount, createEventDispatcher, tick } from 'svelte';
|
||||
|
||||
import { t, _ } from 'svelte-i18n';
|
||||
import Icon from '../../components/Icon.svelte';
|
||||
import { characters } from '../../data/characters';
|
||||
import { weaponList } from '../../data/weaponList';
|
||||
import { getCurrentDay } from '../../stores/server';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const today = getCurrentDay();
|
||||
let characterItems = {};
|
||||
let weaponItems = {};
|
||||
|
||||
function parseTalentBook() {
|
||||
const _characters = {};
|
||||
const _weapons = {};
|
||||
|
||||
for (const [_, character] of Object.entries(characters)) {
|
||||
const item = character.material.book[0];
|
||||
if (!item.day.includes(today)) continue;
|
||||
|
||||
if (_characters[item.id] === undefined) {
|
||||
_characters[item.id] = [];
|
||||
}
|
||||
_characters[item.id].push(character.id);
|
||||
}
|
||||
|
||||
for (const [_, weapon] of Object.entries(weaponList)) {
|
||||
const items = weapon.ascension[0].items;
|
||||
const item = items[0].item;
|
||||
if (item.day) {
|
||||
if (!item.day.includes(today)) continue;
|
||||
if (_weapons[item.id] === undefined) {
|
||||
_weapons[item.id] = [];
|
||||
}
|
||||
_weapons[item.id].push(weapon.id);
|
||||
}
|
||||
|
||||
if (Object.keys(_weapons).length === 2) break;
|
||||
}
|
||||
|
||||
characterItems = _characters;
|
||||
weaponItems = _weapons;
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
parseTalentBook();
|
||||
await tick();
|
||||
dispatch('done');
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="bg-item rounded-xl p-4 flex flex-col">
|
||||
<p class="text-white mb-2">{$t('home.items.title')}</p>
|
||||
<table>
|
||||
{#each Object.entries(characterItems) as [id, characters]}
|
||||
<tr>
|
||||
<td class="border-b border-gray-700 h-14 w-14 pr-2 py-2 align-middle">
|
||||
<img class="h-full" src="/images/items/{id}.png" alt={id} title={id} />
|
||||
</td>
|
||||
<td class="border-b border-gray-700 pt-2 align-middle">
|
||||
{#each characters as char}
|
||||
<img
|
||||
class="h-10 w-auto mb-2 mr-2 inline rounded-full"
|
||||
src="/images/characters/{char}.png"
|
||||
alt={char}
|
||||
title={char}
|
||||
/>
|
||||
{/each}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
<tr>
|
||||
<td colspan="2" class="py-2 align-middle">
|
||||
{#each Object.entries(weaponItems) as [id, _]}
|
||||
<div class="h-10 w-10 mr-4 inline-block">
|
||||
<img class="h-full" src="/images/items/{id}.png" alt={id} title={id} />
|
||||
</div>
|
||||
{/each}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<a
|
||||
href="/items"
|
||||
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"
|
||||
>
|
||||
<img src="/images/items.png" alt="wish" class="mr-2 h-6 w-6" />
|
||||
{$t('home.items.detail')}
|
||||
<Icon path={mdiChevronRight} />
|
||||
</a>
|
||||
</div>
|
17
src/routes/_index/reminder.svelte
Normal file
17
src/routes/_index/reminder.svelte
Normal file
|
@ -0,0 +1,17 @@
|
|||
<script>
|
||||
import { mdiAlarm, mdiChevronRight } from '@mdi/js';
|
||||
|
||||
import { t } from 'svelte-i18n';
|
||||
import Icon from '../../components/Icon.svelte';
|
||||
</script>
|
||||
|
||||
<div class="bg-item rounded-xl p-4 flex flex-col">
|
||||
<p class="text-white">{$t('home.reminder.message')}</p>
|
||||
<div
|
||||
class="flex justify-end items-center self-end lg:self-start text-white mt-4 bg-background-secondary rounded-xl py-2 px-4"
|
||||
>
|
||||
<Icon path={mdiAlarm} className="mr-2" />
|
||||
{$t('home.reminder.detail')}
|
||||
<Icon path={mdiChevronRight} />
|
||||
</div>
|
||||
</div>
|
8
src/routes/_index/welcome.svelte
Normal file
8
src/routes/_index/welcome.svelte
Normal file
|
@ -0,0 +1,8 @@
|
|||
<script>
|
||||
import { t } from 'svelte-i18n';
|
||||
</script>
|
||||
|
||||
<div class="bg-item rounded-xl p-4">
|
||||
<p class="text-white font-bold font-display text-xl">{$t('home.welcome')}</p>
|
||||
<p class="text-white mt-2">{$t('home.message')}</p>
|
||||
</div>
|
107
src/routes/_index/wish.svelte
Normal file
107
src/routes/_index/wish.svelte
Normal file
|
@ -0,0 +1,107 @@
|
|||
<script>
|
||||
import { mdiChevronRight } from '@mdi/js';
|
||||
import dayjs from 'dayjs';
|
||||
import { onMount, createEventDispatcher, tick } from 'svelte';
|
||||
|
||||
import { t } from 'svelte-i18n';
|
||||
import Icon from '../../components/Icon.svelte';
|
||||
|
||||
import { bannerTypes } from '../../data/bannerTypes';
|
||||
import { characters } from '../../data/characters';
|
||||
import { weaponList } from '../../data/weaponList';
|
||||
import { getAccountPrefix } from '../../stores/account';
|
||||
import { readSave } from '../../stores/saveManager';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let latestPull = null;
|
||||
let latestBanner = null;
|
||||
|
||||
function getLatestWish() {
|
||||
const prefix = getAccountPrefix();
|
||||
|
||||
let latestTime = 0;
|
||||
let latest = null;
|
||||
let banner = null;
|
||||
for (let type of bannerTypes) {
|
||||
const path = `wish-counter-${type.id}`;
|
||||
const data = readSave(`${prefix}${path}`);
|
||||
if (data !== null) {
|
||||
const counterData = JSON.parse(data);
|
||||
const pulls = counterData.pulls || [];
|
||||
|
||||
if (pulls.length > 0) {
|
||||
const currentLatest = pulls[pulls.length - 1];
|
||||
if (currentLatest.time > latestTime) {
|
||||
latestTime = currentLatest.time;
|
||||
latest = currentLatest;
|
||||
banner = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
latestPull = latest;
|
||||
latestBanner = banner;
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
getLatestWish();
|
||||
await tick();
|
||||
dispatch('done');
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="bg-item rounded-xl p-4 flex flex-col">
|
||||
{#if latestPull === null}
|
||||
<p class="text-white">{$t('home.wish.message')}</p>
|
||||
{:else}
|
||||
<p class="text-white mb-2">{$t('home.wish.latest')}</p>
|
||||
<div class="flex">
|
||||
<div class="h-16 w-16" style="min-width: 4rem;">
|
||||
<img
|
||||
class="h-full w-auto"
|
||||
src={latestPull.type === 'character'
|
||||
? `/images/characters/${latestPull.id}.png`
|
||||
: latestPull.type === 'weapon'
|
||||
? `/images/weapons/${latestPull.id}.png`
|
||||
: '/images/wish.png'}
|
||||
alt={latestPull.id}
|
||||
/>
|
||||
</div>
|
||||
<table class="text-white">
|
||||
<tr>
|
||||
<td class="text-gray-400 pr-1">{$t('home.wish.banner')}</td>
|
||||
<td>{latestBanner.name}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-gray-400 pr-1">{$t('home.wish.time')}</td>
|
||||
<td>{dayjs.unix(latestPull.time).format('ddd YYYY-MM-DD HH:mm:ss')}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-gray-400 pr-1 align-top">{$t('home.wish.name')}</td>
|
||||
<td>
|
||||
{latestPull.type === 'character'
|
||||
? characters[latestPull.id].name
|
||||
: latestPull.type === 'weapon'
|
||||
? weaponList[latestPull.id].name
|
||||
: 'Unknown'}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-gray-400 pr-1">{$t('home.wish.pity')}</td>
|
||||
<td>{latestPull.pity}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{/if}
|
||||
<a
|
||||
href="/wish"
|
||||
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"
|
||||
>
|
||||
<img src="/images/wish.png" alt="wish" class="mr-2 h-6 w-6" />
|
||||
{$t('home.wish.detail')}
|
||||
<Icon path={mdiChevronRight} />
|
||||
</a>
|
||||
</div>
|
|
@ -77,7 +77,7 @@
|
|||
<div>
|
||||
<a
|
||||
class="text-gray-400 hover:text-primary mr-1 whitespace-no-wrap"
|
||||
href="https://discord.gg/2UqwpAr"
|
||||
href="https://discord.gg/4nbWsCGjjE"
|
||||
target="_blank"
|
||||
>
|
||||
<Icon path={mdiDiscord} size={1} /> Discord
|
||||
|
|
|
@ -1,5 +1,52 @@
|
|||
<script context="module">
|
||||
import Characters from './characters.svelte';
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import debounce from 'lodash/debounce';
|
||||
|
||||
import Masonry from '../components/Masonry.svelte';
|
||||
|
||||
import Welcome from './_index/welcome.svelte';
|
||||
import Banner from './_index/banner.svelte';
|
||||
import Event from './_index/event.svelte';
|
||||
import Reminder from './_index/reminder.svelte';
|
||||
import Wish from './_index/wish.svelte';
|
||||
import Item from './_index/item.svelte';
|
||||
import Calculator from './_index/calculator.svelte';
|
||||
import Discord from './_index/discord.svelte';
|
||||
|
||||
let refreshLayout;
|
||||
|
||||
const onDone = debounce(() => {
|
||||
console.log('refresh');
|
||||
refreshLayout();
|
||||
}, 100);
|
||||
|
||||
onMount(() => {
|
||||
setTimeout(() => {
|
||||
refreshLayout();
|
||||
}, 1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<Characters {...$$props} />
|
||||
<svelte:head>
|
||||
<title>Paimon.moe</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Your best Genshin Impact companion! Help you plan what to farm with ascension calculator and database. Also track your progress with todo and wish counter."
|
||||
/>
|
||||
<meta
|
||||
property="og:description"
|
||||
content="Your best Genshin Impact companion! Help you plan what to farm with ascension calculator and database. Also track your progress with todo and wish counter."
|
||||
/>
|
||||
</svelte:head>
|
||||
<div class="lg:ml-64 pt-16 lg:pt-4 md:px-4">
|
||||
<Masonry bind:refreshLayout gridGap="1rem">
|
||||
<Welcome on:done={onDone} />
|
||||
<Wish on:done={onDone} />
|
||||
<Reminder on:done={onDone} />
|
||||
<Event on:done={onDone} />
|
||||
<Item on:done={onDone} />
|
||||
<Banner on:done={onDone} featured="venti" bannerId={300010} />
|
||||
<Discord on:done={onDone} />
|
||||
<Calculator on:done={onDone} />
|
||||
</Masonry>
|
||||
</div>
|
||||
|
|
BIN
static/images/home.png
Normal file
BIN
static/images/home.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
1
static/images/home/discord.svg
Normal file
1
static/images/home/discord.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 272.1"><style>.st0{fill:#FFFFFF;}</style><path class="st0" d="M142.8 120.1c-5.7 0-10.2 4.9-10.2 11s4.6 11 10.2 11c5.7 0 10.2-4.9 10.2-11s-4.6-11-10.2-11zM106.3 120.1c-5.7 0-10.2 4.9-10.2 11s4.6 11 10.2 11c5.7 0 10.2-4.9 10.2-11 .1-6.1-4.5-11-10.2-11z"/><path class="st0" d="M191.4 36.9h-134c-11.3 0-20.5 9.2-20.5 20.5v134c0 11.3 9.2 20.5 20.5 20.5h113.4l-5.3-18.3 12.8 11.8 12.1 11.1 21.6 18.7V57.4c-.1-11.3-9.3-20.5-20.6-20.5zm-38.6 129.5s-3.6-4.3-6.6-8c13.1-3.7 18.1-11.8 18.1-11.8-4.1 2.7-8 4.6-11.5 5.9-5 2.1-9.8 3.4-14.5 4.3-9.6 1.8-18.4 1.3-25.9-.1-5.7-1.1-10.6-2.6-14.7-4.3-2.3-.9-4.8-2-7.3-3.4-.3-.2-.6-.3-.9-.5-.2-.1-.3-.2-.4-.2-1.8-1-2.8-1.7-2.8-1.7s4.8 7.9 17.5 11.7c-3 3.8-6.7 8.2-6.7 8.2-22.1-.7-30.5-15.1-30.5-15.1 0-31.9 14.4-57.8 14.4-57.8 14.4-10.7 28-10.4 28-10.4l1 1.2c-18 5.1-26.2 13-26.2 13s2.2-1.2 5.9-2.8c10.7-4.7 19.2-5.9 22.7-6.3.6-.1 1.1-.2 1.7-.2 6.1-.8 13-1 20.2-.2 9.5 1.1 19.7 3.9 30.1 9.5 0 0-7.9-7.5-24.9-12.6l1.4-1.6s13.7-.3 28 10.4c0 0 14.4 25.9 14.4 57.8 0-.1-8.4 14.3-30.5 15zM303.8 79.7h-33.2V117l22.1 19.9v-36.2h11.8c7.5 0 11.2 3.6 11.2 9.4v27.7c0 5.8-3.5 9.7-11.2 9.7h-34v21.1h33.2c17.8.1 34.5-8.8 34.5-29.2v-29.8c.1-20.8-16.6-29.9-34.4-29.9zm174 59.7v-30.6c0-11 19.8-13.5 25.8-2.5l18.3-7.4c-7.2-15.8-20.3-20.4-31.2-20.4-17.8 0-35.4 10.3-35.4 30.3v30.6c0 20.2 17.6 30.3 35 30.3 11.2 0 24.6-5.5 32-19.9l-19.6-9c-4.8 12.3-24.9 9.3-24.9-1.4zM417.3 113c-6.9-1.5-11.5-4-11.8-8.3.4-10.3 16.3-10.7 25.6-.8l14.7-11.3c-9.2-11.2-19.6-14.2-30.3-14.2-16.3 0-32.1 9.2-32.1 26.6 0 16.9 13 26 27.3 28.2 7.3 1 15.4 3.9 15.2 8.9-.6 9.5-20.2 9-29.1-1.8l-14.2 13.3c8.3 10.7 19.6 16.1 30.2 16.1 16.3 0 34.4-9.4 35.1-26.6 1-21.7-14.8-27.2-30.6-30.1zm-67 55.5h22.4V79.7h-22.4v88.8zM728 79.7h-33.2V117l22.1 19.9v-36.2h11.8c7.5 0 11.2 3.6 11.2 9.4v27.7c0 5.8-3.5 9.7-11.2 9.7h-34v21.1H728c17.8.1 34.5-8.8 34.5-29.2v-29.8c0-20.8-16.7-29.9-34.5-29.9zm-162.9-1.2c-18.4 0-36.7 10-36.7 30.5v30.3c0 20.3 18.4 30.5 36.9 30.5 18.4 0 36.7-10.2 36.7-30.5V109c0-20.4-18.5-30.5-36.9-30.5zm14.4 60.8c0 6.4-7.2 9.7-14.3 9.7-7.2 0-14.4-3.1-14.4-9.7V109c0-6.5 7-10 14-10 7.3 0 14.7 3.1 14.7 10v30.3zM682.4 109c-.5-20.8-14.7-29.2-33-29.2h-35.5v88.8h22.7v-28.2h4l20.6 28.2h28L665 138.1c10.7-3.4 17.4-12.7 17.4-29.1zm-32.6 12h-13.2v-20.3h13.2c14.1 0 14.1 20.3 0 20.3z"/></svg>
|
After Width: | Height: | Size: 2.3 KiB |
BIN
static/images/home/venti.png
Normal file
BIN
static/images/home/venti.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 80 KiB |
Loading…
Add table
Reference in a new issue