fix is:read filter and add is:reading filter
This commit is contained in:
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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 = [
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
Reference in New Issue
Block a user