You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

193 lines
6.5 KiB

import { defineStore } from 'pinia'
import { authApi, profileApi } from '@/api/auth'
import { courseApi } from '@/api/courses'
import { activityApi } from '@/api/activities'
import type { ApiUser } from '@/types/api'
import type { UserProfile } from '@/types'
import { clearToken, setToken } from '@/utils/request'
import { DEFAULT_AVATAR_SRC } from '@/utils/svg'
import { CAN_SEE_RADAR_STORAGE_KEY } from '@/stores/tabbar'
const STORAGE_KEYS = {
profile: 'slakeProfile',
}
function syncRadarTabPermission(user: ApiUser | null) {
uni.setStorageSync(CAN_SEE_RADAR_STORAGE_KEY, Boolean(user?.can_see_radar))
}
function readJson<T>(key: string, fallback: T): T {
try {
const raw = uni.getStorageSync(key)
if (!raw) return fallback
return typeof raw === 'string' ? JSON.parse(raw) : raw
} catch {
return fallback
}
}
export const useUserStore = defineStore('user', {
state: () => ({
user: null as ApiUser | null,
profile: readJson<UserProfile>(STORAGE_KEYS.profile, {}),
signedCourseIds: [] as number[],
signedActivityIds: [] as number[],
authReady: false,
_authInitPromise: null as Promise<void> | null,
}),
getters: {
isLoggedIn: (state) => Boolean(state.user),
isPartner: (state) => Boolean(state.user?.is_partner),
displayName: (state) => {
if (!state.user) return '未登录'
return state.user.name || state.user.nickname || state.profile.name || '微信用户'
},
avatarSrc: (state) => {
if (!state.user) return DEFAULT_AVATAR_SRC
return state.user.avatar_url || state.profile.avatar || DEFAULT_AVATAR_SRC
},
canSeeRadar: (state) => Boolean(state.user?.can_see_radar),
canSeePapers: (state) => Boolean(state.user?.can_see_papers),
isStaff: (state) => Boolean(state.user?.is_admin || state.user?.is_grid_member),
},
actions: {
async loginWithWechat(code: string, profile?: { nickname?: string; avatar_url?: string }) {
const data = await authApi.wechatLogin({ code, ...profile })
this.applyAuth(data.token, data.user)
},
async loginWithDev(profile?: { nickname?: string; avatar_url?: string }) {
const data = await authApi.devLogin(profile)
this.applyAuth(data.token, data.user)
},
applyAuth(token: string, user: ApiUser) {
setToken(token)
this.user = user
this.profile = {
...this.profile,
name: user.name || user.nickname || this.profile.name,
avatar: user.avatar_url || this.profile.avatar,
company: user.company || this.profile.company,
mobile: user.mobile || this.profile.mobile,
title: user.job_title || this.profile.title,
research_direction: user.research_direction || this.profile.research_direction,
research_direction_ids: user.research_direction_ids || this.profile.research_direction_ids,
custom_research_directions: [],
}
uni.setStorageSync(STORAGE_KEYS.profile, JSON.stringify(this.profile))
syncRadarTabPermission(user)
},
async fetchMe() {
const user = await authApi.me()
this.user = user
this.profile = {
...this.profile,
name: user.name || user.nickname || this.profile.name,
avatar: user.avatar_url || this.profile.avatar,
company: user.company || this.profile.company,
mobile: user.mobile || this.profile.mobile,
title: user.job_title || this.profile.title,
research_direction: user.research_direction || this.profile.research_direction,
research_direction_ids: user.research_direction_ids || this.profile.research_direction_ids,
custom_research_directions: [],
}
uni.setStorageSync(STORAGE_KEYS.profile, JSON.stringify(this.profile))
syncRadarTabPermission(user)
await this.syncSignups()
},
async syncSignups() {
if (!this.isLoggedIn) return
const [courses, activities] = await Promise.all([
courseApi.mySignups(),
activityApi.mySignups(),
])
this.signedCourseIds = courses.items.map((item) => item.id)
this.signedActivityIds = activities.items.map((item) => item.id)
},
async logout() {
try {
await authApi.logout()
} catch {
// ignore
}
this.user = null
this.signedCourseIds = []
this.signedActivityIds = []
syncRadarTabPermission(null)
clearToken()
},
async saveProfile(profile: UserProfile) {
let user = this.user
if (this.isLoggedIn) {
user = await profileApi.update({
name: profile.name,
mobile: profile.mobile,
company: profile.company,
avatar_url: profile.avatar,
nickname: profile.name,
job_title: profile.title,
research_direction_ids: profile.research_direction_ids,
custom_research_directions: profile.custom_research_directions,
})
this.user = user
}
this.profile = {
...this.profile,
...profile,
research_direction: user?.research_direction || this.profile.research_direction,
research_direction_ids: user?.research_direction_ids || profile.research_direction_ids,
custom_research_directions: [],
}
uni.setStorageSync(STORAGE_KEYS.profile, JSON.stringify(this.profile))
},
markCourseSigned(id: number) {
if (!this.signedCourseIds.includes(id)) {
this.signedCourseIds.push(id)
}
},
markActivitySigned(id: number) {
if (!this.signedActivityIds.includes(id)) {
this.signedActivityIds.push(id)
}
},
hasSignedCourse(id: number) {
return this.signedCourseIds.includes(id)
},
hasSignedActivity(id: number) {
return this.signedActivityIds.includes(id)
},
async ensureAuth() {
const token = uni.getStorageSync('slake_miniapp_token')
if (!token) {
this.user = null
this.authReady = true
return
}
if (this.user) {
this.authReady = true
return
}
if (!this._authInitPromise) {
this._authInitPromise = this.fetchMe()
.then(() => {
this.authReady = true
})
.catch(() => {
clearToken()
this.user = null
this.profile = {}
uni.removeStorageSync(STORAGE_KEYS.profile)
this.authReady = true
})
.finally(() => {
this._authInitPromise = null
})
}
await this._authInitPromise
},
async bootstrap() {
await this.ensureAuth()
},
},
})