deploy: auto commit repo-root changes 2026-08-03 21:44:47
This commit is contained in:
@@ -155,6 +155,12 @@
|
||||
<u-icon name="thumb-up" size="34" :color="post.is_liked ? '#1468f5' : '#737373'" />
|
||||
<text :class="{ 'is-active': post.is_liked }">{{ formatCount(post.like_count) }}</text>
|
||||
</view>
|
||||
<view class="detail-interaction__stat detail-interaction__stat--favorite"
|
||||
@tap="handleFavorite">
|
||||
<u-icon :name="isFavorite ? 'star-fill' : 'star'" size="34"
|
||||
:color="isFavorite ? '#e6a23c' : '#737373'" />
|
||||
<text :class="{ 'is-active': isFavorite }">{{ isFavorite ? '已藏' : '收藏' }}</text>
|
||||
</view>
|
||||
<view class="detail-interaction__stat">
|
||||
<u-icon name="chat" size="34" color="#737373" />
|
||||
<text>{{ formatCount(post.comment_count) }}</text>
|
||||
@@ -243,7 +249,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app'
|
||||
import SharePopup from '@/components/share-popup/share-popup.vue'
|
||||
import {
|
||||
addComment,
|
||||
@@ -263,6 +269,7 @@ import { useUserStore } from '@/stores/user'
|
||||
import { AgreementEnum } from '@/enums/agreementEnums'
|
||||
import { formatCount } from '@/utils/number'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import { isPostFavorite, togglePostFavorite } from '@/utils/post-favorite'
|
||||
|
||||
const LIUHE_TAGS = ['旧澳六合', '新澳六合']
|
||||
const appStore = useAppStore()
|
||||
@@ -290,6 +297,7 @@ const inputContent = ref('')
|
||||
const replyTarget = ref<any>(null)
|
||||
const purchasing = ref(false)
|
||||
const likePending = ref(false)
|
||||
const isFavorite = ref(false)
|
||||
const commentLikePending = ref<Set<number>>(new Set())
|
||||
|
||||
const userPoints = ref<number | null>(null)
|
||||
@@ -541,6 +549,7 @@ const fetchDetail = async (id: number) => {
|
||||
try {
|
||||
const result = await getCommunityPostDetail({ id })
|
||||
post.value = result
|
||||
isFavorite.value = isPostFavorite(result?.id || id)
|
||||
translatedText.value = result?.translated_content || ''
|
||||
autoTranslateStarted.value = false
|
||||
previewExpanded.value = !result?.is_locked
|
||||
@@ -607,6 +616,10 @@ onLoad((options) => {
|
||||
void fetchComments()
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
if (post.value?.id) isFavorite.value = isPostFavorite(post.value.id)
|
||||
})
|
||||
|
||||
const submitComment = async () => {
|
||||
if (!checkLogin()) return
|
||||
const content = inputContent.value.trim()
|
||||
@@ -642,6 +655,13 @@ const handleLike = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleFavorite = () => {
|
||||
if (!post.value?.id) return
|
||||
const result = togglePostFavorite(post.value)
|
||||
isFavorite.value = result.favorite
|
||||
uni.showToast({ title: result.favorite ? '已收藏帖子' : '已取消收藏', icon: 'none' })
|
||||
}
|
||||
|
||||
const handleCommentLike = async (comment: any) => {
|
||||
if (!checkLogin()) return
|
||||
const commentId = Number(comment?.id || 0)
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
<template>
|
||||
<page-meta :page-style="$theme.pageStyle">
|
||||
<!-- #ifndef H5 -->
|
||||
<navigation-bar
|
||||
:front-color="$theme.navColor"
|
||||
:background-color="$theme.navBgColor"
|
||||
/>
|
||||
<navigation-bar :front-color="$theme.navColor" :background-color="$theme.navBgColor" />
|
||||
<!-- #endif -->
|
||||
</page-meta>
|
||||
|
||||
@@ -26,14 +23,17 @@
|
||||
<scroll-view scroll-x class="collection-tabs" :show-scrollbar="false">
|
||||
<view class="collection-tabs__inner">
|
||||
<view v-for="tab in collectionTabs" :key="tab.key" class="collection-tab"
|
||||
:class="{ 'collection-tab--active': activeType === tab.key }"
|
||||
@tap="switchType(tab.key)">
|
||||
:class="{ 'collection-tab--active': activeType === tab.key }" @tap="switchType(tab.key)">
|
||||
<u-icon :name="tab.icon" size="28" :color="activeType === tab.key ? '#1468f5' : '#7b8492'" />
|
||||
<text>{{ tab.label }}</text>
|
||||
<text v-if="tab.key === 'article' && articleCount" class="collection-tab__count">{{ articleCount }}</text>
|
||||
<text v-if="tab.key === 'article' && articleCount" class="collection-tab__count">{{ articleCount
|
||||
}}</text>
|
||||
<text v-if="tab.key === 'post' && postCount" class="collection-tab__count">{{ postCount }}</text>
|
||||
<text v-if="tab.key === 'match' && matchCount" class="collection-tab__count">{{ matchCount }}</text>
|
||||
<text v-if="tab.key === 'lottery' && lotteryCount" class="collection-tab__count">{{ lotteryCount }}</text>
|
||||
<text v-if="tab.key === 'crypto' && cryptoCount" class="collection-tab__count">{{ cryptoCount }}</text>
|
||||
<text v-if="tab.key === 'lottery' && lotteryCount" class="collection-tab__count">{{ lotteryCount
|
||||
}}</text>
|
||||
<text v-if="tab.key === 'crypto' && cryptoCount" class="collection-tab__count">{{ cryptoCount
|
||||
}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
@@ -77,17 +77,53 @@
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<template v-else-if="activeType === 'post'">
|
||||
<view v-if="postFavorites.length" class="collection-list post-list">
|
||||
<view v-for="item in postFavorites" :key="item.id" class="post-item" @tap="openPost(item)">
|
||||
<view class="post-item__header">
|
||||
<image v-if="item.user_avatar" class="post-item__avatar" :src="item.user_avatar"
|
||||
mode="aspectFill" />
|
||||
<view v-else class="post-item__avatar post-item__avatar--fallback">
|
||||
{{ item.user_name.slice(0, 1) || '?' }}
|
||||
</view>
|
||||
<view class="post-item__author">
|
||||
<text>{{ item.user_name || '匿名用户' }}</text>
|
||||
<text>{{ formatPostFavoriteTime(item.create_time) }}</text>
|
||||
</view>
|
||||
<view class="post-item__remove" @tap.stop="removePost(item)">
|
||||
<u-icon name="star-fill" size="27" color="#e6a23c" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="post-item__content">
|
||||
<text class="post-item__text">{{ item.content || '图片帖子' }}</text>
|
||||
<image v-if="item.images[0]" class="post-item__image" :src="item.images[0]"
|
||||
mode="aspectFill" />
|
||||
</view>
|
||||
<view v-if="item.tags.length" class="post-item__tags">
|
||||
<text v-for="tag in item.tags.slice(0, 2)" :key="tag">#{{ tag }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="collection-state collection-state--empty">
|
||||
<u-icon name="chat" size="72" color="#cbd5e1" />
|
||||
<text class="collection-state__title">暂无帖子收藏</text>
|
||||
<text class="collection-state__desc">在帖子详情互动区点击星标即可加入收藏</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<template v-else-if="activeType === 'match'">
|
||||
<view v-if="matchFavorites.length" class="collection-list match-list">
|
||||
<view v-for="item in matchFavorites" :key="item.id" class="match-item" @tap="openMatch(item)">
|
||||
<view class="match-item__top">
|
||||
<text class="match-item__league">{{ item.league_name || '赛事' }}{{ item.round_name ? ` · ${item.round_name}` : '' }}</text>
|
||||
<text class="match-item__league">{{ item.league_name || '赛事' }}{{ item.round_name ? ` ·
|
||||
${item.round_name}` : '' }}</text>
|
||||
<text class="match-item__status">{{ getMatchFavoriteStatus(item) }}</text>
|
||||
</view>
|
||||
<view class="match-item__content">
|
||||
<view class="match-item__team">
|
||||
<image v-if="item.home_icon" :src="item.home_icon" mode="aspectFit" />
|
||||
<view v-else class="match-item__team-logo">{{ item.home_team.slice(0, 1) || '?' }}</view>
|
||||
<view v-else class="match-item__team-logo">{{ item.home_team.slice(0, 1) || '?' }}
|
||||
</view>
|
||||
<text>{{ item.home_team || '主队' }}</text>
|
||||
</view>
|
||||
<view class="match-item__center">
|
||||
@@ -100,7 +136,8 @@
|
||||
</view>
|
||||
<view class="match-item__team">
|
||||
<image v-if="item.away_icon" :src="item.away_icon" mode="aspectFit" />
|
||||
<view v-else class="match-item__team-logo">{{ item.away_team.slice(0, 1) || '?' }}</view>
|
||||
<view v-else class="match-item__team-logo">{{ item.away_team.slice(0, 1) || '?' }}
|
||||
</view>
|
||||
<text>{{ item.away_team || '客队' }}</text>
|
||||
</view>
|
||||
<view class="match-item__remove" @tap.stop="removeMatch(item)">
|
||||
@@ -150,7 +187,8 @@
|
||||
<view v-if="coins.length" class="collection-list coin-list">
|
||||
<view v-for="coin in coins" :key="coin.symbol" class="coin-item" @tap="openCoin(coin)">
|
||||
<image v-if="coin.image" class="coin-item__icon" :src="coin.image" mode="aspectFit" />
|
||||
<view v-else class="coin-item__icon coin-item__icon--fallback">{{ coin.symbol.slice(0, 1) }}</view>
|
||||
<view v-else class="coin-item__icon coin-item__icon--fallback">{{ coin.symbol.slice(0, 1) }}
|
||||
</view>
|
||||
<view class="coin-item__name">
|
||||
<text>{{ coin.symbol }}</text>
|
||||
<text>{{ coin.name_zh || coin.name }}</text>
|
||||
@@ -196,6 +234,7 @@ import {
|
||||
} from '@/api/crypto'
|
||||
import { getLotteryFavoriteCodes, setLotteryFavoriteCodes } from '@/utils/lottery-favorite'
|
||||
import { getMatchFavorites, setMatchFavorites, type MatchFavoriteItem } from '@/utils/match-favorite'
|
||||
import { getPostFavorites, setPostFavorites, type PostFavoriteItem } from '@/utils/post-favorite'
|
||||
import { getLocalMatchDate } from '@/utils/match-time'
|
||||
|
||||
type CollectionType = 'article' | 'post' | 'match' | 'lottery' | 'crypto'
|
||||
@@ -205,11 +244,12 @@ const collectionTabs: Array<{ key: CollectionType; label: string; icon: string }
|
||||
{ key: 'post', label: '帖子', icon: 'chat' },
|
||||
{ key: 'match', label: '比赛', icon: 'calendar' },
|
||||
{ key: 'lottery', label: '彩票', icon: 'gift' },
|
||||
{ key: 'crypto', label: '加密货币', icon: 'star' }
|
||||
{ key: 'crypto', label: '行情', icon: 'star' }
|
||||
]
|
||||
|
||||
const activeType = ref<CollectionType>('article')
|
||||
const articles = ref<any[]>([])
|
||||
const postFavorites = ref<PostFavoriteItem[]>(getPostFavorites())
|
||||
const matchFavorites = ref<MatchFavoriteItem[]>(getMatchFavorites())
|
||||
const lotteryFavorites = ref<any[]>(getLotteryFavoriteCodes().map((code) => ({ code })))
|
||||
const coins = ref<CryptoMarketCoin[]>([])
|
||||
@@ -217,10 +257,11 @@ const loading = ref(false)
|
||||
const refreshing = ref(false)
|
||||
|
||||
const articleCount = computed(() => articles.value.length)
|
||||
const postCount = computed(() => postFavorites.value.length)
|
||||
const matchCount = computed(() => matchFavorites.value.length)
|
||||
const lotteryCount = computed(() => lotteryFavorites.value.length)
|
||||
const cryptoCount = computed(() => coins.value.length)
|
||||
const collectionCount = computed(() => articleCount.value + matchCount.value + lotteryCount.value + cryptoCount.value)
|
||||
const collectionCount = computed(() => articleCount.value + postCount.value + matchCount.value + lotteryCount.value + cryptoCount.value)
|
||||
const activeTabLabel = computed(() => collectionTabs.find((tab) => tab.key === activeType.value)?.label || '')
|
||||
|
||||
const loadArticles = async () => {
|
||||
@@ -243,6 +284,10 @@ const loadMatches = () => {
|
||||
matchFavorites.value = getMatchFavorites()
|
||||
}
|
||||
|
||||
const loadPosts = () => {
|
||||
postFavorites.value = getPostFavorites()
|
||||
}
|
||||
|
||||
const loadLottery = async () => {
|
||||
const favoriteCodes = getLotteryFavoriteCodes()
|
||||
if (!favoriteCodes.length) {
|
||||
@@ -282,6 +327,8 @@ const loadCollection = async () => {
|
||||
try {
|
||||
if (activeType.value === 'article') {
|
||||
await loadArticles()
|
||||
} else if (activeType.value === 'post') {
|
||||
loadPosts()
|
||||
} else if (activeType.value === 'match') {
|
||||
loadMatches()
|
||||
} else if (activeType.value === 'lottery') {
|
||||
@@ -326,6 +373,26 @@ const removeArticle = async (item: any) => {
|
||||
}
|
||||
}
|
||||
|
||||
const openPost = (item: PostFavoriteItem) => {
|
||||
uni.navigateTo({ url: `/packages_community/pages/post_detail?id=${item.id}` })
|
||||
}
|
||||
|
||||
const removePost = (item: PostFavoriteItem) => {
|
||||
postFavorites.value = setPostFavorites(postFavorites.value.filter((favorite) => favorite.id !== item.id))
|
||||
uni.showToast({ title: '已取消收藏', icon: 'none' })
|
||||
}
|
||||
|
||||
const formatPostFavoriteTime = (value: number | string) => {
|
||||
const timestamp = Number(value)
|
||||
if (Number.isFinite(timestamp) && timestamp > 0) {
|
||||
const date = new Date(timestamp * 1000)
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
return `${month}-${day}`
|
||||
}
|
||||
return String(value || '').replace(/^\d{4}-/, '').slice(0, 10)
|
||||
}
|
||||
|
||||
const openMatch = (item: MatchFavoriteItem) => {
|
||||
uni.navigateTo({ url: `/pages/match_detail/match_detail?id=${item.id}` })
|
||||
}
|
||||
@@ -395,7 +462,7 @@ const formatChange = (value: number) => {
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
if (activeType.value === 'article' || activeType.value === 'match' || activeType.value === 'lottery' || activeType.value === 'crypto') {
|
||||
if (activeType.value === 'article' || activeType.value === 'post' || activeType.value === 'match' || activeType.value === 'lottery' || activeType.value === 'crypto') {
|
||||
loadCollection()
|
||||
}
|
||||
})
|
||||
@@ -518,7 +585,7 @@ onShow(() => {
|
||||
min-width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 0 24rpx;
|
||||
gap: 18rpx;
|
||||
gap: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,24 +595,24 @@ onShow(() => {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8rpx;
|
||||
gap: 2rpx;
|
||||
position: relative;
|
||||
padding: 0 10rpx;
|
||||
padding: 0 2rpx;
|
||||
box-sizing: border-box;
|
||||
color: #252b36;
|
||||
font-size: 27rpx;
|
||||
font-size: 24rpx;
|
||||
white-space: nowrap;
|
||||
|
||||
&--active {
|
||||
color: #1468f5;
|
||||
font-weight: 700;
|
||||
font-weight: 600;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 0;
|
||||
width: 48rpx;
|
||||
width: 140rpx;
|
||||
height: 6rpx;
|
||||
border-radius: 999rpx;
|
||||
background: #1468f5;
|
||||
@@ -675,6 +742,111 @@ onShow(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.post-item {
|
||||
margin-bottom: 16rpx;
|
||||
padding: 20rpx;
|
||||
background: #fff;
|
||||
border: 1rpx solid #e7eaf0;
|
||||
border-radius: 22rpx;
|
||||
box-shadow: 0 8rpx 30rpx rgba(29, 45, 78, 0.04);
|
||||
animation: collection-rise 0.32s ease both;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
&__avatar {
|
||||
width: 42rpx;
|
||||
height: 42rpx;
|
||||
flex-shrink: 0;
|
||||
border-radius: 50%;
|
||||
background: #edf3ff;
|
||||
|
||||
&--fallback {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #1468f5;
|
||||
font-size: 21rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
&__author {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2rpx;
|
||||
|
||||
text:first-child {
|
||||
overflow: hidden;
|
||||
color: #252b36;
|
||||
font-size: 23rpx;
|
||||
font-weight: 600;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
text:last-child {
|
||||
color: #98a2b3;
|
||||
font-size: 19rpx;
|
||||
}
|
||||
}
|
||||
|
||||
&__remove {
|
||||
width: 42rpx;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
gap: 16rpx;
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
&__text {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
color: #252b36;
|
||||
font-size: 25rpx;
|
||||
line-height: 1.5;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
&__image {
|
||||
width: 126rpx;
|
||||
height: 100rpx;
|
||||
flex-shrink: 0;
|
||||
border-radius: 12rpx;
|
||||
background: #f3f5f8;
|
||||
}
|
||||
|
||||
&__tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8rpx;
|
||||
margin-top: 13rpx;
|
||||
|
||||
text {
|
||||
padding: 3rpx 9rpx;
|
||||
border-radius: 6rpx;
|
||||
background: #edf4ff;
|
||||
color: #1468f5;
|
||||
font-size: 18rpx;
|
||||
line-height: 1.3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.match-item {
|
||||
margin-bottom: 16rpx;
|
||||
padding: 20rpx;
|
||||
@@ -936,8 +1108,13 @@ onShow(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.is-up { color: #16a36a; }
|
||||
.is-down { color: #ef5350; }
|
||||
.is-up {
|
||||
color: #16a36a;
|
||||
}
|
||||
|
||||
.is-down {
|
||||
color: #ef5350;
|
||||
}
|
||||
|
||||
.collection-state {
|
||||
min-height: 480rpx;
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
const POST_FAVORITE_STORAGE_KEY = 'community_post_favorites'
|
||||
const MAX_CONTENT_LENGTH = 600
|
||||
|
||||
export interface PostFavoriteItem {
|
||||
id: number
|
||||
content: string
|
||||
images: string[]
|
||||
tags: string[]
|
||||
user_id: number
|
||||
user_name: string
|
||||
user_avatar: string
|
||||
create_time: number | string
|
||||
is_paid: boolean
|
||||
}
|
||||
|
||||
const normalizeText = (value: unknown, maxLength = MAX_CONTENT_LENGTH): string => {
|
||||
return String(value || '').replace(/\s+/g, ' ').trim().slice(0, maxLength)
|
||||
}
|
||||
|
||||
const normalizeFavorite = (value: any): PostFavoriteItem | null => {
|
||||
const id = Number(value?.id)
|
||||
if (!Number.isFinite(id) || id <= 0) return null
|
||||
|
||||
return {
|
||||
id,
|
||||
content: normalizeText(value?.content),
|
||||
images: Array.isArray(value?.images)
|
||||
? value.images.map((item: unknown) => String(item || '').trim()).filter(Boolean).slice(0, 9)
|
||||
: [],
|
||||
tags: Array.isArray(value?.tags)
|
||||
? value.tags.map((item: unknown) => String(item || '').trim()).filter(Boolean).slice(0, 8)
|
||||
: [],
|
||||
user_id: Number(value?.user_id || value?.user?.id || 0),
|
||||
user_name: normalizeText(value?.user_name || value?.user?.nickname || '匿名用户', 48),
|
||||
user_avatar: String(value?.user_avatar || value?.user?.avatar || ''),
|
||||
create_time: value?.create_time ?? '',
|
||||
is_paid: Number(value?.is_paid || 0) === 1
|
||||
}
|
||||
}
|
||||
|
||||
export const getPostFavorites = (): PostFavoriteItem[] => {
|
||||
const stored = uni.getStorageSync(POST_FAVORITE_STORAGE_KEY)
|
||||
if (!Array.isArray(stored)) return []
|
||||
|
||||
const seen = new Set<number>()
|
||||
return stored.reduce<PostFavoriteItem[]>((favorites, item) => {
|
||||
const favorite = normalizeFavorite(item)
|
||||
if (!favorite || seen.has(favorite.id)) return favorites
|
||||
seen.add(favorite.id)
|
||||
favorites.push(favorite)
|
||||
return favorites
|
||||
}, [])
|
||||
}
|
||||
|
||||
export const setPostFavorites = (favorites: PostFavoriteItem[]): PostFavoriteItem[] => {
|
||||
const seen = new Set<number>()
|
||||
const normalized = favorites.reduce<PostFavoriteItem[]>((items, item) => {
|
||||
const favorite = normalizeFavorite(item)
|
||||
if (!favorite || seen.has(favorite.id)) return items
|
||||
seen.add(favorite.id)
|
||||
items.push(favorite)
|
||||
return items
|
||||
}, [])
|
||||
uni.setStorageSync(POST_FAVORITE_STORAGE_KEY, normalized)
|
||||
return normalized
|
||||
}
|
||||
|
||||
export const isPostFavorite = (postId: number | string): boolean => {
|
||||
const id = Number(postId)
|
||||
return getPostFavorites().some((favorite) => favorite.id === id)
|
||||
}
|
||||
|
||||
export const togglePostFavorite = (post: any): { favorite: boolean; favorites: PostFavoriteItem[] } => {
|
||||
const item = normalizeFavorite(post)
|
||||
const favorites = getPostFavorites()
|
||||
if (!item) return { favorite: false, favorites }
|
||||
|
||||
const favorite = favorites.some((existing) => existing.id === item.id)
|
||||
const nextFavorites = favorite
|
||||
? favorites.filter((existing) => existing.id !== item.id)
|
||||
: [item, ...favorites]
|
||||
return { favorite: !favorite, favorites: setPostFavorites(nextFavorites) }
|
||||
}
|
||||
Reference in New Issue
Block a user