fix:重复点击
This commit is contained in:
@@ -27,7 +27,7 @@ export type AppRuntimeEnv = 'local' | 'dev' | 'prod';
|
||||
* 默认回退到 prod(避免误打到 localhost 导致真机“无法发起网络请求”)。
|
||||
*/
|
||||
const DEFAULT_RUNTIME_ENV: AppRuntimeEnv =
|
||||
typeof __DEV__ !== 'undefined' && __DEV__ ? 'local' : 'prod';
|
||||
typeof __DEV__ !== 'undefined' && __DEV__ ? 'local' : 'dev';
|
||||
|
||||
export const APP_ENV = (getOptionalEnv('EXPO_PUBLIC_ENV', DEFAULT_RUNTIME_ENV) as AppRuntimeEnv) ?? DEFAULT_RUNTIME_ENV;
|
||||
|
||||
|
||||
@@ -5,7 +5,13 @@ import { Platform } from 'react-native';
|
||||
|
||||
import { httpJson } from '../utils/http';
|
||||
import { APP_ENV } from '../constants/env';
|
||||
import { getDailyReminderSettings, getOrCreateClientUserId, getUserProfileScoring } from '../storage/appStorage';
|
||||
import {
|
||||
getDailyReminderSettings,
|
||||
getLastRegisteredPushToken,
|
||||
getOrCreateClientUserId,
|
||||
getUserProfileScoring,
|
||||
setLastRegisteredPushToken,
|
||||
} from '../storage/appStorage';
|
||||
import type { UserProfileScoring } from '../storage/appStorage';
|
||||
import { toBackendLocaleFromLanguageTag } from '../i18n/locale';
|
||||
|
||||
@@ -154,6 +160,22 @@ export async function registerPushToken(args: { pushToken: string }): Promise<vo
|
||||
});
|
||||
}
|
||||
|
||||
export async function ensurePushTokenRegisteredIfPermitted(): Promise<{ ok: boolean; reason: string }> {
|
||||
// 只要系统权限已授权,就应尽早把 token 写入后端(不依赖用户在“每日提醒”里点确认)
|
||||
const settings = await Notifications.getPermissionsAsync();
|
||||
if (settings.status !== 'granted') return { ok: false, reason: 'permission_not_granted' };
|
||||
|
||||
const token = await getExpoPushTokenOrThrow();
|
||||
|
||||
// 简单去重:token 未变化则不重复上报(减少网络与日志噪音)
|
||||
const last = await getLastRegisteredPushToken().catch(() => null);
|
||||
if (last && String(last) === String(token)) return { ok: true, reason: 'already_registered' };
|
||||
|
||||
await registerPushToken({ pushToken: token });
|
||||
await setLastRegisteredPushToken(token);
|
||||
return { ok: true, reason: 'registered' };
|
||||
}
|
||||
|
||||
export async function setPushPreferences(args: { enabled: boolean; timesPerDay: number }): Promise<PushPreferencesResponse> {
|
||||
const clientUserId = await getOrCreateClientUserId();
|
||||
const tz = pickTimezone();
|
||||
|
||||
@@ -18,6 +18,8 @@ const KEY_RECO_FEED_HISTORY = 'reco.feedHistory';
|
||||
const KEY_UI_THEME_MODE = 'ui.theme.mode';
|
||||
const KEY_UI_THEME_SUIXIN_STATE = 'ui.theme.suixin.state';
|
||||
const KEY_DAILY_REMINDER_SETTINGS = 'dailyReminder.settings';
|
||||
const KEY_PUSH_LAST_REGISTERED_TOKEN = 'push.lastRegisteredToken';
|
||||
const KEY_PUSH_LAST_REGISTERED_AT = 'push.lastRegisteredAt';
|
||||
|
||||
export type PushPromptState = 'enabled' | 'skipped' | 'unknown';
|
||||
export type Reaction = 'like' | 'dislike';
|
||||
@@ -69,6 +71,16 @@ export type DailyReminderSettings = {
|
||||
pushEnabled: boolean;
|
||||
};
|
||||
|
||||
export async function getLastRegisteredPushToken(): Promise<string | null> {
|
||||
const raw = await AsyncStorage.getItem(KEY_PUSH_LAST_REGISTERED_TOKEN);
|
||||
return raw ? String(raw) : null;
|
||||
}
|
||||
|
||||
export async function setLastRegisteredPushToken(token: string): Promise<void> {
|
||||
await AsyncStorage.setItem(KEY_PUSH_LAST_REGISTERED_TOKEN, String(token));
|
||||
await AsyncStorage.setItem(KEY_PUSH_LAST_REGISTERED_AT, new Date().toISOString());
|
||||
}
|
||||
|
||||
export type RecoFeedCacheItem = {
|
||||
content_id: number;
|
||||
text: string;
|
||||
@@ -188,7 +200,7 @@ export async function setReaction(contentId: string, reaction: Reaction): Promis
|
||||
}
|
||||
|
||||
export type FavoriteItem = {
|
||||
favId: string; // 唯一标识,支持重复点赞同一文案
|
||||
favId: string; // 唯一标识
|
||||
id: string;
|
||||
/**
|
||||
* 收藏时的文案快照(强烈建议写入,避免后续 cache 覆盖导致无法还原文案)
|
||||
@@ -200,13 +212,28 @@ export type FavoriteItem = {
|
||||
};
|
||||
|
||||
export async function getFavorites(): Promise<FavoriteItem[]> {
|
||||
return await getJson<FavoriteItem[]>(KEY_FAVORITES_ITEMS, []);
|
||||
const raw = await getJson<FavoriteItem[]>(KEY_FAVORITES_ITEMS, []);
|
||||
// 去重:同一条文案只保留最新的一条,防止重复点击喜欢导致弹窗重复展示
|
||||
const seen = new Set<string>();
|
||||
const deduped: FavoriteItem[] = [];
|
||||
for (const item of raw) {
|
||||
const key = String(item.id);
|
||||
if (seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
deduped.push(item);
|
||||
}
|
||||
// 若发现历史数据有重复,顺便写回清理后的版本
|
||||
if (deduped.length !== raw.length) {
|
||||
await setJson(KEY_FAVORITES_ITEMS, deduped);
|
||||
}
|
||||
return deduped;
|
||||
}
|
||||
|
||||
export async function addFavorite(item: FavoriteItem): Promise<void> {
|
||||
const list = await getFavorites();
|
||||
// 允许重复点赞,不再根据 id 去重
|
||||
const newList = [item, ...list];
|
||||
// 幂等写入:同一条文案只保留一条(最新),避免重复点击喜欢产生重复项
|
||||
const filtered = list.filter(x => String(x.id) !== String(item.id));
|
||||
const newList = [item, ...filtered];
|
||||
console.log('Adding to favorites:', JSON.stringify(item));
|
||||
await setJson(KEY_FAVORITES_ITEMS, newList);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user