mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-02-06 23:21:16 +08:00
fix: setup 迁移完成
This commit is contained in:
@@ -230,7 +230,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { ref, computed, onMounted, onBeforeUnmount, nextTick, watch } from 'vue'
|
import { ref, computed, onMounted, onBeforeUnmount, nextTick, watch } from 'vue'
|
||||||
import VueEasyLightbox from 'vue-easy-lightbox'
|
import VueEasyLightbox from 'vue-easy-lightbox'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
@@ -243,7 +243,7 @@ import ReactionsGroup from '~/components/ReactionsGroup.vue'
|
|||||||
import DropdownMenu from '~/components/DropdownMenu.vue'
|
import DropdownMenu from '~/components/DropdownMenu.vue'
|
||||||
import { renderMarkdown, handleMarkdownClick, stripMarkdownLength } from '~/utils/markdown'
|
import { renderMarkdown, handleMarkdownClick, stripMarkdownLength } from '~/utils/markdown'
|
||||||
import { getMedalTitle } from '~/utils/medal'
|
import { getMedalTitle } from '~/utils/medal'
|
||||||
import { API_BASE_URL, toast } from '~/main'
|
import { toast } from '~/main'
|
||||||
import { getToken, authState } from '~/utils/auth'
|
import { getToken, authState } from '~/utils/auth'
|
||||||
import TimeManager from '~/utils/time'
|
import TimeManager from '~/utils/time'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
@@ -251,52 +251,38 @@ import { useIsMobile } from '~/utils/screen'
|
|||||||
import Dropdown from '~/components/Dropdown.vue'
|
import Dropdown from '~/components/Dropdown.vue'
|
||||||
import { ClientOnly } from '#components'
|
import { ClientOnly } from '#components'
|
||||||
|
|
||||||
export default {
|
const config = useRuntimeConfig()
|
||||||
name: 'PostPageView',
|
const API_BASE_URL = config.public.apiBaseUrl
|
||||||
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 title = ref('')
|
const route = useRoute()
|
||||||
const author = ref('')
|
const postId = route.params.id
|
||||||
const postContent = ref('')
|
const router = useRouter()
|
||||||
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
|
const title = ref('')
|
||||||
? parseFloat(
|
const author = ref('')
|
||||||
getComputedStyle(document.documentElement).getPropertyValue('--header-height'),
|
const postContent = ref('')
|
||||||
) || 0
|
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
|
: 0
|
||||||
|
|
||||||
useHead(() => ({
|
useHead(() => ({
|
||||||
title: title.value ? `OpenIsle - ${title.value}` : 'OpenIsle',
|
title: title.value ? `OpenIsle - ${title.value}` : 'OpenIsle',
|
||||||
meta: [
|
meta: [
|
||||||
{
|
{
|
||||||
@@ -304,35 +290,35 @@ export default {
|
|||||||
content: stripMarkdownLength(postContent.value, 400),
|
content: stripMarkdownLength(postContent.value, 400),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}))
|
}))
|
||||||
|
|
||||||
if (process.client) {
|
if (process.client) {
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener('scroll', updateCurrentIndex)
|
window.removeEventListener('scroll', updateCurrentIndex)
|
||||||
if (countdownTimer) clearInterval(countdownTimer)
|
if (countdownTimer) clearInterval(countdownTimer)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const lightboxVisible = ref(false)
|
const lightboxVisible = ref(false)
|
||||||
const lightboxIndex = ref(0)
|
const lightboxIndex = ref(0)
|
||||||
const lightboxImgs = ref([])
|
const lightboxImgs = ref([])
|
||||||
const loggedIn = computed(() => authState.loggedIn)
|
const loggedIn = computed(() => authState.loggedIn)
|
||||||
const isAdmin = computed(() => authState.role === 'ADMIN')
|
const isAdmin = computed(() => authState.role === 'ADMIN')
|
||||||
const isAuthor = computed(() => authState.username === author.value.username)
|
const isAuthor = computed(() => authState.username === author.value.username)
|
||||||
const lottery = ref(null)
|
const lottery = ref(null)
|
||||||
const countdown = ref('00:00:00')
|
const countdown = ref('00:00:00')
|
||||||
let countdownTimer = null
|
let countdownTimer = null
|
||||||
const lotteryParticipants = computed(() => lottery.value?.participants || [])
|
const lotteryParticipants = computed(() => lottery.value?.participants || [])
|
||||||
const lotteryWinners = computed(() => lottery.value?.winners || [])
|
const lotteryWinners = computed(() => lottery.value?.winners || [])
|
||||||
const lotteryEnded = computed(() => {
|
const lotteryEnded = computed(() => {
|
||||||
if (!lottery.value || !lottery.value.endTime) return false
|
if (!lottery.value || !lottery.value.endTime) return false
|
||||||
return new Date(lottery.value.endTime).getTime() <= Date.now()
|
return new Date(lottery.value.endTime).getTime() <= Date.now()
|
||||||
})
|
})
|
||||||
const hasJoined = computed(() => {
|
const hasJoined = computed(() => {
|
||||||
if (!loggedIn.value) return false
|
if (!loggedIn.value) return false
|
||||||
return lotteryParticipants.value.some((p) => p.id === Number(authState.userId))
|
return lotteryParticipants.value.some((p) => p.id === Number(authState.userId))
|
||||||
})
|
})
|
||||||
const updateCountdown = () => {
|
const updateCountdown = () => {
|
||||||
if (!lottery.value || !lottery.value.endTime) {
|
if (!lottery.value || !lottery.value.endTime) {
|
||||||
countdown.value = '00:00:00'
|
countdown.value = '00:00:00'
|
||||||
return
|
return
|
||||||
@@ -350,15 +336,15 @@ export default {
|
|||||||
const m = String(Math.floor((diff % 3600000) / 60000)).padStart(2, '0')
|
const m = String(Math.floor((diff % 3600000) / 60000)).padStart(2, '0')
|
||||||
const s = String(Math.floor((diff % 60000) / 1000)).padStart(2, '0')
|
const s = String(Math.floor((diff % 60000) / 1000)).padStart(2, '0')
|
||||||
countdown.value = `${h}:${m}:${s}`
|
countdown.value = `${h}:${m}:${s}`
|
||||||
}
|
}
|
||||||
const startCountdown = () => {
|
const startCountdown = () => {
|
||||||
if (!process.client) return
|
if (!process.client) return
|
||||||
if (countdownTimer) clearInterval(countdownTimer)
|
if (countdownTimer) clearInterval(countdownTimer)
|
||||||
updateCountdown()
|
updateCountdown()
|
||||||
countdownTimer = setInterval(updateCountdown, 1000)
|
countdownTimer = setInterval(updateCountdown, 1000)
|
||||||
}
|
}
|
||||||
const gotoUser = (id) => router.push(`/users/${id}`)
|
const gotoUser = (id) => router.push(`/users/${id}`)
|
||||||
const articleMenuItems = computed(() => {
|
const articleMenuItems = computed(() => {
|
||||||
const items = []
|
const items = []
|
||||||
if (isAuthor.value || isAdmin.value) {
|
if (isAuthor.value || isAdmin.value) {
|
||||||
items.push({ text: '编辑文章', onClick: () => editPost() })
|
items.push({ text: '编辑文章', onClick: () => editPost() })
|
||||||
@@ -376,9 +362,9 @@ export default {
|
|||||||
items.push({ text: '驳回', color: 'red', onClick: () => rejectPost() })
|
items.push({ text: '驳回', color: 'red', onClick: () => rejectPost() })
|
||||||
}
|
}
|
||||||
return items
|
return items
|
||||||
})
|
})
|
||||||
|
|
||||||
const gatherPostItems = () => {
|
const gatherPostItems = () => {
|
||||||
const items = []
|
const items = []
|
||||||
if (mainContainer.value) {
|
if (mainContainer.value) {
|
||||||
const main = mainContainer.value.querySelector('.info-content-container')
|
const main = mainContainer.value.querySelector('.info-content-container')
|
||||||
@@ -394,9 +380,9 @@ export default {
|
|||||||
items.sort((a, b) => a.top - b.top)
|
items.sort((a, b) => a.top - b.top)
|
||||||
postItems.value = items.map((i) => i.el)
|
postItems.value = items.map((i) => i.el)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapComment = (c, parentUserName = '', level = 0) => ({
|
const mapComment = (c, parentUserName = '', level = 0) => ({
|
||||||
id: c.id,
|
id: c.id,
|
||||||
userName: c.author.username,
|
userName: c.author.username,
|
||||||
medal: c.author.displayMedal,
|
medal: c.author.displayMedal,
|
||||||
@@ -410,13 +396,13 @@ export default {
|
|||||||
src: c.author.avatar,
|
src: c.author.avatar,
|
||||||
iconClick: () => router.push(`/users/${c.author.id}`),
|
iconClick: () => router.push(`/users/${c.author.id}`),
|
||||||
parentUserName: parentUserName,
|
parentUserName: parentUserName,
|
||||||
})
|
})
|
||||||
|
|
||||||
const getTop = (el) => {
|
const getTop = (el) => {
|
||||||
return el.getBoundingClientRect().top + window.scrollY
|
return el.getBoundingClientRect().top + window.scrollY
|
||||||
}
|
}
|
||||||
|
|
||||||
const findCommentPath = (id, list) => {
|
const findCommentPath = (id, list) => {
|
||||||
for (const item of list) {
|
for (const item of list) {
|
||||||
if (item.id === Number(id) || item.id === id) {
|
if (item.id === Number(id) || item.id === id) {
|
||||||
return [item]
|
return [item]
|
||||||
@@ -427,17 +413,17 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const expandCommentPath = (id) => {
|
const expandCommentPath = (id) => {
|
||||||
const path = findCommentPath(id, comments.value)
|
const path = findCommentPath(id, comments.value)
|
||||||
if (!path) return
|
if (!path) return
|
||||||
for (let i = 0; i < path.length - 1; i++) {
|
for (let i = 0; i < path.length - 1; i++) {
|
||||||
path[i].openReplies = true
|
path[i].openReplies = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeCommentFromList = (id, list) => {
|
const removeCommentFromList = (id, list) => {
|
||||||
for (let i = 0; i < list.length; i++) {
|
for (let i = 0; i < list.length; i++) {
|
||||||
const item = list[i]
|
const item = list[i]
|
||||||
if (item.id === id) {
|
if (item.id === id) {
|
||||||
@@ -449,9 +435,9 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleContentClick = (e) => {
|
const handleContentClick = (e) => {
|
||||||
handleMarkdownClick(e)
|
handleMarkdownClick(e)
|
||||||
if (e.target.tagName === 'IMG') {
|
if (e.target.tagName === 'IMG') {
|
||||||
const container = e.target.parentNode
|
const container = e.target.parentNode
|
||||||
@@ -460,14 +446,14 @@ export default {
|
|||||||
lightboxIndex.value = imgs.indexOf(e.target.src)
|
lightboxIndex.value = imgs.indexOf(e.target.src)
|
||||||
lightboxVisible.value = true
|
lightboxVisible.value = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onCommentDeleted = (id) => {
|
const onCommentDeleted = (id) => {
|
||||||
removeCommentFromList(Number(id), comments.value)
|
removeCommentFromList(Number(id), comments.value)
|
||||||
fetchComments()
|
fetchComments()
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchPost = async () => {
|
const fetchPost = async () => {
|
||||||
try {
|
try {
|
||||||
isWaitingFetchingPost.value = true
|
isWaitingFetchingPost.value = true
|
||||||
const token = getToken()
|
const token = getToken()
|
||||||
@@ -498,29 +484,29 @@ export default {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const totalPosts = computed(() => comments.value.length + 1)
|
const totalPosts = computed(() => comments.value.length + 1)
|
||||||
const lastReplyTime = computed(() =>
|
const lastReplyTime = computed(() =>
|
||||||
comments.value.length ? comments.value[comments.value.length - 1].time : postTime.value,
|
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,
|
comments.value.length ? comments.value[0].time : postTime.value,
|
||||||
)
|
)
|
||||||
const scrollerTopTime = computed(() =>
|
const scrollerTopTime = computed(() =>
|
||||||
commentSort.value === 'OLDEST' ? postTime.value : firstReplyTime.value,
|
commentSort.value === 'OLDEST' ? postTime.value : firstReplyTime.value,
|
||||||
)
|
)
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => comments.value.length,
|
() => comments.value.length,
|
||||||
async () => {
|
async () => {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
gatherPostItems()
|
gatherPostItems()
|
||||||
updateCurrentIndex()
|
updateCurrentIndex()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
const updateCurrentIndex = () => {
|
const updateCurrentIndex = () => {
|
||||||
const scrollTop = window.scrollY
|
const scrollTop = window.scrollY
|
||||||
|
|
||||||
for (let i = 0; i < postItems.value.length; i++) {
|
for (let i = 0; i < postItems.value.length; i++) {
|
||||||
@@ -533,9 +519,9 @@ export default {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onSliderInput = (e) => {
|
const onSliderInput = (e) => {
|
||||||
const index = Number(e.target.value)
|
const index = Number(e.target.value)
|
||||||
currentIndex.value = index
|
currentIndex.value = index
|
||||||
const target = postItems.value[index - 1]
|
const target = postItems.value[index - 1]
|
||||||
@@ -543,9 +529,9 @@ export default {
|
|||||||
const top = getTop(target) - headerHeight - 20 // 20 for beauty
|
const top = getTop(target) - headerHeight - 20 // 20 for beauty
|
||||||
window.scrollTo({ top, behavior: 'auto' })
|
window.scrollTo({ top, behavior: 'auto' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const postComment = async (parentUserName, text, clear) => {
|
const postComment = async (parentUserName, text, clear) => {
|
||||||
if (!text.trim()) return
|
if (!text.trim()) return
|
||||||
console.debug('Posting comment', { postId, text })
|
console.debug('Posting comment', { postId, text })
|
||||||
isWaitingPostingComment.value = true
|
isWaitingPostingComment.value = true
|
||||||
@@ -583,15 +569,15 @@ export default {
|
|||||||
} finally {
|
} finally {
|
||||||
isWaitingPostingComment.value = false
|
isWaitingPostingComment.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const copyPostLink = () => {
|
const copyPostLink = () => {
|
||||||
navigator.clipboard.writeText(location.href.split('#')[0]).then(() => {
|
navigator.clipboard.writeText(location.href.split('#')[0]).then(() => {
|
||||||
toast.success('已复制')
|
toast.success('已复制')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const subscribePost = async () => {
|
const subscribePost = async () => {
|
||||||
const token = getToken()
|
const token = getToken()
|
||||||
if (!token) {
|
if (!token) {
|
||||||
toast.error('请先登录')
|
toast.error('请先登录')
|
||||||
@@ -607,9 +593,9 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
toast.error('操作失败')
|
toast.error('操作失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const approvePost = async () => {
|
const approvePost = async () => {
|
||||||
const token = getToken()
|
const token = getToken()
|
||||||
if (!token) return
|
if (!token) return
|
||||||
const res = await fetch(`${API_BASE_URL}/api/admin/posts/${postId}/approve`, {
|
const res = await fetch(`${API_BASE_URL}/api/admin/posts/${postId}/approve`, {
|
||||||
@@ -622,9 +608,9 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
toast.error('操作失败')
|
toast.error('操作失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const pinPost = async () => {
|
const pinPost = async () => {
|
||||||
const token = getToken()
|
const token = getToken()
|
||||||
if (!token) return
|
if (!token) return
|
||||||
const res = await fetch(`${API_BASE_URL}/api/admin/posts/${postId}/pin`, {
|
const res = await fetch(`${API_BASE_URL}/api/admin/posts/${postId}/pin`, {
|
||||||
@@ -637,9 +623,9 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
toast.error('操作失败')
|
toast.error('操作失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const unpinPost = async () => {
|
const unpinPost = async () => {
|
||||||
const token = getToken()
|
const token = getToken()
|
||||||
if (!token) return
|
if (!token) return
|
||||||
const res = await fetch(`${API_BASE_URL}/api/admin/posts/${postId}/unpin`, {
|
const res = await fetch(`${API_BASE_URL}/api/admin/posts/${postId}/unpin`, {
|
||||||
@@ -652,13 +638,13 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
toast.error('操作失败')
|
toast.error('操作失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const editPost = () => {
|
const editPost = () => {
|
||||||
router.push(`/posts/${postId}/edit`)
|
router.push(`/posts/${postId}/edit`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const deletePost = async () => {
|
const deletePost = async () => {
|
||||||
const token = getToken()
|
const token = getToken()
|
||||||
if (!token) {
|
if (!token) {
|
||||||
toast.error('请先登录')
|
toast.error('请先登录')
|
||||||
@@ -674,9 +660,9 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
toast.error('操作失败')
|
toast.error('操作失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const rejectPost = async () => {
|
const rejectPost = async () => {
|
||||||
const token = getToken()
|
const token = getToken()
|
||||||
if (!token) return
|
if (!token) return
|
||||||
const res = await fetch(`${API_BASE_URL}/api/admin/posts/${postId}/reject`, {
|
const res = await fetch(`${API_BASE_URL}/api/admin/posts/${postId}/reject`, {
|
||||||
@@ -689,8 +675,8 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
toast.error('操作失败')
|
toast.error('操作失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const unsubscribePost = async () => {
|
const unsubscribePost = async () => {
|
||||||
const token = getToken()
|
const token = getToken()
|
||||||
if (!token) {
|
if (!token) {
|
||||||
toast.error('请先登录')
|
toast.error('请先登录')
|
||||||
@@ -707,9 +693,9 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
toast.error('操作失败')
|
toast.error('操作失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const joinLottery = async () => {
|
const joinLottery = async () => {
|
||||||
const token = getToken()
|
const token = getToken()
|
||||||
if (!token) {
|
if (!token) {
|
||||||
toast.error('请先登录')
|
toast.error('请先登录')
|
||||||
@@ -725,17 +711,17 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
toast.error('操作失败')
|
toast.error('操作失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchCommentSorts = () => {
|
const fetchCommentSorts = () => {
|
||||||
return Promise.resolve([
|
return Promise.resolve([
|
||||||
{ id: 'NEWEST', name: '最新', icon: 'fas fa-clock' },
|
{ id: 'NEWEST', name: '最新', icon: 'fas fa-clock' },
|
||||||
{ id: 'OLDEST', name: '最旧', icon: 'fas fa-hourglass-start' },
|
{ id: 'OLDEST', name: '最旧', icon: 'fas fa-hourglass-start' },
|
||||||
// { id: 'MOST_INTERACTIONS', name: '最多互动', icon: 'fas fa-fire' }
|
// { id: 'MOST_INTERACTIONS', name: '最多互动', icon: 'fas fa-fire' }
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchComments = async () => {
|
const fetchComments = async () => {
|
||||||
isFetchingComments.value = true
|
isFetchingComments.value = true
|
||||||
console.debug('Fetching comments', { postId, sort: commentSort.value })
|
console.debug('Fetching comments', { postId, sort: commentSort.value })
|
||||||
try {
|
try {
|
||||||
@@ -760,11 +746,11 @@ export default {
|
|||||||
} finally {
|
} finally {
|
||||||
isFetchingComments.value = false
|
isFetchingComments.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(commentSort, fetchComments)
|
watch(commentSort, fetchComments)
|
||||||
|
|
||||||
const jumpToHashComment = async () => {
|
const jumpToHashComment = async () => {
|
||||||
const hash = location.hash
|
const hash = location.hash
|
||||||
if (hash.startsWith('#comment-')) {
|
if (hash.startsWith('#comment-')) {
|
||||||
const id = hash.substring('#comment-'.length)
|
const id = hash.substring('#comment-'.length)
|
||||||
@@ -777,13 +763,13 @@ export default {
|
|||||||
setTimeout(() => el.classList.remove('comment-highlight'), 4000)
|
setTimeout(() => el.classList.remove('comment-highlight'), 4000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const gotoProfile = () => {
|
const gotoProfile = () => {
|
||||||
router.push(`/users/${author.value.id}`)
|
router.push(`/users/${author.value.id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await fetchComments()
|
await fetchComments()
|
||||||
const hash = location.hash
|
const hash = location.hash
|
||||||
const id = hash.startsWith('#comment-') ? hash.substring('#comment-'.length) : null
|
const id = hash.startsWith('#comment-') ? hash.substring('#comment-'.length) : null
|
||||||
@@ -791,69 +777,9 @@ export default {
|
|||||||
updateCurrentIndex()
|
updateCurrentIndex()
|
||||||
window.addEventListener('scroll', updateCurrentIndex)
|
window.addEventListener('scroll', updateCurrentIndex)
|
||||||
jumpToHashComment()
|
jumpToHashComment()
|
||||||
})
|
})
|
||||||
|
|
||||||
await fetchPost()
|
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,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
.post-page-container {
|
.post-page-container {
|
||||||
|
|||||||
@@ -296,7 +296,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { computed, onMounted, ref, watch } from 'vue'
|
import { computed, onMounted, ref, watch } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import AchievementList from '~/components/AchievementList.vue'
|
import AchievementList from '~/components/AchievementList.vue'
|
||||||
@@ -304,42 +304,40 @@ import BasePlaceholder from '~/components/BasePlaceholder.vue'
|
|||||||
import BaseTimeline from '~/components/BaseTimeline.vue'
|
import BaseTimeline from '~/components/BaseTimeline.vue'
|
||||||
import LevelProgress from '~/components/LevelProgress.vue'
|
import LevelProgress from '~/components/LevelProgress.vue'
|
||||||
import UserList from '~/components/UserList.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 { authState, getToken } from '~/utils/auth'
|
||||||
import { prevLevelExp } from '~/utils/level'
|
import { prevLevelExp } from '~/utils/level'
|
||||||
import { stripMarkdown, stripMarkdownLength } from '~/utils/markdown'
|
import { stripMarkdown, stripMarkdownLength } from '~/utils/markdown'
|
||||||
import TimeManager from '~/utils/time'
|
import TimeManager from '~/utils/time'
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const API_BASE_URL = config.public.apiBaseUrl
|
||||||
|
|
||||||
export default {
|
definePageMeta({
|
||||||
name: 'ProfileView',
|
|
||||||
components: { BaseTimeline, UserList, BasePlaceholder, LevelProgress, AchievementList },
|
|
||||||
setup() {
|
|
||||||
definePageMeta({
|
|
||||||
alias: ['/users/:id/'],
|
alias: ['/users/:id/'],
|
||||||
})
|
})
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const username = route.params.id
|
const username = route.params.id
|
||||||
|
|
||||||
const user = ref({})
|
const user = ref({})
|
||||||
const hotPosts = ref([])
|
const hotPosts = ref([])
|
||||||
const hotReplies = ref([])
|
const hotReplies = ref([])
|
||||||
const hotTags = ref([])
|
const hotTags = ref([])
|
||||||
const timelineItems = ref([])
|
const timelineItems = ref([])
|
||||||
const followers = ref([])
|
const followers = ref([])
|
||||||
const followings = ref([])
|
const followings = ref([])
|
||||||
const medals = ref([])
|
const medals = ref([])
|
||||||
const subscribed = ref(false)
|
const subscribed = ref(false)
|
||||||
const isLoading = ref(true)
|
const isLoading = ref(true)
|
||||||
const tabLoading = ref(false)
|
const tabLoading = ref(false)
|
||||||
const selectedTab = ref(
|
const selectedTab = ref(
|
||||||
['summary', 'timeline', 'following', 'achievements'].includes(route.query.tab)
|
['summary', 'timeline', 'following', 'achievements'].includes(route.query.tab)
|
||||||
? route.query.tab
|
? route.query.tab
|
||||||
: 'summary',
|
: 'summary',
|
||||||
)
|
)
|
||||||
const followTab = ref('followers')
|
const followTab = ref('followers')
|
||||||
|
|
||||||
const levelInfo = computed(() => {
|
const levelInfo = computed(() => {
|
||||||
const exp = user.value.experience || 0
|
const exp = user.value.experience || 0
|
||||||
const currentLevel = user.value.currentLevel || 0
|
const currentLevel = user.value.currentLevel || 0
|
||||||
const nextExp = user.value.nextLevelExp || 0
|
const nextExp = user.value.nextLevelExp || 0
|
||||||
@@ -348,20 +346,20 @@ export default {
|
|||||||
const ratio = total > 0 ? (exp - prevExp) / total : 1
|
const ratio = total > 0 ? (exp - prevExp) / total : 1
|
||||||
const percent = Math.max(0, Math.min(1, ratio)) * 100
|
const percent = Math.max(0, Math.min(1, ratio)) * 100
|
||||||
return { exp, currentLevel, nextExp, percent }
|
return { exp, currentLevel, nextExp, percent }
|
||||||
})
|
})
|
||||||
|
|
||||||
const isMine = computed(function () {
|
const isMine = computed(function () {
|
||||||
const mine = authState.username === username || String(authState.userId) === username
|
const mine = authState.username === username || String(authState.userId) === username
|
||||||
console.log(mine)
|
console.log(mine)
|
||||||
return mine
|
return mine
|
||||||
})
|
})
|
||||||
|
|
||||||
const formatDate = (d) => {
|
const formatDate = (d) => {
|
||||||
if (!d) return ''
|
if (!d) return ''
|
||||||
return TimeManager.format(d)
|
return TimeManager.format(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchUser = async () => {
|
const fetchUser = async () => {
|
||||||
const token = getToken()
|
const token = getToken()
|
||||||
const headers = token ? { Authorization: `Bearer ${token}` } : {}
|
const headers = token ? { Authorization: `Bearer ${token}` } : {}
|
||||||
const res = await fetch(`${API_BASE_URL}/api/users/${username}`, { headers })
|
const res = await fetch(`${API_BASE_URL}/api/users/${username}`, { headers })
|
||||||
@@ -372,9 +370,9 @@ export default {
|
|||||||
} else if (res.status === 404) {
|
} else if (res.status === 404) {
|
||||||
router.replace('/404')
|
router.replace('/404')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchSummary = async () => {
|
const fetchSummary = async () => {
|
||||||
const postsRes = await fetch(`${API_BASE_URL}/api/users/${username}/hot-posts`)
|
const postsRes = await fetch(`${API_BASE_URL}/api/users/${username}/hot-posts`)
|
||||||
if (postsRes.ok) {
|
if (postsRes.ok) {
|
||||||
const data = await postsRes.json()
|
const data = await postsRes.json()
|
||||||
@@ -392,9 +390,9 @@ export default {
|
|||||||
const data = await tagsRes.json()
|
const data = await tagsRes.json()
|
||||||
hotTags.value = data.map((t) => ({ icon: 'fas fa-tag', tag: t }))
|
hotTags.value = data.map((t) => ({ icon: 'fas fa-tag', tag: t }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchTimeline = async () => {
|
const fetchTimeline = async () => {
|
||||||
const [postsRes, repliesRes, tagsRes] = await Promise.all([
|
const [postsRes, repliesRes, tagsRes] = await Promise.all([
|
||||||
fetch(`${API_BASE_URL}/api/users/${username}/posts?limit=50`),
|
fetch(`${API_BASE_URL}/api/users/${username}/posts?limit=50`),
|
||||||
fetch(`${API_BASE_URL}/api/users/${username}/replies?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))
|
mapped.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt))
|
||||||
timelineItems.value = mapped
|
timelineItems.value = mapped
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchFollowUsers = async () => {
|
const fetchFollowUsers = async () => {
|
||||||
const [followerRes, followingRes] = await Promise.all([
|
const [followerRes, followingRes] = await Promise.all([
|
||||||
fetch(`${API_BASE_URL}/api/users/${username}/followers`),
|
fetch(`${API_BASE_URL}/api/users/${username}/followers`),
|
||||||
fetch(`${API_BASE_URL}/api/users/${username}/following`),
|
fetch(`${API_BASE_URL}/api/users/${username}/following`),
|
||||||
])
|
])
|
||||||
followers.value = followerRes.ok ? await followerRes.json() : []
|
followers.value = followerRes.ok ? await followerRes.json() : []
|
||||||
followings.value = followingRes.ok ? await followingRes.json() : []
|
followings.value = followingRes.ok ? await followingRes.json() : []
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadSummary = async () => {
|
const loadSummary = async () => {
|
||||||
tabLoading.value = true
|
tabLoading.value = true
|
||||||
await fetchSummary()
|
await fetchSummary()
|
||||||
tabLoading.value = false
|
tabLoading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadTimeline = async () => {
|
const loadTimeline = async () => {
|
||||||
tabLoading.value = true
|
tabLoading.value = true
|
||||||
await fetchTimeline()
|
await fetchTimeline()
|
||||||
tabLoading.value = false
|
tabLoading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadFollow = async () => {
|
const loadFollow = async () => {
|
||||||
tabLoading.value = true
|
tabLoading.value = true
|
||||||
await fetchFollowUsers()
|
await fetchFollowUsers()
|
||||||
tabLoading.value = false
|
tabLoading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchAchievements = async () => {
|
const fetchAchievements = async () => {
|
||||||
const res = await fetch(`${API_BASE_URL}/api/medals?userId=${user.value.id}`)
|
const res = await fetch(`${API_BASE_URL}/api/medals?userId=${user.value.id}`)
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
medals.value = await res.json()
|
medals.value = await res.json()
|
||||||
@@ -462,15 +460,15 @@ export default {
|
|||||||
medals.value = []
|
medals.value = []
|
||||||
toast.error('获取成就失败')
|
toast.error('获取成就失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadAchievements = async () => {
|
const loadAchievements = async () => {
|
||||||
tabLoading.value = true
|
tabLoading.value = true
|
||||||
await fetchAchievements()
|
await fetchAchievements()
|
||||||
tabLoading.value = false
|
tabLoading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const subscribeUser = async () => {
|
const subscribeUser = async () => {
|
||||||
const token = getToken()
|
const token = getToken()
|
||||||
if (!token) {
|
if (!token) {
|
||||||
toast.error('请先登录')
|
toast.error('请先登录')
|
||||||
@@ -486,9 +484,9 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
toast.error('操作失败')
|
toast.error('操作失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsubscribeUser = async () => {
|
const unsubscribeUser = async () => {
|
||||||
const token = getToken()
|
const token = getToken()
|
||||||
if (!token) {
|
if (!token) {
|
||||||
toast.error('请先登录')
|
toast.error('请先登录')
|
||||||
@@ -504,14 +502,14 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
toast.error('操作失败')
|
toast.error('操作失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const gotoTag = (tag) => {
|
const gotoTag = (tag) => {
|
||||||
const value = encodeURIComponent(tag.id ?? tag.name)
|
const value = encodeURIComponent(tag.id ?? tag.name)
|
||||||
router.push({ path: '/', query: { tags: value } })
|
router.push({ path: '/', query: { tags: value } })
|
||||||
}
|
}
|
||||||
|
|
||||||
const init = async () => {
|
const init = async () => {
|
||||||
try {
|
try {
|
||||||
await fetchUser()
|
await fetchUser()
|
||||||
if (selectedTab.value === 'summary') {
|
if (selectedTab.value === 'summary') {
|
||||||
@@ -528,54 +526,20 @@ export default {
|
|||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(init)
|
onMounted(init)
|
||||||
|
|
||||||
watch(selectedTab, async (val) => {
|
watch(selectedTab, async (val) => {
|
||||||
// router.replace({ query: { ...route.query, tab: val } })
|
// router.replace({ query: { ...route.query, tab: val } })
|
||||||
if (val === 'timeline' && timelineItems.value.length === 0) {
|
if (val === 'timeline' && timelineItems.value.length === 0) {
|
||||||
await loadTimeline()
|
await loadTimeline()
|
||||||
} else if (
|
} else if (val === 'following' && followers.value.length === 0 && followings.value.length === 0) {
|
||||||
val === 'following' &&
|
|
||||||
followers.value.length === 0 &&
|
|
||||||
followings.value.length === 0
|
|
||||||
) {
|
|
||||||
await loadFollow()
|
await loadFollow()
|
||||||
} else if (val === 'achievements' && medals.value.length === 0) {
|
} else if (val === 'achievements' && medals.value.length === 0) {
|
||||||
await loadAchievements()
|
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>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { API_BASE_URL } from '~/main'
|
|
||||||
import { reactive } from 'vue'
|
import { reactive } from 'vue'
|
||||||
|
|
||||||
const TOKEN_KEY = 'token'
|
const TOKEN_KEY = 'token'
|
||||||
@@ -65,6 +64,8 @@ export function clearUserInfo() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchCurrentUser() {
|
export async function fetchCurrentUser() {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const API_BASE_URL = config.public.apiBaseUrl
|
||||||
const token = getToken()
|
const token = getToken()
|
||||||
if (!token) return null
|
if (!token) return null
|
||||||
try {
|
try {
|
||||||
@@ -91,6 +92,8 @@ export function isLogin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function checkToken() {
|
export async function checkToken() {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const API_BASE_URL = config.public.apiBaseUrl
|
||||||
const token = getToken()
|
const token = getToken()
|
||||||
if (!token) return false
|
if (!token) return false
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -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 { WEBSITE_BASE_URL } from '../constants'
|
||||||
import { setToken, loadCurrentUser } from './auth'
|
import { setToken, loadCurrentUser } from './auth'
|
||||||
import { registerPush } from './push'
|
import { registerPush } from './push'
|
||||||
@@ -15,6 +15,8 @@ export function discordAuthorize(state = '') {
|
|||||||
|
|
||||||
export async function discordExchange(code, state, reason) {
|
export async function discordExchange(code, state, reason) {
|
||||||
try {
|
try {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const API_BASE_URL = config.public.apiBaseUrl
|
||||||
const res = await fetch(`${API_BASE_URL}/api/auth/discord`, {
|
const res = await fetch(`${API_BASE_URL}/api/auth/discord`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
|||||||
@@ -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 { setToken, loadCurrentUser } from './auth'
|
||||||
import { WEBSITE_BASE_URL } from '../constants'
|
import { WEBSITE_BASE_URL } from '../constants'
|
||||||
import { registerPush } from './push'
|
import { registerPush } from './push'
|
||||||
@@ -15,6 +15,8 @@ export function githubAuthorize(state = '') {
|
|||||||
|
|
||||||
export async function githubExchange(code, state, reason) {
|
export async function githubExchange(code, state, reason) {
|
||||||
try {
|
try {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const API_BASE_URL = config.public.apiBaseUrl
|
||||||
const res = await fetch(`${API_BASE_URL}/api/auth/github`, {
|
const res = await fetch(`${API_BASE_URL}/api/auth/github`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
|||||||
@@ -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 { setToken, loadCurrentUser } from './auth'
|
||||||
import { registerPush } from './push'
|
import { registerPush } from './push'
|
||||||
import { WEBSITE_BASE_URL } from '../constants'
|
import { WEBSITE_BASE_URL } from '../constants'
|
||||||
@@ -32,6 +32,8 @@ export function googleAuthorize() {
|
|||||||
|
|
||||||
export async function googleAuthWithToken(idToken, redirect_success, redirect_not_approved) {
|
export async function googleAuthWithToken(idToken, redirect_success, redirect_not_approved) {
|
||||||
try {
|
try {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const API_BASE_URL = config.public.apiBaseUrl
|
||||||
const res = await fetch(`${API_BASE_URL}/api/auth/google`, {
|
const res = await fetch(`${API_BASE_URL}/api/auth/google`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { API_BASE_URL } from '~/main'
|
|
||||||
import { getToken } from './auth'
|
import { getToken } from './auth'
|
||||||
import { reactive } from 'vue'
|
import { reactive } from 'vue'
|
||||||
|
|
||||||
@@ -7,6 +6,8 @@ export const notificationState = reactive({
|
|||||||
})
|
})
|
||||||
|
|
||||||
export async function fetchUnreadCount() {
|
export async function fetchUnreadCount() {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const API_BASE_URL = config.public.apiBaseUrl
|
||||||
try {
|
try {
|
||||||
const token = getToken()
|
const token = getToken()
|
||||||
if (!token) {
|
if (!token) {
|
||||||
@@ -31,6 +32,9 @@ export async function fetchUnreadCount() {
|
|||||||
|
|
||||||
export async function markNotificationsRead(ids) {
|
export async function markNotificationsRead(ids) {
|
||||||
try {
|
try {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const API_BASE_URL = config.public.apiBaseUrl
|
||||||
|
|
||||||
const token = getToken()
|
const token = getToken()
|
||||||
if (!token || !ids || ids.length === 0) return false
|
if (!token || !ids || ids.length === 0) return false
|
||||||
const res = await fetch(`${API_BASE_URL}/api/notifications/read`, {
|
const res = await fetch(`${API_BASE_URL}/api/notifications/read`, {
|
||||||
@@ -49,6 +53,9 @@ export async function markNotificationsRead(ids) {
|
|||||||
|
|
||||||
export async function fetchNotificationPreferences() {
|
export async function fetchNotificationPreferences() {
|
||||||
try {
|
try {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const API_BASE_URL = config.public.apiBaseUrl
|
||||||
|
|
||||||
const token = getToken()
|
const token = getToken()
|
||||||
if (!token) return []
|
if (!token) return []
|
||||||
const res = await fetch(`${API_BASE_URL}/api/notifications/prefs`, {
|
const res = await fetch(`${API_BASE_URL}/api/notifications/prefs`, {
|
||||||
@@ -63,6 +70,8 @@ export async function fetchNotificationPreferences() {
|
|||||||
|
|
||||||
export async function updateNotificationPreference(type, enabled) {
|
export async function updateNotificationPreference(type, enabled) {
|
||||||
try {
|
try {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const API_BASE_URL = config.public.apiBaseUrl
|
||||||
const token = getToken()
|
const token = getToken()
|
||||||
if (!token) return false
|
if (!token) return false
|
||||||
const res = await fetch(`${API_BASE_URL}/api/notifications/prefs`, {
|
const res = await fetch(`${API_BASE_URL}/api/notifications/prefs`, {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { API_BASE_URL } from '../main'
|
|
||||||
import { getToken } from './auth'
|
import { getToken } from './auth'
|
||||||
|
|
||||||
function urlBase64ToUint8Array(base64String) {
|
function urlBase64ToUint8Array(base64String) {
|
||||||
@@ -21,6 +20,8 @@ function arrayBufferToBase64(buffer) {
|
|||||||
|
|
||||||
export async function registerPush() {
|
export async function registerPush() {
|
||||||
if (!('serviceWorker' in navigator) || !('PushManager' in window)) return
|
if (!('serviceWorker' in navigator) || !('PushManager' in window)) return
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const API_BASE_URL = config.public.apiBaseUrl
|
||||||
try {
|
try {
|
||||||
const reg = await navigator.serviceWorker.register('/notifications-sw.js')
|
const reg = await navigator.serviceWorker.register('/notifications-sw.js')
|
||||||
const res = await fetch(`${API_BASE_URL}/api/push/public-key`)
|
const res = await fetch(`${API_BASE_URL}/api/push/public-key`)
|
||||||
|
|||||||
@@ -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 { WEBSITE_BASE_URL } from '../constants'
|
||||||
import { setToken, loadCurrentUser } from './auth'
|
import { setToken, loadCurrentUser } from './auth'
|
||||||
import { registerPush } from './push'
|
import { registerPush } from './push'
|
||||||
@@ -42,6 +42,8 @@ export async function twitterAuthorize(state = '') {
|
|||||||
|
|
||||||
export async function twitterExchange(code, state, reason) {
|
export async function twitterExchange(code, state, reason) {
|
||||||
try {
|
try {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const API_BASE_URL = config.public.apiBaseUrl
|
||||||
const codeVerifier = sessionStorage.getItem('twitter_code_verifier')
|
const codeVerifier = sessionStorage.getItem('twitter_code_verifier')
|
||||||
sessionStorage.removeItem('twitter_code_verifier')
|
sessionStorage.removeItem('twitter_code_verifier')
|
||||||
const res = await fetch(`${API_BASE_URL}/api/auth/twitter`, {
|
const res = await fetch(`${API_BASE_URL}/api/auth/twitter`, {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { API_BASE_URL } from '../main'
|
|
||||||
|
|
||||||
export async function fetchFollowings(username) {
|
export async function fetchFollowings(username) {
|
||||||
if (!username) return []
|
if (!username) return []
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const API_BASE_URL = config.public.apiBaseUrl
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE_URL}/api/users/${username}/following`)
|
const res = await fetch(`${API_BASE_URL}/api/users/${username}/following`)
|
||||||
return res.ok ? await res.json() : []
|
return res.ok ? await res.json() : []
|
||||||
@@ -11,6 +11,8 @@ export async function fetchFollowings(username) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchAdmins() {
|
export async function fetchAdmins() {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const API_BASE_URL = config.public.apiBaseUrl
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE_URL}/api/users/admins`)
|
const res = await fetch(`${API_BASE_URL}/api/users/admins`)
|
||||||
return res.ok ? await res.json() : []
|
return res.ok ? await res.json() : []
|
||||||
@@ -21,6 +23,8 @@ export async function fetchAdmins() {
|
|||||||
|
|
||||||
export async function searchUsers(keyword) {
|
export async function searchUsers(keyword) {
|
||||||
if (!keyword) return []
|
if (!keyword) return []
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const API_BASE_URL = config.public.apiBaseUrl
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`${API_BASE_URL}/api/search/users?keyword=${encodeURIComponent(keyword)}`,
|
`${API_BASE_URL}/api/search/users?keyword=${encodeURIComponent(keyword)}`,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import Vditor from 'vditor'
|
import Vditor from 'vditor'
|
||||||
import { API_BASE_URL } from '../main'
|
|
||||||
import { getToken, authState } from './auth'
|
import { getToken, authState } from './auth'
|
||||||
import { searchUsers, fetchFollowings, fetchAdmins } from './user'
|
import { searchUsers, fetchFollowings, fetchAdmins } from './user'
|
||||||
import { tiebaEmoji } from './tiebaEmoji'
|
import { tiebaEmoji } from './tiebaEmoji'
|
||||||
@@ -14,6 +13,8 @@ export function getPreviewTheme() {
|
|||||||
|
|
||||||
export function createVditor(editorId, options = {}) {
|
export function createVditor(editorId, options = {}) {
|
||||||
const { placeholder = '', preview = {}, input, after } = options
|
const { placeholder = '', preview = {}, input, after } = options
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const API_BASE_URL = config.public.apiBaseUrl
|
||||||
|
|
||||||
const fetchMentions = async (value) => {
|
const fetchMentions = async (value) => {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
|
|||||||
Reference in New Issue
Block a user