From 057ccd529f66dd8eae06d7fcd66a248e12600509 Mon Sep 17 00:00:00 2001 From: czy0729 <402731062@qq.com> Date: Tue, 13 Jun 2023 19:33:35 +0800 Subject: [PATCH] =?UTF-8?q?-=20[=E5=8F=91=E7=8E=B0]=20=E5=9F=BA=E4=BA=8E?= =?UTF-8?q?=E5=85=A8=E7=AB=99=E6=9D=A1=E7=9B=AE=E6=95=B0=E6=8D=AE=E7=9A=84?= =?UTF-8?q?=E7=8C=9C=E4=BD=A0=E5=96=9C=E6=AC=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/image/index.tsx | 2 + src/constants/events/discovery.ts | 6 +- src/screens/_/base/horizontal-list/index.tsx | 6 +- src/screens/_/base/manage/content/index.tsx | 15 +- src/screens/_/base/manage/content/styles.ts | 5 +- src/screens/_/base/manage/index.tsx | 14 +- src/screens/_/base/manage/types.ts | 5 +- .../_/base/pagination-list-2/index.tsx | 17 +- src/screens/_/base/pagination-list-2/types.ts | 3 + src/screens/_/base/rank/styles.ts | 4 +- src/screens/_/base/rate/index.tsx | 8 +- src/screens/_/base/rate/styles.ts | 6 +- src/screens/_/base/rate/types.ts | 10 +- src/screens/discovery/index/ds.ts | 7 +- src/screens/discovery/like/cate/index.tsx | 4 +- src/screens/discovery/like/ds.ts | 77 ++++- src/screens/discovery/like/index.tsx | 26 +- src/screens/discovery/like/item/index.tsx | 119 +++++-- src/screens/discovery/like/item/styles.ts | 20 +- src/screens/discovery/like/item/sub/index.tsx | 70 ++++ src/screens/discovery/like/item/sub/styles.ts | 14 + src/screens/discovery/like/list/index.tsx | 9 +- src/screens/discovery/like/setting/index.tsx | 93 ++++++ src/screens/discovery/like/setting/styles.ts | 18 + src/screens/discovery/like/store.ts | 313 ++++++++++++++---- src/screens/discovery/like/types.ts | 42 ++- src/screens/discovery/like/utils.ts | 210 ++++++++++-- src/screens/discovery/recommend/index.tsx | 4 +- src/screens/discovery/recommend/store.ts | 43 +-- src/screens/home/subject/book-ep/book-ep.tsx | 4 +- src/stores/collection/fetch.ts | 7 +- src/stores/system/init.ts | 10 + src/utils/utils/index.ts | 11 +- 33 files changed, 987 insertions(+), 215 deletions(-) create mode 100644 src/screens/discovery/like/item/sub/index.tsx create mode 100644 src/screens/discovery/like/item/sub/styles.ts create mode 100644 src/screens/discovery/like/setting/index.tsx create mode 100644 src/screens/discovery/like/setting/styles.ts diff --git a/src/components/image/index.tsx b/src/components/image/index.tsx index 6a6ce1cc6..5cae6d097 100644 --- a/src/components/image/index.tsx +++ b/src/components/image/index.tsx @@ -354,6 +354,7 @@ export const Image = observer( setError451(src) that.recoveryToBgmCover() } else if (this.status === 404) { + setError404(src) that.recoveryToBgmCover() } else { setTimeout(() => { @@ -374,6 +375,7 @@ export const Image = observer( setError451(src) this.recoveryToBgmCover() } else if (String(error).includes('code=404')) { + setError404(src) this.recoveryToBgmCover() } else { setTimeout(() => { diff --git a/src/constants/events/discovery.ts b/src/constants/events/discovery.ts index ba708069b..00ed5a06d 100644 --- a/src/constants/events/discovery.ts +++ b/src/constants/events/discovery.ts @@ -155,5 +155,9 @@ export default { 'Hentai.切换布局': 'Hentai.switchLayout', // 维基人 - '维基人.右上角菜单': 'Wiki.topRightMenu' + '维基人.右上角菜单': 'Wiki.topRightMenu', + + // 猜你喜欢 + '猜你喜欢.跳转': 'Like.to', + '猜你喜欢.切换': 'Like.switch' } diff --git a/src/screens/_/base/horizontal-list/index.tsx b/src/screens/_/base/horizontal-list/index.tsx index 12514f609..eced56ebb 100755 --- a/src/screens/_/base/horizontal-list/index.tsx +++ b/src/screens/_/base/horizontal-list/index.tsx @@ -2,13 +2,13 @@ * @Author: czy0729 * @Date: 2019-04-08 01:25:26 * @Last Modified by: czy0729 - * @Last Modified time: 2023-05-26 12:56:25 + * @Last Modified time: 2023-06-12 04:33:12 */ import React from 'react' import { ScrollView, View } from 'react-native' import { Flex, Text, Touchable } from '@components' import { _ } from '@stores' -import { desc, findSubjectCn, stl } from '@utils' +import { desc, findSubjectCn, HTMLDecode, stl } from '@utils' import { ob } from '@utils/decorators' import { SCROLL_VIEW_RESET_PROPS } from '@constants' import { SubjectTypeCn } from '@types' @@ -147,7 +147,7 @@ export const HorizontalList = ob( ellipsizeMode={ellipsizeMode} bold > - {title} + {HTMLDecode(title)} {!!desc && ( + + + {collection} + + + ) + } + return ( diff --git a/src/screens/_/base/manage/content/styles.ts b/src/screens/_/base/manage/content/styles.ts index 000946cf0..7ace08c6d 100644 --- a/src/screens/_/base/manage/content/styles.ts +++ b/src/screens/_/base/manage/content/styles.ts @@ -2,7 +2,7 @@ * @Author: czy0729 * @Date: 2022-07-22 18:25:21 * @Last Modified by: czy0729 - * @Last Modified time: 2023-03-28 07:29:04 + * @Last Modified time: 2023-06-11 03:29:19 */ import { _ } from '@stores' @@ -10,6 +10,9 @@ export const styles = _.create({ content: { minHeight: 40 }, + horizontal: { + minWidth: 48 + }, icon: { width: 40 }, diff --git a/src/screens/_/base/manage/index.tsx b/src/screens/_/base/manage/index.tsx index 7c20f936e..23554192c 100644 --- a/src/screens/_/base/manage/index.tsx +++ b/src/screens/_/base/manage/index.tsx @@ -2,7 +2,7 @@ * @Author: czy0729 * @Date: 2022-07-22 17:54:53 * @Last Modified by: czy0729 - * @Last Modified time: 2023-04-13 23:42:03 + * @Last Modified time: 2023-06-11 03:26:17 */ import React from 'react' import { Touchable, Flex } from '@components' @@ -24,7 +24,14 @@ const HIT_SLOP = { } export const Manage = ob( - ({ style, subjectId, collection = '', typeCn = '动画', onPress }: ManageProps) => { + ({ + style, + subjectId, + collection = '', + typeCn = '动画', + horizontal, + onPress + }: ManageProps) => { if (SHARE_MODE) return null let icon @@ -61,7 +68,8 @@ export const Manage = ob( icon, size, type, - collection: _collection + collection: _collection, + horizontal } return ( diff --git a/src/screens/_/base/manage/types.ts b/src/screens/_/base/manage/types.ts index c997cf80c..4b26ee468 100644 --- a/src/screens/_/base/manage/types.ts +++ b/src/screens/_/base/manage/types.ts @@ -2,7 +2,7 @@ * @Author: czy0729 * @Date: 2022-07-26 05:06:54 * @Last Modified by: czy0729 - * @Last Modified time: 2023-03-28 06:27:20 + * @Last Modified time: 2023-06-11 03:26:03 */ import { Fn, SubjectId, SubjectTypeCn, ViewStyle } from '@types' @@ -17,6 +17,9 @@ export type Props = { /** 条目类型 (中文) */ typeCn?: SubjectTypeCn + /** 水平布局 */ + horizontal?: boolean + /** 点击 */ onPress: Fn } diff --git a/src/screens/_/base/pagination-list-2/index.tsx b/src/screens/_/base/pagination-list-2/index.tsx index 738017387..ac7ff27d0 100644 --- a/src/screens/_/base/pagination-list-2/index.tsx +++ b/src/screens/_/base/pagination-list-2/index.tsx @@ -20,6 +20,7 @@ export const PaginationList2 = ({ data, limit: _limit = 24, onPage, + onNextPage, ...other }: PaginationList2Props) => { // 用户记住列表看到多少页, 在触发更新后需要使用此值去重新划归数组当前页数 @@ -56,7 +57,11 @@ export const PaginationList2 = ({ if (typeof onPage === 'function') { onPage(data.slice(page * limit, (page + 1) * limit)) } - }, [data, limit, list, onPage]) + + if (typeof onNextPage === 'function') { + onNextPage(data.slice((page + 1) * limit, (page + 2) * limit)) + } + }, [data, limit, list, onPage, onNextPage]) useEffect(() => { const list = data.slice(0, lastPage.current * limit) @@ -69,8 +74,14 @@ export const PaginationList2 = ({ _loaded: getTimestamp() }) - if (typeof onPage === 'function') onPage(list) - }, [data, limit, onPage]) + if (typeof onPage === 'function') { + onPage(list) + } + + if (typeof onNextPage === 'function') { + onNextPage(data.slice(limit, limit * 2)) + } + }, [data, limit, onPage, onNextPage]) return ( any + + /** 下下一页回调 */ + onNextPage?: (nextPageData?: any[]) => any } > diff --git a/src/screens/_/base/rank/styles.ts b/src/screens/_/base/rank/styles.ts index 04c944aef..61aa17642 100644 --- a/src/screens/_/base/rank/styles.ts +++ b/src/screens/_/base/rank/styles.ts @@ -2,7 +2,7 @@ * @Author: czy0729 * @Date: 2022-06-13 10:28:41 * @Last Modified by: czy0729 - * @Last Modified time: 2023-05-26 11:51:20 + * @Last Modified time: 2023-06-11 04:42:27 */ import { _ } from '@stores' @@ -19,7 +19,7 @@ export const memoStyles = _.memoStyles(() => ({ textShadowRadius: 1, textShadowColor: 'rgba(0, 0, 0, 0.48)', backgroundColor: _.select('#ffc107', _._colorDarkModeLevel2), - borderRadius: _.radiusXs, + borderRadius: 4, overflow: 'hidden' }, fit: { diff --git a/src/screens/_/base/rate/index.tsx b/src/screens/_/base/rate/index.tsx index 0e6b511be..ab6125e94 100644 --- a/src/screens/_/base/rate/index.tsx +++ b/src/screens/_/base/rate/index.tsx @@ -2,7 +2,7 @@ * @Author: czy0729 * @Date: 2023-06-10 14:08:09 * @Last Modified by: czy0729 - * @Last Modified time: 2023-06-10 14:27:49 + * @Last Modified time: 2023-06-12 04:02:47 */ import React from 'react' import { View } from 'react-native' @@ -14,10 +14,10 @@ import { IOS } from '@constants' export { RateProps } -export const Rate = ob(({ value = '' }: RateProps) => { +export const Rate = ob(({ value = '', onPress }: RateProps) => { return ( - - + + {value} {IOS ? '' : ' '} diff --git a/src/screens/_/base/rate/styles.ts b/src/screens/_/base/rate/styles.ts index f5395a975..9de84f2a8 100644 --- a/src/screens/_/base/rate/styles.ts +++ b/src/screens/_/base/rate/styles.ts @@ -2,7 +2,7 @@ * @Author: czy0729 * @Date: 2023-06-10 14:08:17 * @Last Modified by: czy0729 - * @Last Modified time: 2023-06-10 14:24:38 + * @Last Modified time: 2023-06-11 03:35:59 */ import { _ } from '@stores' @@ -10,8 +10,8 @@ export const styles = _.create({ rate: { position: 'absolute', zIndex: 1, - top: 8, - right: _.ios(20, 14), + top: _.sm, + right: _.ios(26, 20), opacity: 0.5 }, rateText: { diff --git a/src/screens/_/base/rate/types.ts b/src/screens/_/base/rate/types.ts index 62c0f0399..b0a82bcbd 100644 --- a/src/screens/_/base/rate/types.ts +++ b/src/screens/_/base/rate/types.ts @@ -2,9 +2,17 @@ * @Author: czy0729 * @Date: 2023-06-10 14:18:13 * @Last Modified by: czy0729 - * @Last Modified time: 2023-06-10 14:26:47 + * @Last Modified time: 2023-06-12 04:00:45 */ +import { Fn } from '@types' + export type Props = { /** 评分 */ value: string | number + + /** 对齐方向 */ + position?: 'top' | 'bottom' + + /** 文字点击 */ + onPress?: Fn } diff --git a/src/screens/discovery/index/ds.ts b/src/screens/discovery/index/ds.ts index c4216dee1..4ffae751b 100644 --- a/src/screens/discovery/index/ds.ts +++ b/src/screens/discovery/index/ds.ts @@ -2,7 +2,7 @@ * @Author: czy0729 * @Date: 2021-07-16 14:21:27 * @Last Modified by: czy0729 - * @Last Modified time: 2023-05-30 17:12:51 + * @Last Modified time: 2023-06-13 19:06:31 */ import { _ } from '@stores' import { getTimestamp } from '@utils' @@ -104,6 +104,11 @@ export const MENU_MAP = { size: 20, web: false }, + Like: { + key: 'Like', + name: '猜你喜欢', + icon: 'md-looks' + }, Recommend: { key: 'Recommend', name: '推荐', diff --git a/src/screens/discovery/like/cate/index.tsx b/src/screens/discovery/like/cate/index.tsx index 4bbb377f7..661665afe 100755 --- a/src/screens/discovery/like/cate/index.tsx +++ b/src/screens/discovery/like/cate/index.tsx @@ -2,14 +2,14 @@ * @Author: czy0729 * @Date: 2021-03-16 20:58:10 * @Last Modified by: czy0729 - * @Last Modified time: 2023-06-11 00:59:12 + * @Last Modified time: 2023-06-13 05:32:39 */ import React from 'react' import { SegmentedControl } from '@components' import { obc } from '@utils/decorators' +import { SUBJECT_TYPE } from '@constants' import { Ctx } from '../types' import { memoStyles } from './styles' -import { SUBJECT_TYPE } from '@constants' function Cate(props, { $ }: Ctx) { const styles = memoStyles() diff --git a/src/screens/discovery/like/ds.ts b/src/screens/discovery/like/ds.ts index a5e647b07..7b3b2f47a 100644 --- a/src/screens/discovery/like/ds.ts +++ b/src/screens/discovery/like/ds.ts @@ -2,25 +2,14 @@ * @Author: czy0729 * @Date: 2023-06-10 05:42:00 * @Last Modified by: czy0729 - * @Last Modified time: 2023-06-10 23:57:29 + * @Last Modified time: 2023-06-13 05:34:24 */ -import { SubjectId } from '@types' -import { CollectionsItem } from './types' +import { Loaded, SubjectId, SubjectType, SubjectTypeValue, UserId } from '@types' +import { CollectionsItem, ListItem } from './types' export const NAMESPACE = 'ScreenLike' export const EXCLUDE_STATE = { - collections: [] as CollectionsItem[], - subjects: {} as Record< - SubjectId, - { - name: string - image: string - rate: number - relates: SubjectId[] - } - >, - fetching: false, message: '', current: 0, @@ -28,15 +17,33 @@ export const EXCLUDE_STATE = { } export const STATE = { + /** 当前类型 */ + type: 'anime' as SubjectType, + + /** 关联到的条目的基本信息 */ relates: {} as Record, - type: 'anime', + + /** 列表数据条目的基本信息 */ list: { anime: [], book: [], game: [], music: [], real: [] - }, + } as Record, + + /** 条目基本信息集合 */ + subjects: {} as Record< + SubjectId, + { + type?: SubjectTypeValue + rank?: number + score?: number + total?: number + _loaded: Loaded + } + >, + ...EXCLUDE_STATE, _loaded: false } @@ -44,3 +51,41 @@ export const STATE = { export const HOST_API_V0 = 'https://api.bgm.tv/v0' export const LIMIT = 100 + +export const API_COLLECTIONS = ( + userId: UserId, + subjectType: SubjectTypeValue, + type: any, + offset?: number, + limit: number = LIMIT +) => { + let url = `${HOST_API_V0}/users/${userId}/collections?subject_type=${subjectType}&type=${type}&limit=${limit}` + if (offset) url += `&offset=${offset}` + return url +} + +export const REASONS = [ + '自己评分', + '收藏状态', + '条目排名', + '条目分数', + '已看集数', + '自己点评', + '私密收藏', + '最近收藏', + '标签倾向', + '多次推荐' +] as const + +export const REASONS_INFO = [ + '已评分,高分多加分,低分多扣分', + '想看加分,搁置、抛弃扣分', + '条目自身高排名加分,低排名扣分', + '条目自身高分加分,低分扣分', + '已看过集数多,稍微加分', + '进行过长评稍微加分', + '私密收藏加分', + '最近操作过稍微加分', + '标签是你倾向打的,越多相对加越多分', + '被多个条目推荐到相对加分' +] as const diff --git a/src/screens/discovery/like/index.tsx b/src/screens/discovery/like/index.tsx index d05ac66eb..d0ed11b22 100644 --- a/src/screens/discovery/like/index.tsx +++ b/src/screens/discovery/like/index.tsx @@ -2,12 +2,15 @@ * @Author: czy0729 * @Date: 2023-06-10 05:37:24 * @Last Modified by: czy0729 - * @Last Modified time: 2023-06-10 23:49:48 + * @Last Modified time: 2023-06-12 04:26:34 */ -import React from 'react' +import React, { useEffect } from 'react' import { Page, Header } from '@components' +import { TapListener } from '@_' +import { uiStore } from '@stores' import { ic } from '@utils/decorators' -import { useObserver, useRunAfter } from '@utils/hooks' +import { useIsFocused, useObserver, useRunAfter } from '@utils/hooks' +import Setting from './setting' import Cate from './cate' import List from './list' import Tips from './tips' @@ -15,17 +18,30 @@ import Store from './store' import { Ctx } from './types' const Like = (props, { $ }: Ctx) => { + const isFocused = useIsFocused() + useRunAfter(() => { $.init() }) + useEffect(() => { + if (!isFocused) uiStore.closePopableSubject() + }, [isFocused]) + return useObserver(() => { + const { type, list } = $.state return ( <> -
+
} + /> - + + + diff --git a/src/screens/discovery/like/item/index.tsx b/src/screens/discovery/like/item/index.tsx index 43420fe0f..7a1664cc0 100644 --- a/src/screens/discovery/like/item/index.tsx +++ b/src/screens/discovery/like/item/index.tsx @@ -2,62 +2,111 @@ * @Author: czy0729 * @Date: 2022-04-20 13:52:47 * @Last Modified by: czy0729 - * @Last Modified time: 2023-06-11 01:02:52 + * @Last Modified time: 2023-06-12 05:09:48 */ import React from 'react' -import { Flex, Text, Touchable } from '@components' -import { Cover, Rate } from '@_' -import { HTMLDecode } from '@utils' +import { View } from 'react-native' +import { Flex, Text } from '@components' +import { Cover, Manage, Rank, Rate } from '@_' +import { _, collectionStore, systemStore, uiStore } from '@stores' +import { getAction, alert } from '@utils' import { obc } from '@utils/decorators' -import { IMG_WIDTH_LG, IMG_HEIGHT_LG } from '@constants' +import { t } from '@utils/fetch' +import { MODEL_COLLECTION_STATUS, MODEL_SUBJECT_TYPE } from '@constants' +import { CollectionStatus, SubjectType, SubjectTypeCn } from '@types' +import { getReasonsInfo } from '../utils' +import { Ctx, ListItem } from '../types' +import Sub from './sub' import { memoStyles } from './styles' -import { Ctx } from '../types' -function Item({ item }, { $, navigation }: Ctx) { +function Item( + { + item + }: { + item: ListItem + }, + { $, navigation }: Ctx +) { + const { type } = $.state + const subject: any = $.subjects(item.id) || {} + if (type !== MODEL_SUBJECT_TYPE.getLabel(subject.type)) { + return null + } + + const { likeCollected } = systemStore.setting + const collection = collectionStore.collect(item.id) + if (!likeCollected && collection) return null + const styles = memoStyles() const image = `https://lain.bgm.tv/pic/cover/m/${item.image}.jpg` + const typeCn = MODEL_SUBJECT_TYPE.getTitle(subject.type) + const action = getAction(typeCn) return ( - { navigation.push('Subject', { subjectId: item.id, _cn: item.name, _image: image }) + + t('猜你喜欢.跳转', { + subjectId: item.id, + userId: $.userId + }) }} - > - - + /> - - {item.name} - - - {item.relates.map((i, idx) => { - const subject = $.state.relates[i] - if (!subject || idx >= 5) return null - - // const image = `https://lain.bgm.tv/pic/cover/m/${subject.image}.jpg` - return ( - - - ◆ {HTMLDecode(subject.name)} - - - ) - })} + + + {item.name} + + { + uiStore.showManageModal( + { + subjectId: item.id, + title: item.name, + action, + status: + MODEL_COLLECTION_STATUS.getValue(collection) + }, + '猜你喜欢' + ) + }} + /> + + {!!subject.total && ( + + + + {subject.score.toFixed(1)} + {' '}({subject.total}) + + + )} + + - + { + alert(getReasonsInfo(item.reasons).join('\n'), item.name) + }} + /> ) } diff --git a/src/screens/discovery/like/item/styles.ts b/src/screens/discovery/like/item/styles.ts index d061cc7e2..760119fee 100644 --- a/src/screens/discovery/like/item/styles.ts +++ b/src/screens/discovery/like/item/styles.ts @@ -2,26 +2,26 @@ * @Author: czy0729 * @Date: 2022-08-28 00:22:39 * @Last Modified by: czy0729 - * @Last Modified time: 2023-06-11 01:01:16 + * @Last Modified time: 2023-06-11 16:10:54 */ import { _ } from '@stores' -import { IMG_HEIGHT_LG } from '@constants' +import { IMG_WIDTH_LG, IMG_HEIGHT_LG } from '@constants' export const memoStyles = _.memoStyles(() => ({ item: { paddingHorizontal: _.wind, - paddingVertical: 16 + paddingVertical: _.md, + marginRight: -_.sm + }, + cover: { + width: Math.floor(IMG_WIDTH_LG * 1.1), + height: Math.floor(IMG_HEIGHT_LG * 1.1) }, body: { - height: IMG_HEIGHT_LG, + height: Math.floor(IMG_HEIGHT_LG * 1.1), paddingLeft: _.md }, title: { - marginRight: 64 - }, - sub: { - minWidth: '32%', - marginRight: _.md, - marginTop: _.sm + marginRight: 60 } })) diff --git a/src/screens/discovery/like/item/sub/index.tsx b/src/screens/discovery/like/item/sub/index.tsx new file mode 100644 index 000000000..b9d6e0801 --- /dev/null +++ b/src/screens/discovery/like/item/sub/index.tsx @@ -0,0 +1,70 @@ +/* + * @Author: czy0729 + * @Date: 2023-06-11 15:57:47 + * @Last Modified by: czy0729 + * @Last Modified time: 2023-06-12 05:09:49 + */ +import React from 'react' +import { Flex, Text } from '@components' +import { _, uiStore } from '@stores' +import { desc, similar } from '@utils' +import { obc } from '@utils/decorators' +import { MODEL_COLLECTION_STATUS } from '@constants' +import { Ctx } from '../../types' +import { styles } from './styles' + +function Sub({ name, relates, action }, { $ }: Ctx) { + let len = 0 + let count = 0 + return ( + + {relates + .slice() + .sort((a, b) => { + const mName = name || '' + const aName = $.relates(a)?.name || '' + if (aName.includes(mName) || mName.includes(aName)) { + return -1 + } + + const bName = $.relates(b)?.name || '' + if (bName.includes(mName) || mName.includes(bName)) { + return 1 + } + + return desc(similar(mName, aName), similar(mName, bName)) + }) + .map(item => { + if ((len >= 28 && count >= 3) || count >= 4) return null + + const subject = $.relates(item) + const type = Number(subject?.type) + if (!subject || (type != 1 && type != 2 && type != 3)) return null + + len += subject.name.length + count += 1 + + const status = MODEL_COLLECTION_STATUS.getLabel(subject.type) + return ( + + { + uiStore.showPopableSubject({ + subjectId: subject.id + }) + }} + > + {status ? `[${status.replace('看', action)}] ` : ''} + {subject.name} + + + ) + })} + + ) +} + +export default obc(Sub) diff --git a/src/screens/discovery/like/item/sub/styles.ts b/src/screens/discovery/like/item/sub/styles.ts new file mode 100644 index 000000000..061b4a5b1 --- /dev/null +++ b/src/screens/discovery/like/item/sub/styles.ts @@ -0,0 +1,14 @@ +/* + * @Author: czy0729 + * @Date: 2023-06-11 16:02:39 + * @Last Modified by: czy0729 + * @Last Modified time: 2023-06-12 04:30:46 + */ +import { _ } from '@stores' + +export const styles = _.create({ + sub: { + minWidth: '25%', + marginRight: _.sm + } +}) diff --git a/src/screens/discovery/like/list/index.tsx b/src/screens/discovery/like/list/index.tsx index d4b455469..0794ebc56 100644 --- a/src/screens/discovery/like/list/index.tsx +++ b/src/screens/discovery/like/list/index.tsx @@ -2,7 +2,7 @@ * @Author: czy0729 * @Date: 2023-06-10 05:40:36 * @Last Modified by: czy0729 - * @Last Modified time: 2023-06-11 00:28:06 + * @Last Modified time: 2023-06-13 05:50:18 */ import React from 'react' import { Loading } from '@components' @@ -23,9 +23,12 @@ function List(props, { $ }: Ctx) { keyExtractor={keyExtractor} contentContainerStyle={_.container.bottom} data={list[type]} - limit={10} + limit={12} renderItem={renderItem} - onHeaderRefresh={() => $.getList(true)} + onScroll={$.onScroll} + onPage={$.onPage} + onNextPage={$.onNextPage} + onHeaderRefresh={$.onHeaderRefresh} /> ) } diff --git a/src/screens/discovery/like/setting/index.tsx b/src/screens/discovery/like/setting/index.tsx new file mode 100644 index 000000000..86a1822df --- /dev/null +++ b/src/screens/discovery/like/setting/index.tsx @@ -0,0 +1,93 @@ +/* + * @Author: czy0729 + * @Date: 2023-06-13 05:32:15 + * @Last Modified by: czy0729 + * @Last Modified time: 2023-06-13 05:51:36 + */ +import React from 'react' +import { View } from 'react-native' +import { ActionSheet, Flex, Text, SwitchPro, Divider } from '@components' +import { IconTouchable } from '@_' +import { _, systemStore } from '@stores' +import { useBoolean, useObserver } from '@utils/hooks' +import { REASONS, REASONS_INFO } from '../ds' +import { styles } from './styles' + +function Setting({ length }) { + const { state, setTrue, setFalse } = useBoolean(false) + + return useObserver(() => { + const { likeCollected, likeRec } = systemStore.setting + + return ( + <> + + + + + 这是一个基于全站用户、条目页面中的「猜你喜欢」数据,并根据你目前的收藏,计算出的一个整合数据列表。与另一「推荐番剧」功能不同,此功能非 + AI + 模型推导结果,只是全程在本地进行的一些很简单的规则累加计算。所以只要你收藏、打分越多,数据就会越多。 + + + {!!length && ( + + 当前类型已索引到 {length} 个条目 + + )} + + + + + 显示已收藏条目 + + { + systemStore.switchSetting('likeCollected') + }} + /> + + + + + + + 推荐值计算维度 + + + 计算非基于结果中显示的条目项,而是推荐这个项的条目 + + + {REASONS.map((item, index) => { + return ( + + + + {item} + + {REASONS_INFO[index]} + + + { + const value = [...likeRec] + value[index] = value[index] ? 0 : 1 + systemStore.setSetting('likeRec', value) + }} + /> + + + ) + })} + + + + ) + }) +} + +export default Setting diff --git a/src/screens/discovery/like/setting/styles.ts b/src/screens/discovery/like/setting/styles.ts new file mode 100644 index 000000000..16f96d8d1 --- /dev/null +++ b/src/screens/discovery/like/setting/styles.ts @@ -0,0 +1,18 @@ +/* + * @Author: czy0729 + * @Date: 2023-06-10 23:47:07 + * @Last Modified by: czy0729 + * @Last Modified time: 2023-06-13 05:40:26 + */ +import { _ } from '@stores' + +export const styles = _.create({ + switch: { + marginRight: -4, + transform: [ + { + scale: _.device(0.8, 1.12) + } + ] + } +}) diff --git a/src/screens/discovery/like/store.ts b/src/screens/discovery/like/store.ts index 09aaed7a6..d4f51f050 100644 --- a/src/screens/discovery/like/store.ts +++ b/src/screens/discovery/like/store.ts @@ -2,20 +2,30 @@ * @Author: czy0729 * @Date: 2023-06-10 05:41:50 * @Last Modified by: czy0729 - * @Last Modified time: 2023-06-11 01:21:55 + * @Last Modified time: 2023-06-13 06:00:38 */ import { computed, observable } from 'mobx' -import { systemStore, userStore } from '@stores' -import { asc, desc, HTMLDecode, info, queue, sleep, confirm } from '@utils' +import { collectionStore, uiStore, userStore } from '@stores' +import { + asc, + desc, + HTMLDecode, + info, + queue, + sleep, + confirm, + getTimestamp +} from '@utils' import store from '@utils/store' +import { t } from '@utils/fetch' import { request } from '@utils/fetch.v0' import { get, gets, update } from '@utils/kv' -import { MODEL_SUBJECT_TYPE } from '@constants' +import { MODEL_COLLECTION_STATUS, MODEL_SUBJECT_TYPE } from '@constants' import i18n from '@constants/i18n' -import { SubjectId, SubjectType, SubjectTypeValue } from '@types' -import { NAMESPACE, STATE, EXCLUDE_STATE, HOST_API_V0, LIMIT } from './ds' -import { calc } from './utils' -import { CollectionsItem } from './types' +import { CollectionStatusValue, SubjectId, SubjectType, SubjectTypeValue } from '@types' +import { NAMESPACE, STATE, EXCLUDE_STATE, LIMIT, API_COLLECTIONS } from './ds' +import { calc, dayDiff, mergeArrays } from './utils' +import { CollectionsItem, ListItem } from './types' export default class ScreenLike extends store { state = observable(STATE) @@ -37,51 +47,107 @@ export default class ScreenLike extends store { return false } + const { fetching } = this.state + if (fetching) return false + + const { type } = this.state + const data = [] + + // 看过 this.setState({ fetching: true, message: '获取用户收藏', current: 1, - total: 2 + total: 5 }) - - const data = [] - - // 看过 - const { type } = this.state const subjectType = MODEL_SUBJECT_TYPE.getValue(type) let result = await request( - `${HOST_API_V0}/users/${this.userId}/collections?subject_type=${subjectType}&type=2&limit=${LIMIT}` + API_COLLECTIONS( + this.userId, + subjectType, + MODEL_COLLECTION_STATUS.getTitle('看过') + ) ) - if (Array.isArray(result?.data)) { - data.push(...result?.data) - - // 非高级会员只请求 1 页 - if (systemStore.advance) { - // 最多请求 5 页 - if (result?.total > 100) { - for (let i = 2; i <= Math.min(Math.ceil(result.total / LIMIT), 5); i += 1) { - result = await request( - `${HOST_API_V0}/users/${ - this.userId - }/collections?subject_type=${subjectType}&type=2&offset=${ - (i - 1) * LIMIT - }&limit=${LIMIT}` + if (Array.isArray(result?.data) && result.data.length) { + data.push(...result.data) + + // 最多 5 页 + if (result?.total > 100) { + for (let i = 2; i <= Math.min(Math.ceil(result.total / LIMIT), 5); i += 1) { + result = await request( + API_COLLECTIONS( + this.userId, + subjectType, + MODEL_COLLECTION_STATUS.getTitle('看过'), + (i - 1) * LIMIT ) - data.push(...result?.data) + ) + if (Array.isArray(result?.data) && result.data.length) { + data.push(...result.data) } } } } + // 在看 1 页 this.setState({ current: 2 }) + result = await request( + API_COLLECTIONS( + this.userId, + subjectType, + MODEL_COLLECTION_STATUS.getTitle('在看') + ) + ) + if (Array.isArray(result?.data) && result.data.length) { + data.push(...result.data) + } + + // 想看 1 页 + this.setState({ + current: 3 + }) + result = await request( + API_COLLECTIONS( + this.userId, + subjectType, + MODEL_COLLECTION_STATUS.getTitle('想看') + ) + ) + if (Array.isArray(result?.data) && result.data.length) { + data.push(...result.data) + } + + // 搁置 1 页 + this.setState({ + current: 4 + }) + result = await request( + API_COLLECTIONS( + this.userId, + subjectType, + MODEL_COLLECTION_STATUS.getTitle('搁置') + ) + ) + if (Array.isArray(result?.data) && result.data.length) { + data.push(...result.data) + } - // 在看最多请求 1 页 + // 抛弃 1 页 + this.setState({ + current: 5 + }) result = await request( - `${HOST_API_V0}/users/${this.userId}/collections?subject_type=${subjectType}&type=3&limit=${LIMIT}` + API_COLLECTIONS( + this.userId, + subjectType, + MODEL_COLLECTION_STATUS.getTitle('抛弃') + ) ) - if (Array.isArray(result?.data)) data.push(...result?.data) + if (Array.isArray(result?.data) && result.data.length) { + data.push(...result.data) + } this.setState({ ...EXCLUDE_STATE @@ -90,7 +156,7 @@ export default class ScreenLike extends store { if (!data.length) { setTimeout(() => { confirm( - '没有获取到任何该类型的收藏数据,也有可能是授权信息过期了,尝试重新授权后再次获取?', + '没有获取到任何该类型的收藏数据,可能是您的授权信息过期了,也有可能是服务崩了,尝试重新自动授权后再次获取?', async () => { await userStore.reOauth() setTimeout(() => { @@ -102,17 +168,49 @@ export default class ScreenLike extends store { return false } + // 预先计算标签的推荐值 + const pattern = /^\d+$|^.*([年月]).*$/ + const tags: Record = {} + data.forEach(item => { + if (Array.isArray(item.tags)) { + item.tags.forEach((tag: string) => { + if (!pattern.test(tag)) { + if (tag in tags) { + tags[tag] += 1 + } else { + tags[tag] = 1 + } + } + }) + } + }) + return data .slice() .sort((a, b) => asc(a.updated_at, b.updated_at)) - .map(item => ({ - id: item.subject_id, - name: item.subject.name_cn || item.subject.name, - image: item.subject.images.large.split('/l/')?.[1].split('.jpg')?.[0] || '', - rank: item.subject.rank || 0, - score: item.subject.score || 0, - rate: item.rate - })) + .map(item => { + let rec = 0 + if (Array.isArray(item.tags)) { + item.tags.forEach((tag: string) => { + rec += tags[tag] || 0 + }) + } + + return { + id: item.subject_id, + name: HTMLDecode(item.subject.name_cn || item.subject.name), + image: item.subject.images.large.split('/l/')?.[1].split('.jpg')?.[0] || '', + rank: item.subject.rank || 0, + score: item.subject.score || 0, + rate: item.rate || 0, + type: item.type, + ep: item.ep_status || 0, + comment: item.comment?.length || 0, + private: item.private || false, + diff: dayDiff(item.updated_at), + rec + } + }) } /** 获取每个条目的猜你喜欢 */ @@ -172,21 +270,30 @@ export default class ScreenLike extends store { // 计算出显示的列表 const relates: typeof this.state.relates = {} - const subjects: typeof this.state.subjects = {} + const subjects = {} collections.forEach(item => { ;(likes[item.id] || []).forEach(subject => { if (subjects[subject.id]) { subjects[subject.id].relates.push(item.id) - subjects[subject.id].rate += calc(item, subjects[subject.id].relates.length) + + const { reasons, rate } = calc(item, subjects[subject.id].relates.length) + subjects[subject.id].rate += rate + subjects[subject.id].reasons = mergeArrays( + subjects[subject.id].reasons, + reasons + ) } else { relates[item.id] = { ...item } + + const { reasons, rate } = calc(item) subjects[subject.id] = { name: subject.name, image: subject.image, - rate: calc(item), - relates: [item.id] + relates: [item.id], + rate, + reasons } } }) @@ -196,28 +303,69 @@ export default class ScreenLike extends store { relates, ...EXCLUDE_STATE }) - return subjects + return subjects as any } /** 生成列表数据 */ getList = async (refresh: boolean = false) => { - const { type } = this.state - if (!refresh && this.state.list[type].length) return + try { + const { type } = this.state + if (!refresh && this.state.list[type].length) return - const collections = await this.fetchCollections() - if (!collections) return false + const collections = await this.fetchCollections() + if (!collections) return false - const subjects = await this.fetchLike(collections) - this.setState({ - list: { - [type]: Object.entries(subjects) - .map(([key, item]) => ({ - id: Number(key), - ...item - })) - .sort((a, b) => desc(a.rate, b.rate)) + const subjects = await this.fetchLike(collections) + this.setState({ + list: { + [type]: Object.entries(subjects) + .map(([key, item]) => ({ + id: Number(key), + // @ts-expect-error + ...item + })) + .sort((a, b) => desc(a.rate, b.rate)) + } + }) + this.save() + } catch (error) { + info('请求发生错误,请重试') + } + } + + /** 获取推荐条目的基本信息 */ + fetchSubjects = async (subjectIds: SubjectId[]) => { + const { subjects } = this.state + const fetchIds = [] + subjectIds.forEach(subjectId => { + if (!(subjectId in subjects)) [fetchIds.push(`subject_${subjectId}`)] + }) + if (!fetchIds.length) return + + const datas = await gets(fetchIds) + + const values = { + ...subjects + } + Object.entries(datas).forEach(([key, item]) => { + const id = key.replace('subject_', '') + if (!item) { + values[id] = { + _loaded: getTimestamp() + } + } else { + values[id] = { + type: item.type, + rank: item.rank || 0, + score: item?.rating?.score || 0, + total: item?.rating?.total || 0, + _loaded: getTimestamp() + } } }) + this.setState({ + subjects: values + }) this.save() } @@ -228,15 +376,48 @@ export default class ScreenLike extends store { fetching: true, type }) + this.save() setTimeout(() => { this.setState({ fetching: false }) this.getList() + + t('猜你喜欢.切换', { + title + }) }, 80) } + /** 渲染下一页 */ + onPage = (data: ListItem[]) => { + if (!data.length) return + + const subjectIds = data.map(item => item.id) + this.fetchSubjects(subjectIds) + collectionStore.fetchCollectionStatusQueue(subjectIds) + } + + /** 预渲染下一页 */ + onNextPage = (data: ListItem[]) => { + setTimeout(() => { + if (!data.length) return + + const subjectIds = data.map(item => item.id) + this.fetchSubjects(subjectIds) + collectionStore.fetchCollectionStatusQueue(subjectIds) + }, 2000) + } + + onHeaderRefresh = () => { + return this.getList(true) + } + + onScroll = () => { + uiStore.closePopableSubject() + } + save = () => { this.saveStorage(NAMESPACE, EXCLUDE_STATE) } @@ -245,4 +426,16 @@ export default class ScreenLike extends store { @computed get userId() { return userStore.usersInfo(userStore.myUserId).username || userStore.myUserId } + + relates(subjectId: SubjectId) { + return computed(() => { + return this.state.relates[subjectId] + }).get() + } + + subjects(subjectId: SubjectId) { + return computed(() => { + return this.state.subjects[subjectId] + }).get() + } } diff --git a/src/screens/discovery/like/types.ts b/src/screens/discovery/like/types.ts index be658e207..06eabeeb9 100644 --- a/src/screens/discovery/like/types.ts +++ b/src/screens/discovery/like/types.ts @@ -2,10 +2,10 @@ * @Author: czy0729 * @Date: 2023-06-10 05:43:44 * @Last Modified by: czy0729 - * @Last Modified time: 2023-06-11 00:25:24 + * @Last Modified time: 2023-06-11 19:06:41 */ import { factory } from '@utils' -import { Navigation, SubjectId } from '@types' +import { CollectionStatusValue, Navigation, SubjectId } from '@types' import Store from './store' const f = factory(Store) @@ -18,10 +18,48 @@ export type Ctx = { } export type CollectionsItem = { + /** 条目 Id */ id: SubjectId + + /** 条目名字 */ name: string + + /** 条目封面 */ image: string + + /** 条目排名 */ rank: number + + /** 条目分数 */ score: number + + /** 用户自己的打分 */ + rate: number + + /** 用户看到多少话 */ + ep: number + + /** 用户观看状态 */ + type: CollectionStatusValue + + /** 用户评论字数 */ + comment: number + + /** 用户私密状态 */ + private: boolean + + /** 用户最后管理与现在差多少天 */ + diff: number + + /** 标签推荐值 */ + rec: number +} + +export type ListItem = { + id: SubjectId + name: string + image: string rate: number + relates: SubjectId[] + reasons: number[] } diff --git a/src/screens/discovery/like/utils.ts b/src/screens/discovery/like/utils.ts index 60fa22dca..5690a1209 100644 --- a/src/screens/discovery/like/utils.ts +++ b/src/screens/discovery/like/utils.ts @@ -2,50 +2,200 @@ * @Author: czy0729 * @Date: 2023-06-10 15:07:58 * @Last Modified by: czy0729 - * @Last Modified time: 2023-06-11 01:28:32 + * @Last Modified time: 2023-06-13 06:01:21 */ +import { systemStore } from '@stores' +import { desc } from '@utils' +import { MODEL_COLLECTION_STATUS } from '@constants' +import { CollectionStatusCn } from '@types' +import { REASONS } from './ds' import { CollectionsItem } from './types' /** 推荐值 */ export function calc(item: CollectionsItem, length = 1) { - const { rate, rank, score } = item - let value = 0 + const { likeRec } = systemStore.setting + const { rate, rank, score, ep, comment, private: _private, diff, rec } = item + const type = MODEL_COLLECTION_STATUS.getLabel(item.type) + let reasons: number[] = [] - // 收藏条目用户自己的打分 - if (rate >= 8) { - // 最高分可以获得很多推荐分,乘以系数 2 - value = rate * 2 - } else if (rate <= 4) { - // 最低分需要减很多推荐分,乘以系数 -4 - value = (rate - 5) * -4 + // [0] 用户自己的打分 + if (likeRec[0]) { + if (rate >= 8) { + reasons.push(rate * 3) + } else if (rate <= 4) { + reasons.push(Math.abs(rate - 5) * -3) + } else { + reasons.push(rate) + } } else { - // 2-8 分之间的分数采用线性插值计算 - value = rate + reasons.push(0) } - // 收藏条目的自身评价 - if (rank) { - if (rank <= 100) { - value += 20 - } else if (rank <= 1000) { - value += 10 - } else if (rank <= 2000) { - value += 5 - } else if (rank >= 3000) { - value -= 5 - } else if (rank >= 4000) { - value -= 10 + // [1] 用户观看状态 + if (likeRec[1]) { + if (type === '想看') { + reasons.push(10) + } else if (type === '搁置') { + reasons.push(-40) + } else if (type === '抛弃') { + reasons.push(-80) + } else { + reasons.push(0) } + } else { + reasons.push(0) } - if (rank) { - value += score + // [2] 条目自身排名, 高的相对加分, 低扣分 + const canRec = type === '想看' || type === '在看' || type === '看过' + if (!likeRec[2] || !canRec) { + reasons.push(0) } else { - value += score / 2 + if (rank) { + if (rank <= 100) { + reasons.push(20) + } else if (rank <= 1000) { + reasons.push(10) + } else if (rank <= 2000) { + reasons.push(5) + } else if (rank >= 4000) { + reasons.push(-5) + } else if (rank >= 5000) { + reasons.push(-10) + } else { + reasons.push(0) + } + } else { + reasons.push(0) + } } - // 推荐条目在多个收藏条目中推荐到 - value += length * 1.1 + // [3] 条目自身评分, 高的相对加分, 低加得少 + if (!likeRec[3] || !canRec) { + reasons.push(0) + } else { + if (rank) { + reasons.push(score) + } else { + reasons.push(score / 2) + } + } - return value + // [4] 条目收看了很多集相对多加分 + if (!likeRec[4] || !canRec) { + reasons.push(0) + } else { + if (ep >= 12) { + reasons.push(10) + } else { + reasons.push(0) + } + } + + // [5] 长评相对多加分 + if (!likeRec[5] || !canRec) { + reasons.push(0) + } else { + if (comment >= 32) { + reasons.push(10) + } else { + reasons.push(0) + } + } + + // [6] 私密相对多加分 + if (!likeRec[6] || !canRec) { + reasons.push(0) + } else { + if (_private) { + reasons.push(15) + } else { + reasons.push(0) + } + } + + // [7] 最后收藏时间相对较近多加分 + if (!likeRec[7] || !canRec) { + reasons.push(0) + } else { + if (diff && diff <= 30) { + reasons.push(10) + } else if (diff >= 365) { + reasons.push(-10) + } else { + reasons.push(0) + } + } + + // [8] 标签推荐值 + if (!likeRec[8] || !canRec) { + reasons.push(0) + } else { + if (rec) { + reasons.push(rec / 5) + } else { + reasons.push(0) + } + } + + // [9] 条目被多个收藏条目同时推荐到 + if (!likeRec[9] || !canRec) { + reasons.push(0) + } else { + if (length) { + reasons.push(4) + } else { + reasons.push(0) + } + } + + reasons = reasons.map(item => Math.floor(item)) + return { + reasons, + rate: sumArray(reasons) + } +} + +export function getReasonsInfo(reasons: number[]) { + return reasons + .map((item, index) => { + let info = '' + if (item !== 0) { + info += `${REASONS[index]} ` + info += (item > 0 ? '↑' : '↓').repeat( + item >= 80 ? 5 : item >= 60 ? 4 : item >= 40 ? 3 : item >= 20 ? 2 : 1 + ) + } + return [info, item] + }) + .filter(item => !!item[0]) + .sort((a, b) => desc(a[1], b[1])) + .map(item => item[0]) +} + +/** 数组求和 */ +function sumArray(arr: number[]) { + let sum = 0 + for (let i = 0; i < arr.length; i += 1) { + if (isNaN(arr[i])) continue + sum += Number(arr[i]) + } + return sum +} + +/** 合并数组 */ +export function mergeArrays(arr1: number[], arr2: number[]) { + return arr1.map((value, index) => value + arr2[index]) +} + +/** 计算时间跟现在差距多少天 */ +export function dayDiff(updatedAt: string) { + try { + const date = new Date(updatedAt.replace('T', ' ')) + const now = new Date() + const timeDiff = Math.abs(now.getTime() - date.getTime()) + return Math.ceil(timeDiff / (1000 * 3600 * 24)) + } catch (error) { + return null + } } diff --git a/src/screens/discovery/recommend/index.tsx b/src/screens/discovery/recommend/index.tsx index b41534709..e92881c29 100644 --- a/src/screens/discovery/recommend/index.tsx +++ b/src/screens/discovery/recommend/index.tsx @@ -2,7 +2,7 @@ * @Author: czy0729 * @Date: 2023-05-24 10:28:04 * @Last Modified by: czy0729 - * @Last Modified time: 2023-06-03 12:29:25 + * @Last Modified time: 2023-06-13 18:51:55 */ import React from 'react' import { View } from 'react-native' @@ -29,7 +29,7 @@ const Recommend = (props, { $, navigation }: Ctx) => { return ( <>
{ if (STORYBOOK) return null diff --git a/src/screens/discovery/recommend/store.ts b/src/screens/discovery/recommend/store.ts index 3cc1121c2..199a3d2f5 100644 --- a/src/screens/discovery/recommend/store.ts +++ b/src/screens/discovery/recommend/store.ts @@ -2,11 +2,11 @@ * @Author: czy0729 * @Date: 2023-05-24 11:13:26 * @Last Modified by: czy0729 - * @Last Modified time: 2023-06-03 16:59:50 + * @Last Modified time: 2023-06-13 18:50:45 */ import { computed, observable } from 'mobx' import { subjectStore, userStore } from '@stores' -import { desc, getTimestamp, info, pick, queue, updateVisibleBottom } from '@utils' +import { desc, getTimestamp, info, pick, updateVisibleBottom } from '@utils' import axios from '@utils/thirdParty/axios' import store from '@utils/store' import { gets } from '@utils/kv' @@ -39,24 +39,27 @@ export default class ScreenRecommend extends store { } fetchSubjects = async () => { - const ids = [...this.ids] - if (!ids.length) return true - - await subjectStore.initSubjectV2(ids) - const now = getTimestamp() - const fetchs = [] - ids.forEach(id => { - const { _loaded } = subjectStore.subjectV2(id) - if (!_loaded || now - Number(_loaded) >= 60 * 60) { - fetchs.push(() => { - console.info('fetchSubjects', id) - return subjectStore.fetchSubjectV2(id) - }) - } - }) - if (!fetchs.length) return true - - return queue(fetchs) + // 这个接口太慢了, 而且不太依赖, 暂时屏蔽 + return true + + // const ids = [...this.ids] + // if (!ids.length) return true + + // await subjectStore.initSubjectV2(ids) + // const now = getTimestamp() + // const fetchs = [] + // ids.forEach(id => { + // const { _loaded } = subjectStore.subjectV2(id) + // if (!_loaded || now - Number(_loaded) >= 60 * 60) { + // fetchs.push(() => { + // console.info('fetchSubjects', id) + // return subjectStore.fetchSubjectV2(id) + // }) + // } + // }) + // if (!fetchs.length) return true + + // return queue(fetchs) } fetchSubjectsFromOSS = async () => { diff --git a/src/screens/home/subject/book-ep/book-ep.tsx b/src/screens/home/subject/book-ep/book-ep.tsx index 39e3e6c16..17860c327 100644 --- a/src/screens/home/subject/book-ep/book-ep.tsx +++ b/src/screens/home/subject/book-ep/book-ep.tsx @@ -2,7 +2,7 @@ * @Author: czy0729 * @Date: 2022-07-09 16:36:29 * @Last Modified by: czy0729 - * @Last Modified time: 2023-03-11 14:42:43 + * @Last Modified time: 2023-06-11 03:52:45 */ import React from 'react' import { View } from 'react-native' @@ -62,6 +62,7 @@ export default memo( keyboardType='numeric' value={chap} placeholder={String(book.chap) || '0'} + editable={canSubmit} clearButtonMode='never' returnKeyType='done' returnKeyLabel='更新' @@ -112,6 +113,7 @@ export default memo( keyboardType='numeric' value={vol} placeholder={String(book.vol) || '0'} + editable={canSubmit} clearButtonMode='never' returnKeyType='done' returnKeyLabel='更新' diff --git a/src/stores/collection/fetch.ts b/src/stores/collection/fetch.ts index b85cb45a0..ba826b79b 100644 --- a/src/stores/collection/fetch.ts +++ b/src/stores/collection/fetch.ts @@ -2,7 +2,7 @@ * @Author: czy0729 * @Date: 2023-04-24 03:01:50 * @Last Modified by: czy0729 - * @Last Modified time: 2023-05-12 08:49:42 + * @Last Modified time: 2023-06-11 03:08:40 */ import { findTreeNode, @@ -380,7 +380,7 @@ export default class Fetch extends Computed { * - [1] 需要登录 * - [2] subjectIds 长度 = 1 时, 都会请求 * - [3] subjectIds 长度 > 1 时, 当前有记录为 [看过 | 搁置 | 抛弃] 时不重新请求 - * - [4] 批量请求时, 若条件通过, 条目重请求依然有 1 小时的间隔 + * - [4] 批量请求时, 若条件通过, 条目重请求依然有 12 小时的间隔 * */ fetchCollectionStatusQueue = async (subjectIds: SubjectId[] = []) => { if (!userStore.isLogin || !subjectIds.length) return {} // [1] @@ -393,9 +393,10 @@ export default class Fetch extends Computed { if ( subjectIds.length === 1 || // [2] (!['看过', '搁置', '抛弃'].includes(this.collect(subjectId)) && // [3] - now - this._collectionStatusLastFetchMS(subjectId) >= 60 * 60) // [4] + now - this._collectionStatusLastFetchMS(subjectId) >= 60 * 60 * 12) // [4] ) { fetchs.push(async () => { + console.info('fetchCollectionStatusQueue', subjectId) const collection = await fetchCollectionSingleV0({ subjectId, userId: userStore.myId diff --git a/src/stores/system/init.ts b/src/stores/system/init.ts index ae679828f..5e286526e 100755 --- a/src/stores/system/init.ts +++ b/src/stores/system/init.ts @@ -341,6 +341,16 @@ export const INIT_SETTING = { /** webhook 地址 */ webhookUrl: '', + /** + * 猜你喜欢推荐分数是否添加对应维度权重 + * ['自己评分', '收藏状态', '条目排名', '条目分数', '已看集数', + * '自己点评', '私密收藏', '最近收藏', '标签倾向', '多次推荐'] + */ + likeRec: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] as (1 | 0)[], + + /** 猜你喜欢显示已收藏条目 */ + likeCollected: true, + /** 条目页面布局 */ ...INIT_SUBJECT_LAYOUT } diff --git a/src/utils/utils/index.ts b/src/utils/utils/index.ts index 0b2baa259..1d7852abb 100644 --- a/src/utils/utils/index.ts +++ b/src/utils/utils/index.ts @@ -478,6 +478,8 @@ export function cleanQ(str: any) { return String(str).replace(/['!"#$%&\\'()*+,./:;<=>?@[\\\]^`{|}~']/g, ' ') } +const similarCache = new Map() + /** * 字符串相似度 * @param {*} s 字符串1 @@ -490,6 +492,10 @@ export function similar(s: string, t: string, f?: number) { m = t.length, l = Math.max(n, m) if (n === 0 || m === 0) return l + + const key = `${s}|${t}|${f}` + if (similarCache.has(key)) return similarCache.get(key) + const d = Array.from({ length: n + 1 }, (_, i) => [i]) d[0] = Array.from({ length: m + 1 }, (_, i) => i) for (let i = 1; i <= n; i++) { @@ -501,7 +507,10 @@ export function similar(s: string, t: string, f?: number) { } } const res = 1 - d[n][m] / l - return parseFloat(res.toFixed(f)) + + const value = parseFloat(res.toFixed(f)) + similarCache.set(key, value) + return value } /** 工厂辅助函数 */