Skip to content

Commit 61747c3

Browse files
authored
refactor(asset): full rewrite asset logic (#73)
* refactor(asset): full rewrite asset logic
1 parent 8869923 commit 61747c3

File tree

12 files changed

+249
-252
lines changed

12 files changed

+249
-252
lines changed

.changes/refactor-asset.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"algohub": patch:feat
3+
---
4+
5+
Full rewrite asset logic, use latest api and support get assets by api.

src-tauri/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/UniversalToolBar.vue

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,8 @@ const toString = (value: any) => {
128128
<img :src="themeStore.dark ? '/acm-light.png' : '/acm.png'" width="40"></img>
129129
<Breadcrumb v-if="path?.length" :model="path" class="!bg-transparent !p-0">
130130
<template #item="{ item }">
131-
<Button v-if="item.link" v-ripple @click="router.push(item.link)" :icon="item.icon" :label="toString(item.label)" size="small" plain
132-
text></Button>
131+
<Button v-if="item.link" v-ripple @click="router.push(item.link)" :icon="item.icon"
132+
:label="toString(item.label)" size="small" plain text></Button>
133133
<a v-else :href="item.link" class="px-2">
134134
<span v-if="item.icon" class="item.icon"></span>
135135
<span class="text-sm">{{ item.label }}</span>
@@ -140,12 +140,13 @@ const toString = (value: any) => {
140140
<Skeleton v-else class="ml-2 my-1" width="100px" height="20px"></Skeleton>
141141
</div>
142142
<div class="inline-flex justify-center items-center gap-3">
143-
<Button v-ripple @click="toggleCreateMenu" aria-haspopup="true" aria-controls="overlay_menu" plain outlined>
143+
<Button v-if="accountStore.isLoggedIn" v-ripple @click="toggleCreateMenu" aria-haspopup="true"
144+
aria-controls="overlay_menu" plain outlined>
144145
<span class="pi pi-plus" data-pc-section="icon"></span>
145146
<span class="w-0" data-pc-section="label">&nbsp;</span>
146147
<i class="pi pi-angle-down"></i>
147148
</Button>
148-
<Menu ref="menu" id="overlay_menu" :model="createMenuItems" :popup="true">
149+
<Menu v-if="accountStore.isLoggedIn" ref="menu" id="overlay_menu" :model="createMenuItems" :popup="true">
149150
<template #submenuitem="{ item }">
150151
<span class="font-bold">{{ item.label }}</span>
151152
</template>
@@ -156,12 +157,13 @@ const toString = (value: any) => {
156157
</a>
157158
</template>
158159
</Menu>
159-
<Button v-ripple @click="router.push(`/account/${accountStore.account?.username}?tab=problems`)"
160-
icon="pi pi-book" plain outlined></Button>
160+
<Button v-if="accountStore.isLoggedIn" v-ripple
161+
@click="router.push(`/account/${accountStore.account?.username}?tab=problems`)" icon="pi pi-book" plain
162+
outlined></Button>
161163
<Button v-ripple @click="themeStore.toggle" :icon="`pi pi-${themeStore.dark ? 'moon' : 'sun'}`" plain
162164
outlined></Button>
163-
<Divider layout="vertical" class="!mx-1"></Divider>
164-
<Avatar v-if="accountStore.avatarUrl" @click="isShowUserPanel = !isShowUserPanel"
165+
<Divider v-if="accountStore.isLoggedIn" layout="vertical" class="!mx-1"></Divider>
166+
<Avatar v-if="accountStore.isLoggedIn && accountStore.avatarUrl" @click="isShowUserPanel = !isShowUserPanel"
165167
:image="accountStore.avatarUrl" class="!cursor-pointer" shape="circle">
166168
</Avatar>
167169
<Avatar v-else-if="accountStore.isLoggedIn" @click="isShowUserPanel = !isShowUserPanel"

src/scripts/api.ts

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import { AxiosError } from "axios";
22
import axios from "@/scripts/axios";
33
import { handleAxiosError } from "@/scripts/utils";
4-
import type { Credentials, ProblemDetail, Profile } from "./types";
4+
import type {
5+
CreateAsset,
6+
Credentials,
7+
ProblemDetail,
8+
Profile,
9+
UserContent,
10+
} from "./types";
511

612
export interface Response<D> {
713
success: boolean;
@@ -30,25 +36,14 @@ export const register = async (form: Register) => {
3036
}
3137
};
3238

33-
interface Upload {
34-
id: string;
35-
token: string;
36-
file: File;
37-
}
38-
39-
interface UploadResponse {
40-
uri: string;
41-
path: string;
42-
}
43-
44-
export const uploadContent = async (form: Upload) => {
39+
export const uploadContent = async (form: CreateAsset) => {
4540
try {
46-
const response = await axios.put("/account/content/upload", form, {
41+
const response = await axios.put("/asset/upload", form, {
4742
headers: {
4843
"Content-Type": "multipart/form-data",
4944
},
5045
});
51-
return response.data as Response<UploadResponse>;
46+
return response.data as Response<UserContent>;
5247
} catch (error) {
5348
return handleAxiosError(AxiosError.from(error));
5449
}
@@ -69,12 +64,12 @@ export const updateProfile = async (form: ProfileForm) => {
6964
}
7065
};
7166

72-
interface LoginForm {
67+
interface Login {
7368
identity: string;
7469
password: string;
7570
}
7671

77-
export const login = async (form: LoginForm) => {
72+
export const login = async (form: Login) => {
7873
try {
7974
const response = await axios.post("/account/login", form);
8075
return response.data as Response<Credentials>;
@@ -92,7 +87,7 @@ export const fetchProfile = async (id: string) => {
9287
}
9388
};
9489

95-
interface ProblemForm {
90+
interface CreateProblem {
9691
id: string;
9792
token: string;
9893

@@ -114,7 +109,7 @@ interface ProblemResponse {
114109
id: string;
115110
}
116111

117-
export const createProblem = async (form: ProblemForm) => {
112+
export const createProblem = async (form: CreateProblem) => {
118113
try {
119114
const response = await axios.post("/problem/create", form);
120115
return response.data as Response<ProblemResponse>;

src/scripts/store.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { defineStore } from "pinia";
22
import { computed, ref } from "vue";
3-
import { expandUrl } from "./utils";
4-
import { Credentials } from "./types";
3+
import { expandAssetUrl } from "./utils";
4+
import { Credentials, RecordId } from "./types";
55

66
const prefersDarkMode = () => {
77
return (
@@ -88,7 +88,12 @@ export const useAccountStore = defineStore(
8888
}
8989
: undefined;
9090
});
91-
const avatarUrl = computed(() => expandUrl(account?.value?.avatar));
91+
const avatarUrl = computed<string | undefined>(
92+
() => account.value?.avatar && expandAssetUrl(account.value?.avatar)
93+
);
94+
const recordId = computed<RecordId>(() => {
95+
return { tb: "account", id: account.value?.id! };
96+
});
9297

9398
const mergeProfile = (profile: Partial<Account>) => {
9499
if (account.value) {
@@ -100,7 +105,15 @@ export const useAccountStore = defineStore(
100105
account.value = {};
101106
};
102107

103-
return { account, auth, avatarUrl, isLoggedIn, mergeProfile, logout };
108+
return {
109+
account,
110+
auth,
111+
avatarUrl,
112+
recordId,
113+
isLoggedIn,
114+
mergeProfile,
115+
logout,
116+
};
104117
},
105118
{
106119
persist: true,

src/scripts/types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@ export interface Profile {
3535
rating: number;
3636
}
3737

38+
export interface CreateAsset {
39+
auth: Credentials;
40+
owner: string;
41+
file: File;
42+
}
43+
44+
export interface UserContent {
45+
id: string;
46+
}
47+
3848
export interface ProblemDetail {
3949
id: RecordId;
4050
title: string;

src/scripts/utils.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,7 @@ export const withoutHeadSlash = (url: string) => {
3030
export const expandUrl = (url?: string) => {
3131
return config.base + withoutHeadSlash(url ?? "");
3232
};
33+
34+
export const expandAssetUrl = (url: string) => {
35+
return config.base + withoutHeadSlash(`asset/${url}`);
36+
};

src/views/account/[id].vue

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import * as api from "@/scripts/api";
33
import { useAccountStore, useThemeStore } from "@/scripts/store";
44
import { timeAgo } from "@/scripts/time";
55
import { ProblemDetail, type Profile } from "@/scripts/types";
6-
import { expandUrl } from "@/scripts/utils";
7-
import { useToast } from "primevue";
6+
import { expandAssetUrl } from "@/scripts/utils";
7+
import { Avatar, useToast } from "primevue";
88
import { onMounted, ref, watch } from "vue";
99
import { useRoute, useRouter } from "vue-router";
1010
@@ -96,8 +96,12 @@ onMounted(async () => {
9696
<div class="w-full max-w-[1200px] flex flex-col md:flex-row my-[2em] gap-6 mx-8">
9797
<div v-if="!loading && profile" class="flex flex-col h-full">
9898
<div class="flex gap-4 sm:flex-col sm:gap-1">
99-
<img class="w-[8em] h-[8em] sm:w-[18em] sm:h-[18em] rounded-full border-[2px] border-zinc-300 dark:border-zinc-700"
100-
:src="expandUrl(profile!.avatar)"></img>
99+
<img v-if="profile?.avatar"
100+
class="w-[8em] h-[8em] sm:w-[18em] sm:h-[18em] rounded-full border-[2px] border-zinc-300 dark:border-zinc-700"
101+
:src="expandAssetUrl(profile.avatar)"></img>
102+
<Avatar pt:label:class="text-4xl sm:text-9xl" :label="(profile?.nickname ?? '?')[0]" v-else
103+
class="!w-[8em] !h-[8em] sm:!w-[18em] sm:!h-[18em] !rounded-full border-[2px] border-zinc-300 dark:border-zinc-700">
104+
</Avatar>
101105
<div class="flex flex-col items-start justify-center">
102106
<h3 class="text-2xl font-bold">{{ profile.nickname }}</h3>
103107
<span class="text-lg text-gray-500">{{ profile.username }} · {{ profile.sex ?
@@ -112,15 +116,15 @@ onMounted(async () => {
112116
<i class="pi pi-envelope text-gray-500"></i>
113117
<span>{{ profile.email }}</span>
114118
</div>
115-
<div class="inline-flex items-center gap-2">
119+
<div v-if="profile.school" class="inline-flex items-center gap-2">
116120
<i class="pi pi-building text-gray-500"></i>
117121
<span>{{ profile.school }}</span>
118122
</div>
119-
<div class="inline-flex items-center gap-2">
123+
<div v-if="profile.college" class="inline-flex items-center gap-2">
120124
<i class="pi pi-building-columns text-gray-500"></i>
121125
<span>{{ profile.college }}</span>
122126
</div>
123-
<div class="inline-flex items-center gap-2">
127+
<div v-if="profile.major" class="inline-flex items-center gap-2">
124128
<i class="pi pi-graduation-cap text-gray-500"></i>
125129
<span>{{ profile.major }}</span>
126130
</div>

src/views/contest/create.vue

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -141,45 +141,6 @@ const onSelectedFiles = (event: FileUploadSelectEvent) => {
141141
});
142142
};
143143
144-
const uploadTestCases = async (callback: () => void) => {
145-
normalizedFiles.value.forEach(async (fileTuple) => {
146-
if (!fileTuple.input) {
147-
return toast.add({ severity: 'error', summary: 'Error', detail: 'Input file not found for ' + fileTuple.output?.name, life: 3000 });
148-
} else if (!fileTuple.output) {
149-
return toast.add({ severity: 'error', summary: 'Error', detail: 'Output file not found for ' + fileTuple.input?.name, life: 3000 });
150-
}
151-
152-
const res = await api.uploadContent({
153-
id: accountStore.account.id!,
154-
token: accountStore.account.token!,
155-
file: fileTuple.input,
156-
})
157-
if (!res.success) {
158-
return toast.add({ severity: 'error', summary: 'Error', detail: res.message, life: 3000 });
159-
} else {
160-
totalUploadedSize.value += parseInt(formatSize(fileTuple.input.size));
161-
}
162-
163-
const outputRes = await api.uploadContent({
164-
id: accountStore.account.id!,
165-
token: accountStore.account.token!,
166-
file: fileTuple.output,
167-
})
168-
if (!outputRes.success) {
169-
return toast.add({ severity: 'error', summary: 'Error', detail: outputRes.message, life: 3000 });
170-
} else {
171-
totalUploadedSize.value += parseInt(formatSize(fileTuple.output.size));
172-
}
173-
174-
testCases.push({
175-
input: res.data!.path,
176-
output: outputRes.data!.path,
177-
})
178-
normalizedFiles.value.splice(normalizedFiles.value.indexOf(fileTuple), 1);
179-
});
180-
callback();
181-
}
182-
183144
const normalizeFiles = (files: File[]) => {
184145
const normalizedFiles: { input?: File, output?: File }[] = [];
185146

src/views/dashboard.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ onMounted(async () => {
2727
if (!accountStore.isLoggedIn) return;
2828
const profile = await api.fetchProfile(accountStore.account.id!);
2929
if (!profile.success) {
30+
accountStore.logout();
3031
return toast.add({ severity: 'error', summary: 'Error', detail: profile.message, life: 3000 });
3132
}
3233
accountStore.mergeProfile(profile.data!);

0 commit comments

Comments
 (0)