fix is:read filter and add is:reading filter

This commit is contained in:
Hongbo Wu
2023-10-10 12:21:32 +08:00
parent 3e91f3836b
commit 93c410a564
5 changed files with 85 additions and 62 deletions

View File

@ -1,34 +0,0 @@
import {
EntitySubscriberInterface,
EventSubscriber,
InsertEvent,
} from 'typeorm'
import { Profile } from '../../entity/profile'
import { createDefaultFiltersForUser } from '../../services/create_user'
import { addPopularReadsForNewUser } from '../../services/popular_reads'
@EventSubscriber()
export class AddPopularReadsToNewUser
implements EntitySubscriberInterface<Profile>
{
listenTo() {
return Profile
}
async afterInsert(event: InsertEvent<Profile>): Promise<void> {
await addPopularReadsForNewUser(event.entity.user.id, event.manager)
}
}
@EventSubscriber()
export class AddDefaultFiltersToNewUser
implements EntitySubscriberInterface<Profile>
{
listenTo() {
return Profile
}
async afterInsert(event: InsertEvent<Profile>): Promise<void> {
await createDefaultFiltersForUser(event.manager)(event.entity.user.id)
}
}

View File

@ -15,6 +15,7 @@ import { analytics } from '../utils/analytics'
import { IntercomClient } from '../utils/intercom'
import { logger } from '../utils/logger'
import { validateUsername } from '../utils/usernamePolicy'
import { addPopularReadsForNewUser } from './popular_reads'
import { sendConfirmationEmail } from './send_emails'
export const MAX_RECORDS_LIMIT = 1000
@ -103,6 +104,9 @@ export const createUser = async (input: {
})
}
await addPopularReadsForNewUser(user.id, t)
await createDefaultFiltersForUser(t)(user.id)
return [user, profile]
}
)
@ -146,7 +150,7 @@ export const createUser = async (input: {
return [user, profile]
}
export const createDefaultFiltersForUser =
const createDefaultFiltersForUser =
(t: EntityManager) =>
async (userId: string): Promise<Filter[]> => {
const defaultFilters = [

View File

@ -1,4 +1,13 @@
import { DeepPartial, SelectQueryBuilder } from 'typeorm'
import {
Between,
DeepPartial,
In,
IsNull,
LessThan,
MoreThan,
Not,
SelectQueryBuilder,
} from 'typeorm'
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity'
import { EntityLabel } from '../entity/entity_label'
import { Highlight } from '../entity/highlight'
@ -106,10 +115,14 @@ const buildWhereClause = (
if (args.inFilter !== InFilter.ALL) {
switch (args.inFilter) {
case InFilter.INBOX:
queryBuilder.andWhere('library_item.archived_at IS NULL')
queryBuilder.andWhere({
archivedAt: IsNull(),
})
break
case InFilter.ARCHIVE:
queryBuilder.andWhere('library_item.archived_at IS NOT NULL')
queryBuilder.andWhere({
archivedAt: Not(IsNull()),
})
break
case InFilter.TRASH:
// return only deleted pages within 14 days
@ -119,16 +132,20 @@ const buildWhereClause = (
break
case InFilter.SUBSCRIPTION:
queryBuilder
.andWhere('library_item.subscription IS NOT NULL')
.andWhere("NOT ('library' ILIKE ANY (library_item.label_names))")
.andWhere('library_item.archived_at IS NULL')
.andWhere({
subscription: Not(IsNull()),
archivedAt: IsNull(),
})
break
case InFilter.LIBRARY:
queryBuilder
.andWhere(
"(library_item.subscription IS NULL OR 'library' ILIKE ANY (library_item.label_names))"
)
.andWhere('library_item.archived_at IS NULL')
.andWhere({
archivedAt: IsNull(),
})
break
}
}
@ -136,10 +153,17 @@ const buildWhereClause = (
if (args.readFilter !== ReadFilter.ALL) {
switch (args.readFilter) {
case ReadFilter.READ:
queryBuilder.andWhere('library_item.reading_progress_top_percent >= 98')
queryBuilder.andWhere({
readingProgressBottomPercent: MoreThan(98),
})
break
case ReadFilter.READING:
queryBuilder.andWhere({ readingProgressBottomPercent: Between(2, 98) })
break
case ReadFilter.UNREAD:
queryBuilder.andWhere('library_item.reading_progress_top_percent < 98')
queryBuilder.andWhere({
readingProgressBottomPercent: LessThan(2),
})
break
}
}
@ -148,12 +172,10 @@ const buildWhereClause = (
args.hasFilters.forEach((filter) => {
switch (filter) {
case HasFilter.HIGHLIGHTS:
queryBuilder.andWhere(
'array_length(library_item.highlight_annotations, 1) > 0'
)
queryBuilder.andWhere("library_item.highlight_annotations <> '{}'")
break
case HasFilter.LABELS:
queryBuilder.andWhere('array_length(library_item.label_names, 1) > 0')
queryBuilder.andWhere("library_item.label_names <> '{}'")
break
}
})
@ -225,18 +247,20 @@ const buildWhereClause = (
}
if (args.ids && args.ids.length > 0) {
queryBuilder.andWhere('library_item.id IN (:...ids)', { ids: args.ids })
queryBuilder.andWhere({
id: In(args.ids),
})
}
if (!args.includePending) {
queryBuilder.andWhere('library_item.state != :state', {
state: LibraryItemState.Processing,
queryBuilder.andWhere({
state: Not(LibraryItemState.Processing),
})
}
if (!args.includeDeleted && args.inFilter !== InFilter.TRASH) {
queryBuilder.andWhere('library_item.state != :state', {
state: LibraryItemState.Deleted,
queryBuilder.andWhere({
state: Not(LibraryItemState.Deleted),
})
}
@ -303,7 +327,7 @@ export const searchLibraryItems = async (
const queryBuilder = tx
.createQueryBuilder(LibraryItem, 'library_item')
.select(selectColumns)
.where('library_item.user_id = :userId', { userId })
.where({ user: { id: userId } })
// build the where clause
buildWhereClause(queryBuilder, args)

View File

@ -14,6 +14,7 @@ import { InputMaybe, PageType, SortParams } from '../generated/graphql'
export enum ReadFilter {
ALL,
READ,
READING,
UNREAD,
}
@ -110,6 +111,8 @@ const parseIsFilter = (str: string | undefined): ReadFilter => {
switch (str?.toUpperCase()) {
case 'READ':
return ReadFilter.READ
case 'READING':
return ReadFilter.READING
case 'UNREAD':
return ReadFilter.UNREAD
}

View File

@ -818,6 +818,7 @@ describe('Article API', () => {
let keyword = ''
before(async () => {
const readingProgressArray = [0, 2, 97, 98, 100]
// Create some test items
for (let i = 0; i < 5; i++) {
const itemToSave: DeepPartial<LibraryItem> = {
@ -827,6 +828,7 @@ describe('Article API', () => {
slug: 'test slug',
originalUrl: `${url}/${i}`,
siteName: 'Example',
readingProgressBottomPercent: readingProgressArray[i],
}
const item = await createLibraryItem(itemToSave, user.id)
items.push(item)
@ -887,15 +889,39 @@ describe('Article API', () => {
keyword = `'${searchedKeyword}' is:unread`
})
it('should return unread articles in descending order', async () => {
it('returns unread articles in descending order', async () => {
const res = await graphqlRequest(query, authToken).expect(200)
expect(res.body.data.search.edges.length).to.eq(5)
expect(res.body.data.search.edges.length).to.eq(1)
expect(res.body.data.search.edges[0].node.id).to.eq(items[0].id)
})
})
context('when is:reading is in the query', () => {
before(() => {
keyword = `'${searchedKeyword}' is:reading`
})
it('returns reading articles in descending order', async () => {
const res = await graphqlRequest(query, authToken).expect(200)
expect(res.body.data.search.edges.length).to.eq(3)
expect(res.body.data.search.edges[0].node.id).to.eq(items[3].id)
expect(res.body.data.search.edges[1].node.id).to.eq(items[2].id)
expect(res.body.data.search.edges[2].node.id).to.eq(items[1].id)
})
})
context('when is:read is in the query', () => {
before(() => {
keyword = `'${searchedKeyword}' is:read`
})
it('returns fully read articles in descending order', async () => {
const res = await graphqlRequest(query, authToken).expect(200)
expect(res.body.data.search.edges.length).to.eq(1)
expect(res.body.data.search.edges[0].node.id).to.eq(items[4].id)
expect(res.body.data.search.edges[1].node.id).to.eq(items[3].id)
expect(res.body.data.search.edges[2].node.id).to.eq(items[2].id)
expect(res.body.data.search.edges[3].node.id).to.eq(items[1].id)
expect(res.body.data.search.edges[4].node.id).to.eq(items[0].id)
})
})
@ -1122,7 +1148,7 @@ describe('Article API', () => {
slug: 'test slug 2',
originalUrl: `${url}/test2`,
archivedAt: new Date(),
readingProgressTopPercent: 100,
readingProgressBottomPercent: 100,
},
{
user,
@ -1140,7 +1166,7 @@ describe('Article API', () => {
await deleteLibraryItems(items, user.id)
})
it('returns unfinished archived items', async () => {
it('returns unread archived items', async () => {
const res = await graphqlRequest(query, authToken).expect(200)
expect(res.body.data.search.pageInfo.totalCount).to.eq(1)
@ -1221,7 +1247,7 @@ describe('Article API', () => {
slug: 'test slug 2',
originalUrl: `${url}/test2`,
deletedAt: new Date(),
readingProgressTopPercent: 100,
readingProgressBottomPercent: 100,
},
{
user,