add folder to library_item table

This commit is contained in:
Hongbo Wu 2023-11-10 17:34:46 +08:00
parent 69dc1ba47a
commit adf13131db
20 changed files with 160 additions and 244 deletions

View file

@ -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
}

View file

@ -253,24 +253,6 @@ export enum ContentReader {
Web = 'WEB'
}
export type CopyFromFollowingToLibraryError = {
__typename?: 'CopyFromFollowingToLibraryError';
errorCodes: Array<CopyFromFollowingToLibraryErrorCode>;
};
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<CreateArticleErrorCode>;
@ -1301,11 +1283,28 @@ export type MoveLabelSuccess = {
label: Label;
};
export type MoveToFolderError = {
__typename?: 'MoveToFolderError';
errorCodes: Array<MoveToFolderErrorCode>;
};
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<BulkActionSuccess>;
BulkActionType: BulkActionType;
ContentReader: ContentReader;
CopyFromFollowingToLibraryError: ResolverTypeWrapper<CopyFromFollowingToLibraryError>;
CopyFromFollowingToLibraryErrorCode: CopyFromFollowingToLibraryErrorCode;
CopyFromFollowingToLibraryResult: ResolversTypes['CopyFromFollowingToLibraryError'] | ResolversTypes['CopyFromFollowingToLibrarySuccess'];
CopyFromFollowingToLibrarySuccess: ResolverTypeWrapper<CopyFromFollowingToLibrarySuccess>;
CreateArticleError: ResolverTypeWrapper<CreateArticleError>;
CreateArticleErrorCode: CreateArticleErrorCode;
CreateArticleInput: CreateArticleInput;
@ -3648,6 +3645,10 @@ export type ResolversTypes = {
MoveLabelInput: MoveLabelInput;
MoveLabelResult: ResolversTypes['MoveLabelError'] | ResolversTypes['MoveLabelSuccess'];
MoveLabelSuccess: ResolverTypeWrapper<MoveLabelSuccess>;
MoveToFolderError: ResolverTypeWrapper<MoveToFolderError>;
MoveToFolderErrorCode: MoveToFolderErrorCode;
MoveToFolderResult: ResolversTypes['MoveToFolderError'] | ResolversTypes['MoveToFolderSuccess'];
MoveToFolderSuccess: ResolverTypeWrapper<MoveToFolderSuccess>;
Mutation: ResolverTypeWrapper<{}>;
NewsletterEmail: ResolverTypeWrapper<NewsletterEmail>;
NewsletterEmailsError: ResolverTypeWrapper<NewsletterEmailsError>;
@ -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<ContextType = ResolverContext, ParentType
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type CopyFromFollowingToLibraryErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['CopyFromFollowingToLibraryError'] = ResolversParentTypes['CopyFromFollowingToLibraryError']> = {
errorCodes?: Resolver<Array<ResolversTypes['CopyFromFollowingToLibraryErrorCode']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type CopyFromFollowingToLibraryResultResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['CopyFromFollowingToLibraryResult'] = ResolversParentTypes['CopyFromFollowingToLibraryResult']> = {
__resolveType: TypeResolveFn<'CopyFromFollowingToLibraryError' | 'CopyFromFollowingToLibrarySuccess', ParentType, ContextType>;
};
export type CopyFromFollowingToLibrarySuccessResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['CopyFromFollowingToLibrarySuccess'] = ResolversParentTypes['CopyFromFollowingToLibrarySuccess']> = {
articleSavingRequest?: Resolver<ResolversTypes['ArticleSavingRequest'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type CreateArticleErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['CreateArticleError'] = ResolversParentTypes['CreateArticleError']> = {
errorCodes?: Resolver<Array<ResolversTypes['CreateArticleErrorCode']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
@ -5274,10 +5261,23 @@ export type MoveLabelSuccessResolvers<ContextType = ResolverContext, ParentType
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type MoveToFolderErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['MoveToFolderError'] = ResolversParentTypes['MoveToFolderError']> = {
errorCodes?: Resolver<Array<ResolversTypes['MoveToFolderErrorCode']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type MoveToFolderResultResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['MoveToFolderResult'] = ResolversParentTypes['MoveToFolderResult']> = {
__resolveType: TypeResolveFn<'MoveToFolderError' | 'MoveToFolderSuccess', ParentType, ContextType>;
};
export type MoveToFolderSuccessResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['MoveToFolderSuccess'] = ResolversParentTypes['MoveToFolderSuccess']> = {
articleSavingRequest?: Resolver<ResolversTypes['ArticleSavingRequest'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type MutationResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['Mutation'] = ResolversParentTypes['Mutation']> = {
addPopularRead?: Resolver<ResolversTypes['AddPopularReadResult'], ParentType, ContextType, RequireFields<MutationAddPopularReadArgs, 'name'>>;
bulkAction?: Resolver<ResolversTypes['BulkActionResult'], ParentType, ContextType, RequireFields<MutationBulkActionArgs, 'action' | 'query'>>;
copyFromFollowingToLibrary?: Resolver<ResolversTypes['CopyFromFollowingToLibraryResult'], ParentType, ContextType, RequireFields<MutationCopyFromFollowingToLibraryArgs, 'id'>>;
createArticle?: Resolver<ResolversTypes['CreateArticleResult'], ParentType, ContextType, RequireFields<MutationCreateArticleArgs, 'input'>>;
createArticleSavingRequest?: Resolver<ResolversTypes['CreateArticleSavingRequestResult'], ParentType, ContextType, RequireFields<MutationCreateArticleSavingRequestArgs, 'input'>>;
createGroup?: Resolver<ResolversTypes['CreateGroupResult'], ParentType, ContextType, RequireFields<MutationCreateGroupArgs, 'input'>>;
@ -5303,6 +5303,7 @@ export type MutationResolvers<ContextType = ResolverContext, ParentType extends
mergeHighlight?: Resolver<ResolversTypes['MergeHighlightResult'], ParentType, ContextType, RequireFields<MutationMergeHighlightArgs, 'input'>>;
moveFilter?: Resolver<ResolversTypes['MoveFilterResult'], ParentType, ContextType, RequireFields<MutationMoveFilterArgs, 'input'>>;
moveLabel?: Resolver<ResolversTypes['MoveLabelResult'], ParentType, ContextType, RequireFields<MutationMoveLabelArgs, 'input'>>;
moveToFolder?: Resolver<ResolversTypes['MoveToFolderResult'], ParentType, ContextType, RequireFields<MutationMoveToFolderArgs, 'folder' | 'id'>>;
optInFeature?: Resolver<ResolversTypes['OptInFeatureResult'], ParentType, ContextType, RequireFields<MutationOptInFeatureArgs, 'input'>>;
recommend?: Resolver<ResolversTypes['RecommendResult'], ParentType, ContextType, RequireFields<MutationRecommendArgs, 'input'>>;
recommendHighlights?: Resolver<ResolversTypes['RecommendHighlightsResult'], ParentType, ContextType, RequireFields<MutationRecommendHighlightsArgs, 'input'>>;
@ -6373,9 +6374,6 @@ export type Resolvers<ContextType = ResolverContext> = {
BulkActionError?: BulkActionErrorResolvers<ContextType>;
BulkActionResult?: BulkActionResultResolvers<ContextType>;
BulkActionSuccess?: BulkActionSuccessResolvers<ContextType>;
CopyFromFollowingToLibraryError?: CopyFromFollowingToLibraryErrorResolvers<ContextType>;
CopyFromFollowingToLibraryResult?: CopyFromFollowingToLibraryResultResolvers<ContextType>;
CopyFromFollowingToLibrarySuccess?: CopyFromFollowingToLibrarySuccessResolvers<ContextType>;
CreateArticleError?: CreateArticleErrorResolvers<ContextType>;
CreateArticleResult?: CreateArticleResultResolvers<ContextType>;
CreateArticleSavingRequestError?: CreateArticleSavingRequestErrorResolvers<ContextType>;
@ -6515,6 +6513,9 @@ export type Resolvers<ContextType = ResolverContext> = {
MoveLabelError?: MoveLabelErrorResolvers<ContextType>;
MoveLabelResult?: MoveLabelResultResolvers<ContextType>;
MoveLabelSuccess?: MoveLabelSuccessResolvers<ContextType>;
MoveToFolderError?: MoveToFolderErrorResolvers<ContextType>;
MoveToFolderResult?: MoveToFolderResultResolvers<ContextType>;
MoveToFolderSuccess?: MoveToFolderSuccessResolvers<ContextType>;
Mutation?: MutationResolvers<ContextType>;
NewsletterEmail?: NewsletterEmailResolvers<ContextType>;
NewsletterEmailsError?: NewsletterEmailsErrorResolvers<ContextType>;

View file

@ -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!

View file

@ -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) {

View file

@ -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

View file

@ -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,

View file

@ -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
)

View file

@ -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
)

View file

@ -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
)
}

View file

@ -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

View file

@ -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,
{

View file

@ -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:

View file

@ -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',
}
}

View file

@ -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,

View file

@ -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
}
}

View file

@ -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: '<p>test 1</p>',
slug: 'test slug 1',
originalUrl: `${url}/test1`,
archivedAt: new Date(),
state: LibraryItemState.Archived,
folder: 'archive',
},
{
user,
@ -1040,8 +1038,7 @@ describe('Article API', () => {
readableContent: '<p>test 2</p>',
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: '<p>test 3</p>',
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: '<p>test 2</p>',
slug: 'test slug 2',
originalUrl: `${url}/test2`,
archivedAt: new Date(),
folder: 'archive',
readingProgressBottomPercent: 100,
},
{
@ -1325,8 +1321,8 @@ describe('Article API', () => {
readableContent: '<p>test 1</p>',
slug: 'test slug 1',
originalUrl: `${url}/test1`,
archivedAt: new Date(),
subscription: 'feed',
folder: 'archive',
},
{
user,
@ -1342,7 +1338,7 @@ describe('Article API', () => {
readableContent: '<p>test 3</p>',
slug: 'test slug 3',
originalUrl: `${url}/test3`,
archivedAt: new Date(),
folder: 'archive',
},
],
user.id
@ -1375,7 +1371,7 @@ describe('Article API', () => {
readableContent: '<p>test 1</p>',
slug: 'test slug 1',
originalUrl: `${url}/test1`,
deletedAt: new Date(),
folder: 'trash',
},
{
user,
@ -1383,8 +1379,8 @@ describe('Article API', () => {
readableContent: '<p>test 2</p>',
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])

View file

@ -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)
})

View file

@ -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

View file

@ -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,

View file

@ -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
}