This commit is contained in:
limepotato 2024-07-05 17:20:29 -06:00 committed by Iceshrimp development
parent 52792b706e
commit 8b4a927bce
3 changed files with 213 additions and 209 deletions

BIN
.yarn/cache/libopenmpt-wasm-https-c2e0cb89a7-ace14fcb0e.zip (Stored with Git LFS) vendored Normal file

Binary file not shown.

View file

@ -1,34 +1,34 @@
<template> <template>
<div> <div>
<div v-if="achievements" class="root"> <div v-if="achievements" :class="'.root'">
<div v-for="achievement in achievements" :key="achievement" class="_panel achievement"> <div v-for="achievement in achievements" :key="achievement" :class="'.achievement'" class="_panel">
<div class="icon"> <div :class="'.icon'">
<div :class="[['iconFrame'], ['iconFrame_' + ACHIEVEMENT_BADGES[achievement.name].frame]]"> <div :class="['.iconFrame', ['iconFrame_' + ACHIEVEMENT_BADGES[achievement.name].frame]]">
<div class="iconInner" :style="{ background: ACHIEVEMENT_BADGES[achievement.name].bg }"> <div :class="['.iconInner']" :style="{ background: ACHIEVEMENT_BADGES[achievement.name].bg }">
<img class="iconImg" :src="ACHIEVEMENT_BADGES[achievement.name].img"> <img :class="'.iconImg'" :src="ACHIEVEMENT_BADGES[achievement.name].img">
</div> </div>
</div> </div>
</div> </div>
<div class="body"> <div :class="'.body'">
<div class="header"> <div :class="'.header'">
<span class="title">{{ i18n.ts._achievements._types['_' + achievement.name].title }}</span> <span :class="'.title'">{{ i18n.ts._achievements._types['_' + achievement.name].title }}</span>
<span class="time"> <span :class="'.time'">
<time v-tooltip="new Date(achievement.unlockedAt).toLocaleString()">{{ new Date(achievement.unlockedAt).getFullYear() }}/{{ new Date(achievement.unlockedAt).getMonth() + 1 }}/{{ new Date(achievement.unlockedAt).getDate() }}</time> <time v-tooltip="new Date(achievement.unlockedAt).toLocaleString()">{{ new Date(achievement.unlockedAt).getFullYear() }}/{{ new Date(achievement.unlockedAt).getMonth() + 1 }}/{{ new Date(achievement.unlockedAt).getDate() }}</time>
</span> </span>
</div> </div>
<div class="description">{{ i18n.ts._achievements._types['_' + achievement.name].description }}</div> <div :class="'.description'">{{ i18n.ts._achievements._types['_' + achievement.name].description }}</div>
<div v-if="i18n.ts._achievements._types['_' + achievement.name].flavor" class="flavor">{{ i18n.ts._achievements._types['_' + achievement.name].flavor }}</div> <div v-if="i18n.ts._achievements._types['_' + achievement.name].flavor" :class="'.flavor'">{{ i18n.ts._achievements._types['_' + achievement.name].flavor }}</div>
</div> </div>
</div> </div>
<template v-if="withLocked"> <template v-if="withLocked">
<div v-for="achievement in lockedAchievements" :key="achievement" class="_panel achievement locked" @click="achievement === 'clickedClickHere' ? clickHere() : () => {}"> <div v-for="achievement in lockedAchievements" :key="achievement" :class="['.achievement', '.locked']" class="_panel" @click="achievement === 'clickedClickHere' ? clickHere() : () => {}">
<div class="icon"> <div :class="'.icon'">
</div> </div>
<div class="body"> <div :class="'.body'">
<div class="header"> <div :class="'.header'">
<span class="title">???</span> <span :class="'.title'">???</span>
</div> </div>
<div class="description">???</div> <div :class="'.description'">???</div>
</div> </div>
</div> </div>
</template> </template>
@ -36,27 +36,27 @@
<div v-else> <div v-else>
<MkLoading/> <MkLoading/>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import * as misskey from 'misskey-js'; import * as misskey from 'misskey-js';
import { onMounted } from 'vue'; import { onMounted } from 'vue';
import * as os from '@/os'; import * as os from '@/os';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/scripts/achievements'; import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/scripts/achievements';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
user: misskey.entities.User; user: misskey.entities.User;
withLocked: boolean; withLocked: boolean;
}>(), { }>(), {
withLocked: true, withLocked: true,
}); });
let achievements = $ref(); let achievements = $ref();
const lockedAchievements = $computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements ?? []).some(a => a.name === x))); const lockedAchievements = $computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements ?? []).some(a => a.name === x)));
function fetch() { function fetch() {
os.api('users/achievements', { userId: props.user.id }).then(res => { os.api('users/achievements', { userId: props.user.id }).then(res => {
achievements = []; achievements = [];
for (const t of ACHIEVEMENT_TYPES) { for (const t of ACHIEVEMENT_TYPES) {
@ -65,46 +65,46 @@ function fetch() {
} }
//achievements = res.sort((a, b) => b.unlockedAt - a.unlockedAt); //achievements = res.sort((a, b) => b.unlockedAt - a.unlockedAt);
}); });
} }
function clickHere() { function clickHere() {
claimAchievement('clickedClickHere'); claimAchievement('clickedClickHere');
fetch(); fetch();
} }
onMounted(() => { onMounted(() => {
fetch(); fetch();
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" module>
.root { .root {
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, min(380px, 100%)); grid-template-columns: repeat(auto-fill, min(380px, 100%));
grid-gap: 12px; grid-gap: 12px;
place-content: center; place-content: center;
} }
.achievement { .achievement {
display: flex; display: flex;
padding: 16px; padding: 16px;
&.locked { &.locked {
opacity: 0.5; opacity: 0.5;
} }
} }
.icon { .icon {
flex-shrink: 0; flex-shrink: 0;
margin-right: 12px; margin-right: 12px;
} }
@keyframes shine { @keyframes shine {
0% { translate: -30px; } 0% { translate: -30px; }
100% { translate: -130px; } 100% { translate: -130px; }
} }
.iconFrame { .iconFrame {
width: 58px; width: 58px;
height: 58px; height: 58px;
padding: 6px; padding: 6px;
@ -115,22 +115,22 @@ onMounted(() => {
filter: drop-shadow(0px 2px 2px #00000044); filter: drop-shadow(0px 2px 2px #00000044);
box-shadow: 0 1px 0px #ffffff88 inset; box-shadow: 0 1px 0px #ffffff88 inset;
overflow: clip; overflow: clip;
} }
.iconFrame_bronze { .iconFrame_bronze {
background: linear-gradient(0deg, #703827, #d37566); background: linear-gradient(0deg, #703827, #d37566);
> .iconInner { > .iconInner {
background: linear-gradient(0deg, #d37566, #703827); background: linear-gradient(0deg, #d37566, #703827);
} }
} }
.iconFrame_silver { .iconFrame_silver {
background: linear-gradient(0deg, #7c7c7c, #e1e1e1); background: linear-gradient(0deg, #7c7c7c, #e1e1e1);
> .iconInner { > .iconInner {
background: linear-gradient(0deg, #e1e1e1, #7c7c7c); background: linear-gradient(0deg, #e1e1e1, #7c7c7c);
} }
} }
.iconFrame_gold { .iconFrame_gold {
background: linear-gradient(0deg, rgba(255,182,85,1) 0%, rgba(233,133,0,1) 49%, rgba(255,243,93,1) 51%, rgba(255,187,25,1) 100%); background: linear-gradient(0deg, rgba(255,182,85,1) 0%, rgba(233,133,0,1) 49%, rgba(255,243,93,1) 51%, rgba(255,187,25,1) 100%);
> .iconInner { > .iconInner {
@ -149,8 +149,8 @@ onMounted(() => {
background: #ffffff88; background: #ffffff88;
animation: shine 2s infinite; animation: shine 2s infinite;
} }
} }
.iconFrame_platinum { .iconFrame_platinum {
background: linear-gradient(0deg, rgba(154,154,154,1) 0%, rgba(226,226,226,1) 49%, rgba(255,255,255,1) 51%, rgba(195,195,195,1) 100%); background: linear-gradient(0deg, rgba(154,154,154,1) 0%, rgba(226,226,226,1) 49%, rgba(255,255,255,1) 51%, rgba(195,195,195,1) 100%);
> .iconInner { > .iconInner {
@ -169,17 +169,17 @@ onMounted(() => {
background: #ffffffee; background: #ffffffee;
animation: shine 2s infinite; animation: shine 2s infinite;
} }
} }
.iconInner { .iconInner {
position: relative; position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: 100%; border-radius: 100%;
box-shadow: 0 1px 0px #ffffff88 inset; box-shadow: 0 1px 0px #ffffff88 inset;
} }
.iconImg { .iconImg {
width: calc(100% - 12px); width: calc(100% - 12px);
height: calc(100% - 12px); height: calc(100% - 12px);
position: absolute; position: absolute;
@ -189,36 +189,37 @@ onMounted(() => {
left: 0; left: 0;
margin: auto; margin: auto;
filter: drop-shadow(0px 1px 2px #000000aa); filter: drop-shadow(0px 1px 2px #000000aa);
} }
.body { .body {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
} }
.header { .header {
margin-bottom: 8px; margin-bottom: 8px;
display: flex; display: flex;
} }
.title { .title {
font-weight: bold; font-weight: bold;
} }
.time { .time {
margin-left: auto; margin-left: auto;
font-size: 85%; font-size: 85%;
opacity: 0.7; opacity: 0.7;
} }
.description { .description {
font-size: 85%; font-size: 85%;
} }
.flavor { .flavor {
opacity: 0.7; opacity: 0.7;
transform: skewX(-15deg); transform: skewX(-15deg);
font-size: 85%; font-size: 85%;
margin-top: 8px; margin-top: 8px;
} }
</style> </style>

View file

@ -225,7 +225,7 @@
<MkA <MkA
v-if="notification.type === 'achievementEarned'" v-if="notification.type === 'achievementEarned'"
class="text" class="text"
:to="/my/achievements"> :to="'/my/achievements'">
{{ i18n.ts._achievements._types['_' + notification.achievement].title }} {{ i18n.ts._achievements._types['_' + notification.achievement].title }}
</MkA> </MkA>
<span <span