Skip to content
This repository has been archived by the owner on Apr 4, 2024. It is now read-only.

Commit

Permalink
feat(*): optimize code and add tetr.io personal account bind and hist…
Browse files Browse the repository at this point in the history
…ory query
  • Loading branch information
WOSHIZHAZHA120 committed Feb 28, 2024
1 parent 4fbfe0e commit 6c65f22
Show file tree
Hide file tree
Showing 12 changed files with 455 additions and 115 deletions.
4 changes: 2 additions & 2 deletions src/components/app/menus/left.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts" setup>
import { HomeOutlined, UserOutlined } from '@vicons/antd'
import { type MenuOption } from 'naive-ui'
import { isDefined, merge } from 'remeda'
import { merge } from 'remeda'
const user = useSupabaseUser()
Expand All @@ -12,7 +12,7 @@ const menus = computed<MenuOption[]>(() => {
children: [
createRouteMenuWithClassIcon('段位', 'tetrio-ranks', 'i-ic:outline-leaderboard'),
merge(createRouteMenu('', 'tetrio-me', UserOutlined), {
disabled: !isDefined(user.value)
disabled: user.value === null
})
]
})
Expand Down
25 changes: 21 additions & 4 deletions src/components/app/menus/right.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
<script lang="ts" setup>
import { LoginOutlined, LogoutOutlined, UserOutlined } from '@vicons/antd'
import { type MenuOption } from 'naive-ui'
import { isDefined, merge } from 'remeda'
import { merge } from 'remeda'
import type { Database } from '~/types/supabase'
const supabase = useSupabaseClient<Database>()
const user = useSupabaseUser()
const logoutWaiting = ref(false)
const menus = computed<MenuOption[]>(() => {
if (isDefined(user.value)) {
if (user.value !== null) {
return [
merge(createMenu(user.value.email ?? '未知', UserOutlined), {
children: [
createMenu('登出', LogoutOutlined)
merge(createMenu('登出', LogoutOutlined), {
disabled: logoutWaiting.value,
callback: async () => {
logoutWaiting.value = true
await supabase.auth.signOut()
logoutWaiting.value = false
}
})
]
})
]
Expand All @@ -20,10 +31,16 @@ const menus = computed<MenuOption[]>(() => {
]
}
})
const handleUpdate = (_key: string, option: MenuOption) => {
if ('callback' in option && typeof option.callback === 'function') {
option.callback()
}
}
</script>

<template>
<div class="w-fit">
<n-menu :options="menus" :value="$route.name" mode="horizontal"/>
<n-menu :options="menus" :value="$route.name" mode="horizontal" @update:value="handleUpdate"/>
</div>
</template>
119 changes: 119 additions & 0 deletions src/components/tetrio/me/bound.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<script lang="ts" setup>
import { useMessage } from 'naive-ui'
import { isEmpty } from 'remeda/dist/es'
import type { Database } from '~/types/supabase'
const props = defineProps<{
readonly id?: string
readonly name?: string
}>()
const name = ref<string>(props.name ?? '')
const waiting = ref(false)
const available = computed(() => {
return isEmpty(props.name) && !isEmpty(name.value) && !waiting.value
})
const supabase = useSupabaseClient<Database>()
const user = useSupabaseUser()
const $message = useMessage()
const save = async () => {
waiting.value = true
if (user.value === null) {
$message.error('请先登录')
return
}
const already_used = await supabase.from('tetrio_bindings')
.select()
.eq('tetrio_name', name.value)
.then(result => {
if (result.error !== null) {
$message.error('发生了一个错误, 请到控制台查看')
console.error(result.error)
}
if (result.data === null) {
return false
}
return result.data.length > 0
})
if (already_used) {
$message.error('这个用户名已经被其他人绑定了')
return
}
const result = await supabase.functions.invoke('tetrio_profile', {
method: 'POST',
body: JSON.stringify({
name: name.value
})
})
if (result.error !== null) {
$message.error('发生了一个错误, 请到控制台查看')
console.error(result.error)
}
if (result.data !== null && 'data' in result.data) {
await supabase.from('tetrio_bindings')
.upsert({
id: user.value.id,
tetrio_id: result.data.data.id,
tetrio_name: name.value
})
.then(result => {
if (result.error !== null) {
$message.error('发生了一个错误, 请到控制台查看')
console.error(result.error)
}
})
}
waiting.value = false
}
const bound = computed(() => {
return !isEmpty(props.id) && !isEmpty(props.name)
})
</script>

<template>
<n-card class="sm:w-1/2" size="small" title="账号绑定">
<template v-if="!bound">
<n-flex :size="0" class="leading-tight" vertical>
<n-input v-model:value="name" :disabled="props.name !== undefined" placeholder="tetr.io 用户名"/>
<n-text :depth="3" class="text-sm">{{ id }}</n-text>
</n-flex>

</template>

<template v-else>
<n-flex :size="0" class="text-center leading-tight" vertical>
<n-text class="text-2xl fw-bold">{{ name }}</n-text>
<n-text :depth="3" class="text-xl fw-bold">{{ id }}</n-text>
</n-flex>
</template>

<template #action>
<n-flex justify="end">
<template v-if="!bound">
<n-button :disabled="!available" :loading="waiting" @click="save">保存</n-button>
</template>

<template v-else>
<n-flex :size="5" justify="end">
<n-text>如需更改绑定请联系</n-text>
<n-button href="https://zhazha120.cn" tag="a" text type="primary">渣渣120</n-button>
</n-flex>
</template>
</n-flex>
</template>
</n-card>
</template>
104 changes: 104 additions & 0 deletions src/components/tetrio/me/chart.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<script lang="ts" setup>
import { asyncComputed } from '@vueuse/core'
import { LineChart, type LineSeriesOption } from 'echarts/charts'
import {
GridComponent,
type GridComponentOption,
LegendComponent,
type LegendComponentOption,
TitleComponent,
type TitleComponentOption,
ToolboxComponent,
type ToolboxComponentOption,
TooltipComponent,
type TooltipComponentOption
} from 'echarts/components'
import { type ComposeOption, use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import { useOsTheme } from 'naive-ui'
import { map, pipe, prop, uniq } from 'remeda'
import VChart, { THEME_KEY } from 'vue-echarts'
import type { Database } from '~/types/supabase'
const props = defineProps<{
readonly name: string
readonly records: Database['public']['Tables']['tetrio_players']['Row'][]
readonly type: 'tr' | 'apm' | 'pps' | 'vs'
readonly title: string
}>()
use([
TitleComponent,
TooltipComponent,
LegendComponent,
ToolboxComponent,
GridComponent,
LineChart,
CanvasRenderer
])
if (useOsTheme().value === 'dark') {
provide(THEME_KEY, 'dark')
}
const chart = asyncComputed(async () => {
return {
title: {
text: props.title
},
tooltip: {
trigger: 'axis'
},
legend: {
data: pipe(
props.records,
map(prop('name')),
uniq()
)
},
toolbox: {
feature: {
saveAsImage: {}
}
},
xAxis: {
type: 'category',
data: pipe(
props.records,
map(record => {
const date = new Date(record.record_at)
date.setMinutes(0)
date.setSeconds(0)
return date.toLocaleString()
}),
uniq()
)
},
yAxis: {
type: 'value',
min: 'dataMin',
max: 'dataMax'
},
series: {
type: 'line',
name: props.name,
data: map(props.records, record => {
return record[props.type].toFixed(2)
}),
endLabel: {
show: true
}
} as LineSeriesOption
} as ComposeOption<TitleComponentOption | TooltipComponentOption | LegendComponentOption | ToolboxComponentOption | GridComponentOption | LineSeriesOption>
})
</script>

<template>
<div class="overflow-auto">
<div class="mx-auto w-300 h-250">
<v-chart :option="chart"/>
</div>
</div>
</template>
Loading

0 comments on commit 6c65f22

Please sign in to comment.