diff --git a/packages/api/src/entity/library_item.ts b/packages/api/src/entity/library_item.ts index 4ef28cb19..447de3bfa 100644 --- a/packages/api/src/entity/library_item.ts +++ b/packages/api/src/entity/library_item.ts @@ -197,15 +197,6 @@ export class LibraryItem { @Column('text', { nullable: true }) recommenderNames?: string[] | null - @Column('timestamptz') - hiddenAt?: Date | null - - @Column('timestamptz') - addedToFollowingAt?: Date | null - - @Column('text') - addedToFollowingBy?: string | null - @Column('jsonb') links?: any | null @@ -216,8 +207,5 @@ export class LibraryItem { previewContentType?: string | null @Column('text') - addedToFollowingFrom?: string | null - - @Column('timestamptz') - addedToLibraryAt?: Date | null + folder!: string } diff --git a/packages/api/src/generated/graphql.ts b/packages/api/src/generated/graphql.ts index c9ba27c77..e6c518eeb 100644 --- a/packages/api/src/generated/graphql.ts +++ b/packages/api/src/generated/graphql.ts @@ -253,24 +253,6 @@ export enum ContentReader { Web = 'WEB' } -export type CopyFromFollowingToLibraryError = { - __typename?: 'CopyFromFollowingToLibraryError'; - errorCodes: Array; -}; - -export enum CopyFromFollowingToLibraryErrorCode { - AlreadyExists = 'ALREADY_EXISTS', - BadRequest = 'BAD_REQUEST', - Unauthorized = 'UNAUTHORIZED' -} - -export type CopyFromFollowingToLibraryResult = CopyFromFollowingToLibraryError | CopyFromFollowingToLibrarySuccess; - -export type CopyFromFollowingToLibrarySuccess = { - __typename?: 'CopyFromFollowingToLibrarySuccess'; - articleSavingRequest: ArticleSavingRequest; -}; - export type CreateArticleError = { __typename?: 'CreateArticleError'; errorCodes: Array; @@ -1301,11 +1283,28 @@ export type MoveLabelSuccess = { label: Label; }; +export type MoveToFolderError = { + __typename?: 'MoveToFolderError'; + errorCodes: Array; +}; + +export enum MoveToFolderErrorCode { + AlreadyExists = 'ALREADY_EXISTS', + BadRequest = 'BAD_REQUEST', + Unauthorized = 'UNAUTHORIZED' +} + +export type MoveToFolderResult = MoveToFolderError | MoveToFolderSuccess; + +export type MoveToFolderSuccess = { + __typename?: 'MoveToFolderSuccess'; + articleSavingRequest: ArticleSavingRequest; +}; + export type Mutation = { __typename?: 'Mutation'; addPopularRead: AddPopularReadResult; bulkAction: BulkActionResult; - copyFromFollowingToLibrary: CopyFromFollowingToLibraryResult; createArticle: CreateArticleResult; createArticleSavingRequest: CreateArticleSavingRequestResult; createGroup: CreateGroupResult; @@ -1331,6 +1330,7 @@ export type Mutation = { mergeHighlight: MergeHighlightResult; moveFilter: MoveFilterResult; moveLabel: MoveLabelResult; + moveToFolder: MoveToFolderResult; optInFeature: OptInFeatureResult; recommend: RecommendResult; recommendHighlights: RecommendHighlightsResult; @@ -1380,11 +1380,6 @@ export type MutationBulkActionArgs = { }; -export type MutationCopyFromFollowingToLibraryArgs = { - id: Scalars['ID']; -}; - - export type MutationCreateArticleArgs = { input: CreateArticleInput; }; @@ -1500,6 +1495,12 @@ export type MutationMoveLabelArgs = { }; +export type MutationMoveToFolderArgs = { + folder: Scalars['String']; + id: Scalars['ID']; +}; + + export type MutationOptInFeatureArgs = { input: OptInFeatureInput; }; @@ -3444,10 +3445,6 @@ export type ResolversTypes = { BulkActionSuccess: ResolverTypeWrapper; BulkActionType: BulkActionType; ContentReader: ContentReader; - CopyFromFollowingToLibraryError: ResolverTypeWrapper; - CopyFromFollowingToLibraryErrorCode: CopyFromFollowingToLibraryErrorCode; - CopyFromFollowingToLibraryResult: ResolversTypes['CopyFromFollowingToLibraryError'] | ResolversTypes['CopyFromFollowingToLibrarySuccess']; - CopyFromFollowingToLibrarySuccess: ResolverTypeWrapper; CreateArticleError: ResolverTypeWrapper; CreateArticleErrorCode: CreateArticleErrorCode; CreateArticleInput: CreateArticleInput; @@ -3648,6 +3645,10 @@ export type ResolversTypes = { MoveLabelInput: MoveLabelInput; MoveLabelResult: ResolversTypes['MoveLabelError'] | ResolversTypes['MoveLabelSuccess']; MoveLabelSuccess: ResolverTypeWrapper; + MoveToFolderError: ResolverTypeWrapper; + MoveToFolderErrorCode: MoveToFolderErrorCode; + MoveToFolderResult: ResolversTypes['MoveToFolderError'] | ResolversTypes['MoveToFolderSuccess']; + MoveToFolderSuccess: ResolverTypeWrapper; Mutation: ResolverTypeWrapper<{}>; NewsletterEmail: ResolverTypeWrapper; NewsletterEmailsError: ResolverTypeWrapper; @@ -3956,9 +3957,6 @@ export type ResolversParentTypes = { BulkActionError: BulkActionError; BulkActionResult: ResolversParentTypes['BulkActionError'] | ResolversParentTypes['BulkActionSuccess']; BulkActionSuccess: BulkActionSuccess; - CopyFromFollowingToLibraryError: CopyFromFollowingToLibraryError; - CopyFromFollowingToLibraryResult: ResolversParentTypes['CopyFromFollowingToLibraryError'] | ResolversParentTypes['CopyFromFollowingToLibrarySuccess']; - CopyFromFollowingToLibrarySuccess: CopyFromFollowingToLibrarySuccess; CreateArticleError: CreateArticleError; CreateArticleInput: CreateArticleInput; CreateArticleResult: ResolversParentTypes['CreateArticleError'] | ResolversParentTypes['CreateArticleSuccess']; @@ -4116,6 +4114,9 @@ export type ResolversParentTypes = { MoveLabelInput: MoveLabelInput; MoveLabelResult: ResolversParentTypes['MoveLabelError'] | ResolversParentTypes['MoveLabelSuccess']; MoveLabelSuccess: MoveLabelSuccess; + MoveToFolderError: MoveToFolderError; + MoveToFolderResult: ResolversParentTypes['MoveToFolderError'] | ResolversParentTypes['MoveToFolderSuccess']; + MoveToFolderSuccess: MoveToFolderSuccess; Mutation: {}; NewsletterEmail: NewsletterEmail; NewsletterEmailsError: NewsletterEmailsError; @@ -4516,20 +4517,6 @@ export type BulkActionSuccessResolvers; }; -export type CopyFromFollowingToLibraryErrorResolvers = { - errorCodes?: Resolver, ParentType, ContextType>; - __isTypeOf?: IsTypeOfResolverFn; -}; - -export type CopyFromFollowingToLibraryResultResolvers = { - __resolveType: TypeResolveFn<'CopyFromFollowingToLibraryError' | 'CopyFromFollowingToLibrarySuccess', ParentType, ContextType>; -}; - -export type CopyFromFollowingToLibrarySuccessResolvers = { - articleSavingRequest?: Resolver; - __isTypeOf?: IsTypeOfResolverFn; -}; - export type CreateArticleErrorResolvers = { errorCodes?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; @@ -5274,10 +5261,23 @@ export type MoveLabelSuccessResolvers; }; +export type MoveToFolderErrorResolvers = { + errorCodes?: Resolver, ParentType, ContextType>; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type MoveToFolderResultResolvers = { + __resolveType: TypeResolveFn<'MoveToFolderError' | 'MoveToFolderSuccess', ParentType, ContextType>; +}; + +export type MoveToFolderSuccessResolvers = { + articleSavingRequest?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type MutationResolvers = { addPopularRead?: Resolver>; bulkAction?: Resolver>; - copyFromFollowingToLibrary?: Resolver>; createArticle?: Resolver>; createArticleSavingRequest?: Resolver>; createGroup?: Resolver>; @@ -5303,6 +5303,7 @@ export type MutationResolvers>; moveFilter?: Resolver>; moveLabel?: Resolver>; + moveToFolder?: Resolver>; optInFeature?: Resolver>; recommend?: Resolver>; recommendHighlights?: Resolver>; @@ -6373,9 +6374,6 @@ export type Resolvers = { BulkActionError?: BulkActionErrorResolvers; BulkActionResult?: BulkActionResultResolvers; BulkActionSuccess?: BulkActionSuccessResolvers; - CopyFromFollowingToLibraryError?: CopyFromFollowingToLibraryErrorResolvers; - CopyFromFollowingToLibraryResult?: CopyFromFollowingToLibraryResultResolvers; - CopyFromFollowingToLibrarySuccess?: CopyFromFollowingToLibrarySuccessResolvers; CreateArticleError?: CreateArticleErrorResolvers; CreateArticleResult?: CreateArticleResultResolvers; CreateArticleSavingRequestError?: CreateArticleSavingRequestErrorResolvers; @@ -6515,6 +6513,9 @@ export type Resolvers = { MoveLabelError?: MoveLabelErrorResolvers; MoveLabelResult?: MoveLabelResultResolvers; MoveLabelSuccess?: MoveLabelSuccessResolvers; + MoveToFolderError?: MoveToFolderErrorResolvers; + MoveToFolderResult?: MoveToFolderResultResolvers; + MoveToFolderSuccess?: MoveToFolderSuccessResolvers; Mutation?: MutationResolvers; NewsletterEmail?: NewsletterEmailResolvers; NewsletterEmailsError?: NewsletterEmailsErrorResolvers; diff --git a/packages/api/src/generated/schema.graphql b/packages/api/src/generated/schema.graphql index 4a8dcedc4..01ea5b6b3 100644 --- a/packages/api/src/generated/schema.graphql +++ b/packages/api/src/generated/schema.graphql @@ -211,22 +211,6 @@ enum ContentReader { WEB } -type CopyFromFollowingToLibraryError { - errorCodes: [CopyFromFollowingToLibraryErrorCode!]! -} - -enum CopyFromFollowingToLibraryErrorCode { - ALREADY_EXISTS - BAD_REQUEST - UNAUTHORIZED -} - -union CopyFromFollowingToLibraryResult = CopyFromFollowingToLibraryError | CopyFromFollowingToLibrarySuccess - -type CopyFromFollowingToLibrarySuccess { - articleSavingRequest: ArticleSavingRequest! -} - type CreateArticleError { errorCodes: [CreateArticleErrorCode!]! } @@ -1165,10 +1149,25 @@ type MoveLabelSuccess { label: Label! } +type MoveToFolderError { + errorCodes: [MoveToFolderErrorCode!]! +} + +enum MoveToFolderErrorCode { + ALREADY_EXISTS + BAD_REQUEST + UNAUTHORIZED +} + +union MoveToFolderResult = MoveToFolderError | MoveToFolderSuccess + +type MoveToFolderSuccess { + articleSavingRequest: ArticleSavingRequest! +} + type Mutation { addPopularRead(name: String!): AddPopularReadResult! bulkAction(action: BulkActionType!, async: Boolean, expectedCount: Int, labelIds: [ID!], query: String!): BulkActionResult! - copyFromFollowingToLibrary(id: ID!): CopyFromFollowingToLibraryResult! createArticle(input: CreateArticleInput!): CreateArticleResult! createArticleSavingRequest(input: CreateArticleSavingRequestInput!): CreateArticleSavingRequestResult! createGroup(input: CreateGroupInput!): CreateGroupResult! @@ -1194,6 +1193,7 @@ type Mutation { mergeHighlight(input: MergeHighlightInput!): MergeHighlightResult! moveFilter(input: MoveFilterInput!): MoveFilterResult! moveLabel(input: MoveLabelInput!): MoveLabelResult! + moveToFolder(folder: String!, id: ID!): MoveToFolderResult! optInFeature(input: OptInFeatureInput!): OptInFeatureResult! recommend(input: RecommendInput!): RecommendResult! recommendHighlights(input: RecommendHighlightsInput!): RecommendHighlightsResult! diff --git a/packages/api/src/resolvers/article/index.ts b/packages/api/src/resolvers/article/index.ts index 6a2d03e55..8f9adaedb 100644 --- a/packages/api/src/resolvers/article/index.ts +++ b/packages/api/src/resolvers/article/index.ts @@ -6,7 +6,7 @@ import { Readability } from '@omnivore/readability' import graphqlFields from 'graphql-fields' import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity' -import { LibraryItem, LibraryItemState } from '../../entity/library_item' +import { LibraryItem } from '../../entity/library_item' import { env } from '../../env' import { ArticleError, @@ -405,7 +405,7 @@ export const getArticleResolver = authorized< }) ) - if (!libraryItem || libraryItem.state === LibraryItemState.Deleted) { + if (!libraryItem || libraryItem.folder === InFilter.TRASH) { return { errorCodes: [ArticleErrorCode.NotFound] } } @@ -527,8 +527,8 @@ export const setBookmarkArticleResolver = authorized< const deletedLibraryItem = await updateLibraryItem( articleID, { - state: LibraryItemState.Deleted, - deletedAt: new Date(), + folder: InFilter.TRASH, + savedAt: new Date(), }, uid, pubsub @@ -754,6 +754,7 @@ export const updatesSinceResolver = authorized< includeDeleted: true, dateFilters: [{ field: 'updatedAt', startDate }], sort, + inFilter: InFilter.ALL, }, uid ) @@ -865,7 +866,7 @@ export const setFavoriteArticleResolver = authorized< }) const getUpdateReason = (libraryItem: LibraryItem, since: Date) => { - if (libraryItem.state === LibraryItemState.Deleted) { + if (libraryItem.folder === InFilter.TRASH) { return UpdateReason.Deleted } if (libraryItem.createdAt >= since) { diff --git a/packages/api/src/resolvers/following/index.ts b/packages/api/src/resolvers/following/index.ts index 439ee7bd0..6c8f1deb7 100644 --- a/packages/api/src/resolvers/following/index.ts +++ b/packages/api/src/resolvers/following/index.ts @@ -1,14 +1,13 @@ -import { IsNull, Not } from 'typeorm' import { LibraryItem } from '../../entity/library_item' import { - CopyFromFollowingToLibraryError, - CopyFromFollowingToLibraryErrorCode, - CopyFromFollowingToLibrarySuccess, FeedEdge, FeedsError, FeedsErrorCode, FeedsSuccess, - MutationCopyFromFollowingToLibraryArgs, + MoveToFolderError, + MoveToFolderErrorCode, + MoveToFolderSuccess, + MutationMoveToFolderArgs, QueryFeedsArgs, } from '../../generated/graphql' import { feedRepository } from '../../repository/feed' @@ -72,16 +71,17 @@ export const feedsResolver = authorized< } }) -export const copyFromFollowingToLibraryResolver = authorized< - CopyFromFollowingToLibrarySuccess, - CopyFromFollowingToLibraryError, - MutationCopyFromFollowingToLibraryArgs ->(async (_, { id }, { authTrx, pubsub, uid }) => { +export const moveToFolderResolver = authorized< + MoveToFolderSuccess, + MoveToFolderError, + MutationMoveToFolderArgs +>(async (_, { id, folder }, { authTrx, pubsub, uid }) => { analytics.track({ userId: uid, - event: 'copy_from_following_to_library', + event: 'move_to_folder', properties: { id, + folder, }, }) @@ -89,7 +89,6 @@ export const copyFromFollowingToLibraryResolver = authorized< tx.getRepository(LibraryItem).findOne({ where: { id, - addedToFollowingAt: Not(IsNull()), }, relations: ['user'], }) @@ -97,17 +96,17 @@ export const copyFromFollowingToLibraryResolver = authorized< if (!item) { return { - errorCodes: [CopyFromFollowingToLibraryErrorCode.Unauthorized], + errorCodes: [MoveToFolderErrorCode.Unauthorized], } } - if (item.addedToLibraryAt) { + if (item.folder === folder) { return { - errorCodes: [CopyFromFollowingToLibraryErrorCode.AlreadyExists], + errorCodes: [MoveToFolderErrorCode.AlreadyExists], } } - const addedToLibraryAt = new Date() + const savedAt = new Date() // if the content is not fetched yet, create a page save request if (!item.readableContent) { @@ -117,12 +116,12 @@ export const copyFromFollowingToLibraryResolver = authorized< articleSavingRequestId: id, priority: 'high', publishedAt: item.publishedAt || undefined, - savedAt: addedToLibraryAt, + savedAt, pubsub, }) return { - __typename: 'CopyFromFollowingToLibrarySuccess', + __typename: 'MoveToFolderSuccess', articleSavingRequest, } } @@ -130,15 +129,15 @@ export const copyFromFollowingToLibraryResolver = authorized< const updatedItem = await updateLibraryItem( item.id, { - savedAt: addedToLibraryAt, - addedToLibraryAt, + folder, + savedAt, }, uid, pubsub ) return { - __typename: 'CopyFromFollowingToLibrarySuccess', + __typename: 'MoveToFolderSuccess', articleSavingRequest: libraryItemToArticleSavingRequest( updatedItem.user, updatedItem diff --git a/packages/api/src/resolvers/function_resolvers.ts b/packages/api/src/resolvers/function_resolvers.ts index 57200c42e..ff363e78f 100644 --- a/packages/api/src/resolvers/function_resolvers.ts +++ b/packages/api/src/resolvers/function_resolvers.ts @@ -29,7 +29,7 @@ import { generateUploadFilePathName, } from '../utils/uploads' import { optInFeatureResolver } from './features' -import { copyFromFollowingToLibraryResolver, feedsResolver } from './following' +import { feedsResolver, moveToFolderResolver } from './following' import { uploadImportFileResolver } from './importers/uploadImportFileResolver' import { addPopularReadResolver, @@ -216,7 +216,7 @@ export const functionResolvers = { updateSubscription: updateSubscriptionResolver, updateFilter: updateFilterResolver, updateEmail: updateEmailResolver, - copyFromFollowingToLibrary: copyFromFollowingToLibraryResolver, + moveToFolder: moveToFolderResolver, }, Query: { me: getMeUserResolver, diff --git a/packages/api/src/resolvers/links/index.ts b/packages/api/src/resolvers/links/index.ts index 9e2dd3025..d16b9a155 100644 --- a/packages/api/src/resolvers/links/index.ts +++ b/packages/api/src/resolvers/links/index.ts @@ -1,4 +1,3 @@ -import { LibraryItemState } from '../../entity/library_item' import { env } from '../../env' import { ArchiveLinkError, @@ -9,6 +8,7 @@ import { import { updateLibraryItem } from '../../services/library_item' import { analytics } from '../../utils/analytics' import { authorized } from '../../utils/helpers' +import { InFilter } from '../../utils/search' // export const updateLinkShareInfoResolver = authorized< // UpdateLinkShareInfoSuccess, @@ -66,10 +66,8 @@ export const setLinkArchivedResolver = authorized< await updateLibraryItem( args.input.linkId, { - archivedAt: args.input.archived ? new Date() : null, - state: args.input.archived - ? LibraryItemState.Archived - : LibraryItemState.Succeeded, + savedAt: new Date(), + folder: args.input.archived ? InFilter.ARCHIVE : InFilter.INBOX, }, uid ) diff --git a/packages/api/src/resolvers/update/index.ts b/packages/api/src/resolvers/update/index.ts index 255efd2b5..10eb68d32 100644 --- a/packages/api/src/resolvers/update/index.ts +++ b/packages/api/src/resolvers/update/index.ts @@ -1,4 +1,3 @@ -import { LibraryItemState } from '../../entity/library_item' import { MutationUpdatePageArgs, UpdatePageError, @@ -21,9 +20,6 @@ export const updatePageResolver = authorized< savedAt: input.savedAt ? new Date(input.savedAt) : undefined, publishedAt: input.publishedAt ? new Date(input.publishedAt) : undefined, thumbnail: input.previewImage ?? undefined, - state: input.state - ? (input.state as unknown as LibraryItemState) - : undefined, }, uid ) diff --git a/packages/api/src/routers/svc/following.ts b/packages/api/src/routers/svc/following.ts index 21c26d4de..44a191352 100644 --- a/packages/api/src/routers/svc/following.ts +++ b/packages/api/src/routers/svc/following.ts @@ -10,7 +10,6 @@ export interface SaveFollowingItemRequest { title: string url: string itemId: string - addedToFollowingAt: Date addedToFollowingBy: string addedToFollowingFrom: SourceOfFollowing author?: string @@ -19,7 +18,7 @@ export interface SaveFollowingItemRequest { previewContent?: string previewContentType?: string publishedAt?: Date - savedAt?: Date + savedAt: Date } function isSaveFollowingItemRequest( @@ -27,12 +26,12 @@ function isSaveFollowingItemRequest( ): body is SaveFollowingItemRequest { return ( 'userIds' in body && - 'addedToFollowingAt' in body && 'addedToFollowingBy' in body && 'addedToFollowingFrom' in body && 'url' in body && 'itemId' in body && - 'title' in body + 'title' in body && + 'savedAt' in body ) } diff --git a/packages/api/src/schema.ts b/packages/api/src/schema.ts index 69a95fd79..1ddb694b9 100755 --- a/packages/api/src/schema.ts +++ b/packages/api/src/schema.ts @@ -2651,19 +2651,17 @@ const schema = gql` author: String } - union CopyFromFollowingToLibraryResult = - CopyFromFollowingToLibrarySuccess - | CopyFromFollowingToLibraryError + union MoveToFolderResult = MoveToFolderSuccess | MoveToFolderError - type CopyFromFollowingToLibrarySuccess { + type MoveToFolderSuccess { articleSavingRequest: ArticleSavingRequest! } - type CopyFromFollowingToLibraryError { - errorCodes: [CopyFromFollowingToLibraryErrorCode!]! + type MoveToFolderError { + errorCodes: [MoveToFolderErrorCode!]! } - enum CopyFromFollowingToLibraryErrorCode { + enum MoveToFolderErrorCode { UNAUTHORIZED BAD_REQUEST ALREADY_EXISTS @@ -2772,7 +2770,7 @@ const schema = gql` updateSubscription( input: UpdateSubscriptionInput! ): UpdateSubscriptionResult! - copyFromFollowingToLibrary(id: ID!): CopyFromFollowingToLibraryResult! + moveToFolder(id: ID!, folder: String!): MoveToFolderResult! } # FIXME: remove sort from feedArticles after all cached tabs are closed diff --git a/packages/api/src/services/create_page_save_request.ts b/packages/api/src/services/create_page_save_request.ts index 5e8beca29..01db62eb2 100644 --- a/packages/api/src/services/create_page_save_request.ts +++ b/packages/api/src/services/create_page_save_request.ts @@ -131,10 +131,7 @@ export const createPageSaveRequest = async ({ ) } // reset state to processing if not in following - if ( - libraryItem.state !== LibraryItemState.Processing && - !libraryItem.addedToFollowingAt - ) { + if (libraryItem.state !== LibraryItemState.Processing) { libraryItem = await updateLibraryItem( libraryItem.id, { diff --git a/packages/api/src/services/library_item.ts b/packages/api/src/services/library_item.ts index 7e0eba5af..10488c1e4 100644 --- a/packages/api/src/services/library_item.ts +++ b/packages/api/src/services/library_item.ts @@ -31,7 +31,7 @@ export interface SearchArgs { size?: number sort?: Sort query?: string - inFilter?: InFilter + inFilter: InFilter readFilter?: ReadFilter typeFilter?: string labelFilters?: LabelFilter[] @@ -64,7 +64,6 @@ export interface SearchResultItem { title: string uploadFileId?: string | null url: string - archivedAt?: Date | null readingProgressTopPercent?: number readingProgressPercent: number readingProgressAnchorIndex: number @@ -106,40 +105,9 @@ const buildWhereClause = ( } if (args.inFilter !== InFilter.ALL) { - if (args.inFilter === InFilter.FOLLOWING) { - queryBuilder - .andWhere('library_item.added_to_following_at IS NOT NULL') - .andWhere('library_item.hidden_at IS NULL') - } else { - queryBuilder.andWhere('library_item.added_to_library_at IS NOT NULL') - - switch (args.inFilter) { - case InFilter.INBOX: - queryBuilder.andWhere('library_item.archived_at IS NULL') - break - case InFilter.ARCHIVE: - queryBuilder.andWhere('library_item.archived_at IS NOT NULL') - break - case InFilter.TRASH: - // return only deleted pages within 14 days - queryBuilder.andWhere( - "library_item.deleted_at >= now() - interval '14 days'" - ) - break - case InFilter.SUBSCRIPTION: - queryBuilder - .andWhere("NOT ('library' ILIKE ANY (library_item.label_names))") - .andWhere('library_item.archived_at IS NULL') - .andWhere('library_item.subscription IS NOT NULL') - 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') - } - } + queryBuilder.andWhere('library_item.folder = :folder', { + folder: args.inFilter, + }) } if (args.readFilter !== ReadFilter.ALL) { @@ -171,6 +139,8 @@ const buildWhereClause = ( case HasFilter.LABELS: queryBuilder.andWhere("library_item.label_names <> '{}'") break + case HasFilter.SUBSCRIPTIONS: + queryBuilder.andWhere('library_item.subscription is NOT NULL') } }) } @@ -259,8 +229,8 @@ const buildWhereClause = ( } if (!args.includeDeleted && args.inFilter !== InFilter.TRASH) { - queryBuilder.andWhere('library_item.state <> :state', { - state: LibraryItemState.Deleted, + queryBuilder.andWhere('library_item.folder <> :folder', { + folder: InFilter.TRASH, }) } @@ -397,8 +367,6 @@ export const restoreLibraryItem = async ( { state: LibraryItemState.Succeeded, savedAt: new Date(), - archivedAt: null, - deletedAt: null, }, userId, pubsub @@ -414,22 +382,6 @@ export const updateLibraryItem = async ( const updatedLibraryItem = await authTrx( async (tx) => { const itemRepo = tx.withRepository(libraryItemRepository) - - // reset deletedAt and archivedAt - switch (libraryItem.state) { - case LibraryItemState.Archived: - libraryItem.archivedAt = new Date() - break - case LibraryItemState.Deleted: - libraryItem.deletedAt = new Date() - break - case LibraryItemState.Processing: - case LibraryItemState.Succeeded: - libraryItem.archivedAt = null - libraryItem.deletedAt = null - break - } - await itemRepo.update(id, libraryItem) return itemRepo.findOneByOrFail({ id }) @@ -578,7 +530,7 @@ export const saveFeedItemInFollowing = (input: SaveFollowingItemRequest) => { user: { id: userId }, originalUrl: input.url, subscription: input.addedToFollowingBy, - addedToLibraryAt: null, + folder: InFilter.FOLLOWING, })) return tx @@ -649,14 +601,14 @@ export const updateLibraryItems = async ( switch (action) { case BulkActionType.Archive: values = { - archivedAt: new Date(), - state: LibraryItemState.Archived, + folder: InFilter.ARCHIVE, + savedAt: new Date(), } break case BulkActionType.Delete: values = { - deletedAt: new Date(), - state: LibraryItemState.Deleted, + savedAt: new Date(), + folder: InFilter.TRASH, } break case BulkActionType.AddLabels: diff --git a/packages/api/src/services/save_page.ts b/packages/api/src/services/save_page.ts index ef8c91882..811b176a1 100644 --- a/packages/api/src/services/save_page.ts +++ b/packages/api/src/services/save_page.ts @@ -261,9 +261,6 @@ export const parsedContentToLibraryItem = ({ uploadFileId: uploadFileId || undefined, readingProgressTopPercent: 0, readingProgressHighestReadAnchor: 0, - state: state - ? (state as unknown as LibraryItemState) - : LibraryItemState.Succeeded, createdAt: validatedDate(saveTime), savedAt: validatedDate(saveTime), siteName: parsedContent?.siteName, @@ -272,8 +269,6 @@ export const parsedContentToLibraryItem = ({ wordCount: wordsCount(parsedContent?.textContent || ''), contentReader: contentReaderForLibraryItem(itemType, uploadFileId), subscription: rssFeedUrl, - archivedAt: - state === ArticleSavingRequestStatus.Archived ? new Date() : undefined, - addedToLibraryAt: validatedDate(saveTime), + folder: state === ArticleSavingRequestStatus.Archived ? 'archive' : 'inbox', } } diff --git a/packages/api/src/utils/helpers.ts b/packages/api/src/utils/helpers.ts index b0ff7cfcf..4a1554536 100644 --- a/packages/api/src/utils/helpers.ts +++ b/packages/api/src/utils/helpers.ts @@ -30,6 +30,7 @@ import { validateUrl } from '../services/create_page_save_request' import { updateLibraryItem } from '../services/library_item' import { Merge } from '../util' import { logger } from './logger' +import { InFilter } from './search' interface InputObject { // eslint-disable-next-line @typescript-eslint/no-explicit-any [key: string]: any @@ -239,7 +240,7 @@ export const libraryItemToArticle = (item: LibraryItem): Article => ({ state: item.state as unknown as ArticleSavingRequestStatus, content: item.readableContent, hash: item.textContentHash || '', - isArchived: !!item.archivedAt, + isArchived: item.folder != InFilter.ARCHIVE, recommendations: item.recommendations?.map( recommandationDataToRecommendation ), @@ -258,7 +259,7 @@ export const libraryItemToSearchItem = (item: LibraryItem): SearchItem => ({ url: item.originalUrl, state: item.state as unknown as ArticleSavingRequestStatus, content: item.readableContent, - isArchived: !!item.archivedAt, + isArchived: item.folder != InFilter.ARCHIVE, pageType: item.itemType as unknown as PageType, readingProgressPercent: item.readingProgressBottomPercent, contentReader: item.contentReader as unknown as ContentReader, diff --git a/packages/api/src/utils/search.ts b/packages/api/src/utils/search.ts index 60f6d64e0..af8e26d9f 100644 --- a/packages/api/src/utils/search.ts +++ b/packages/api/src/utils/search.ts @@ -20,13 +20,11 @@ export enum ReadFilter { } export enum InFilter { - ALL, - INBOX, - ARCHIVE, - TRASH, - SUBSCRIPTION, - LIBRARY, - FOLLOWING, + ALL = 'all', + INBOX = 'inbox', + ARCHIVE = 'archive', + TRASH = 'trash', + FOLLOWING = 'following', } export interface SearchFilter { @@ -59,6 +57,7 @@ export type LabelFilter = { export enum HasFilter { HIGHLIGHTS, LABELS, + SUBSCRIPTIONS, } export interface DateFilter { @@ -134,10 +133,6 @@ const parseInFilter = ( return InFilter.ARCHIVE case 'TRASH': return InFilter.TRASH - case 'SUBSCRIPTION': - return InFilter.SUBSCRIPTION - case 'LIBRARY': - return InFilter.LIBRARY case 'FOLLOWING': return InFilter.FOLLOWING } @@ -236,6 +231,8 @@ const parseHasFilter = (str?: string): HasFilter | undefined => { return HasFilter.HIGHLIGHTS case 'LABELS': return HasFilter.LABELS + case 'SUBSCRIPTIONS': + return HasFilter.SUBSCRIPTIONS } } diff --git a/packages/api/test/resolvers/article.test.ts b/packages/api/test/resolvers/article.test.ts index 0763a1a49..be0e3d86c 100644 --- a/packages/api/test/resolvers/article.test.ts +++ b/packages/api/test/resolvers/article.test.ts @@ -410,8 +410,7 @@ describe('Article API', () => { title, user: { id: user.id }, originalUrl: url, - archivedAt: new Date(), - state: LibraryItemState.Archived, + folder: 'archive', }, user.id ) @@ -609,7 +608,7 @@ describe('Article API', () => { ).expect(200) const savedItem = await findLibraryItemByUrl(url, user.id) - expect(savedItem?.archivedAt).to.not.be.null + expect(savedItem?.folder).to.eql('archive') expect(savedItem?.labels?.map((l) => l.name)).to.eql(labels) }) }) @@ -1031,8 +1030,7 @@ describe('Article API', () => { readableContent: '

test 1

', slug: 'test slug 1', originalUrl: `${url}/test1`, - archivedAt: new Date(), - state: LibraryItemState.Archived, + folder: 'archive', }, { user, @@ -1040,8 +1038,7 @@ describe('Article API', () => { readableContent: '

test 2

', slug: 'test slug 2', originalUrl: `${url}/test2`, - archivedAt: new Date(), - state: LibraryItemState.Archived, + folder: 'archive', }, { user, @@ -1153,12 +1150,12 @@ describe('Article API', () => { }) }) - context("when in:library label:test' is in the query", () => { + context("when in:inbox label:test' is in the query", () => { let items: LibraryItem[] = [] let label: Label before(async () => { - keyword = 'in:library label:test' + keyword = 'in:inbox label:test' // Create some test items label = await createLabel('test', '', user.id) items = await createLibraryItems( @@ -1184,8 +1181,7 @@ describe('Article API', () => { readableContent: '

test 3

', slug: 'test slug 3', originalUrl: `${url}/test3`, - archivedAt: new Date(), - state: LibraryItemState.Archived, + folder: 'archive', }, ], user.id @@ -1276,7 +1272,7 @@ describe('Article API', () => { slug: 'test slug 1', originalUrl: `${url}/test1`, itemType: PageType.File, - archivedAt: new Date(), + folder: 'archive', }, { user, @@ -1284,7 +1280,7 @@ describe('Article API', () => { readableContent: '

test 2

', slug: 'test slug 2', originalUrl: `${url}/test2`, - archivedAt: new Date(), + folder: 'archive', readingProgressBottomPercent: 100, }, { @@ -1325,8 +1321,8 @@ describe('Article API', () => { readableContent: '

test 1

', slug: 'test slug 1', originalUrl: `${url}/test1`, - archivedAt: new Date(), subscription: 'feed', + folder: 'archive', }, { user, @@ -1342,7 +1338,7 @@ describe('Article API', () => { readableContent: '

test 3

', slug: 'test slug 3', originalUrl: `${url}/test3`, - archivedAt: new Date(), + folder: 'archive', }, ], user.id @@ -1375,7 +1371,7 @@ describe('Article API', () => { readableContent: '

test 1

', slug: 'test slug 1', originalUrl: `${url}/test1`, - deletedAt: new Date(), + folder: 'trash', }, { user, @@ -1383,8 +1379,8 @@ describe('Article API', () => { readableContent: '

test 2

', slug: 'test slug 2', originalUrl: `${url}/test2`, - deletedAt: new Date(), readingProgressBottomPercent: 100, + folder: 'trash', }, { user, @@ -1746,7 +1742,7 @@ describe('Article API', () => { for (let i = 0; i < 3; i++) { await updateLibraryItem( items[i].id, - { state: LibraryItemState.Deleted, deletedAt: new Date() }, + { folder: 'trash', savedAt: new Date() }, user.id ) deletedItems.push(items[i]) diff --git a/packages/api/test/routers/auth.test.ts b/packages/api/test/routers/auth.test.ts index 47d87737f..5cf40e3e3 100644 --- a/packages/api/test/routers/auth.test.ts +++ b/packages/api/test/routers/auth.test.ts @@ -15,6 +15,7 @@ import { generateVerificationToken, hashPassword, } from '../../src/utils/auth' +import { InFilter } from '../../src/utils/search' import * as util from '../../src/utils/sendEmail' import { createTestUser } from '../db' import { generateFakeUuid, request } from '../util' @@ -607,7 +608,10 @@ describe('auth router', () => { 'web' ).expect(200) const user = await userRepository.findOneByOrFail({ name }) - const { count } = await searchLibraryItems({}, user.id) + const { count } = await searchLibraryItems( + { inFilter: InFilter.ALL }, + user.id + ) expect(count).to.eql(3) }) @@ -628,7 +632,10 @@ describe('auth router', () => { 'ios' ).expect(200) const user = await userRepository.findOneByOrFail({ name }) - const { count } = await searchLibraryItems({}, user.id) + const { count } = await searchLibraryItems( + { inFilter: InFilter.ALL }, + user.id + ) expect(count).to.eql(4) }) diff --git a/packages/db/migrations/0146.do.following.sql b/packages/db/migrations/0146.do.following.sql index e5ea56937..44afe8da5 100755 --- a/packages/db/migrations/0146.do.following.sql +++ b/packages/db/migrations/0146.do.following.sql @@ -9,14 +9,10 @@ ALTER TABLE omnivore.subscriptions ADD COLUMN auto_add_to_library boolean; ALTER TABLE omnivore.library_item - ADD COLUMN hidden_at timestamptz, - ADD COLUMN added_to_following_at timestamptz, - ADD COLUMN added_to_following_by text, ADD COLUMN links jsonb, ADD COLUMN preview_content text, ADD COLUMN preview_content_type text, - ADD COLUMN added_to_following_from text, - ADD COLUMN added_to_library_at timestamptz DEFAULT current_timestamp; + ADD COLUMN folder text NOT NULL DEFAULT 'inbox'; CREATE POLICY library_item_admin_policy on omnivore.library_item FOR ALL diff --git a/packages/db/migrations/0146.undo.following.sql b/packages/db/migrations/0146.undo.following.sql index 46d8abbae..10054874c 100755 --- a/packages/db/migrations/0146.undo.following.sql +++ b/packages/db/migrations/0146.undo.following.sql @@ -9,14 +9,10 @@ DROP TABLE omnivore.feed; DROP policy library_item_admin_policy ON omnivore.library_item; ALTER TABLE omnivore.library_item - DROP COLUMN hidden_at, - DROP COLUMN added_to_following_at, - DROP COLUMN added_to_following_by, DROP COLUMN links, - DROP COLUMN preview_content_type, DROP COLUMN preview_content, - DROP COLUMN added_to_following_from, - DROP COLUMN added_to_library_at; + DROP COLUMN preview_content_type, + DROP COLUMN folder; ALTER TABLE omnivore.subscriptions DROP COLUMN is_private, diff --git a/packages/rss-handler/src/index.ts b/packages/rss-handler/src/index.ts index 1648cdefc..34b0a7f9d 100644 --- a/packages/rss-handler/src/index.ts +++ b/packages/rss-handler/src/index.ts @@ -184,7 +184,6 @@ const createFollowingTask = async ( addedToFollowingBy: feedUrl, savedAt: item.isoDate, publishedAt: item.isoDate, - addedToFollowingAt: item.isoDate, previewContentType: 'text/html', // TODO: get content type from feed }