Add whale level

pull/1/head
Made Baruna 2022-03-13 17:17:39 +07:00
parent 6e96eab5b5
commit fea83ff141
6 changed files with 198 additions and 22 deletions

View File

@ -29,7 +29,7 @@
"percentage": "from all {rarity}",
"avg": "Pity average",
"subtitle": "Calculated from data submitted by {user} paimon.moe users",
"detail": "Global Wish Tally"
"detail": "Global Wish Stats"
},
"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 📊",
@ -135,7 +135,7 @@
"manual": "If you want to manually input the data, you can enable it here:",
"manualButton": "Enable Manual Input",
"errorBanner": "Banner time missmatch! Please adjust your server on settings page. Still not working? please leave a message on Discord 😅",
"globalWishTally": "Global Wish Tally",
"globalWishTally": "Global Wish Stats",
"pityTooltip": [
"Shows your current {rarity} pity",
"{count} pulls to guaranteed {rarity}"
@ -173,8 +173,8 @@
"noNewData": "There is no new wish, please wait about 1 hour for the wish data to update!",
"success": "Import success 😀!",
"server": "Select your server:",
"wishTallyCheck": "Submit pity for global wish tally",
"wishTally": "We are doing a global wish tally! You can submit your wish tally to participate. All pity data will be aggregated to know what is the average pity of paimon.moe users.",
"wishTallyCheck": "Submit pity for global wish stats",
"wishTally": "We are doing a global wish stats! You can submit your wish stats to participate. All pity data will be aggregated to know what is the average pity of paimon.moe users.",
"wishTallyCollected": [
"What will be collected:",
"and",
@ -205,9 +205,9 @@
],
"q5": "Do you store my temporary key or wish history?",
"a5": [
"Paimon.moe will never store your keys, and uses HTTPS to pass your URL to a CORS proxy to make the CORS work. Paimon.moe will save your 4* pity, 5* pity, and 5* wish information if you submit your wish for our global wish tally (no private information saved! please check",
"Paimon.moe will never store your keys, and uses HTTPS to pass your URL to a CORS proxy to make the CORS work. Paimon.moe will save your 4* pity, 5* pity, and 5* wish information if you submit your wish for our global wish stats (no private information saved! please check",
"Privacy Policy",
"for more information). You can uncheck submit wish tally to disable wish submission. Then all your wish history is saved on your device (or your Google Drive if you turned on sync).",
"for more information). You can uncheck submit wish stats to disable wish submission. Then all your wish history is saved on your device (or your Google Drive if you turned on sync).",
"If you don't want any passing around your url, you can use the small importer script to process the wish history on your local PC (PC Local option) "
],
"q6": "I've done all the steps, but I got some API error?",
@ -355,14 +355,14 @@
"exporting": "Exporting...",
"import": "Import From Excel",
"exportFinish": "Export success, please wait until the browser download the file!",
"wishTallyTitle": "Submit Wish Tally",
"wishTally": "We are doing a global wish tally! You can submit your wish tally to participate. All pity data will be aggregated to know what is the average pity of paimon.moe users.",
"wishTallyTitle": "Submit Wish Stats",
"wishTally": "We are doing a global wish stats! You can submit your wish stats to participate. All pity data will be aggregated to know what is the average pity of paimon.moe users.",
"wishTallyCollected": [
"What will be collected:",
"and",
"pity from your wish history"
],
"wishTallySubmit": "Submit Wish Tally",
"wishTallySubmit": "Submit Wish Stats",
"wishTallyThankyou": "Thankyou for participating!",
"manualTitle": "Manual Input Setting",
"enableManual": "Enable Manual Input",
@ -418,7 +418,7 @@
"unknown_3_star": "Unknown"
},
"tally": {
"title": "Wish Tally",
"title": "Global Wish Stats",
"subtitle": "Global average pity from paimon.moe users submission",
"update": "Updated",
"summoned": "Summoned",
@ -452,6 +452,11 @@
"backupReminder": {
"title": "All data are saved on your browser!",
"desc": "So if by any chance the browser cache got deleted, your wish history will be gone! You can enable Google Drive sync on setting to backup your data, or download the data manually there."
},
"rank": {
"level": "Your Whale Level",
"based": "Based on Global Wish Stats",
"current": "Current {banner} median is {median} pulls"
}
},
"calculator": {
@ -698,8 +703,8 @@
{
"title": "Wish Pity",
"content": [
"Paimon.moe will save 4* pity count, 5* pity count, and 5* wish information (wish time, item name, pity count) if you check 'Submit pity for global wish tally' on Wish Auto Import or manually submit it on Help & Setting menu. Paimon.moe will aggregate the data and use it to calculate the average pity for each banner on paimon.moe users. You can check it here",
"wish tally"
"Paimon.moe will save 4* pity count, 5* pity count, and 5* wish information (wish time, item name, pity count) if you check 'Submit pity for global wish stats' on Wish Auto Import or manually submit it on Help & Setting menu. Paimon.moe will aggregate the data and use it to calculate the average pity for each banner on paimon.moe users. You can check it here",
"wish stats"
]
}
],

