Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/refactor-asset.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"algohub": patch:feat
---

Full rewrite asset logic, use latest api and support get assets by api.
2 changes: 1 addition & 1 deletion src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 10 additions & 8 deletions src/components/UniversalToolBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@ const toString = (value: any) => {
<img :src="themeStore.dark ? '/acm-light.png' : '/acm.png'" width="40"></img>
<Breadcrumb v-if="path?.length" :model="path" class="!bg-transparent !p-0">
<template #item="{ item }">
<Button v-if="item.link" v-ripple @click="router.push(item.link)" :icon="item.icon" :label="toString(item.label)" size="small" plain
text></Button>
<Button v-if="item.link" v-ripple @click="router.push(item.link)" :icon="item.icon"
:label="toString(item.label)" size="small" plain text></Button>
<a v-else :href="item.link" class="px-2">
<span v-if="item.icon" class="item.icon"></span>
<span class="text-sm">{{ item.label }}</span>
Expand All @@ -140,12 +140,13 @@ const toString = (value: any) => {
<Skeleton v-else class="ml-2 my-1" width="100px" height="20px"></Skeleton>
</div>
<div class="inline-flex justify-center items-center gap-3">
<Button v-ripple @click="toggleCreateMenu" aria-haspopup="true" aria-controls="overlay_menu" plain outlined>
<Button v-if="accountStore.isLoggedIn" v-ripple @click="toggleCreateMenu" aria-haspopup="true"
aria-controls="overlay_menu" plain outlined>
<span class="pi pi-plus" data-pc-section="icon"></span>
<span class="w-0" data-pc-section="label">&nbsp;</span>
<i class="pi pi-angle-down"></i>
</Button>
<Menu ref="menu" id="overlay_menu" :model="createMenuItems" :popup="true">
<Menu v-if="accountStore.isLoggedIn" ref="menu" id="overlay_menu" :model="createMenuItems" :popup="true">
<template #submenuitem="{ item }">
<span class="font-bold">{{ item.label }}</span>
</template>
Expand All @@ -156,12 +157,13 @@ const toString = (value: any) => {
</a>
</template>
</Menu>
<Button v-ripple @click="router.push(`/account/${accountStore.account?.username}?tab=problems`)"
icon="pi pi-book" plain outlined></Button>
<Button v-if="accountStore.isLoggedIn" v-ripple
@click="router.push(`/account/${accountStore.account?.username}?tab=problems`)" icon="pi pi-book" plain
outlined></Button>
<Button v-ripple @click="themeStore.toggle" :icon="`pi pi-${themeStore.dark ? 'moon' : 'sun'}`" plain
outlined></Button>
<Divider layout="vertical" class="!mx-1"></Divider>
<Avatar v-if="accountStore.avatarUrl" @click="isShowUserPanel = !isShowUserPanel"
<Divider v-if="accountStore.isLoggedIn" layout="vertical" class="!mx-1"></Divider>
<Avatar v-if="accountStore.isLoggedIn && accountStore.avatarUrl" @click="isShowUserPanel = !isShowUserPanel"
:image="accountStore.avatarUrl" class="!cursor-pointer" shape="circle">
</Avatar>
<Avatar v-else-if="accountStore.isLoggedIn" @click="isShowUserPanel = !isShowUserPanel"
Expand Down
33 changes: 14 additions & 19 deletions src/scripts/api.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { AxiosError } from "axios";
import axios from "@/scripts/axios";
import { handleAxiosError } from "@/scripts/utils";
import type { Credentials, ProblemDetail, Profile } from "./types";
import type {
CreateAsset,
Credentials,
ProblemDetail,
Profile,
UserContent,
} from "./types";

export interface Response<D> {
success: boolean;
Expand Down Expand Up @@ -30,25 +36,14 @@ export const register = async (form: Register) => {
}
};

interface Upload {
id: string;
token: string;
file: File;
}

interface UploadResponse {
uri: string;
path: string;
}

export const uploadContent = async (form: Upload) => {
export const uploadContent = async (form: CreateAsset) => {
try {
const response = await axios.put("/account/content/upload", form, {
const response = await axios.put("/asset/upload", form, {
headers: {
"Content-Type": "multipart/form-data",
},
});
return response.data as Response<UploadResponse>;
return response.data as Response<UserContent>;
} catch (error) {
return handleAxiosError(AxiosError.from(error));
}
Expand All @@ -69,12 +64,12 @@ export const updateProfile = async (form: ProfileForm) => {
}
};

interface LoginForm {
interface Login {
identity: string;
password: string;
}

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

interface ProblemForm {
interface CreateProblem {
id: string;
token: string;

Expand All @@ -114,7 +109,7 @@ interface ProblemResponse {
id: string;
}

export const createProblem = async (form: ProblemForm) => {
export const createProblem = async (form: CreateProblem) => {
try {
const response = await axios.post("/problem/create", form);
return response.data as Response<ProblemResponse>;
Expand Down
21 changes: 17 additions & 4 deletions src/scripts/store.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { defineStore } from "pinia";
import { computed, ref } from "vue";
import { expandUrl } from "./utils";
import { Credentials } from "./types";
import { expandAssetUrl } from "./utils";
import { Credentials, RecordId } from "./types";

const prefersDarkMode = () => {
return (
Expand Down Expand Up @@ -88,7 +88,12 @@ export const useAccountStore = defineStore(
}
: undefined;
});
const avatarUrl = computed(() => expandUrl(account?.value?.avatar));
const avatarUrl = computed<string | undefined>(
() => account.value?.avatar && expandAssetUrl(account.value?.avatar)
);
const recordId = computed<RecordId>(() => {
return { tb: "account", id: account.value?.id! };
});

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

return { account, auth, avatarUrl, isLoggedIn, mergeProfile, logout };
return {
account,
auth,
avatarUrl,
recordId,
isLoggedIn,
mergeProfile,
logout,
};
},
{
persist: true,
Expand Down
10 changes: 10 additions & 0 deletions src/scripts/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ export interface Profile {
rating: number;
}

export interface CreateAsset {
auth: Credentials;
owner: string;
file: File;
}

export interface UserContent {
id: string;
}

export interface ProblemDetail {
id: RecordId;
title: string;
Expand Down
4 changes: 4 additions & 0 deletions src/scripts/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ export const withoutHeadSlash = (url: string) => {
export const expandUrl = (url?: string) => {
return config.base + withoutHeadSlash(url ?? "");
};

export const expandAssetUrl = (url: string) => {
return config.base + withoutHeadSlash(`asset/${url}`);
};
18 changes: 11 additions & 7 deletions src/views/account/[id].vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import * as api from "@/scripts/api";
import { useAccountStore, useThemeStore } from "@/scripts/store";
import { timeAgo } from "@/scripts/time";
import { ProblemDetail, type Profile } from "@/scripts/types";
import { expandUrl } from "@/scripts/utils";
import { useToast } from "primevue";
import { expandAssetUrl } from "@/scripts/utils";
import { Avatar, useToast } from "primevue";
import { onMounted, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";

Expand Down Expand Up @@ -96,8 +96,12 @@ onMounted(async () => {
<div class="w-full max-w-[1200px] flex flex-col md:flex-row my-[2em] gap-6 mx-8">
<div v-if="!loading && profile" class="flex flex-col h-full">
<div class="flex gap-4 sm:flex-col sm:gap-1">
<img class="w-[8em] h-[8em] sm:w-[18em] sm:h-[18em] rounded-full border-[2px] border-zinc-300 dark:border-zinc-700"
:src="expandUrl(profile!.avatar)"></img>
<img v-if="profile?.avatar"
class="w-[8em] h-[8em] sm:w-[18em] sm:h-[18em] rounded-full border-[2px] border-zinc-300 dark:border-zinc-700"
:src="expandAssetUrl(profile.avatar)"></img>
<Avatar pt:label:class="text-4xl sm:text-9xl" :label="(profile?.nickname ?? '?')[0]" v-else
class="!w-[8em] !h-[8em] sm:!w-[18em] sm:!h-[18em] !rounded-full border-[2px] border-zinc-300 dark:border-zinc-700">
</Avatar>
<div class="flex flex-col items-start justify-center">
<h3 class="text-2xl font-bold">{{ profile.nickname }}</h3>
<span class="text-lg text-gray-500">{{ profile.username }} · {{ profile.sex ?
Expand All @@ -112,15 +116,15 @@ onMounted(async () => {
<i class="pi pi-envelope text-gray-500"></i>
<span>{{ profile.email }}</span>
</div>
<div class="inline-flex items-center gap-2">
<div v-if="profile.school" class="inline-flex items-center gap-2">
<i class="pi pi-building text-gray-500"></i>
<span>{{ profile.school }}</span>
</div>
<div class="inline-flex items-center gap-2">
<div v-if="profile.college" class="inline-flex items-center gap-2">
<i class="pi pi-building-columns text-gray-500"></i>
<span>{{ profile.college }}</span>
</div>
<div class="inline-flex items-center gap-2">
<div v-if="profile.major" class="inline-flex items-center gap-2">
<i class="pi pi-graduation-cap text-gray-500"></i>
<span>{{ profile.major }}</span>
</div>
Expand Down
39 changes: 0 additions & 39 deletions src/views/contest/create.vue
Original file line number Diff line number Diff line change
Expand Up @@ -141,45 +141,6 @@ const onSelectedFiles = (event: FileUploadSelectEvent) => {
});
};

const uploadTestCases = async (callback: () => void) => {
normalizedFiles.value.forEach(async (fileTuple) => {
if (!fileTuple.input) {
return toast.add({ severity: 'error', summary: 'Error', detail: 'Input file not found for ' + fileTuple.output?.name, life: 3000 });
} else if (!fileTuple.output) {
return toast.add({ severity: 'error', summary: 'Error', detail: 'Output file not found for ' + fileTuple.input?.name, life: 3000 });
}

const res = await api.uploadContent({
id: accountStore.account.id!,
token: accountStore.account.token!,
file: fileTuple.input,
})
if (!res.success) {
return toast.add({ severity: 'error', summary: 'Error', detail: res.message, life: 3000 });
} else {
totalUploadedSize.value += parseInt(formatSize(fileTuple.input.size));
}

const outputRes = await api.uploadContent({
id: accountStore.account.id!,
token: accountStore.account.token!,
file: fileTuple.output,
})
if (!outputRes.success) {
return toast.add({ severity: 'error', summary: 'Error', detail: outputRes.message, life: 3000 });
} else {
totalUploadedSize.value += parseInt(formatSize(fileTuple.output.size));
}

testCases.push({
input: res.data!.path,
output: outputRes.data!.path,
})
normalizedFiles.value.splice(normalizedFiles.value.indexOf(fileTuple), 1);
});
callback();
}

const normalizeFiles = (files: File[]) => {
const normalizedFiles: { input?: File, output?: File }[] = [];

Expand Down
1 change: 1 addition & 0 deletions src/views/dashboard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ onMounted(async () => {
if (!accountStore.isLoggedIn) return;
const profile = await api.fetchProfile(accountStore.account.id!);
if (!profile.success) {
accountStore.logout();
return toast.add({ severity: 'error', summary: 'Error', detail: profile.message, life: 3000 });
}
accountStore.mergeProfile(profile.data!);
Expand Down
12 changes: 6 additions & 6 deletions src/views/problem/create.vue
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,8 @@ const uploadTestCases = async (callback: () => void) => {
}

const res = await api.uploadContent({
id: accountStore.account.id!,
token: accountStore.account.token!,
auth: accountStore.auth!,
owner: `account:${accountStore.account.id}`,
file: fileTuple.input,
})
if (!res.success) {
Expand All @@ -156,8 +156,8 @@ const uploadTestCases = async (callback: () => void) => {
}

const outputRes = await api.uploadContent({
id: accountStore.account.id!,
token: accountStore.account.token!,
auth: accountStore.auth!,
owner: `account:${accountStore.account.id}`,
file: fileTuple.output,
})
if (!outputRes.success) {
Expand All @@ -167,8 +167,8 @@ const uploadTestCases = async (callback: () => void) => {
}

testCases.push({
input: res.data!.path,
output: outputRes.data!.path,
input: res.data!.id,
output: outputRes.data!.id,
})
normalizedFiles.value.splice(normalizedFiles.value.indexOf(fileTuple), 1);
});
Expand Down
Loading
Loading