docs(readme): 📝 update platform support information for macOS and iOS
Add Mac26 support confirmation for macOS Clarify iOS26 real device support and Intel chip simulator limitation Update both English and Chinese documentation files
This commit is contained in:
@@ -95,9 +95,9 @@
|
||||
| Platform | Supported Versions |
|
||||
|----------|-------------------|
|
||||
| Windows | Windows 10, Windows 11 |
|
||||
| macOS | macOS 10.5+ |
|
||||
| macOS | macOS 10.5+ Mac26 has supported |
|
||||
| Linux | Ubuntu 22.0+ |
|
||||
| iOS | iOS 9.0+ ⚠️(ios26 compatible) |
|
||||
| iOS | iOS 9.0+ (iOS26 Real machine is supported. Tauri does not support Intel chips running on ios26 emulators) |
|
||||
| Android | Android 12+ (SDK30+) |
|
||||
| Web | ⚠️Not currently supported (custom removal required) |
|
||||
|
||||
|
||||
@@ -94,9 +94,9 @@
|
||||
| 平台 | 支持版本 |
|
||||
| ------- | ------------------------------------ |
|
||||
| Windows | Windows 10, Windows 11 |
|
||||
| macOS | macOS 10.5+ |
|
||||
| macOS | macOS 10.5+ Mac26已支持 |
|
||||
| Linux | Ubuntu 22.0+ |
|
||||
| iOS | iOS 9.0+ ⚠️(ios26后续兼容) |
|
||||
| iOS | iOS 9.0+ (iOS26 真机已支持, Tauri不支持Intel芯片在ios26模拟器上运行) |
|
||||
| Android | Android 12+ (SDK30+) |
|
||||
| Web | ⚠️暂不支持(需要自定义移除对桌面功能) |
|
||||
|
||||
|
||||
@@ -96,9 +96,9 @@
|
||||
| Platform | Supported Versions |
|
||||
|----------|-------------------|
|
||||
| Windows | Windows 10, Windows 11 |
|
||||
| macOS | macOS 10.5+ |
|
||||
| macOS | macOS 10.5+ Mac26 has supported |
|
||||
| Linux | Ubuntu 22.0+ |
|
||||
| iOS | iOS 9.0+ ⚠️(ios26 compatible) |
|
||||
| iOS | iOS 9.0+ (iOS26 Real machine is supported. Tauri does not support Intel chips running on ios26 emulators) |
|
||||
| Android | Android 12+ (SDK30+) |
|
||||
| Web | ⚠️Not currently supported (custom removal required) |
|
||||
|
||||
|
||||
@@ -95,9 +95,9 @@
|
||||
| 平台 | 支持版本 |
|
||||
| ------- | ------------------------------------ |
|
||||
| Windows | Windows 10, Windows 11 |
|
||||
| macOS | macOS 10.5+ |
|
||||
| macOS | macOS 10.5+ Mac26已支持 |
|
||||
| Linux | Ubuntu 22.0+ |
|
||||
| iOS | iOS 9.0+ ⚠️(ios26后续兼容) |
|
||||
| iOS | iOS 9.0+ (iOS26 真机已支持, Tauri不支持Intel芯片在ios26模拟器上运行) |
|
||||
| Android | Android 12+ (SDK30+) |
|
||||
| Web | ⚠️暂不支持(需要自定义移除对桌面功能) |
|
||||
|
||||
|
||||
@@ -544,9 +544,6 @@ const requestNetworkPermissionForIOS = async () => {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 初始化朋友圈通知存储
|
||||
feedNotificationStore.initialize()
|
||||
|
||||
// iOS应用启动时预请求网络权限(必须在最开始执行)
|
||||
if (isIOS()) {
|
||||
requestNetworkPermissionForIOS()
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<h3 class="text-16px font-600">朋友圈通知</h3>
|
||||
<div class="flex items-center gap-8px">
|
||||
<n-button
|
||||
v-if="notificationStore.notificationStats.unreadCount > 0"
|
||||
v-if="feednotificationStore.notificationStats.unreadCount > 0"
|
||||
text
|
||||
type="primary"
|
||||
size="small"
|
||||
@@ -24,13 +24,13 @@
|
||||
<!-- 通知列表 -->
|
||||
<div class="flex-1 overflow-y-auto">
|
||||
<div
|
||||
v-if="notificationStore.notifications.length === 0"
|
||||
v-if="feednotificationStore.notifications.length === 0"
|
||||
class="flex items-center justify-center h-full text-#999">
|
||||
暂无通知
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="notification in notificationStore.notifications"
|
||||
v-for="notification in feednotificationStore.notifications"
|
||||
:key="notification.id"
|
||||
class="border-b border-#f0f0f0 p-12px hover:bg-#f9f9f9 cursor-pointer transition-colors"
|
||||
@click="handleNotificationClick(notification)">
|
||||
@@ -73,7 +73,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 底部操作 -->
|
||||
<div v-if="notificationStore.notifications.length > 0" class="border-t border-#e5e5e5 p-12px flex gap-8px">
|
||||
<div v-if="feednotificationStore.notifications.length > 0" class="border-t border-#e5e5e5 p-12px flex gap-8px">
|
||||
<n-button type="error" text block size="small" @click="clearAllNotifications">清空所有通知</n-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -122,14 +122,14 @@
|
||||
import { useFeedNotificationStore } from '@/stores/feedNotification'
|
||||
import { AvatarUtils } from '@/utils/AvatarUtils'
|
||||
|
||||
const notificationStore = useFeedNotificationStore()
|
||||
const feednotificationStore = useFeedNotificationStore()
|
||||
const showPopup = ref(false)
|
||||
const showCommentModal = ref(false)
|
||||
const selectedNotification = ref<any>(null)
|
||||
|
||||
// 监听通知列表变化
|
||||
watch(
|
||||
() => notificationStore.notifications.length,
|
||||
() => feednotificationStore.notifications.length,
|
||||
(newLength) => {
|
||||
console.log('📢 通知列表变化,当前通知数:', newLength)
|
||||
}
|
||||
@@ -139,8 +139,8 @@ watch(
|
||||
* 打开弹窗
|
||||
*/
|
||||
const openPopup = () => {
|
||||
console.log('🔔 打开通知弹窗,当前通知数:', notificationStore.notifications.length)
|
||||
console.log('🔔 通知列表:', notificationStore.notifications)
|
||||
console.log('🔔 打开通知弹窗,当前通知数:', feednotificationStore.notifications.length)
|
||||
console.log('🔔 通知列表:', feednotificationStore.notifications)
|
||||
showPopup.value = true
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ const closePopup = () => {
|
||||
* 处理通知点击
|
||||
*/
|
||||
const handleNotificationClick = (notification: any) => {
|
||||
notificationStore.markAsRead(notification.id)
|
||||
feednotificationStore.markAsRead(notification.id)
|
||||
selectedNotification.value = notification
|
||||
showCommentModal.value = true
|
||||
}
|
||||
@@ -164,14 +164,14 @@ const handleNotificationClick = (notification: any) => {
|
||||
* 标记所有为已读
|
||||
*/
|
||||
const markAllAsRead = () => {
|
||||
notificationStore.markAllAsRead()
|
||||
feednotificationStore.markAllAsRead()
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除通知
|
||||
*/
|
||||
const deleteNotification = (notificationId: string) => {
|
||||
notificationStore.deleteNotification(notificationId)
|
||||
feednotificationStore.deleteNotification(notificationId)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,7 +179,7 @@ const deleteNotification = (notificationId: string) => {
|
||||
*/
|
||||
const clearAllNotifications = () => {
|
||||
if (confirm('确定要清空所有通知吗?')) {
|
||||
notificationStore.clearAllNotifications()
|
||||
feednotificationStore.clearAllNotifications()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -195,7 +195,9 @@ export enum StoresEnum {
|
||||
/** 文件管理 */
|
||||
FILE = 'file',
|
||||
/** 缩略图缓存 */
|
||||
THUMBNAIL_CACHE = 'thumbnailCache'
|
||||
THUMBNAIL_CACHE = 'thumbnailCache',
|
||||
/** 初始化同步状态 */
|
||||
INITIAL_SYNC = 'initialSync'
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -41,6 +41,7 @@ import { useChatStore } from '@/stores/chat'
|
||||
import { useFileStore } from '@/stores/file'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { useSettingStore } from '@/stores/setting.ts'
|
||||
import { useInitialSyncStore } from '@/stores/initialSync.ts'
|
||||
import { invokeSilently } from '@/utils/TauriInvokeHandler'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { audioManager } from '@/utils/AudioManager'
|
||||
@@ -50,48 +51,42 @@ const userStore = useUserStore()
|
||||
const chatStore = useChatStore()
|
||||
const fileStore = useFileStore()
|
||||
const settingStore = useSettingStore()
|
||||
// 负责记录哪些账号已经完成过首次同步的全局 store,避免多账号串数据
|
||||
const initialSyncStore = useInitialSyncStore()
|
||||
const userUid = computed(() => userStore.userInfo?.uid ?? '')
|
||||
const hasCachedSessions = computed(() => chatStore.sessionList.length > 0)
|
||||
const appWindow = WebviewWindow.getCurrent()
|
||||
const loadingPercentage = ref(10)
|
||||
const loadingText = ref('正在加载应用...')
|
||||
const { resetLoginState, logout, init } = useLogin()
|
||||
const INITIAL_SYNC_FLAG_PREFIX = 'hula_initial_sync_'
|
||||
const initialSyncStorageKey = computed(() => (userUid.value ? `${INITIAL_SYNC_FLAG_PREFIX}${userUid.value}` : ''))
|
||||
// 是否需要阻塞首屏并做初始化同步
|
||||
const requiresInitialSync = ref(true)
|
||||
const shouldBlockInitialRender = computed(() => requiresInitialSync.value && !hasCachedSessions.value)
|
||||
|
||||
// 根据当前 uid 判断是否需要阻塞首屏并重新同步(依赖持久化的初始化完成名单)
|
||||
const syncInitialSyncState = () => {
|
||||
if (!initialSyncStorageKey.value || typeof window === 'undefined') {
|
||||
if (!userUid.value || typeof window === 'undefined') {
|
||||
requiresInitialSync.value = true
|
||||
return
|
||||
}
|
||||
try {
|
||||
requiresInitialSync.value = localStorage.getItem(initialSyncStorageKey.value) !== '1'
|
||||
} catch (error) {
|
||||
console.warn('[layout] 读取初始化标记失败:', error)
|
||||
requiresInitialSync.value = true
|
||||
}
|
||||
requiresInitialSync.value = !initialSyncStore.isSynced(userUid.value)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => initialSyncStorageKey.value,
|
||||
() => userUid.value,
|
||||
() => {
|
||||
syncInitialSyncState()
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
// 初始化同步成功后标记当前 uid,后续启动直接走增量
|
||||
const markInitialSyncCompleted = () => {
|
||||
if (!initialSyncStorageKey.value || typeof window === 'undefined') {
|
||||
if (!userUid.value || typeof window === 'undefined') {
|
||||
requiresInitialSync.value = false
|
||||
return
|
||||
}
|
||||
try {
|
||||
localStorage.setItem(initialSyncStorageKey.value, '1')
|
||||
} catch (error) {
|
||||
console.warn('[layout] 写入初始化标记失败:', error)
|
||||
}
|
||||
initialSyncStore.markSynced(userUid.value)
|
||||
requiresInitialSync.value = false
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,11 @@ export interface FeedNotificationItem {
|
||||
isRead: boolean // 是否已读
|
||||
}
|
||||
|
||||
interface NotificationStats {
|
||||
unreadCount: number
|
||||
totalCount: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 朋友圈通知 Store
|
||||
* 管理朋友圈的点赞和评论通知
|
||||
@@ -27,7 +32,7 @@ export const useFeedNotificationStore = defineStore(StoresEnum.FEED_NOTIFICATION
|
||||
const notifications = ref<FeedNotificationItem[]>([])
|
||||
|
||||
// 通知统计
|
||||
const notificationStats = reactive({
|
||||
const notificationStats = reactive<NotificationStats>({
|
||||
unreadCount: 0, // 未读通知数
|
||||
totalCount: 0 // 总通知数
|
||||
})
|
||||
@@ -52,9 +57,6 @@ export const useFeedNotificationStore = defineStore(StoresEnum.FEED_NOTIFICATION
|
||||
if (!notification.isRead) {
|
||||
notificationStats.unreadCount++
|
||||
}
|
||||
|
||||
// 保存到本地存储
|
||||
saveToLocalStorage()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +68,6 @@ export const useFeedNotificationStore = defineStore(StoresEnum.FEED_NOTIFICATION
|
||||
if (notification && !notification.isRead) {
|
||||
notification.isRead = true
|
||||
notificationStats.unreadCount = Math.max(0, notificationStats.unreadCount - 1)
|
||||
saveToLocalStorage()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +79,6 @@ export const useFeedNotificationStore = defineStore(StoresEnum.FEED_NOTIFICATION
|
||||
n.isRead = true
|
||||
})
|
||||
notificationStats.unreadCount = 0
|
||||
saveToLocalStorage()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,7 +93,6 @@ export const useFeedNotificationStore = defineStore(StoresEnum.FEED_NOTIFICATION
|
||||
}
|
||||
notifications.value.splice(index, 1)
|
||||
notificationStats.totalCount = Math.max(0, notificationStats.totalCount - 1)
|
||||
saveToLocalStorage()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,46 +103,6 @@ export const useFeedNotificationStore = defineStore(StoresEnum.FEED_NOTIFICATION
|
||||
notifications.value = []
|
||||
notificationStats.unreadCount = 0
|
||||
notificationStats.totalCount = 0
|
||||
localStorage.removeItem('feedNotifications')
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存到本地存储
|
||||
*/
|
||||
const saveToLocalStorage = () => {
|
||||
try {
|
||||
const data = {
|
||||
notifications: notifications.value,
|
||||
stats: notificationStats
|
||||
}
|
||||
localStorage.setItem('feedNotifications', JSON.stringify(data))
|
||||
} catch (error) {
|
||||
console.error('保存通知到本地存储失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从本地存储加载
|
||||
*/
|
||||
const loadFromLocalStorage = () => {
|
||||
try {
|
||||
const data = localStorage.getItem('feedNotifications')
|
||||
if (data) {
|
||||
const parsed = JSON.parse(data)
|
||||
notifications.value = parsed.notifications || []
|
||||
notificationStats.unreadCount = parsed.stats?.unreadCount || 0
|
||||
notificationStats.totalCount = parsed.stats?.totalCount || 0
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('从本地存储加载通知失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化(应在应用启动时调用)
|
||||
*/
|
||||
const initialize = () => {
|
||||
loadFromLocalStorage()
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -153,7 +112,6 @@ export const useFeedNotificationStore = defineStore(StoresEnum.FEED_NOTIFICATION
|
||||
markAsRead,
|
||||
markAllAsRead,
|
||||
deleteNotification,
|
||||
clearAllNotifications,
|
||||
initialize
|
||||
clearAllNotifications
|
||||
}
|
||||
})
|
||||
|
||||
31
src/stores/initialSync.ts
Normal file
31
src/stores/initialSync.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { ref } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import { StoresEnum } from '@/enums'
|
||||
|
||||
export const useInitialSyncStore = defineStore(StoresEnum.INITIAL_SYNC, () => {
|
||||
const syncedUsers = ref<string[]>([])
|
||||
|
||||
/** 判断指定 uid 是否已经完成过初始化同步*/
|
||||
const isSynced = (uid: string) => {
|
||||
if (!uid) {
|
||||
return false
|
||||
}
|
||||
return syncedUsers.value.includes(uid)
|
||||
}
|
||||
|
||||
/** 标记指定 uid 已完成初始化同步 */
|
||||
const markSynced = (uid: string) => {
|
||||
if (!uid) {
|
||||
return
|
||||
}
|
||||
if (!syncedUsers.value.includes(uid)) {
|
||||
syncedUsers.value = [...syncedUsers.value, uid]
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
syncedUsers,
|
||||
isSynced,
|
||||
markSynced
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user