| 1 | /** |
| 2 | * 浏览器插件相关的自定义 Hooks |
| 3 | */ |
| 4 | |
| 5 | 'use client' |
| 6 | |
| 7 | import type { |
| 8 | OperationResult, |
| 9 | PluginPlatformType, |
| 10 | ProgressCallback, |
| 11 | PublishParams, |
| 12 | } from './types/baseTypes' |
| 13 | import { useCallback, useEffect } from 'react' |
| 14 | import { DEFAULT_POLLING_INTERVAL } from './constants' |
| 15 | import { usePluginStore } from './store' |
| 16 | import { PluginStatus } from './types/baseTypes' |
| 17 | |
| 18 | /** |
| 19 | * 使用插件状态和方法的 Hook |
| 20 | * @param autoPolling 是否自动轮询插件状态,默认 true |
| 21 | * @param pollingInterval 轮询间隔(毫秒),默认 2000ms |
| 22 | */ |
| 23 | export function usePlugin(autoPolling = true, pollingInterval = DEFAULT_POLLING_INTERVAL) { |
| 24 | const { |
| 25 | status, |
| 26 | isPublishing, |
| 27 | publishProgress, |
| 28 | checkPlugin, |
| 29 | startPolling, |
| 30 | stopPolling, |
| 31 | login, |
| 32 | publish, |
| 33 | resetPublishState, |
| 34 | } = usePluginStore() |
| 35 | |
| 36 | // 自动轮询 |
| 37 | useEffect(() => { |
| 38 | if (autoPolling) { |
| 39 | startPolling(pollingInterval) |
| 40 | return () => { |
| 41 | stopPolling() |
| 42 | } |
| 43 | } |
| 44 | }, [autoPolling, pollingInterval, startPolling, stopPolling]) |
| 45 | |
| 46 | // 判断插件是否已就绪 |
| 47 | const isReady = status === PluginStatus.READY |
| 48 | |
| 49 | // 判断插件是否已连接(兼容旧代码) |
| 50 | const isConnected = isReady |
| 51 | |
| 52 | // 判断插件是否未安装 |
| 53 | const isNotInstalled = status === PluginStatus.NOT_INSTALLED |
| 54 | |
| 55 | // 判断是否正在检测 |
| 56 | const isChecking = status === PluginStatus.CHECKING |
| 57 | |
| 58 | // 判断插件是否已安装但未授权 |
| 59 | const isInstalledNoPermission = status === PluginStatus.INSTALLED_NO_PERMISSION |
| 60 | |
| 61 | return { |
| 62 | // 状态 |
| 63 | status, |
| 64 | isReady, |
| 65 | isConnected, |
| 66 | isNotInstalled, |
| 67 | isChecking, |
| 68 | isInstalledNoPermission, |
| 69 | isPublishing, |
| 70 | publishProgress, |
| 71 | |
| 72 | // 方法 |
| 73 | checkPlugin, |
| 74 | startPolling, |
| 75 | stopPolling, |
| 76 | login, |
| 77 | publish, |
| 78 | resetPublishState, |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | /** |
| 83 | * 使用平台登录的 Hook |
| 84 | */ |
| 85 | export function usePluginLogin() { |
| 86 | const { login } = usePluginStore() |
| 87 | |
| 88 | /** |
| 89 | * 登录到指定平台 |
| 90 | * @param platform 平台类型 |
| 91 | * @returns Promise<账号信息> |
| 92 | */ |
| 93 | const loginToPlatform = useCallback( |
| 94 | async (platform: PluginPlatformType) => { |
| 95 | try { |
| 96 | const accountInfo = await login(platform) |
| 97 | return { success: true, data: accountInfo } as OperationResult |
| 98 | } |
| 99 | catch (error) { |
| 100 | return { |
| 101 | success: false, |
| 102 | error: error instanceof Error ? error.message : '登录失败', |
| 103 | } as OperationResult |
| 104 | } |
| 105 | }, |
| 106 | [login], |
| 107 | ) |
| 108 | |
| 109 | return { |
| 110 | login: loginToPlatform, |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | /** |
| 115 | * 使用发布功能的 Hook |
| 116 | */ |
| 117 | export function usePluginPublish() { |
| 118 | const { publish, isPublishing, publishProgress, resetPublishState } = usePluginStore() |
| 119 | |
| 120 | /** |
| 121 | * 发布内容 |
| 122 | * @param params 发布参数 |
| 123 | * @param onProgress 进度回调 |
| 124 | * @returns Promise<发布结果> |
| 125 | */ |
| 126 | const publishContent = useCallback( |
| 127 | async (params: PublishParams, onProgress?: ProgressCallback) => { |
| 128 | try { |
| 129 | const result = await publish(params, onProgress) |
| 130 | return { success: true, data: result } as OperationResult |
| 131 | } |
| 132 | catch (error) { |
| 133 | return { |
| 134 | success: false, |
| 135 | error: error instanceof Error ? error.message : '发布失败', |
| 136 | } as OperationResult |
| 137 | } |
| 138 | }, |
| 139 | [publish], |
| 140 | ) |
| 141 | |
| 142 | /** |
| 143 | * 发布视频 |
| 144 | */ |
| 145 | const publishVideo = useCallback( |
| 146 | async ( |
| 147 | platform: PluginPlatformType, |
| 148 | video: File | string, |
| 149 | cover: File | string, |
| 150 | options: { |
| 151 | title?: string |
| 152 | desc?: string |
| 153 | topics?: string[] |
| 154 | visibility?: 'public' | 'private' | 'friends' |
| 155 | } = {}, |
| 156 | onProgress?: ProgressCallback, |
| 157 | ) => { |
| 158 | return publishContent( |
| 159 | { |
| 160 | platform, |
| 161 | type: 'video', |
| 162 | video, |
| 163 | cover, |
| 164 | ...options, |
| 165 | }, |
| 166 | onProgress, |
| 167 | ) |
| 168 | }, |
| 169 | [publishContent], |
| 170 | ) |
| 171 | |
| 172 | /** |
| 173 | * 发布图文 |
| 174 | */ |
| 175 | const publishImages = useCallback( |
| 176 | async ( |
| 177 | platform: PluginPlatformType, |
| 178 | images: (File | string)[], |
| 179 | options: { |
| 180 | title?: string |
| 181 | desc?: string |
| 182 | topics?: string[] |
| 183 | visibility?: 'public' | 'private' | 'friends' |
| 184 | } = {}, |
| 185 | onProgress?: ProgressCallback, |
| 186 | ) => { |
| 187 | return publishContent( |
| 188 | { |
| 189 | platform, |
| 190 | type: 'image', |
| 191 | images, |
| 192 | ...options, |
| 193 | }, |
| 194 | onProgress, |
| 195 | ) |
| 196 | }, |
| 197 | [publishContent], |
| 198 | ) |
| 199 | |
| 200 | return { |
| 201 | publish: publishContent, |
| 202 | publishVideo, |
| 203 | publishImages, |
| 204 | isPublishing, |
| 205 | publishProgress, |
| 206 | resetPublishState, |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | /** |
| 211 | * 完整的插件工作流 Hook(登录 + 发布) |
| 212 | */ |
| 213 | export function usePluginWorkflow() { |
| 214 | const { isReady } = usePlugin() |
| 215 | const { login } = usePluginLogin() |
| 216 | const { publishVideo, publishImages } = usePluginPublish() |
| 217 | |
| 218 | /** |
| 219 | * 登录并发布视频 |
| 220 | */ |
| 221 | const loginAndPublishVideo = useCallback( |
| 222 | async ( |
| 223 | platform: PluginPlatformType, |
| 224 | video: File | string, |
| 225 | cover: File | string, |
| 226 | options: { |
| 227 | title?: string |
| 228 | desc?: string |
| 229 | topics?: string[] |
| 230 | } = {}, |
| 231 | onProgress?: ProgressCallback, |
| 232 | ) => { |
| 233 | // 第一步:登录 |
| 234 | const loginResult = await login(platform) |
| 235 | if (!loginResult.success) { |
| 236 | return loginResult |
| 237 | } |
| 238 | |
| 239 | // 第二步:发布 |
| 240 | return publishVideo(platform, video, cover, options, onProgress) |
| 241 | }, |
| 242 | [login, publishVideo], |
| 243 | ) |
| 244 | |
| 245 | /** |
| 246 | * 登录并发布图文 |
| 247 | */ |
| 248 | const loginAndPublishImages = useCallback( |
| 249 | async ( |
| 250 | platform: PluginPlatformType, |
| 251 | images: (File | string)[], |
| 252 | options: { |
| 253 | title?: string |
| 254 | desc?: string |
| 255 | topics?: string[] |
| 256 | } = {}, |
| 257 | onProgress?: ProgressCallback, |
| 258 | ) => { |
| 259 | // 第一步:登录 |
| 260 | const loginResult = await login(platform) |
| 261 | if (!loginResult.success) { |
| 262 | return loginResult |
| 263 | } |
| 264 | |
| 265 | // 第二步:发布 |
| 266 | return publishImages(platform, images, options, onProgress) |
| 267 | }, |
| 268 | [login, publishImages], |
| 269 | ) |
| 270 | |
| 271 | return { |
| 272 | isReady, |
| 273 | loginAndPublishVideo, |
| 274 | loginAndPublishImages, |
| 275 | } |
| 276 | } |
| 277 |