View File

@ -452,6 +452,11 @@
"backupReminder": {
"title": "Semua data disimpan pada browser mu!",
"desc": "Jadi jika terjadi sesuatu yang menyebabkan cache browser mu terhapus, data riwayat wish mu akan hilang! Kamu bisa menyalakan Google Drive sync di setting untuk membackup datamu, atau kamu bisa mendownload secara manual datanya di sana."
},
"rank": {
"level": "Level Whale Mu",
"based": "Dari Perhitungan Pity Global",
"current": "Median {banner} saat ini {median} pull"
}
},
"calculator": {
@ -699,7 +704,7 @@
"title": "Wish Pity",
"content": [
"Paimon.moe akan menyimpan perhitungan pity 4*, perhitungan pity 5*, dan informasi wish 5* (yaitu waktu wish, nama item, angka pity) jika kamu mencentang 'Kirim pity untuk perhitungan pity global' di Auto Import Wish atau secara manual mensubmit di menu bantuan dan pengaturan. Paimon.moe akan menyatukan data-datanya kemudian menghitung pity rata-rata tiap banner dari pengguna paimon.moe. Kamu bisa mengecek nya di",
"wish tally"
"wish stats"
]
}
],

View File

@ -1,7 +1,7 @@
<script>
import { t } from 'svelte-i18n';
import { onMount, getContext } from 'svelte';
import { onMount, getContext, createEventDispatcher } from 'svelte';
import { slide } from 'svelte/transition';
import { mdiPencil, mdiStar, mdiChevronDown, mdiTableOfContents, mdiArrowUpCircle } from '@mdi/js';
import debounce from 'lodash/debounce';
@ -18,7 +18,8 @@
import dayjs from 'dayjs';
import { weaponList } from '../../data/weaponList';
import { getAccountPrefix } from '../../stores/account';
import Tooltip from '../../components/Tooltip.svelte';
const dispatch = createEventDispatcher();
let numberFormat = Intl.NumberFormat();
@ -210,6 +211,8 @@
guaranteed.legendary = counterData.guaranteed.legendary;
guaranteed.rare = counterData.guaranteed.rare;
}
dispatch('counterread', total);
}
}

View File

@ -0,0 +1,125 @@
<script>
import { mdiLoading } from '@mdi/js';
import { t } from 'svelte-i18n';
import Icon from '../../components/Icon.svelte';
export let wishTotal = {
'character-event': 0,
'weapon-event': 0,
standard: 0,
};
let current = 'character-event';
let wishCount = 0;
let total = 0;
let data = [];
let percentage = 0;
let median = '...';
let loading = true;
export async function getData() {
loading = true;
median = '...';
wishCount = wishTotal[current];
try {
const url = new URL(`${__paimon.env.API_HOST}/wish/summary`);
const query = new URLSearchParams({ banner: current });
url.search = query.toString();
const res = await fetch(url, {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
});
data = await res.json();
total = 0;
const sorted = [];
for (const item of data) {
total += item[1];
sorted.push(...new Array(item[1]).fill(item[0]));
}
median = sorted[Math.round(sorted.length / 2)];
getPercentile(wishCount);
} catch (err) {
console.error(err);
}
}
function getPercentile(value) {
let totalLower = 0;
for (const item of data) {
const qty = item[0];
const amount = item[1];
totalLower += amount;
if (qty >= value) break;
}
percentage = (totalLower / total) * 100;
console.log(totalLower, percentage, value);
loading = false;
}
function change(type) {
current = type;
getData();
}
</script>
<div class="flex flex-col items-center bg-item rounded-xl p-4 w-full mt-4">
<div class="flex -m-1 pb-5">
<button class="pill m-1 {current === 'character-event' ? 'active' : ''}" on:click={() => change('character-event')}>
{$t('wish.detail.character')}
</button>
<button class="pill m-1 {current === 'weapon-event' ? 'active' : ''}" on:click={() => change('weapon-event')}>
{$t('wish.detail.weapon')}
</button>
<button class="pill m-1 {current === 'standard' ? 'active' : ''}" on:click={() => change('standard')}>
{$t('wish.types.standard')}
</button>
</div>
<div class="flex flex-row items-end">
<div class="flex flex-col text-white font-black text-4xl pr-4">
<span class="text-sm text-left text-gray-400" style="line-height: 0.7;">TOP</span>
<span class="text-right leading-none">{loading ? '...' : percentage.toFixed(0)}%</span>
</div>
<div class="flex flex-col text-white" style="margin-bottom: 2px;">
<p>{$t('wish.rank.level')}</p>
<p class="text-sm text-gray-400 leading-none">{$t('wish.rank.based')}</p>
</div>
</div>
<p class="text-sm text-gray-400 mt-2 text-center">
{$t('wish.rank.current', { values: { median, banner: $t(`wish.types.${current}`) } })}
</p>
</div>
<style>
.pill {
@apply text-sm;
@apply rounded-2xl;
@apply border-2;
@apply border-white;
@apply border-opacity-25;
@apply text-white;
@apply px-4;
@apply py-1;
@apply outline-none;
@apply transition;
@apply duration-100;
&:hover {
@apply border-primary;
}
&.active {
@apply bg-primary;
@apply border-primary;
@apply text-background;
}
}
</style>

