@quicktvui/tv-ad-unlock 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +155 -0
- package/package.json +28 -0
- package/src/TVAdUnlock.vue +218 -0
- package/src/index.ts +3 -0
- package/src/types.ts +57 -0
- package/src/useTVAdUnlock.ts +277 -0
package/README.md
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# @quicktvui/tv-ad-unlock
|
|
2
|
+
|
|
3
|
+
TV广告解锁组件,用于QuickTVUI框架的电视端广告解锁功能。
|
|
4
|
+
|
|
5
|
+
## 功能特性
|
|
6
|
+
|
|
7
|
+
- 生成电视端广告二维码
|
|
8
|
+
- 轮询广告观看状态
|
|
9
|
+
- 自动管理解锁状态
|
|
10
|
+
- 支持自定义配置
|
|
11
|
+
|
|
12
|
+
## 安装
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add @quicktvui/tv-ad-unlock
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 使用方法
|
|
19
|
+
|
|
20
|
+
### 1. 基础使用
|
|
21
|
+
|
|
22
|
+
```vue
|
|
23
|
+
<template>
|
|
24
|
+
<TVAdUnlock
|
|
25
|
+
:config="adConfig"
|
|
26
|
+
:storageKeys="storageKeys"
|
|
27
|
+
:requestManager="requestManager"
|
|
28
|
+
@unlockSuccess="onUnlockSuccess"
|
|
29
|
+
@unlockFailed="onUnlockFailed"
|
|
30
|
+
/>
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<script setup lang="ts">
|
|
34
|
+
import { TVAdUnlock, useTVAdUnlockStorage } from '@quicktvui/tv-ad-unlock'
|
|
35
|
+
import type { TVAdUnlockConfig, TVAdUnlockStorageKeys, RequestManager } from '@quicktvui/tv-ad-unlock'
|
|
36
|
+
|
|
37
|
+
const adConfig: TVAdUnlockConfig = {
|
|
38
|
+
packageName: 'com.example.app',
|
|
39
|
+
superRequestBaseUrl: 'https://superapi.extscreen.com/extscreenapi/api',
|
|
40
|
+
invalidTimeout: 300000,
|
|
41
|
+
pollInterval: 1000
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const storageKeys: TVAdUnlockStorageKeys = {
|
|
45
|
+
playCountKey: 'app_play_count',
|
|
46
|
+
unlockDateKey: 'app_ad_unlock_date',
|
|
47
|
+
unlockedTodayKey: 'app_ad_unlocked_today'
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const requestManager: RequestManager = {
|
|
51
|
+
post: async (url, data) => { /* 实现请求逻辑 */ },
|
|
52
|
+
get: async (url, data) => { /* 实现请求逻辑 */ }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function onUnlockSuccess() {
|
|
56
|
+
console.log('解锁成功')
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function onUnlockFailed(error: Error) {
|
|
60
|
+
console.error('解锁失败', error)
|
|
61
|
+
}
|
|
62
|
+
</script>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 2. 使用 Composable
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { useTVAdUnlock, useTVAdUnlockStorage } from '@quicktvui/tv-ad-unlock'
|
|
69
|
+
import { useESLocalStorage } from '@extscreen/es3-core'
|
|
70
|
+
|
|
71
|
+
const storage = useESLocalStorage()
|
|
72
|
+
|
|
73
|
+
const storageKeys = {
|
|
74
|
+
playCountKey: 'app_play_count',
|
|
75
|
+
unlockDateKey: 'app_ad_unlock_date',
|
|
76
|
+
unlockedTodayKey: 'app_ad_unlocked_today'
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const adStorage = useTVAdUnlockStorage(storage, storageKeys, 32)
|
|
80
|
+
|
|
81
|
+
async function checkNeedShowAd() {
|
|
82
|
+
const needAd = await adStorage.checkNeedAd()
|
|
83
|
+
if (needAd) {
|
|
84
|
+
router.push({ name: 'ad' })
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 3. 路由配置
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { ESRouteType } from '@extscreen/es3-router'
|
|
93
|
+
|
|
94
|
+
const routes = [
|
|
95
|
+
{
|
|
96
|
+
path: '/ad',
|
|
97
|
+
name: 'ad',
|
|
98
|
+
component: () => import('./views/ad/index.vue'),
|
|
99
|
+
type: ESRouteType.ES_ROUTE_TYPE_DIALOG
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## API
|
|
105
|
+
|
|
106
|
+
### Props
|
|
107
|
+
|
|
108
|
+
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|
|
109
|
+
|------|------|------|--------|------|
|
|
110
|
+
| config | TVAdUnlockConfig | 是 | - | 广告配置 |
|
|
111
|
+
| storageKeys | TVAdUnlockStorageKeys | 是 | - | 存储键名配置 |
|
|
112
|
+
| requestManager | RequestManager | 是 | - | 请求管理器 |
|
|
113
|
+
| title | string | 否 | '观看30秒广告 当日解锁' | 标题文字 |
|
|
114
|
+
| subTitle | string | 否 | '手机打开【微信】扫码' | 副标题文字 |
|
|
115
|
+
| invalidCodeImage | string | 否 | '' | 二维码失效图片 |
|
|
116
|
+
| scannedCodeImage | string | 否 | '' | 已扫码图片 |
|
|
117
|
+
| scanTitle | string | 否 | '观看奖励' | 扫码标题 |
|
|
118
|
+
| scanContent | string | 否 | '观看广告后可获得奖励' | 扫码内容 |
|
|
119
|
+
| scanToast | string | 否 | '奖励已获得,请前往电视端观看' | 扫码提示 |
|
|
120
|
+
|
|
121
|
+
### Events
|
|
122
|
+
|
|
123
|
+
| 事件名 | 参数 | 说明 |
|
|
124
|
+
|--------|------|------|
|
|
125
|
+
| unlockSuccess | - | 解锁成功 |
|
|
126
|
+
| unlockFailed | error: Error | 解锁失败 |
|
|
127
|
+
| qrCodeInvalid | - | 二维码失效 |
|
|
128
|
+
| qrCodeScanned | - | 二维码已扫码 |
|
|
129
|
+
|
|
130
|
+
### Types
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
interface TVAdUnlockConfig {
|
|
134
|
+
packageName: string
|
|
135
|
+
superRequestBaseUrl: string
|
|
136
|
+
trackBaseUrl?: string
|
|
137
|
+
invalidTimeout?: number
|
|
138
|
+
pollInterval?: number
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
interface TVAdUnlockStorageKeys {
|
|
142
|
+
playCountKey: string
|
|
143
|
+
unlockDateKey: string
|
|
144
|
+
unlockedTodayKey: string
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
interface RequestManager {
|
|
148
|
+
post(url: string, data: Record<string, any>): Promise<any>
|
|
149
|
+
get(url: string, data: string): Promise<any>
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## License
|
|
154
|
+
|
|
155
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@quicktvui/tv-ad-unlock",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "TV广告解锁组件 - 用于QuickTVUI框架的电视端广告解锁功能",
|
|
5
|
+
"main": "src/index.ts",
|
|
6
|
+
"types": "src/index.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"src"
|
|
9
|
+
],
|
|
10
|
+
"keywords": [
|
|
11
|
+
"quicktvui",
|
|
12
|
+
"tv",
|
|
13
|
+
"ad",
|
|
14
|
+
"unlock",
|
|
15
|
+
"qrcode"
|
|
16
|
+
],
|
|
17
|
+
"author": "",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"vue": "^3.0.0",
|
|
21
|
+
"@extscreen/es3-core": ">=3.0.0",
|
|
22
|
+
"@extscreen/es3-router": ">=3.0.0"
|
|
23
|
+
},
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": ""
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="ad-container">
|
|
3
|
+
<div class="content">
|
|
4
|
+
<div class="content-bg" />
|
|
5
|
+
<span class="title">{{ title }}</span>
|
|
6
|
+
<span class="sub-title">{{ subTitle }}</span>
|
|
7
|
+
<div class="qrcode-container">
|
|
8
|
+
<qt-qr-code
|
|
9
|
+
v-if="adQrCode && !isInvalid && qrCodeState !== 1"
|
|
10
|
+
class="qrcode"
|
|
11
|
+
:content="adQrCode"
|
|
12
|
+
/>
|
|
13
|
+
<img
|
|
14
|
+
v-if="isInvalid"
|
|
15
|
+
class="invalid-code"
|
|
16
|
+
:src="invalidCodeImage"
|
|
17
|
+
/>
|
|
18
|
+
<img
|
|
19
|
+
v-if="qrCodeState === 1"
|
|
20
|
+
class="invalid-code"
|
|
21
|
+
:src="scannedCodeImage"
|
|
22
|
+
>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<script setup lang="ts">
|
|
29
|
+
import { ref, computed } from 'vue'
|
|
30
|
+
import { ESKeyCode, useESRuntime, useESToast, useESLocalStorage } from '@extscreen/es3-core'
|
|
31
|
+
import { useESRouter } from '@extscreen/es3-router'
|
|
32
|
+
import { useTVAdUnlock, useTVAdUnlockStorage } from './useTVAdUnlock'
|
|
33
|
+
import type { TVAdUnlockConfig, TVAdUnlockParams, TVAdUnlockStorageKeys, RequestManager } from './types'
|
|
34
|
+
|
|
35
|
+
interface Props {
|
|
36
|
+
config: TVAdUnlockConfig
|
|
37
|
+
storageKeys: TVAdUnlockStorageKeys
|
|
38
|
+
requestManager: RequestManager
|
|
39
|
+
title?: string
|
|
40
|
+
subTitle?: string
|
|
41
|
+
invalidCodeImage?: string
|
|
42
|
+
scannedCodeImage?: string
|
|
43
|
+
scanTitle?: string
|
|
44
|
+
scanContent?: string
|
|
45
|
+
scanToast?: string
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
49
|
+
title: '观看30秒广告 当日解锁',
|
|
50
|
+
subTitle: '手机打开【微信】扫码',
|
|
51
|
+
invalidCodeImage: '',
|
|
52
|
+
scannedCodeImage: '',
|
|
53
|
+
scanTitle: '观看奖励',
|
|
54
|
+
scanContent: '观看广告后可获得奖励',
|
|
55
|
+
scanToast: '奖励已获得,请前往电视端观看'
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
const emit = defineEmits<{
|
|
59
|
+
(e: 'unlockSuccess'): void
|
|
60
|
+
(e: 'unlockFailed', error: Error): void
|
|
61
|
+
(e: 'qrCodeInvalid'): void
|
|
62
|
+
(e: 'qrCodeScanned'): void
|
|
63
|
+
}>()
|
|
64
|
+
|
|
65
|
+
const toast = useESToast()
|
|
66
|
+
const storage = useESLocalStorage()
|
|
67
|
+
const router = useESRouter()
|
|
68
|
+
const runtime = useESRuntime()
|
|
69
|
+
const dnum = runtime.getRuntimeDeviceId()
|
|
70
|
+
|
|
71
|
+
const payParams = ref<TVAdUnlockParams>({
|
|
72
|
+
assetId: '',
|
|
73
|
+
assetName: '',
|
|
74
|
+
fromId: '',
|
|
75
|
+
fromName: ''
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
const {
|
|
79
|
+
adQrCode,
|
|
80
|
+
isInvalid,
|
|
81
|
+
qrCodeState,
|
|
82
|
+
isLoading,
|
|
83
|
+
isShowConfirmToast,
|
|
84
|
+
getAdQrCode,
|
|
85
|
+
stopPollTvADStatusTimer,
|
|
86
|
+
stopInvalidTimer,
|
|
87
|
+
reset
|
|
88
|
+
} = useTVAdUnlock(
|
|
89
|
+
props.config,
|
|
90
|
+
props.storageKeys,
|
|
91
|
+
props.requestManager,
|
|
92
|
+
{
|
|
93
|
+
onUnlockSuccess: () => handleUnlockSuccess(),
|
|
94
|
+
onUnlockFailed: (error) => emit('unlockFailed', error),
|
|
95
|
+
onQrCodeInvalid: () => emit('qrCodeInvalid'),
|
|
96
|
+
onQrCodeScanned: () => emit('qrCodeScanned')
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
const adStorage = useTVAdUnlockStorage(storage, props.storageKeys)
|
|
101
|
+
|
|
102
|
+
async function handleUnlockSuccess() {
|
|
103
|
+
await adStorage.setTodayUnlocked()
|
|
104
|
+
emit('unlockSuccess')
|
|
105
|
+
toast.showToast('解锁成功,继续观看吧!')
|
|
106
|
+
router.back()
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function onKeyDown(event: any) {
|
|
110
|
+
switch (event.keyCode) {
|
|
111
|
+
case ESKeyCode.ES_KEYCODE_DPAD_CENTER:
|
|
112
|
+
case ESKeyCode.ES_KEYCODE_ENTER:
|
|
113
|
+
if (isInvalid.value || qrCodeState.value === 1) {
|
|
114
|
+
getAdQrCode(dnum, props.scanTitle, props.scanContent, props.scanToast)
|
|
115
|
+
}
|
|
116
|
+
break
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function onESCreate(params: TVAdUnlockParams) {
|
|
121
|
+
payParams.value = params || { assetId: '', assetName: '', fromId: '', fromName: '' }
|
|
122
|
+
getAdQrCode(dnum, props.scanTitle, props.scanContent, props.scanToast)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function onBackPressed() {
|
|
126
|
+
if (!isShowConfirmToast.value && qrCodeState.value === 1) {
|
|
127
|
+
toast.showToast('广告播放中,请不要关闭弹窗')
|
|
128
|
+
isShowConfirmToast.value = true
|
|
129
|
+
return true
|
|
130
|
+
} else {
|
|
131
|
+
stopPollTvADStatusTimer()
|
|
132
|
+
stopInvalidTimer()
|
|
133
|
+
router.back()
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function onESDestroy() {
|
|
138
|
+
stopPollTvADStatusTimer()
|
|
139
|
+
stopInvalidTimer()
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
defineExpose({
|
|
143
|
+
onESCreate,
|
|
144
|
+
onKeyDown,
|
|
145
|
+
onBackPressed,
|
|
146
|
+
onESDestroy
|
|
147
|
+
})
|
|
148
|
+
</script>
|
|
149
|
+
|
|
150
|
+
<style scoped>
|
|
151
|
+
.ad-container {
|
|
152
|
+
width: 1920px;
|
|
153
|
+
height: 1080px;
|
|
154
|
+
background-color: rgba(0, 0, 0, 0.7);
|
|
155
|
+
display: flex;
|
|
156
|
+
justify-content: center;
|
|
157
|
+
align-items: center;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.content {
|
|
161
|
+
width: 892px;
|
|
162
|
+
height: 646px;
|
|
163
|
+
display: flex;
|
|
164
|
+
flex-direction: column;
|
|
165
|
+
align-items: center;
|
|
166
|
+
justify-content: center;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.content-bg {
|
|
170
|
+
position: absolute;
|
|
171
|
+
width: 892px;
|
|
172
|
+
height: 646px;
|
|
173
|
+
background-color: #FBE6C4;
|
|
174
|
+
border-radius: 30px;
|
|
175
|
+
border-width: 1px;
|
|
176
|
+
border-color: #979797;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.title {
|
|
180
|
+
font-weight: 500;
|
|
181
|
+
font-size: 46px;
|
|
182
|
+
color: #000000;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.sub-title {
|
|
186
|
+
margin-top: 32px;
|
|
187
|
+
font-weight: 400;
|
|
188
|
+
font-size: 30px;
|
|
189
|
+
color: rgba(0, 0, 0, 0.65);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.qrcode-container {
|
|
193
|
+
margin-top: 32px;
|
|
194
|
+
width: 300px;
|
|
195
|
+
height: 300px;
|
|
196
|
+
border-radius: 30px;
|
|
197
|
+
background-color: #FFFFFF;
|
|
198
|
+
display: flex;
|
|
199
|
+
justify-content: center;
|
|
200
|
+
align-items: center;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.qrcode {
|
|
204
|
+
position: absolute;
|
|
205
|
+
width: 260px;
|
|
206
|
+
height: 260px;
|
|
207
|
+
border-radius: 30px;
|
|
208
|
+
background-color: #FFFFFF;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.invalid-code {
|
|
212
|
+
position: absolute;
|
|
213
|
+
width: 300px;
|
|
214
|
+
height: 300px;
|
|
215
|
+
border-radius: 30px;
|
|
216
|
+
background-color: transparent;
|
|
217
|
+
}
|
|
218
|
+
</style>
|
package/src/index.ts
ADDED
package/src/types.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export interface TVAdUnlockConfig {
|
|
2
|
+
packageName: string
|
|
3
|
+
superRequestBaseUrl: string
|
|
4
|
+
trackBaseUrl?: string
|
|
5
|
+
invalidTimeout?: number
|
|
6
|
+
pollInterval?: number
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface TVAdUnlockParams {
|
|
10
|
+
assetId?: string
|
|
11
|
+
assetName?: string
|
|
12
|
+
fromId?: string
|
|
13
|
+
fromName?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface TVAdUnlockCallbacks {
|
|
17
|
+
onUnlockSuccess?: () => void
|
|
18
|
+
onUnlockFailed?: (error: Error) => void
|
|
19
|
+
onQrCodeInvalid?: () => void
|
|
20
|
+
onQrCodeScanned?: () => void
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface TVAdUnlockState {
|
|
24
|
+
adQrCode: string
|
|
25
|
+
isInvalid: boolean
|
|
26
|
+
qrCodeState: number
|
|
27
|
+
isLoading: boolean
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface TVAdUnlockStorageKeys {
|
|
31
|
+
playCountKey: string
|
|
32
|
+
unlockDateKey: string
|
|
33
|
+
unlockedTodayKey: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface TVAdUnlockOptions {
|
|
37
|
+
config: TVAdUnlockConfig
|
|
38
|
+
storageKeys: TVAdUnlockStorageKeys
|
|
39
|
+
freePlayCount?: number
|
|
40
|
+
callbacks?: TVAdUnlockCallbacks
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface RequestManager {
|
|
44
|
+
post(url: string, data: Record<string, any>): Promise<any>
|
|
45
|
+
get(url: string, data: string): Promise<any>
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface TrackEventParams {
|
|
49
|
+
id: string
|
|
50
|
+
name: string
|
|
51
|
+
pageId?: string
|
|
52
|
+
pageName?: string
|
|
53
|
+
pageSourceId?: string
|
|
54
|
+
pageSourceName?: string
|
|
55
|
+
contentId?: string
|
|
56
|
+
contentName?: string
|
|
57
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { ref, watch, onUnmounted } from 'vue'
|
|
2
|
+
import type {
|
|
3
|
+
TVAdUnlockConfig,
|
|
4
|
+
TVAdUnlockParams,
|
|
5
|
+
TVAdUnlockState,
|
|
6
|
+
TVAdUnlockStorageKeys,
|
|
7
|
+
TVAdUnlockCallbacks,
|
|
8
|
+
RequestManager
|
|
9
|
+
} from './types'
|
|
10
|
+
|
|
11
|
+
const DEFAULT_INVALID_TIMEOUT = 300000
|
|
12
|
+
const DEFAULT_POLL_INTERVAL = 1000
|
|
13
|
+
|
|
14
|
+
export function useTVAdUnlock(
|
|
15
|
+
config: TVAdUnlockConfig,
|
|
16
|
+
storageKeys: TVAdUnlockStorageKeys,
|
|
17
|
+
requestManager: RequestManager,
|
|
18
|
+
callbacks?: TVAdUnlockCallbacks
|
|
19
|
+
) {
|
|
20
|
+
const adQrCode = ref('')
|
|
21
|
+
const isInvalid = ref(false)
|
|
22
|
+
const qrCodeState = ref(0)
|
|
23
|
+
const isLoading = ref(false)
|
|
24
|
+
const isStopPoll = ref(false)
|
|
25
|
+
const isShowConfirmToast = ref(false)
|
|
26
|
+
|
|
27
|
+
let invalidTimer: ReturnType<typeof setTimeout> | null = null
|
|
28
|
+
let pollTimer: ReturnType<typeof setTimeout> | null = null
|
|
29
|
+
|
|
30
|
+
const invalidTimeout = config.invalidTimeout ?? DEFAULT_INVALID_TIMEOUT
|
|
31
|
+
const pollInterval = config.pollInterval ?? DEFAULT_POLL_INTERVAL
|
|
32
|
+
|
|
33
|
+
const urlGetDeviceCode = config.superRequestBaseUrl + '/superc/ad/open/v2/tv/getAdUrl'
|
|
34
|
+
const urlPollTVADStatus = config.superRequestBaseUrl + '/superc/ad/open/v2/tv/pollTVADStatus'
|
|
35
|
+
|
|
36
|
+
watch(() => qrCodeState.value, (newVal, oldVal) => {
|
|
37
|
+
if (newVal === 1 && oldVal === 0) {
|
|
38
|
+
startInvalidTimer()
|
|
39
|
+
callbacks?.onQrCodeScanned?.()
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
function getTodayString(): string {
|
|
44
|
+
const today = new Date()
|
|
45
|
+
return `${today.getFullYear()}-${today.getMonth() + 1}-${today.getDate()}`
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function isTodayUnlocked(storage: { getString: (key: string, defaultValue?: string) => Promise<string> }): Promise<boolean> {
|
|
49
|
+
const today = getTodayString()
|
|
50
|
+
const unlockedDate = await storage.getString(storageKeys.unlockedTodayKey, '')
|
|
51
|
+
return unlockedDate === today
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function getTodayPlayCount(storage: { getString: (key: string, defaultValue?: string) => Promise<string>; putString: (key: string, value: string) => Promise<void> }): Promise<number> {
|
|
55
|
+
const today = getTodayString()
|
|
56
|
+
const savedDate = await storage.getString(storageKeys.unlockDateKey, '')
|
|
57
|
+
|
|
58
|
+
if (savedDate !== today) {
|
|
59
|
+
await storage.putString(storageKeys.unlockDateKey, today)
|
|
60
|
+
await storage.putString(storageKeys.playCountKey, '0')
|
|
61
|
+
return 0
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const count = await storage.getString(storageKeys.playCountKey, '0')
|
|
65
|
+
return parseInt(count) || 0
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function incrementPlayCount(storage: { getString: (key: string, defaultValue?: string) => Promise<string>; putString: (key: string, value: string) => Promise<void> }): Promise<number> {
|
|
69
|
+
const count = await getTodayPlayCount(storage)
|
|
70
|
+
const newCount = count + 1
|
|
71
|
+
await storage.putString(storageKeys.playCountKey, String(newCount))
|
|
72
|
+
return newCount
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function checkNeedAd(storage: { getString: (key: string, defaultValue?: string) => Promise<string>; putString: (key: string, value: string) => Promise<void> }, freePlayCount: number): Promise<boolean> {
|
|
76
|
+
const unlocked = await isTodayUnlocked(storage)
|
|
77
|
+
if (unlocked) {
|
|
78
|
+
return false
|
|
79
|
+
}
|
|
80
|
+
const count = await getTodayPlayCount(storage)
|
|
81
|
+
return count >= freePlayCount
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function pollTVADStatus(deviceId: string): Promise<void> {
|
|
85
|
+
try {
|
|
86
|
+
const res = await requestManager.post(urlPollTVADStatus, {
|
|
87
|
+
data: { deviceId }
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
if (isStopPoll.value) {
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
qrCodeState.value = parseInt(`${res}`)
|
|
95
|
+
|
|
96
|
+
if (qrCodeState.value === 2) {
|
|
97
|
+
callbacks?.onUnlockSuccess?.()
|
|
98
|
+
} else {
|
|
99
|
+
pollTimer = setTimeout(() => {
|
|
100
|
+
pollTVADStatus(deviceId)
|
|
101
|
+
}, pollInterval)
|
|
102
|
+
}
|
|
103
|
+
} catch {
|
|
104
|
+
pollTimer = setTimeout(() => {
|
|
105
|
+
pollTVADStatus(deviceId)
|
|
106
|
+
}, pollInterval)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function getAdQrCode(deviceId: string, scanTitle: string = '观看奖励', scanContent: string = '观看广告后可获得奖励', scanToast: string = '奖励已获得,请前往电视端观看'): Promise<string> {
|
|
111
|
+
isLoading.value = true
|
|
112
|
+
isInvalid.value = false
|
|
113
|
+
qrCodeState.value = 0
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
const res = await requestManager.post(urlGetDeviceCode, {
|
|
117
|
+
data: {
|
|
118
|
+
deviceId,
|
|
119
|
+
scanTitle,
|
|
120
|
+
scanContent,
|
|
121
|
+
scanToast
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
if (res) {
|
|
126
|
+
adQrCode.value = res
|
|
127
|
+
isInvalid.value = false
|
|
128
|
+
startPollTvADStatusTimer(deviceId)
|
|
129
|
+
startInvalidTimer()
|
|
130
|
+
isLoading.value = false
|
|
131
|
+
return res
|
|
132
|
+
} else {
|
|
133
|
+
isLoading.value = false
|
|
134
|
+
callbacks?.onUnlockFailed?.(new Error('获取二维码失败'))
|
|
135
|
+
return ''
|
|
136
|
+
}
|
|
137
|
+
} catch (e) {
|
|
138
|
+
isLoading.value = false
|
|
139
|
+
callbacks?.onUnlockFailed?.(e as Error)
|
|
140
|
+
return ''
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function startPollTvADStatusTimer(deviceId: string): void {
|
|
145
|
+
stopPollTvADStatusTimer()
|
|
146
|
+
isStopPoll.value = false
|
|
147
|
+
pollTimer = setTimeout(() => {
|
|
148
|
+
pollTVADStatus(deviceId)
|
|
149
|
+
}, pollInterval)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function stopPollTvADStatusTimer(): void {
|
|
153
|
+
if (pollTimer) {
|
|
154
|
+
clearTimeout(pollTimer)
|
|
155
|
+
pollTimer = null
|
|
156
|
+
isStopPoll.value = true
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function stopInvalidTimer(): void {
|
|
161
|
+
if (invalidTimer) {
|
|
162
|
+
clearTimeout(invalidTimer)
|
|
163
|
+
invalidTimer = null
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function startInvalidTimer(): void {
|
|
168
|
+
stopInvalidTimer()
|
|
169
|
+
invalidTimer = setTimeout(() => {
|
|
170
|
+
invalidQrCode()
|
|
171
|
+
invalidTimer = null
|
|
172
|
+
}, invalidTimeout)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function invalidQrCode(): void {
|
|
176
|
+
isInvalid.value = true
|
|
177
|
+
stopPollTvADStatusTimer()
|
|
178
|
+
callbacks?.onQrCodeInvalid?.()
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function reset(): void {
|
|
182
|
+
adQrCode.value = ''
|
|
183
|
+
isInvalid.value = false
|
|
184
|
+
qrCodeState.value = 0
|
|
185
|
+
isLoading.value = false
|
|
186
|
+
isStopPoll.value = false
|
|
187
|
+
isShowConfirmToast.value = false
|
|
188
|
+
stopPollTvADStatusTimer()
|
|
189
|
+
stopInvalidTimer()
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
onUnmounted(() => {
|
|
193
|
+
stopPollTvADStatusTimer()
|
|
194
|
+
stopInvalidTimer()
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
adQrCode,
|
|
199
|
+
isInvalid,
|
|
200
|
+
qrCodeState,
|
|
201
|
+
isLoading,
|
|
202
|
+
isShowConfirmToast,
|
|
203
|
+
getAdQrCode,
|
|
204
|
+
startPollTvADStatusTimer,
|
|
205
|
+
stopPollTvADStatusTimer,
|
|
206
|
+
startInvalidTimer,
|
|
207
|
+
stopInvalidTimer,
|
|
208
|
+
invalidQrCode,
|
|
209
|
+
reset,
|
|
210
|
+
isTodayUnlocked,
|
|
211
|
+
getTodayPlayCount,
|
|
212
|
+
incrementPlayCount,
|
|
213
|
+
checkNeedAd
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function useTVAdUnlockStorage(
|
|
218
|
+
storage: { getString: (key: string, defaultValue?: string) => Promise<string>; putString: (key: string, value: string) => Promise<void> },
|
|
219
|
+
storageKeys: TVAdUnlockStorageKeys,
|
|
220
|
+
freePlayCount: number = 32
|
|
221
|
+
) {
|
|
222
|
+
function getTodayString(): string {
|
|
223
|
+
const today = new Date()
|
|
224
|
+
return `${today.getFullYear()}-${today.getMonth() + 1}-${today.getDate()}`
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async function isTodayUnlocked(): Promise<boolean> {
|
|
228
|
+
const today = getTodayString()
|
|
229
|
+
const unlockedDate = await storage.getString(storageKeys.unlockedTodayKey, '')
|
|
230
|
+
return unlockedDate === today
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async function getTodayPlayCount(): Promise<number> {
|
|
234
|
+
const today = getTodayString()
|
|
235
|
+
const savedDate = await storage.getString(storageKeys.unlockDateKey, '')
|
|
236
|
+
|
|
237
|
+
if (savedDate !== today) {
|
|
238
|
+
await storage.putString(storageKeys.unlockDateKey, today)
|
|
239
|
+
await storage.putString(storageKeys.playCountKey, '0')
|
|
240
|
+
return 0
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const count = await storage.getString(storageKeys.playCountKey, '0')
|
|
244
|
+
return parseInt(count) || 0
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async function incrementPlayCount(): Promise<number> {
|
|
248
|
+
const count = await getTodayPlayCount()
|
|
249
|
+
const newCount = count + 1
|
|
250
|
+
await storage.putString(storageKeys.playCountKey, String(newCount))
|
|
251
|
+
return newCount
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async function checkNeedAd(): Promise<boolean> {
|
|
255
|
+
const unlocked = await isTodayUnlocked()
|
|
256
|
+
if (unlocked) {
|
|
257
|
+
return false
|
|
258
|
+
}
|
|
259
|
+
const count = await getTodayPlayCount()
|
|
260
|
+
return count >= freePlayCount
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async function setTodayUnlocked(): Promise<void> {
|
|
264
|
+
const today = getTodayString()
|
|
265
|
+
await storage.putString(storageKeys.unlockedTodayKey, today)
|
|
266
|
+
await storage.putString(storageKeys.playCountKey, '0')
|
|
267
|
+
await storage.putString(storageKeys.unlockDateKey, today)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
isTodayUnlocked,
|
|
272
|
+
getTodayPlayCount,
|
|
273
|
+
incrementPlayCount,
|
|
274
|
+
checkNeedAd,
|
|
275
|
+
setTodayUnlocked
|
|
276
|
+
}
|
|
277
|
+
}
|