fix: setup 迁移完成

This commit is contained in:
Tim
2025-08-13 17:59:38 +08:00
parent 0034839e8d
commit 902fce5174
11 changed files with 769 additions and 853 deletions

View File

@@ -230,7 +230,7 @@
</div>
</template>
<script>
<script setup>
import { ref, computed, onMounted, onBeforeUnmount, nextTick, watch } from 'vue'
import VueEasyLightbox from 'vue-easy-lightbox'
import { useRoute } from 'vue-router'
@@ -243,7 +243,7 @@ import ReactionsGroup from '~/components/ReactionsGroup.vue'
import DropdownMenu from '~/components/DropdownMenu.vue'
import { renderMarkdown, handleMarkdownClick, stripMarkdownLength } from '~/utils/markdown'
import { getMedalTitle } from '~/utils/medal'
import { API_BASE_URL, toast } from '~/main'
import { toast } from '~/main'
import { getToken, authState } from '~/utils/auth'
import TimeManager from '~/utils/time'
import { useRouter } from 'vue-router'
@@ -251,52 +251,38 @@ import { useIsMobile } from '~/utils/screen'
import Dropdown from '~/components/Dropdown.vue'
import { ClientOnly } from '#components'
export default {
name: 'PostPageView',
components: {
CommentItem,
CommentEditor,
BaseTimeline,
ArticleTags,
ArticleCategory,
ReactionsGroup,
DropdownMenu,
VueEasyLightbox,
Dropdown,
ClientOnly,
},
async setup() {
const route = useRoute()
const postId = route.params.id
const router = useRouter()
const config = useRuntimeConfig()
const API_BASE_URL = config.public.apiBaseUrl
const title = ref('')
const author = ref('')
const postContent = ref('')
const category = ref('')
const tags = ref([])
const postReactions = ref([])
const comments = ref([])
const status = ref('PUBLISHED')
const pinnedAt = ref(null)
const isWaitingFetchingPost = ref(false)
const isWaitingPostingComment = ref(false)
const postTime = ref('')
const postItems = ref([])
const mainContainer = ref(null)
const currentIndex = ref(1)
const subscribed = ref(false)
const commentSort = ref('NEWEST')
const isFetchingComments = ref(false)
const isMobile = useIsMobile()
const route = useRoute()
const postId = route.params.id
const router = useRouter()
const headerHeight = process.client
? parseFloat(
getComputedStyle(document.documentElement).getPropertyValue('--header-height'),
) || 0
const title = ref('')
const author = ref('')
const postContent = ref('')
const category = ref('')
const tags = ref([])
const postReactions = ref([])
const comments = ref([])
const status = ref('PUBLISHED')
const pinnedAt = ref(null)
const isWaitingFetchingPost = ref(false)
const isWaitingPostingComment = ref(false)
const postTime = ref('')
const postItems = ref([])
const mainContainer = ref(null)
const currentIndex = ref(1)
const subscribed = ref(false)
const commentSort = ref('NEWEST')
const isFetchingComments = ref(false)
const isMobile = useIsMobile()
const headerHeight = process.client
? parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--header-height')) || 0
: 0
useHead(() => ({
useHead(() => ({
title: title.value ? `OpenIsle - ${title.value}` : 'OpenIsle',
meta: [
{
@@ -304,35 +290,35 @@ export default {
content: stripMarkdownLength(postContent.value, 400),
},
],
}))
}))
if (process.client) {
if (process.client) {
onBeforeUnmount(() => {
window.removeEventListener('scroll', updateCurrentIndex)
if (countdownTimer) clearInterval(countdownTimer)
})
}
}
const lightboxVisible = ref(false)
const lightboxIndex = ref(0)
const lightboxImgs = ref([])
const loggedIn = computed(() => authState.loggedIn)
const isAdmin = computed(() => authState.role === 'ADMIN')
const isAuthor = computed(() => authState.username === author.value.username)
const lottery = ref(null)
const countdown = ref('00:00:00')
let countdownTimer = null
const lotteryParticipants = computed(() => lottery.value?.participants || [])
const lotteryWinners = computed(() => lottery.value?.winners || [])
const lotteryEnded = computed(() => {
const lightboxVisible = ref(false)
const lightboxIndex = ref(0)
const lightboxImgs = ref([])
const loggedIn = computed(() => authState.loggedIn)
const isAdmin = computed(() => authState.role === 'ADMIN')
const isAuthor = computed(() => authState.username === author.value.username)
const lottery = ref(null)
const countdown = ref('00:00:00')
let countdownTimer = null
const lotteryParticipants = computed(() => lottery.value?.participants || [])
const lotteryWinners = computed(() => lottery.value?.winners || [])
const lotteryEnded = computed(() => {
if (!lottery.value || !lottery.value.endTime) return false
return new Date(lottery.value.endTime).getTime() <= Date.now()
})
const hasJoined = computed(() => {
})
const hasJoined = computed(() => {
if (!loggedIn.value) return false
return lotteryParticipants.value.some((p) => p.id === Number(authState.userId))
})
const updateCountdown = () => {
})
const updateCountdown = () => {
if (!lottery.value || !lottery.value.endTime) {
countdown.value = '00:00:00'
return
@@ -350,15 +336,15 @@ export default {
const m = String(Math.floor((diff % 3600000) / 60000)).padStart(2, '0')
const s = String(Math.floor((diff % 60000) / 1000)).padStart(2, '0')
countdown.value = `${h}:${m}:${s}`
}
const startCountdown = () => {
}
const startCountdown = () => {
if (!process.client) return
if (countdownTimer) clearInterval(countdownTimer)
updateCountdown()
countdownTimer = setInterval(updateCountdown, 1000)
}
const gotoUser = (id) => router.push(`/users/${id}`)
const articleMenuItems = computed(() => {
}
const gotoUser = (id) => router.push(`/users/${id}`)
const articleMenuItems = computed(() => {
const items = []
if (isAuthor.value || isAdmin.value) {
items.push({ text: '编辑文章', onClick: () => editPost() })
@@ -376,9 +362,9 @@ export default {
items.push({ text: '驳回', color: 'red', onClick: () => rejectPost() })
}
return items
})
})
const gatherPostItems = () => {
const gatherPostItems = () => {
const items = []
if (mainContainer.value) {
const main = mainContainer.value.querySelector('.info-content-container')
@@ -394,9 +380,9 @@ export default {
items.sort((a, b) => a.top - b.top)
postItems.value = items.map((i) => i.el)
}
}
}
const mapComment = (c, parentUserName = '', level = 0) => ({
const mapComment = (c, parentUserName = '', level = 0) => ({
id: c.id,
userName: c.author.username,
medal: c.author.displayMedal,
@@ -410,13 +396,13 @@ export default {
src: c.author.avatar,
iconClick: () => router.push(`/users/${c.author.id}`),
parentUserName: parentUserName,
})
})
const getTop = (el) => {
const getTop = (el) => {
return el.getBoundingClientRect().top + window.scrollY
}
}
const findCommentPath = (id, list) => {
const findCommentPath = (id, list) => {
for (const item of list) {
if (item.id === Number(id) || item.id === id) {
return [item]
@@ -427,17 +413,17 @@ export default {
}
}
return null
}
}
const expandCommentPath = (id) => {
const expandCommentPath = (id) => {
const path = findCommentPath(id, comments.value)
if (!path) return
for (let i = 0; i < path.length - 1; i++) {
path[i].openReplies = true
}
}
}
const removeCommentFromList = (id, list) => {
const removeCommentFromList = (id, list) => {
for (let i = 0; i < list.length; i++) {
const item = list[i]
if (item.id === id) {
@@ -449,9 +435,9 @@ export default {
}
}
return false
}
}
const handleContentClick = (e) => {
const handleContentClick = (e) => {
handleMarkdownClick(e)
if (e.target.tagName === 'IMG') {
const container = e.target.parentNode
@@ -460,14 +446,14 @@ export default {
lightboxIndex.value = imgs.indexOf(e.target.src)
lightboxVisible.value = true
}
}
}
const onCommentDeleted = (id) => {
const onCommentDeleted = (id) => {
removeCommentFromList(Number(id), comments.value)
fetchComments()
}
}
const fetchPost = async () => {
const fetchPost = async () => {
try {
isWaitingFetchingPost.value = true
const token = getToken()
@@ -498,29 +484,29 @@ export default {
} catch (e) {
console.error(e)
}
}
}
const totalPosts = computed(() => comments.value.length + 1)
const lastReplyTime = computed(() =>
const totalPosts = computed(() => comments.value.length + 1)
const lastReplyTime = computed(() =>
comments.value.length ? comments.value[comments.value.length - 1].time : postTime.value,
)
const firstReplyTime = computed(() =>
)
const firstReplyTime = computed(() =>
comments.value.length ? comments.value[0].time : postTime.value,
)
const scrollerTopTime = computed(() =>
)
const scrollerTopTime = computed(() =>
commentSort.value === 'OLDEST' ? postTime.value : firstReplyTime.value,
)
)
watch(
watch(
() => comments.value.length,
async () => {
await nextTick()
gatherPostItems()
updateCurrentIndex()
},
)
)
const updateCurrentIndex = () => {
const updateCurrentIndex = () => {
const scrollTop = window.scrollY
for (let i = 0; i < postItems.value.length; i++) {
@@ -533,9 +519,9 @@ export default {
break
}
}
}
}
const onSliderInput = (e) => {
const onSliderInput = (e) => {
const index = Number(e.target.value)
currentIndex.value = index
const target = postItems.value[index - 1]
@@ -543,9 +529,9 @@ export default {
const top = getTop(target) - headerHeight - 20 // 20 for beauty
window.scrollTo({ top, behavior: 'auto' })
}
}
}
const postComment = async (parentUserName, text, clear) => {
const postComment = async (parentUserName, text, clear) => {
if (!text.trim()) return
console.debug('Posting comment', { postId, text })
isWaitingPostingComment.value = true
@@ -583,15 +569,15 @@ export default {
} finally {
isWaitingPostingComment.value = false
}
}
}
const copyPostLink = () => {
const copyPostLink = () => {
navigator.clipboard.writeText(location.href.split('#')[0]).then(() => {
toast.success('已复制')
})
}
}
const subscribePost = async () => {
const subscribePost = async () => {
const token = getToken()
if (!token) {
toast.error('请先登录')
@@ -607,9 +593,9 @@ export default {
} else {
toast.error('操作失败')
}
}
}
const approvePost = async () => {
const approvePost = async () => {
const token = getToken()
if (!token) return
const res = await fetch(`${API_BASE_URL}/api/admin/posts/${postId}/approve`, {
@@ -622,9 +608,9 @@ export default {
} else {
toast.error('操作失败')
}
}
}
const pinPost = async () => {
const pinPost = async () => {
const token = getToken()
if (!token) return
const res = await fetch(`${API_BASE_URL}/api/admin/posts/${postId}/pin`, {
@@ -637,9 +623,9 @@ export default {
} else {
toast.error('操作失败')
}
}
}
const unpinPost = async () => {
const unpinPost = async () => {
const token = getToken()
if (!token) return
const res = await fetch(`${API_BASE_URL}/api/admin/posts/${postId}/unpin`, {
@@ -652,13 +638,13 @@ export default {
} else {
toast.error('操作失败')
}
}
}
const editPost = () => {
const editPost = () => {
router.push(`/posts/${postId}/edit`)
}
}
const deletePost = async () => {
const deletePost = async () => {
const token = getToken()
if (!token) {
toast.error('请先登录')
@@ -674,9 +660,9 @@ export default {
} else {
toast.error('操作失败')
}
}
}
const rejectPost = async () => {
const rejectPost = async () => {
const token = getToken()
if (!token) return
const res = await fetch(`${API_BASE_URL}/api/admin/posts/${postId}/reject`, {
@@ -689,8 +675,8 @@ export default {
} else {
toast.error('操作失败')
}
}
const unsubscribePost = async () => {
}
const unsubscribePost = async () => {
const token = getToken()
if (!token) {
toast.error('请先登录')
@@ -707,9 +693,9 @@ export default {
} else {
toast.error('操作失败')
}
}
}
const joinLottery = async () => {
const joinLottery = async () => {
const token = getToken()
if (!token) {
toast.error('请先登录')
@@ -725,17 +711,17 @@ export default {
} else {
toast.error('操作失败')
}
}
}
const fetchCommentSorts = () => {
const fetchCommentSorts = () => {
return Promise.resolve([
{ id: 'NEWEST', name: '最新', icon: 'fas fa-clock' },
{ id: 'OLDEST', name: '最旧', icon: 'fas fa-hourglass-start' },
// { id: 'MOST_INTERACTIONS', name: '最多互动', icon: 'fas fa-fire' }
])
}
}
const fetchComments = async () => {
const fetchComments = async () => {
isFetchingComments.value = true
console.debug('Fetching comments', { postId, sort: commentSort.value })
try {
@@ -760,11 +746,11 @@ export default {
} finally {
isFetchingComments.value = false
}
}
}
watch(commentSort, fetchComments)
watch(commentSort, fetchComments)
const jumpToHashComment = async () => {
const jumpToHashComment = async () => {
const hash = location.hash
if (hash.startsWith('#comment-')) {
const id = hash.substring('#comment-'.length)
@@ -777,13 +763,13 @@ export default {
setTimeout(() => el.classList.remove('comment-highlight'), 4000)
}
}
}
}
const gotoProfile = () => {
const gotoProfile = () => {
router.push(`/users/${author.value.id}`)
}
}
onMounted(async () => {
onMounted(async () => {
await fetchComments()
const hash = location.hash
const id = hash.startsWith('#comment-') ? hash.substring('#comment-'.length) : null
@@ -791,69 +777,9 @@ export default {
updateCurrentIndex()
window.addEventListener('scroll', updateCurrentIndex)
jumpToHashComment()
})
})
await fetchPost()
return {
postContent,
author,
title,
category,
tags,
comments,
postTime,
scrollerTopTime,
lastReplyTime,
postItems,
mainContainer,
currentIndex,
totalPosts,
postReactions,
articleMenuItems,
postId,
postComment,
onSliderInput,
copyPostLink,
subscribePost,
unsubscribePost,
joinLottery,
renderMarkdown,
isWaitingFetchingPost,
isWaitingPostingComment,
gotoProfile,
gotoUser,
subscribed,
loggedIn,
isAuthor,
status,
isAdmin,
approvePost,
editPost,
onCommentDeleted,
deletePost,
pinPost,
unpinPost,
rejectPost,
lightboxVisible,
lightboxIndex,
lightboxImgs,
handleContentClick,
isMobile,
pinnedAt,
commentSort,
fetchCommentSorts,
isFetchingComments,
getMedalTitle,
lottery,
countdown,
lotteryParticipants,
lotteryWinners,
lotteryEnded,
hasJoined,
}
},
}
await fetchPost()
</script>
<style>
.post-page-container {

View File

@@ -296,7 +296,7 @@
</div>
</template>
<script>
<script setup>
import { computed, onMounted, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import AchievementList from '~/components/AchievementList.vue'
@@ -304,42 +304,40 @@ import BasePlaceholder from '~/components/BasePlaceholder.vue'
import BaseTimeline from '~/components/BaseTimeline.vue'
import LevelProgress from '~/components/LevelProgress.vue'
import UserList from '~/components/UserList.vue'
import { API_BASE_URL, toast } from '~/main'
import { toast } from '~/main'
import { authState, getToken } from '~/utils/auth'
import { prevLevelExp } from '~/utils/level'
import { stripMarkdown, stripMarkdownLength } from '~/utils/markdown'
import TimeManager from '~/utils/time'
const config = useRuntimeConfig()
const API_BASE_URL = config.public.apiBaseUrl
export default {
name: 'ProfileView',
components: { BaseTimeline, UserList, BasePlaceholder, LevelProgress, AchievementList },
setup() {
definePageMeta({
definePageMeta({
alias: ['/users/:id/'],
})
const route = useRoute()
const router = useRouter()
const username = route.params.id
})
const route = useRoute()
const router = useRouter()
const username = route.params.id
const user = ref({})
const hotPosts = ref([])
const hotReplies = ref([])
const hotTags = ref([])
const timelineItems = ref([])
const followers = ref([])
const followings = ref([])
const medals = ref([])
const subscribed = ref(false)
const isLoading = ref(true)
const tabLoading = ref(false)
const selectedTab = ref(
const user = ref({})
const hotPosts = ref([])
const hotReplies = ref([])
const hotTags = ref([])
const timelineItems = ref([])
const followers = ref([])
const followings = ref([])
const medals = ref([])
const subscribed = ref(false)
const isLoading = ref(true)
const tabLoading = ref(false)
const selectedTab = ref(
['summary', 'timeline', 'following', 'achievements'].includes(route.query.tab)
? route.query.tab
: 'summary',
)
const followTab = ref('followers')
)
const followTab = ref('followers')
const levelInfo = computed(() => {
const levelInfo = computed(() => {
const exp = user.value.experience || 0
const currentLevel = user.value.currentLevel || 0
const nextExp = user.value.nextLevelExp || 0
@@ -348,20 +346,20 @@ export default {
const ratio = total > 0 ? (exp - prevExp) / total : 1
const percent = Math.max(0, Math.min(1, ratio)) * 100
return { exp, currentLevel, nextExp, percent }
})
})
const isMine = computed(function () {
const isMine = computed(function () {
const mine = authState.username === username || String(authState.userId) === username
console.log(mine)
return mine
})
})
const formatDate = (d) => {
const formatDate = (d) => {
if (!d) return ''
return TimeManager.format(d)
}
}
const fetchUser = async () => {
const fetchUser = async () => {
const token = getToken()
const headers = token ? { Authorization: `Bearer ${token}` } : {}
const res = await fetch(`${API_BASE_URL}/api/users/${username}`, { headers })
@@ -372,9 +370,9 @@ export default {
} else if (res.status === 404) {
router.replace('/404')
}
}
}
const fetchSummary = async () => {
const fetchSummary = async () => {
const postsRes = await fetch(`${API_BASE_URL}/api/users/${username}/hot-posts`)
if (postsRes.ok) {
const data = await postsRes.json()
@@ -392,9 +390,9 @@ export default {
const data = await tagsRes.json()
hotTags.value = data.map((t) => ({ icon: 'fas fa-tag', tag: t }))
}
}
}
const fetchTimeline = async () => {
const fetchTimeline = async () => {
const [postsRes, repliesRes, tagsRes] = await Promise.all([
fetch(`${API_BASE_URL}/api/users/${username}/posts?limit=50`),
fetch(`${API_BASE_URL}/api/users/${username}/replies?limit=50`),
@@ -425,36 +423,36 @@ export default {
]
mapped.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt))
timelineItems.value = mapped
}
}
const fetchFollowUsers = async () => {
const fetchFollowUsers = async () => {
const [followerRes, followingRes] = await Promise.all([
fetch(`${API_BASE_URL}/api/users/${username}/followers`),
fetch(`${API_BASE_URL}/api/users/${username}/following`),
])
followers.value = followerRes.ok ? await followerRes.json() : []
followings.value = followingRes.ok ? await followingRes.json() : []
}
}
const loadSummary = async () => {
const loadSummary = async () => {
tabLoading.value = true
await fetchSummary()
tabLoading.value = false
}
}
const loadTimeline = async () => {
const loadTimeline = async () => {
tabLoading.value = true
await fetchTimeline()
tabLoading.value = false
}
}
const loadFollow = async () => {
const loadFollow = async () => {
tabLoading.value = true
await fetchFollowUsers()
tabLoading.value = false
}
}
const fetchAchievements = async () => {
const fetchAchievements = async () => {
const res = await fetch(`${API_BASE_URL}/api/medals?userId=${user.value.id}`)
if (res.ok) {
medals.value = await res.json()
@@ -462,15 +460,15 @@ export default {
medals.value = []
toast.error('获取成就失败')
}
}
}
const loadAchievements = async () => {
const loadAchievements = async () => {
tabLoading.value = true
await fetchAchievements()
tabLoading.value = false
}
}
const subscribeUser = async () => {
const subscribeUser = async () => {
const token = getToken()
if (!token) {
toast.error('请先登录')
@@ -486,9 +484,9 @@ export default {
} else {
toast.error('操作失败')
}
}
}
const unsubscribeUser = async () => {
const unsubscribeUser = async () => {
const token = getToken()
if (!token) {
toast.error('请先登录')
@@ -504,14 +502,14 @@ export default {
} else {
toast.error('操作失败')
}
}
}
const gotoTag = (tag) => {
const gotoTag = (tag) => {
const value = encodeURIComponent(tag.id ?? tag.name)
router.push({ path: '/', query: { tags: value } })
}
}
const init = async () => {
const init = async () => {
try {
await fetchUser()
if (selectedTab.value === 'summary') {
@@ -528,54 +526,20 @@ export default {
} finally {
isLoading.value = false
}
}
}
onMounted(init)
onMounted(init)
watch(selectedTab, async (val) => {
watch(selectedTab, async (val) => {
// router.replace({ query: { ...route.query, tab: val } })
if (val === 'timeline' && timelineItems.value.length === 0) {
await loadTimeline()
} else if (
val === 'following' &&
followers.value.length === 0 &&
followings.value.length === 0
) {
} else if (val === 'following' && followers.value.length === 0 && followings.value.length === 0) {
await loadFollow()
} else if (val === 'achievements' && medals.value.length === 0) {
await loadAchievements()
}
})
return {
user,
hotPosts,
hotReplies,
timelineItems,
followers,
followings,
medals,
subscribed,
isMine,
isLoading,
tabLoading,
selectedTab,
followTab,
formatDate,
stripMarkdown,
stripMarkdownLength,
loadTimeline,
loadFollow,
loadAchievements,
loadSummary,
subscribeUser,
unsubscribeUser,
gotoTag,
hotTags,
levelInfo,
}
},
}
})
</script>
<style scoped>

View File

@@ -1,4 +1,3 @@
import { API_BASE_URL } from '~/main'
import { reactive } from 'vue'
const TOKEN_KEY = 'token'
@@ -65,6 +64,8 @@ export function clearUserInfo() {
}
export async function fetchCurrentUser() {
const config = useRuntimeConfig()
const API_BASE_URL = config.public.apiBaseUrl
const token = getToken()
if (!token) return null
try {
@@ -91,6 +92,8 @@ export function isLogin() {
}
export async function checkToken() {
const config = useRuntimeConfig()
const API_BASE_URL = config.public.apiBaseUrl
const token = getToken()
if (!token) return false
try {

View File

@@ -1,4 +1,4 @@
import { API_BASE_URL, DISCORD_CLIENT_ID, toast } from '../main'
import { DISCORD_CLIENT_ID, toast } from '../main'
import { WEBSITE_BASE_URL } from '../constants'
import { setToken, loadCurrentUser } from './auth'
import { registerPush } from './push'
@@ -15,6 +15,8 @@ export function discordAuthorize(state = '') {
export async function discordExchange(code, state, reason) {
try {
const config = useRuntimeConfig()
const API_BASE_URL = config.public.apiBaseUrl
const res = await fetch(`${API_BASE_URL}/api/auth/discord`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },

View File

@@ -1,4 +1,4 @@
import { API_BASE_URL, GITHUB_CLIENT_ID, toast } from '../main'
import { GITHUB_CLIENT_ID, toast } from '../main'
import { setToken, loadCurrentUser } from './auth'
import { WEBSITE_BASE_URL } from '../constants'
import { registerPush } from './push'
@@ -15,6 +15,8 @@ export function githubAuthorize(state = '') {
export async function githubExchange(code, state, reason) {
try {
const config = useRuntimeConfig()
const API_BASE_URL = config.public.apiBaseUrl
const res = await fetch(`${API_BASE_URL}/api/auth/github`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },

View File

@@ -1,4 +1,4 @@
import { API_BASE_URL, GOOGLE_CLIENT_ID, toast } from '../main'
import { GOOGLE_CLIENT_ID, toast } from '../main'
import { setToken, loadCurrentUser } from './auth'
import { registerPush } from './push'
import { WEBSITE_BASE_URL } from '../constants'
@@ -32,6 +32,8 @@ export function googleAuthorize() {
export async function googleAuthWithToken(idToken, redirect_success, redirect_not_approved) {
try {
const config = useRuntimeConfig()
const API_BASE_URL = config.public.apiBaseUrl
const res = await fetch(`${API_BASE_URL}/api/auth/google`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },

View File

@@ -1,4 +1,3 @@
import { API_BASE_URL } from '~/main'
import { getToken } from './auth'
import { reactive } from 'vue'
@@ -7,6 +6,8 @@ export const notificationState = reactive({
})
export async function fetchUnreadCount() {
const config = useRuntimeConfig()
const API_BASE_URL = config.public.apiBaseUrl
try {
const token = getToken()
if (!token) {
@@ -31,6 +32,9 @@ export async function fetchUnreadCount() {
export async function markNotificationsRead(ids) {
try {
const config = useRuntimeConfig()
const API_BASE_URL = config.public.apiBaseUrl
const token = getToken()
if (!token || !ids || ids.length === 0) return false
const res = await fetch(`${API_BASE_URL}/api/notifications/read`, {
@@ -49,6 +53,9 @@ export async function markNotificationsRead(ids) {
export async function fetchNotificationPreferences() {
try {
const config = useRuntimeConfig()
const API_BASE_URL = config.public.apiBaseUrl
const token = getToken()
if (!token) return []
const res = await fetch(`${API_BASE_URL}/api/notifications/prefs`, {
@@ -63,6 +70,8 @@ export async function fetchNotificationPreferences() {
export async function updateNotificationPreference(type, enabled) {
try {
const config = useRuntimeConfig()
const API_BASE_URL = config.public.apiBaseUrl
const token = getToken()
if (!token) return false
const res = await fetch(`${API_BASE_URL}/api/notifications/prefs`, {

View File

@@ -1,4 +1,3 @@
import { API_BASE_URL } from '../main'
import { getToken } from './auth'
function urlBase64ToUint8Array(base64String) {
@@ -21,6 +20,8 @@ function arrayBufferToBase64(buffer) {
export async function registerPush() {
if (!('serviceWorker' in navigator) || !('PushManager' in window)) return
const config = useRuntimeConfig()
const API_BASE_URL = config.public.apiBaseUrl
try {
const reg = await navigator.serviceWorker.register('/notifications-sw.js')
const res = await fetch(`${API_BASE_URL}/api/push/public-key`)

View File

@@ -1,4 +1,4 @@
import { API_BASE_URL, TWITTER_CLIENT_ID, toast } from '../main'
import { TWITTER_CLIENT_ID, toast } from '../main'
import { WEBSITE_BASE_URL } from '../constants'
import { setToken, loadCurrentUser } from './auth'
import { registerPush } from './push'
@@ -42,6 +42,8 @@ export async function twitterAuthorize(state = '') {
export async function twitterExchange(code, state, reason) {
try {
const config = useRuntimeConfig()
const API_BASE_URL = config.public.apiBaseUrl
const codeVerifier = sessionStorage.getItem('twitter_code_verifier')
sessionStorage.removeItem('twitter_code_verifier')
const res = await fetch(`${API_BASE_URL}/api/auth/twitter`, {

View File

@@ -1,7 +1,7 @@
import { API_BASE_URL } from '../main'
export async function fetchFollowings(username) {
if (!username) return []
const config = useRuntimeConfig()
const API_BASE_URL = config.public.apiBaseUrl
try {
const res = await fetch(`${API_BASE_URL}/api/users/${username}/following`)
return res.ok ? await res.json() : []
@@ -11,6 +11,8 @@ export async function fetchFollowings(username) {
}
export async function fetchAdmins() {
const config = useRuntimeConfig()
const API_BASE_URL = config.public.apiBaseUrl
try {
const res = await fetch(`${API_BASE_URL}/api/users/admins`)
return res.ok ? await res.json() : []
@@ -21,6 +23,8 @@ export async function fetchAdmins() {
export async function searchUsers(keyword) {
if (!keyword) return []
const config = useRuntimeConfig()
const API_BASE_URL = config.public.apiBaseUrl
try {
const res = await fetch(
`${API_BASE_URL}/api/search/users?keyword=${encodeURIComponent(keyword)}`,

View File

@@ -1,5 +1,4 @@
import Vditor from 'vditor'
import { API_BASE_URL } from '../main'
import { getToken, authState } from './auth'
import { searchUsers, fetchFollowings, fetchAdmins } from './user'
import { tiebaEmoji } from './tiebaEmoji'
@@ -14,6 +13,8 @@ export function getPreviewTheme() {
export function createVditor(editorId, options = {}) {
const { placeholder = '', preview = {}, input, after } = options
const config = useRuntimeConfig()
const API_BASE_URL = config.public.apiBaseUrl
const fetchMentions = async (value) => {
if (!value) {