View File

@ -16,12 +16,14 @@
const hue = percentage * 120;
return `color: hsl(${hue}, 100%, 60%);`;
}
$: textSize = avg.legendary.total > 20 ? 'text-sm' : 'text-base';
</script>
<div class="bg-item rounded-xl p-4 flex flex-col w-full" style="height: min-content;">
<table>
<tr>
<td class="text-white text-md font-semibold pr-2 md:pr-4 flex-1 w-full">{type.name}</td>
<td class="text-white text-md font-semibold pr-2 md:pr-4 flex-1 w-full">{$t(`wish.types.${type.id}`)}</td>
<td class="text-gray-400 text-sm font-display pr-2 md:pr-4 text-right">Total</td>
<td class="text-gray-400 text-sm font-display pr-2 md:pr-4 text-right">Percent</td>
<td class="text-gray-400 text-sm font-display text-right whitespace-no-wrap">Pity AVG</td>
@ -82,7 +84,9 @@
{#if avg.legendary.pulls.length > 0}
<div class="flex flex-wrap mt-2 overflow-y-auto" style="max-height: 500px;">
{#each avg.legendary.pulls as pull}
<span class="pity">{$t(pull.name)} <span style={calculateColor((90 - pull.pity) / 90)}>{pull.pity}</span></span>
<span class="pity {textSize}"
>{$t(pull.name)} <span style={calculateColor((90 - pull.pity) / 90)}>{pull.pity}</span></span
>
{/each}
</div>
{/if}

View File

@ -6,11 +6,11 @@
import Button from '../../components/Button.svelte';
import Icon from '../../components/Icon.svelte';
import ImportModal from '../../components/WishImportModal.svelte';
import { fromRemote, readSave, updateSave } from '../../stores/saveManager';
import Summary from './_summary.svelte';
import Counter from './_counter.svelte';
import Rank from './_rank.svelte';
import FirstTimePopup from './_firstTime.svelte';
import MonthlyGraph from './_monthlyGraph.svelte';
import HowToModal from './_helpModal.svelte';
@ -25,6 +25,13 @@
let counter2;
let counter3;
let counter4;
let rank;
let wishTotal = {
'character-event': 0,
'weapon-event': 0,
standard: 0,
};
const path = 'wish-counter-setting';
@ -102,6 +109,14 @@
);
}
function setRankWishTotal(type, event) {
wishTotal[type] = event.detail;
if (type === 'character-event') {
rank.getData();
}
}
function closeImportModal() {
closeModal();
counter1.readLocalData();
@ -175,18 +190,37 @@
<Ad type="mobile" variant="mpu" id="2" />
</div>
<div class="grid gap-4 grid-cols-1 md:grid-cols-2 xl:grid-cols-3 max-w-screen-xl">
<Counter bind:this={counter1} manualInput={settings.manualInput} id="character-event" name="Character Event" />
<Counter
on:counterread={(val) => setRankWishTotal('character-event', val)}
bind:this={counter1}
manualInput={settings.manualInput}
id="character-event"
name={$t('wish.types.character-event')}
/>
<Counter
on:counterread={(val) => setRankWishTotal('weapon-event', val)}
bind:this={counter2}
manualInput={settings.manualInput}
id="weapon-event"
name="Weapon Event"
name={$t('wish.types.weapon-event')}
legendaryPity={80}
/>
<Counter bind:this={counter3} manualInput={settings.manualInput} id="standard" name="Standard" />
<Counter
on:counterread={(val) => setRankWishTotal('standard', val)}
bind:this={counter3}
manualInput={settings.manualInput}
id="standard"
name={$t('wish.types.standard')}
/>
<div class="flex flex-col w-full">
<Counter bind:this={counter4} manualInput={settings.manualInput} id="beginners" name="Beginners' Wish" />
<Counter
bind:this={counter4}
manualInput={settings.manualInput}
id="beginners"
name={$t('wish.types.beginners')}
/>
<MonthlyGraph bind:data={monthlyData} />
<Rank bind:this={rank} {wishTotal} />
<div class="mt-4 flex justify-center">
<Ad type="mobile" variant="mpu" id="1" />
</div>