feat: 添加用户登录检测和再生成功能
- 在首页添加用户会话检测机制 - 新增再来一张按钮功能,允许用户快速重新生成图片 - 完善平台抽象层,支持字节跳动小程序用户信息接口 - 优化下载区域组件,支持更多交互功能 - 修复错误提示组件文本显示问题
This commit is contained in:
parent
cef1c697a5
commit
a38eab405c
|
|
@ -6,6 +6,7 @@ import './app.css'
|
||||||
function App({ children }: PropsWithChildren<any>) {
|
function App({ children }: PropsWithChildren<any>) {
|
||||||
useLaunch(() => {
|
useLaunch(() => {
|
||||||
console.log('App launched.')
|
console.log('App launched.')
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// children 是将要会渲染的页面
|
// children 是将要会渲染的页面
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,27 @@
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.regenerate-btn {
|
||||||
|
width: 80%;
|
||||||
|
height: 100rpx;
|
||||||
|
background: linear-gradient(45deg, #52c41a, #73d13d);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.regenerate-btn:active {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
.download-tip {
|
.download-tip {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,11 @@ import './index.css'
|
||||||
|
|
||||||
interface DownloadSectionProps {
|
interface DownloadSectionProps {
|
||||||
onDownload: () => void
|
onDownload: () => void
|
||||||
|
onRegenerate?: () => void
|
||||||
loading: boolean
|
loading: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const DownloadSection: React.FC<DownloadSectionProps> = memo(({ onDownload, loading }) => {
|
const DownloadSection: React.FC<DownloadSectionProps> = memo(({ onDownload, onRegenerate, loading }) => {
|
||||||
return (
|
return (
|
||||||
<View className="download-section">
|
<View className="download-section">
|
||||||
<View
|
<View
|
||||||
|
|
@ -16,6 +17,11 @@ const DownloadSection: React.FC<DownloadSectionProps> = memo(({ onDownload, load
|
||||||
>
|
>
|
||||||
<Text>{loading ? '广告加载中...' : '📱 看广告下载到相册'}</Text>
|
<Text>{loading ? '广告加载中...' : '📱 看广告下载到相册'}</Text>
|
||||||
</View>
|
</View>
|
||||||
|
{onRegenerate && (
|
||||||
|
<View className="regenerate-btn" onClick={onRegenerate}>
|
||||||
|
<Text>🎨 再来一张</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
<Text className="download-tip">观看完整广告即可免费下载所有图片</Text>
|
<Text className="download-tip">观看完整广告即可免费下载所有图片</Text>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,11 @@
|
||||||
font-size: 40rpx;
|
font-size: 40rpx;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: 20rpx;
|
margin-bottom: 20rpx;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-hint {
|
.error-hint {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ import { useSdk } from '../../hooks/index'
|
||||||
import UploadButton from '../../components/UploadButton'
|
import UploadButton from '../../components/UploadButton'
|
||||||
import LoadingOverlay from '../../components/LoadingOverlay'
|
import LoadingOverlay from '../../components/LoadingOverlay'
|
||||||
import ErrorOverlay from '../../components/ErrorOverlay'
|
import ErrorOverlay from '../../components/ErrorOverlay'
|
||||||
|
import { createPlatformFactory } from '../../platforms'
|
||||||
|
|
||||||
type PageStep = 'upload' | 'loading' | 'error'
|
type PageStep = 'upload' | 'loading' | 'error'
|
||||||
|
|
||||||
|
|
@ -23,6 +24,9 @@ export default function Index() {
|
||||||
|
|
||||||
const chooseAndGenerateImage = async () => {
|
const chooseAndGenerateImage = async () => {
|
||||||
try {
|
try {
|
||||||
|
const platformFactory = createPlatformFactory()
|
||||||
|
await platformFactory.createUserInfo().checkSession().catch(e => console.error(e))
|
||||||
|
|
||||||
// 选择图片,选择完成后会自动触发loading状态
|
// 选择图片,选择完成后会自动触发loading状态
|
||||||
const task_id = await sdk.chooseAndGenerateImage({
|
const task_id = await sdk.chooseAndGenerateImage({
|
||||||
onImageSelected: () => {
|
onImageSelected: () => {
|
||||||
|
|
@ -30,7 +34,7 @@ export default function Index() {
|
||||||
setState({ step: 'loading', error: null })
|
setState({ step: 'loading', error: null })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 等待生成完成
|
// 等待生成完成
|
||||||
await new Promise((resolve) => setTimeout(resolve, 5000))
|
await new Promise((resolve) => setTimeout(resolve, 5000))
|
||||||
const urls = await sdk.getTaskStatus(task_id)
|
const urls = await sdk.getTaskStatus(task_id)
|
||||||
|
|
@ -39,7 +43,7 @@ export default function Index() {
|
||||||
navigateTo({
|
navigateTo({
|
||||||
url: `/pages/result/index?images=${encodeURIComponent(JSON.stringify(urls))}`
|
url: `/pages/result/index?images=${encodeURIComponent(JSON.stringify(urls))}`
|
||||||
})
|
})
|
||||||
|
|
||||||
// 重置状态
|
// 重置状态
|
||||||
setState({ step: 'upload', error: null })
|
setState({ step: 'upload', error: null })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -54,13 +58,13 @@ export default function Index() {
|
||||||
|
|
||||||
const renderCurrentStep = () => {
|
const renderCurrentStep = () => {
|
||||||
switch (state.step) {
|
switch (state.step) {
|
||||||
case 'upload':
|
case 'upload':
|
||||||
return <UploadButton onUpload={chooseAndGenerateImage} />
|
return <UploadButton onUpload={chooseAndGenerateImage} />
|
||||||
case 'loading':
|
case 'loading':
|
||||||
return <LoadingOverlay />
|
return <LoadingOverlay />
|
||||||
case 'error':
|
case 'error':
|
||||||
return <ErrorOverlay onRetry={resetToUpload} />
|
return <ErrorOverlay onRetry={resetToUpload} />
|
||||||
default:
|
default:
|
||||||
return <UploadButton onUpload={chooseAndGenerateImage} />
|
return <UploadButton onUpload={chooseAndGenerateImage} />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,11 @@ const ResultPage: React.FC = memo(() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 再来一张
|
||||||
|
const handleRegenerate = () => {
|
||||||
|
navigateBack()
|
||||||
|
}
|
||||||
|
|
||||||
if (!images.length) {
|
if (!images.length) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
@ -96,7 +101,7 @@ const ResultPage: React.FC = memo(() => {
|
||||||
<Image className="result-image" src={images[0]} mode="aspectFit" />
|
<Image className="result-image" src={images[0]} mode="aspectFit" />
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<DownloadSection onDownload={handleDownloadImages} loading={adLoading} />
|
<DownloadSection onDownload={handleDownloadImages} onRegenerate={handleRegenerate} loading={adLoading} />
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -123,4 +123,44 @@ export abstract class RewardedVideoAd {
|
||||||
* @returns Promise<void> 预加载完成的 Promise
|
* @returns Promise<void> 预加载完成的 Promise
|
||||||
*/
|
*/
|
||||||
abstract preload(): Promise<void>;
|
abstract preload(): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户信息
|
||||||
|
*/
|
||||||
|
export interface IUserInfo {
|
||||||
|
avatarUrl: string;
|
||||||
|
city: string;
|
||||||
|
country: string;
|
||||||
|
gender: number;
|
||||||
|
language: string;
|
||||||
|
nickName: string;
|
||||||
|
province: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IUserProfile {
|
||||||
|
userInfo: IUserInfo;
|
||||||
|
cloudId: string;
|
||||||
|
encryptedData: string;
|
||||||
|
errMsg: string;
|
||||||
|
iv: string;
|
||||||
|
rawData: string;
|
||||||
|
signature: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ILogin {
|
||||||
|
anonymousCode: string;
|
||||||
|
code: string;
|
||||||
|
errMsg: string;
|
||||||
|
isLogin: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICheckSession {
|
||||||
|
errMsg: string;
|
||||||
|
}
|
||||||
|
export abstract class UserInfo {
|
||||||
|
abstract getUserProfile(): Promise<IUserProfile>;
|
||||||
|
abstract login(): Promise<ILogin>;
|
||||||
|
abstract checkSession(): Promise<ICheckSession>;
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { RewardedVideoAdTT } from "./tt";
|
import { RewardedVideoAdTT, UserInfoTT } from "./tt";
|
||||||
import { RewardedVideoAd, RewardedVideoAdOptions } from "./core";
|
import { RewardedVideoAd, RewardedVideoAdOptions, UserInfo } from "./core";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 小程序平台全局对象类型声明
|
* 小程序平台全局对象类型声明
|
||||||
|
|
@ -54,6 +54,24 @@ export class PlatformFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
createUserInfo(): UserInfo {
|
||||||
|
switch (this.platform) {
|
||||||
|
case 'tt':
|
||||||
|
return new UserInfoTT();
|
||||||
|
case 'weapp':
|
||||||
|
throw new Error(`微信小程序平台暂未实现`);
|
||||||
|
case 'alipay':
|
||||||
|
throw new Error(`支付宝小程序平台暂未实现`);
|
||||||
|
case 'swan':
|
||||||
|
throw new Error(`百度智能小程序平台暂未实现`);
|
||||||
|
case 'qq':
|
||||||
|
throw new Error(`QQ 小程序平台暂未实现`);
|
||||||
|
default:
|
||||||
|
throw new Error(`不支持的平台类型: ${this.platform}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前平台类型
|
* 获取当前平台类型
|
||||||
* @returns Platform 当前平台
|
* @returns Platform 当前平台
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,11 @@ import {
|
||||||
RewardedVideoCloseCb,
|
RewardedVideoCloseCb,
|
||||||
RewardedVideoErrorCb,
|
RewardedVideoErrorCb,
|
||||||
RewardedVideoLoadCb,
|
RewardedVideoLoadCb,
|
||||||
RewardedVideoAdOptions
|
RewardedVideoAdOptions,
|
||||||
|
UserInfo,
|
||||||
|
IUserInfo,
|
||||||
|
IUserProfile,
|
||||||
|
ICheckSession
|
||||||
} from "./core";
|
} from "./core";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -194,4 +198,47 @@ export class RewardedVideoAdTT extends RewardedVideoAd {
|
||||||
getNativeAd(): any {
|
getNativeAd(): any {
|
||||||
return this.ad;
|
return this.ad;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UserInfoTT extends UserInfo {
|
||||||
|
getUserProfile(): Promise<IUserProfile> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
tt.getUserProfile({
|
||||||
|
success: (res: any) => {
|
||||||
|
console.log({ res })
|
||||||
|
resolve(res)
|
||||||
|
},
|
||||||
|
fail: (res: any) => {
|
||||||
|
console.log({ res })
|
||||||
|
reject(res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
login(): Promise<any> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
tt.login({
|
||||||
|
success: (res: any) => {
|
||||||
|
resolve(res)
|
||||||
|
},
|
||||||
|
fail: (res: any) => {
|
||||||
|
reject(res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
checkSession(): Promise<ICheckSession> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
tt.checkSession({
|
||||||
|
success: (res: any) => {
|
||||||
|
resolve(res)
|
||||||
|
},
|
||||||
|
fail: (res: any) => {
|
||||||
|
reject(res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue