Merge pull request #4000 from omnivore-app/fix/home

fix: home items order is not accurate
This commit is contained in:
Hongbo Wu
2024-05-29 11:31:47 +08:00
committed by GitHub
10 changed files with 99 additions and 97 deletions

View File

@ -40,7 +40,7 @@ import {
countDailyServiceUsage,
createServiceUsage,
} from './services/service_usage'
import { findSubscriptionsByNames } from './services/subscriptions'
import { batchGetSubscriptionsByNames } from './services/subscriptions'
import { batchGetUploadFilesByIds } from './services/upload_file'
import { tracer } from './tracing'
import { getClaimsByToken, setAuthInCookie } from './utils/auth'
@ -122,7 +122,7 @@ const contextFunc: ContextFunction<ExpressContext, ResolverContext> = async ({
throw new Error('No user id found in claims')
}
return findSubscriptionsByNames(claims?.uid || '', names as string[])
return batchGetSubscriptionsByNames(claims.uid, names as string[])
}),
},
}

View File

@ -1303,6 +1303,7 @@ export type HomeItem = {
previewContent?: Maybe<Scalars['String']>;
saveCount?: Maybe<Scalars['Int']>;
seen_at?: Maybe<Scalars['Date']>;
slug?: Maybe<Scalars['String']>;
source?: Maybe<HomeItemSource>;
thumbnail?: Maybe<Scalars['String']>;
title: Scalars['String'];
@ -6043,6 +6044,7 @@ export type HomeItemResolvers<ContextType = ResolverContext, ParentType extends
previewContent?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
saveCount?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
seen_at?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
slug?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
source?: Resolver<Maybe<ResolversTypes['HomeItemSource']>, ParentType, ContextType>;
thumbnail?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
title?: Resolver<ResolversTypes['String'], ParentType, ContextType>;

View File

@ -1172,6 +1172,7 @@ type HomeItem {
previewContent: String
saveCount: Int
seen_at: Date
slug: String
source: HomeItemSource
thumbnail: String
title: String!

View File

@ -311,7 +311,8 @@ const mixHomeItems = (rankedHomeItems: Array<Candidate>): Array<Section> => {
const authorCount = batch.filter((i) => i.author === item.author).length
const siteCount = batch.filter((i) => i.siteName === item.siteName).length
const subscriptionCount = batch.filter(
(i) => i.subscription?.name === item.subscription?.name
(i) =>
item.subscription && i.subscription?.name === item.subscription.name
).length
return (

View File

@ -639,12 +639,17 @@ export const functionResolvers = {
_: unknown,
ctx: WithDataSourcesContext
) {
const libraryItemIds = section.items
const items = section.items
console.log('items', items)
const libraryItemIds = items
.filter((item) => item.type === 'library_item')
.map((item) => item.id)
console.log('libraryItemIds', libraryItemIds)
const libraryItems = (
await ctx.dataLoaders.libraryItems.loadMany(libraryItemIds)
).filter((libraryItem) => !isError(libraryItem)) as Array<LibraryItem>
console.log('libraryItems', libraryItems)
const publicItemIds = section.items
.filter((item) => item.type === 'public_item')
@ -653,10 +658,15 @@ export const functionResolvers = {
await ctx.dataLoaders.publicItems.loadMany(publicItemIds)
).filter((publicItem) => !isError(publicItem)) as Array<PublicItem>
return libraryItems
.map(
(libraryItem) =>
({
return items
.map((item) => {
console.log('item', item)
const libraryItem = libraryItems.find(
(libraryItem) => item.id === libraryItem.id
)
console.log('libraryItem', libraryItem)
if (libraryItem) {
return {
id: libraryItem.id,
title: libraryItem.title,
author: libraryItem.author,
@ -667,36 +677,44 @@ export const functionResolvers = {
canArchive: !libraryItem.archivedAt,
canDelete: !libraryItem.deletedAt,
canSave: false,
canComment: false,
canShare: true,
dir: libraryItem.directionality,
previewContent: libraryItem.description,
subscription: libraryItem.subscription,
siteName: libraryItem.siteName,
siteIcon: libraryItem.siteIcon,
} as HomeItem)
)
.concat(
publicItems.map(
(publicItem) =>
({
id: publicItem.id,
title: publicItem.title,
author: publicItem.author,
dir: publicItem.dir,
previewContent: publicItem.previewContent,
thumbnail: publicItem.thumbnail,
wordCount: publicItem.wordCount,
date: publicItem.createdAt,
url: publicItem.url,
canArchive: false,
canDelete: false,
canSave: true,
broadcastCount: publicItem.stats.broadcastCount,
likeCount: publicItem.stats.likeCount,
saveCount: publicItem.stats.saveCount,
source: publicItem.source,
} as HomeItem)
slug: libraryItem.slug,
}
}
const publicItem = publicItems.find(
(publicItem) => item.id === publicItem.id
)
)
if (publicItem) {
return {
id: publicItem.id,
title: publicItem.title,
author: publicItem.author,
dir: publicItem.dir,
previewContent: publicItem.previewContent,
thumbnail: publicItem.thumbnail,
wordCount: publicItem.wordCount,
date: publicItem.createdAt,
url: publicItem.url,
canArchive: false,
canDelete: false,
canSave: true,
canComment: true,
canShare: true,
broadcastCount: publicItem.stats.broadcastCount,
likeCount: publicItem.stats.likeCount,
saveCount: publicItem.stats.saveCount,
source: publicItem.source,
}
}
})
.filter((item) => !!item)
},
},
HomeItem: {
@ -723,6 +741,13 @@ export const functionResolvers = {
const subscription = await ctx.dataLoaders.subscriptions.load(
item.subscription
)
if (!subscription) {
return {
name: item.siteName,
icon: item.siteIcon,
type: HomeItemSourceType.Library,
}
}
return {
id: subscription.id,

View File

@ -55,9 +55,9 @@ export interface RequestContext {
highlights: DataLoader<string, Highlight[]>
recommendations: DataLoader<string, Recommendation[]>
uploadFiles: DataLoader<string, UploadFile | undefined>
libraryItems: DataLoader<string, LibraryItem>
publicItems: DataLoader<string, PublicItem>
subscriptions: DataLoader<string, Subscription>
libraryItems: DataLoader<string, LibraryItem | undefined>
publicItems: DataLoader<string, PublicItem | undefined>
subscriptions: DataLoader<string, Subscription | undefined>
}
}

View File

@ -3126,6 +3126,7 @@ const schema = gql`
likeCount: Int
broadcastCount: Int
date: Date!
slug: String
author: String
dir: String
seen_at: Date

View File

@ -1,77 +1,18 @@
import { PublicItem } from '../entity/public_item'
import { HomeItem } from '../generated/graphql'
import { authTrx } from '../repository'
import { findLibraryItemsByIds } from './library_item'
export const batchGetPublicItems = async (
ids: readonly string[]
): Promise<Array<PublicItem>> => {
return authTrx(async (tx) =>
tx
.getRepository(PublicItem)
.createQueryBuilder('public_item')
.where('public_item.id IN (:...ids)', { ids })
.getMany()
)
}
export const batchGetHomeItems = async (
ids: readonly string[]
): Promise<Array<HomeItem>> => {
const libraryItems = await findLibraryItemsByIds(ids as string[])
): Promise<Array<PublicItem | undefined>> => {
const publicItems = await authTrx(async (tx) =>
tx
.getRepository(PublicItem)
.createQueryBuilder('public_item')
.innerJoin(
'public_item_stats',
'stats',
'stats.public_item_id = public_item.id'
)
.innerJoin(
'public_item_source',
'source',
'source.id = public_item.source_id'
)
.where('public_item.id IN (:...ids)', { ids })
.getMany()
)
return ids
.map((id) => {
const libraryItem = libraryItems.find((li) => li.id === id)
if (libraryItem) {
return {
...libraryItem,
date: libraryItem.savedAt,
url: libraryItem.originalUrl,
canArchive: !libraryItem.archivedAt,
canDelete: !libraryItem.deletedAt,
canSave: false,
dir: libraryItem.directionality,
subscription: null,
previewContent: libraryItem.description,
} as HomeItem
} else {
const publicItem = publicItems.find((pi) => pi.id === id)
return publicItem
? ({
...publicItem,
date: publicItem.createdAt,
url: publicItem.url,
canArchive: false,
canDelete: false,
canSave: true,
broadcastCount: publicItem.stats.broadcastCount,
likeCount: publicItem.stats.likeCount,
saveCount: publicItem.stats.saveCount,
subscription: publicItem.source,
} as HomeItem)
: undefined
}
})
.filter((item) => item !== undefined) as Array<HomeItem>
return ids.map((id) => publicItems.find((pi) => pi.id === id))
}
export const findUnseenPublicItems = async (

View File

@ -141,7 +141,27 @@ interface Select {
const readingProgressDataSource = new ReadingProgressDataSource()
export const batchGetLibraryItems = async (ids: readonly string[]) => {
return findLibraryItemsByIds(ids as string[])
const items = await findLibraryItemsByIds(ids as string[], undefined, {
select: [
'id',
'title',
'author',
'thumbnail',
'wordCount',
'savedAt',
'originalUrl',
'directionality',
'description',
'subscription',
'siteName',
'siteIcon',
'archivedAt',
'deletedAt',
'slug',
],
})
return ids.map((id) => items.find((item) => item.id === id) || undefined)
}
export const getItemUrl = (id: string) => `${env.client.url}/me/${id}`

View File

@ -21,6 +21,17 @@ interface SaveSubscriptionInput {
export const UNSUBSCRIBE_EMAIL_TEXT =
'This message was automatically generated by Omnivore.'
export const batchGetSubscriptionsByNames = async (
userId: string,
names: string[]
) => {
const subscriptions = await findSubscriptionsByNames(userId, names)
return names.map((name) =>
subscriptions.find((s) => s?.name === name || s?.url === name)
)
}
export const parseUnsubscribeMailTo = (unsubscribeMailTo: string) => {
const parsed = new URL(`mailto://${unsubscribeMailTo}`)
const subject = parsed.searchParams.get('subject') || 'Unsubscribe'