fix label constraint
This commit is contained in:
@ -57,7 +57,7 @@ export class Highlight {
|
||||
createdAt!: Date
|
||||
|
||||
@UpdateDateColumn()
|
||||
updatedAt!: Date
|
||||
updatedAt?: Date | null
|
||||
|
||||
@Column('timestamp')
|
||||
sharedAt?: Date
|
||||
|
||||
@ -5,6 +5,7 @@ import {
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm'
|
||||
import { User } from './user'
|
||||
|
||||
@ -34,4 +35,7 @@ export class Label {
|
||||
|
||||
@Column('boolean', { default: false })
|
||||
internal!: boolean
|
||||
|
||||
@UpdateDateColumn()
|
||||
updatedAt?: Date | null
|
||||
}
|
||||
|
||||
@ -103,7 +103,7 @@ export class LibraryItem {
|
||||
readAt?: Date | null
|
||||
|
||||
@UpdateDateColumn()
|
||||
updatedAt!: Date
|
||||
updatedAt?: Date | null
|
||||
|
||||
@Column('text', { nullable: true })
|
||||
itemLanguage?: string | null
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
OneToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm'
|
||||
import { LibraryItem } from './library_item'
|
||||
import { User } from './user'
|
||||
|
||||
@Entity({ name: 'library_item_preview' })
|
||||
export class LibraryItemPreview {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id?: string
|
||||
|
||||
@OneToOne(() => User, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'sender_id' })
|
||||
sender!: User
|
||||
|
||||
@Column('text', { name: 'recipient_ids', array: true })
|
||||
recipientIds!: string[]
|
||||
|
||||
@OneToOne(() => LibraryItem, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'library_item_id' })
|
||||
libraryItem!: LibraryItem
|
||||
|
||||
@Column('text')
|
||||
thumbnail?: string
|
||||
|
||||
@Column('bool', { default: false })
|
||||
includesNote?: boolean
|
||||
|
||||
@Column('bool', { default: false })
|
||||
includesHighlight?: boolean
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt?: Date
|
||||
|
||||
@UpdateDateColumn()
|
||||
updatedAt?: Date
|
||||
}
|
||||
@ -19,7 +19,7 @@ export class PublishEntitySubscriber implements EntitySubscriberInterface {
|
||||
const msg = JSON.stringify({
|
||||
type: 'EntityCreated',
|
||||
entity: event.entity,
|
||||
entityClass: event.entity.constructor.name,
|
||||
entityClass: event.entity?.constructor?.name,
|
||||
})
|
||||
|
||||
if (env.dev.isLocal) {
|
||||
|
||||
@ -124,7 +124,7 @@ export type Article = {
|
||||
title: Scalars['String'];
|
||||
unsubHttpUrl?: Maybe<Scalars['String']>;
|
||||
unsubMailTo?: Maybe<Scalars['String']>;
|
||||
updatedAt: Scalars['Date'];
|
||||
updatedAt?: Maybe<Scalars['Date']>;
|
||||
uploadFileId?: Maybe<Scalars['ID']>;
|
||||
url: Scalars['String'];
|
||||
wordsCount?: Maybe<Scalars['Int']>;
|
||||
@ -167,7 +167,7 @@ export type ArticleSavingRequest = {
|
||||
id: Scalars['ID'];
|
||||
slug: Scalars['String'];
|
||||
status: ArticleSavingRequestStatus;
|
||||
updatedAt: Scalars['Date'];
|
||||
updatedAt?: Maybe<Scalars['Date']>;
|
||||
url: Scalars['String'];
|
||||
user: User;
|
||||
/** @deprecated userId has been replaced with user */
|
||||
@ -721,7 +721,7 @@ export type Feature = {
|
||||
id: Scalars['ID'];
|
||||
name: Scalars['String'];
|
||||
token: Scalars['String'];
|
||||
updatedAt: Scalars['Date'];
|
||||
updatedAt?: Maybe<Scalars['Date']>;
|
||||
};
|
||||
|
||||
export type FeedArticle = {
|
||||
@ -771,7 +771,7 @@ export type Filter = {
|
||||
id: Scalars['ID'];
|
||||
name: Scalars['String'];
|
||||
position: Scalars['Int'];
|
||||
updatedAt: Scalars['Date'];
|
||||
updatedAt?: Maybe<Scalars['Date']>;
|
||||
visible?: Maybe<Scalars['Boolean']>;
|
||||
};
|
||||
|
||||
@ -928,7 +928,7 @@ export type Highlight = {
|
||||
shortId: Scalars['String'];
|
||||
suffix?: Maybe<Scalars['String']>;
|
||||
type: HighlightType;
|
||||
updatedAt: Scalars['Date'];
|
||||
updatedAt?: Maybe<Scalars['Date']>;
|
||||
user: User;
|
||||
};
|
||||
|
||||
@ -938,7 +938,7 @@ export type HighlightReply = {
|
||||
highlight: Highlight;
|
||||
id: Scalars['ID'];
|
||||
text: Scalars['String'];
|
||||
updatedAt: Scalars['Date'];
|
||||
updatedAt?: Maybe<Scalars['Date']>;
|
||||
user: User;
|
||||
};
|
||||
|
||||
@ -979,7 +979,7 @@ export type Integration = {
|
||||
taskName?: Maybe<Scalars['String']>;
|
||||
token: Scalars['String'];
|
||||
type: IntegrationType;
|
||||
updatedAt: Scalars['Date'];
|
||||
updatedAt?: Maybe<Scalars['Date']>;
|
||||
};
|
||||
|
||||
export enum IntegrationType {
|
||||
@ -1082,7 +1082,7 @@ export type Link = {
|
||||
shareInfo: LinkShareInfo;
|
||||
shareStats: ShareStats;
|
||||
slug: Scalars['String'];
|
||||
updatedAt: Scalars['Date'];
|
||||
updatedAt?: Maybe<Scalars['Date']>;
|
||||
url: Scalars['String'];
|
||||
};
|
||||
|
||||
@ -1646,7 +1646,7 @@ export type Page = {
|
||||
readableHtml: Scalars['String'];
|
||||
title: Scalars['String'];
|
||||
type: PageType;
|
||||
updatedAt: Scalars['Date'];
|
||||
updatedAt?: Maybe<Scalars['Date']>;
|
||||
url: Scalars['String'];
|
||||
};
|
||||
|
||||
@ -1953,7 +1953,7 @@ export type RecommendationGroup = {
|
||||
members: Array<User>;
|
||||
name: Scalars['String'];
|
||||
topics?: Maybe<Array<Scalars['String']>>;
|
||||
updatedAt: Scalars['Date'];
|
||||
updatedAt?: Maybe<Scalars['Date']>;
|
||||
};
|
||||
|
||||
export type RecommendingUser = {
|
||||
@ -2037,7 +2037,7 @@ export type Rule = {
|
||||
filter: Scalars['String'];
|
||||
id: Scalars['ID'];
|
||||
name: Scalars['String'];
|
||||
updatedAt: Scalars['Date'];
|
||||
updatedAt?: Maybe<Scalars['Date']>;
|
||||
};
|
||||
|
||||
export type RuleAction = {
|
||||
@ -2642,7 +2642,7 @@ export type Subscription = {
|
||||
type: SubscriptionType;
|
||||
unsubscribeHttpUrl?: Maybe<Scalars['String']>;
|
||||
unsubscribeMailTo?: Maybe<Scalars['String']>;
|
||||
updatedAt: Scalars['Date'];
|
||||
updatedAt?: Maybe<Scalars['Date']>;
|
||||
url?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
@ -3191,7 +3191,7 @@ export type Webhook = {
|
||||
eventTypes: Array<WebhookEvent>;
|
||||
id: Scalars['ID'];
|
||||
method: Scalars['String'];
|
||||
updatedAt: Scalars['Date'];
|
||||
updatedAt?: Maybe<Scalars['Date']>;
|
||||
url: Scalars['String'];
|
||||
};
|
||||
|
||||
@ -4314,7 +4314,7 @@ export type ArticleResolvers<ContextType = ResolverContext, ParentType extends R
|
||||
title?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
unsubHttpUrl?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
unsubMailTo?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
updatedAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
|
||||
uploadFileId?: Resolver<Maybe<ResolversTypes['ID']>, ParentType, ContextType>;
|
||||
url?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
wordsCount?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
|
||||
@ -4343,7 +4343,7 @@ export type ArticleSavingRequestResolvers<ContextType = ResolverContext, ParentT
|
||||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
slug?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
status?: Resolver<ResolversTypes['ArticleSavingRequestStatus'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
|
||||
url?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
user?: Resolver<ResolversTypes['User'], ParentType, ContextType>;
|
||||
userId?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
@ -4712,7 +4712,7 @@ export type FeatureResolvers<ContextType = ResolverContext, ParentType extends R
|
||||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
token?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
@ -4760,7 +4760,7 @@ export type FilterResolvers<ContextType = ResolverContext, ParentType extends Re
|
||||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
position?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
|
||||
visible?: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
@ -4882,7 +4882,7 @@ export type HighlightResolvers<ContextType = ResolverContext, ParentType extends
|
||||
shortId?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
suffix?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
type?: Resolver<ResolversTypes['HighlightType'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
|
||||
user?: Resolver<ResolversTypes['User'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
@ -4892,7 +4892,7 @@ export type HighlightReplyResolvers<ContextType = ResolverContext, ParentType ex
|
||||
highlight?: Resolver<ResolversTypes['Highlight'], ParentType, ContextType>;
|
||||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
text?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
|
||||
user?: Resolver<ResolversTypes['User'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
@ -4924,7 +4924,7 @@ export type IntegrationResolvers<ContextType = ResolverContext, ParentType exten
|
||||
taskName?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
token?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
type?: Resolver<ResolversTypes['IntegrationType'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
@ -5007,7 +5007,7 @@ export type LinkResolvers<ContextType = ResolverContext, ParentType extends Reso
|
||||
shareInfo?: Resolver<ResolversTypes['LinkShareInfo'], ParentType, ContextType>;
|
||||
shareStats?: Resolver<ResolversTypes['ShareStats'], ParentType, ContextType>;
|
||||
slug?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
|
||||
url?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
@ -5216,7 +5216,7 @@ export type PageResolvers<ContextType = ResolverContext, ParentType extends Reso
|
||||
readableHtml?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
title?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
type?: Resolver<ResolversTypes['PageType'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
|
||||
url?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
@ -5379,7 +5379,7 @@ export type RecommendationGroupResolvers<ContextType = ResolverContext, ParentTy
|
||||
members?: Resolver<Array<ResolversTypes['User']>, ParentType, ContextType>;
|
||||
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
topics?: Resolver<Maybe<Array<ResolversTypes['String']>>, ParentType, ContextType>;
|
||||
updatedAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
@ -5440,7 +5440,7 @@ export type RuleResolvers<ContextType = ResolverContext, ParentType extends Reso
|
||||
filter?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
@ -5790,7 +5790,7 @@ export type SubscriptionResolvers<ContextType = ResolverContext, ParentType exte
|
||||
type?: SubscriptionResolver<ResolversTypes['SubscriptionType'], "type", ParentType, ContextType>;
|
||||
unsubscribeHttpUrl?: SubscriptionResolver<Maybe<ResolversTypes['String']>, "unsubscribeHttpUrl", ParentType, ContextType>;
|
||||
unsubscribeMailTo?: SubscriptionResolver<Maybe<ResolversTypes['String']>, "unsubscribeMailTo", ParentType, ContextType>;
|
||||
updatedAt?: SubscriptionResolver<ResolversTypes['Date'], "updatedAt", ParentType, ContextType>;
|
||||
updatedAt?: SubscriptionResolver<Maybe<ResolversTypes['Date']>, "updatedAt", ParentType, ContextType>;
|
||||
url?: SubscriptionResolver<Maybe<ResolversTypes['String']>, "url", ParentType, ContextType>;
|
||||
};
|
||||
|
||||
@ -6138,7 +6138,7 @@ export type WebhookResolvers<ContextType = ResolverContext, ParentType extends R
|
||||
eventTypes?: Resolver<Array<ResolversTypes['WebhookEvent']>, ParentType, ContextType>;
|
||||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
method?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
|
||||
url?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
@ -100,7 +100,7 @@ type Article {
|
||||
title: String!
|
||||
unsubHttpUrl: String
|
||||
unsubMailTo: String
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
uploadFileId: ID
|
||||
url: String!
|
||||
wordsCount: Int
|
||||
@ -134,7 +134,7 @@ type ArticleSavingRequest {
|
||||
id: ID!
|
||||
slug: String!
|
||||
status: ArticleSavingRequestStatus!
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
url: String!
|
||||
user: User!
|
||||
userId: ID! @deprecated(reason: "userId has been replaced with user")
|
||||
@ -638,7 +638,7 @@ type Feature {
|
||||
id: ID!
|
||||
name: String!
|
||||
token: String!
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
type FeedArticle {
|
||||
@ -683,7 +683,7 @@ type Filter {
|
||||
id: ID!
|
||||
name: String!
|
||||
position: Int!
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
visible: Boolean
|
||||
}
|
||||
|
||||
@ -825,7 +825,7 @@ type Highlight {
|
||||
shortId: String!
|
||||
suffix: String
|
||||
type: HighlightType!
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
user: User!
|
||||
}
|
||||
|
||||
@ -834,7 +834,7 @@ type HighlightReply {
|
||||
highlight: Highlight!
|
||||
id: ID!
|
||||
text: String!
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
user: User!
|
||||
}
|
||||
|
||||
@ -871,7 +871,7 @@ type Integration {
|
||||
taskName: String
|
||||
token: String!
|
||||
type: IntegrationType!
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
enum IntegrationType {
|
||||
@ -964,7 +964,7 @@ type Link {
|
||||
shareInfo: LinkShareInfo!
|
||||
shareStats: ShareStats!
|
||||
slug: String!
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
url: String!
|
||||
}
|
||||
|
||||
@ -1217,7 +1217,7 @@ type Page {
|
||||
readableHtml: String!
|
||||
title: String!
|
||||
type: PageType!
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
url: String!
|
||||
}
|
||||
|
||||
@ -1444,7 +1444,7 @@ type RecommendationGroup {
|
||||
members: [User!]!
|
||||
name: String!
|
||||
topics: [String!]
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
type RecommendingUser {
|
||||
@ -1520,7 +1520,7 @@ type Rule {
|
||||
filter: String!
|
||||
id: ID!
|
||||
name: String!
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
type RuleAction {
|
||||
@ -2082,7 +2082,7 @@ type Subscription {
|
||||
type: SubscriptionType!
|
||||
unsubscribeHttpUrl: String
|
||||
unsubscribeMailTo: String
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
url: String
|
||||
}
|
||||
|
||||
@ -2585,7 +2585,7 @@ type Webhook {
|
||||
eventTypes: [WebhookEvent!]!
|
||||
id: ID!
|
||||
method: String!
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
url: String!
|
||||
}
|
||||
|
||||
|
||||
@ -26,10 +26,15 @@ export const highlightRepository = entityManager
|
||||
})
|
||||
},
|
||||
|
||||
createAndSave(highlight: DeepPartial<Highlight>, userId: string) {
|
||||
createAndSave(
|
||||
highlight: DeepPartial<Highlight>,
|
||||
libraryItemId: string,
|
||||
userId: string
|
||||
) {
|
||||
return this.save({
|
||||
...unescapeHighlight(highlight),
|
||||
user: { id: userId },
|
||||
libraryItem: { id: libraryItemId },
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-floating-promises */
|
||||
import { Readability } from '@omnivore/readability'
|
||||
import graphqlFields from 'graphql-fields'
|
||||
import {
|
||||
LibraryItem,
|
||||
LibraryItemState,
|
||||
@ -12,7 +11,6 @@ import {
|
||||
} from '../../entity/library_item'
|
||||
import { env } from '../../env'
|
||||
import {
|
||||
Article,
|
||||
ArticleError,
|
||||
ArticleErrorCode,
|
||||
ArticleSavingRequestStatus,
|
||||
@ -25,13 +23,11 @@ import {
|
||||
CreateArticleError,
|
||||
CreateArticleErrorCode,
|
||||
CreateArticleSuccess,
|
||||
FeedArticle,
|
||||
MutationBulkActionArgs,
|
||||
MutationCreateArticleArgs,
|
||||
MutationSaveArticleReadingProgressArgs,
|
||||
MutationSetBookmarkArticleArgs,
|
||||
MutationSetFavoriteArticleArgs,
|
||||
PageInfo,
|
||||
PageType,
|
||||
QueryArticleArgs,
|
||||
QuerySearchArgs,
|
||||
@ -48,7 +44,6 @@ import {
|
||||
SetFavoriteArticleError,
|
||||
SetFavoriteArticleErrorCode,
|
||||
SetFavoriteArticleSuccess,
|
||||
SetShareArticleSuccess,
|
||||
TypeaheadSearchError,
|
||||
TypeaheadSearchErrorCode,
|
||||
TypeaheadSearchSuccess,
|
||||
@ -80,7 +75,6 @@ import {
|
||||
setFileUploadComplete,
|
||||
} from '../../services/upload_file'
|
||||
import { traceAs } from '../../tracing'
|
||||
import { Merge } from '../../util'
|
||||
import { analytics } from '../../utils/analytics'
|
||||
import { isSiteBlockedForParse } from '../../utils/blocked'
|
||||
import {
|
||||
@ -89,7 +83,7 @@ import {
|
||||
generateSlug,
|
||||
isBase64Image,
|
||||
isParsingTimeout,
|
||||
libraryItemToPartialArticle,
|
||||
libraryItemToArticle,
|
||||
libraryItemToSearchItem,
|
||||
pageError,
|
||||
titleForFilePath,
|
||||
@ -117,16 +111,6 @@ export enum ArticleFormat {
|
||||
HighlightedMarkdown = 'highlightedMarkdown',
|
||||
}
|
||||
|
||||
export type PartialArticle = Omit<
|
||||
Article,
|
||||
| 'updatedAt'
|
||||
| 'readingProgressPercent'
|
||||
| 'readingProgressAnchorIndex'
|
||||
| 'savedAt'
|
||||
| 'highlights'
|
||||
| 'contentReader'
|
||||
>
|
||||
|
||||
// These two page types are better handled by the backend
|
||||
// where we can use APIs to fetch their underlying content.
|
||||
const FORCE_PUPPETEER_URLS = [
|
||||
@ -136,12 +120,8 @@ const FORCE_PUPPETEER_URLS = [
|
||||
]
|
||||
const UNPARSEABLE_CONTENT = '<p>We were unable to parse this page.</p>'
|
||||
|
||||
export type CreateArticlesSuccessPartial = Merge<
|
||||
CreateArticleSuccess,
|
||||
{ createdArticle: PartialArticle }
|
||||
>
|
||||
export const createArticleResolver = authorized<
|
||||
CreateArticlesSuccessPartial,
|
||||
CreateArticleSuccess,
|
||||
CreateArticleError,
|
||||
MutationCreateArticleArgs
|
||||
>(
|
||||
@ -232,6 +212,11 @@ export const createArticleResolver = authorized<
|
||||
url,
|
||||
hash: '',
|
||||
isArchived: false,
|
||||
readingProgressAnchorIndex: 0,
|
||||
readingProgressPercent: 0,
|
||||
highlights: [],
|
||||
savedAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
}
|
||||
|
||||
@ -378,7 +363,7 @@ export const createArticleResolver = authorized<
|
||||
return {
|
||||
user,
|
||||
created: true,
|
||||
createdArticle: libraryItemToPartialArticle(libraryItemToReturn),
|
||||
createdArticle: libraryItemToArticle(libraryItemToReturn),
|
||||
}
|
||||
} catch (error) {
|
||||
log.error('Error creating article', error)
|
||||
@ -394,29 +379,28 @@ export const createArticleResolver = authorized<
|
||||
}
|
||||
)
|
||||
|
||||
export type ArticleSuccessPartial = Merge<
|
||||
ArticleSuccess,
|
||||
{ article: PartialArticle }
|
||||
>
|
||||
export const getArticleResolver = authorized<
|
||||
ArticleSuccessPartial,
|
||||
ArticleSuccess,
|
||||
ArticleError,
|
||||
QueryArticleArgs
|
||||
>(async (_obj, { slug, format }, { authTrx, uid, log }, info) => {
|
||||
try {
|
||||
const includeOriginalHtml =
|
||||
format === ArticleFormat.Distiller ||
|
||||
!!graphqlFields(info).article.originalHtml
|
||||
// const includeOriginalHtml =
|
||||
// format === ArticleFormat.Distiller ||
|
||||
// !!graphqlFields(info).article.originalHtml
|
||||
|
||||
// We allow the backend to use the ID instead of a slug to fetch the article
|
||||
const libraryItem = await authTrx((tx) =>
|
||||
tx
|
||||
.withRepository(libraryItemRepository)
|
||||
.createQueryBuilder('library_item')
|
||||
.leftJoinAndSelect('library_item.labels', 'labels')
|
||||
.leftJoinAndSelect('library_item.highlights', 'highlights')
|
||||
.where('library_item.id = :id', { id: slug })
|
||||
.getOne()
|
||||
tx.withRepository(libraryItemRepository).findOne({
|
||||
where: { slug },
|
||||
relations: {
|
||||
labels: true,
|
||||
highlights: {
|
||||
user: true,
|
||||
labels: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
if (!libraryItem || libraryItem.state === LibraryItemState.Deleted) {
|
||||
@ -444,7 +428,7 @@ export const getArticleResolver = authorized<
|
||||
}
|
||||
|
||||
return {
|
||||
article: libraryItemToPartialArticle(libraryItem),
|
||||
article: libraryItemToArticle(libraryItem),
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(error)
|
||||
@ -452,26 +436,26 @@ export const getArticleResolver = authorized<
|
||||
}
|
||||
})
|
||||
|
||||
type PaginatedPartialArticles = {
|
||||
edges: { cursor: string; node: PartialArticle }[]
|
||||
pageInfo: PageInfo
|
||||
}
|
||||
// type PaginatedPartialArticles = {
|
||||
// edges: { cursor: string; node: PartialArticle }[]
|
||||
// pageInfo: PageInfo
|
||||
// }
|
||||
|
||||
export type SetShareArticleSuccessPartial = Merge<
|
||||
SetShareArticleSuccess,
|
||||
{
|
||||
updatedFeedArticle?: Omit<
|
||||
FeedArticle,
|
||||
| 'sharedBy'
|
||||
| 'article'
|
||||
| 'highlightsCount'
|
||||
| 'annotationsCount'
|
||||
| 'reactions'
|
||||
>
|
||||
updatedFeedArticleId?: string
|
||||
updatedArticle: PartialArticle
|
||||
}
|
||||
>
|
||||
// export type SetShareArticleSuccessPartial = Merge<
|
||||
// SetShareArticleSuccess,
|
||||
// {
|
||||
// updatedFeedArticle?: Omit<
|
||||
// FeedArticle,
|
||||
// | 'sharedBy'
|
||||
// | 'article'
|
||||
// | 'highlightsCount'
|
||||
// | 'annotationsCount'
|
||||
// | 'reactions'
|
||||
// >
|
||||
// updatedFeedArticleId?: string
|
||||
// updatedArticle: PartialArticle
|
||||
// }
|
||||
// >
|
||||
|
||||
// export const setShareArticleResolver = authorized<
|
||||
// SetShareArticleSuccessPartial,
|
||||
@ -532,12 +516,8 @@ export type SetShareArticleSuccessPartial = Merge<
|
||||
// }
|
||||
// )
|
||||
|
||||
export type SetBookmarkArticleSuccessPartial = Merge<
|
||||
SetBookmarkArticleSuccess,
|
||||
{ bookmarkedArticle: PartialArticle }
|
||||
>
|
||||
export const setBookmarkArticleResolver = authorized<
|
||||
SetBookmarkArticleSuccessPartial,
|
||||
SetBookmarkArticleSuccess,
|
||||
SetBookmarkArticleError,
|
||||
MutationSetBookmarkArticleArgs
|
||||
>(async (_, { input: { articleID } }, { uid, log, pubsub }) => {
|
||||
@ -574,16 +554,12 @@ export const setBookmarkArticleResolver = authorized<
|
||||
})
|
||||
// Make sure article.id instead of userArticle.id has passed. We use it for cache updates
|
||||
return {
|
||||
bookmarkedArticle: libraryItemToPartialArticle(deletedLibraryItem),
|
||||
bookmarkedArticle: libraryItemToArticle(deletedLibraryItem),
|
||||
}
|
||||
})
|
||||
|
||||
export type SaveArticleReadingProgressSuccessPartial = Merge<
|
||||
SaveArticleReadingProgressSuccess,
|
||||
{ updatedArticle: PartialArticle }
|
||||
>
|
||||
export const saveArticleReadingProgressResolver = authorized<
|
||||
SaveArticleReadingProgressSuccessPartial,
|
||||
SaveArticleReadingProgressSuccess,
|
||||
SaveArticleReadingProgressError,
|
||||
MutationSaveArticleReadingProgressArgs
|
||||
>(
|
||||
@ -648,7 +624,7 @@ export const saveArticleReadingProgressResolver = authorized<
|
||||
const updatedItem = await updateLibraryItem(id, updatedPart, uid, pubsub)
|
||||
|
||||
return {
|
||||
updatedArticle: libraryItemToPartialArticle(updatedItem),
|
||||
updatedArticle: libraryItemToArticle(updatedItem),
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@ -29,9 +29,10 @@ import {
|
||||
} from '../../generated/graphql'
|
||||
import { highlightRepository } from '../../repository/highlight'
|
||||
import {
|
||||
createHighlight,
|
||||
deleteHighlightById,
|
||||
mergeHighlights,
|
||||
saveHighlight,
|
||||
updateHighlight,
|
||||
} from '../../services/highlights'
|
||||
import { analytics } from '../../utils/analytics'
|
||||
import { authorized } from '../../utils/helpers'
|
||||
@ -54,7 +55,12 @@ export const createHighlightResolver = authorized<
|
||||
MutationCreateHighlightArgs
|
||||
>(async (_, { input }, { log, pubsub, uid }) => {
|
||||
try {
|
||||
const newHighlight = await saveHighlight(input, uid, pubsub)
|
||||
const newHighlight = await createHighlight(
|
||||
input,
|
||||
input.articleId,
|
||||
uid,
|
||||
pubsub
|
||||
)
|
||||
|
||||
analytics.track({
|
||||
userId: uid,
|
||||
@ -131,6 +137,7 @@ export const mergeHighlightResolver = authorized<
|
||||
const newHighlight = await mergeHighlights(
|
||||
overlapHighlightIdList,
|
||||
highlight,
|
||||
input.articleId,
|
||||
uid,
|
||||
pubsub
|
||||
)
|
||||
@ -162,7 +169,12 @@ export const updateHighlightResolver = authorized<
|
||||
MutationUpdateHighlightArgs
|
||||
>(async (_, { input }, { pubsub, uid, log }) => {
|
||||
try {
|
||||
const updatedHighlight = await saveHighlight(input, uid, pubsub)
|
||||
const updatedHighlight = await updateHighlight(
|
||||
input.highlightId,
|
||||
input,
|
||||
uid,
|
||||
pubsub
|
||||
)
|
||||
|
||||
return { highlight: highlightDataToHighlight(updatedHighlight) }
|
||||
} catch (error) {
|
||||
|
||||
@ -276,6 +276,7 @@ export const setLabelsForHighlightResolver = authorized<
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// save labels in the library item
|
||||
await saveLabelsInHighlight(labelsSet, input.highlightId, uid, pubsub)
|
||||
|
||||
@ -293,7 +294,7 @@ export const setLabelsForHighlightResolver = authorized<
|
||||
labels: labelsSet,
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(error)
|
||||
log.error('setLabelsForHighlightResolver error', error)
|
||||
return {
|
||||
errorCodes: [SetLabelsErrorCode.BadRequest],
|
||||
}
|
||||
|
||||
@ -291,7 +291,7 @@ const schema = gql`
|
||||
slug: String!
|
||||
savedBy: User!
|
||||
savedAt: Date!
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
savedByViewer: Boolean!
|
||||
postedByViewer: Boolean!
|
||||
|
||||
@ -333,7 +333,7 @@ const schema = gql`
|
||||
originalHtml: String!
|
||||
readableHtml: String!
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
type RecommendingUser {
|
||||
@ -368,7 +368,7 @@ const schema = gql`
|
||||
originalHtml: String
|
||||
createdAt: Date!
|
||||
savedAt: Date!
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
publishedAt: Date
|
||||
readingProgressTopPercent: Float
|
||||
readingProgressPercent: Float!
|
||||
@ -705,7 +705,7 @@ const schema = gql`
|
||||
replies: [HighlightReply!]!
|
||||
sharedAt: Date
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
reactions: [Reaction!]!
|
||||
createdByMe: Boolean!
|
||||
highlightPositionPercent: Float
|
||||
@ -835,7 +835,7 @@ const schema = gql`
|
||||
highlight: Highlight!
|
||||
text: String!
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
input CreateHighlightReplyInput {
|
||||
@ -1109,7 +1109,7 @@ const schema = gql`
|
||||
status: ArticleSavingRequestStatus!
|
||||
errorCode: CreateArticleErrorCode
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
url: String!
|
||||
}
|
||||
|
||||
@ -1652,7 +1652,7 @@ const schema = gql`
|
||||
count: Int!
|
||||
lastFetchedAt: Date
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
enum SubscriptionStatus {
|
||||
@ -1757,7 +1757,7 @@ const schema = gql`
|
||||
method: String!
|
||||
enabled: Boolean!
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
type SetWebhookError {
|
||||
@ -1949,7 +1949,7 @@ const schema = gql`
|
||||
token: String!
|
||||
enabled: Boolean!
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
taskName: String
|
||||
}
|
||||
|
||||
@ -2047,7 +2047,7 @@ const schema = gql`
|
||||
name: String!
|
||||
token: String!
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
grantedAt: Date
|
||||
expiresAt: Date
|
||||
}
|
||||
@ -2074,7 +2074,7 @@ const schema = gql`
|
||||
actions: [RuleAction!]!
|
||||
enabled: Boolean!
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
eventTypes: [RuleEventType!]!
|
||||
}
|
||||
|
||||
@ -2188,7 +2188,7 @@ const schema = gql`
|
||||
category: String!
|
||||
description: String
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
defaultFilter: Boolean
|
||||
visible: Boolean
|
||||
}
|
||||
@ -2304,7 +2304,7 @@ const schema = gql`
|
||||
admins: [User!]!
|
||||
members: [User!]!
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
updatedAt: Date
|
||||
canPost: Boolean!
|
||||
description: String
|
||||
topics: [String!]
|
||||
|
||||
@ -6,7 +6,7 @@ import { createPubSubClient, EntityType } from '../pubsub'
|
||||
import { authTrx, setClaims } from '../repository'
|
||||
import { highlightRepository } from '../repository/highlight'
|
||||
|
||||
type HighlightEvent = Highlight & { pageId: string }
|
||||
type HighlightEvent = DeepPartial<Highlight> & { pageId: string }
|
||||
|
||||
export const getHighlightLocation = (patch: string): number | undefined => {
|
||||
const dmp = new diff_match_patch()
|
||||
@ -17,8 +17,9 @@ export const getHighlightLocation = (patch: string): number | undefined => {
|
||||
export const getHighlightUrl = (slug: string, highlightId: string): string =>
|
||||
`${homePageURL()}/me/${slug}#${highlightId}`
|
||||
|
||||
export const saveHighlight = async (
|
||||
export const createHighlight = async (
|
||||
highlight: DeepPartial<Highlight>,
|
||||
libraryItemId: string,
|
||||
userId: string,
|
||||
pubsub = createPubSubClient()
|
||||
) => {
|
||||
@ -27,12 +28,12 @@ export const saveHighlight = async (
|
||||
|
||||
return tx
|
||||
.withRepository(highlightRepository)
|
||||
.createAndSave(highlight, userId)
|
||||
.createAndSave(highlight, libraryItemId, userId)
|
||||
})
|
||||
|
||||
await pubsub.entityCreated<HighlightEvent>(
|
||||
EntityType.HIGHLIGHT,
|
||||
{ ...newHighlight, pageId: newHighlight.libraryItem.id },
|
||||
{ ...newHighlight, pageId: libraryItemId },
|
||||
userId
|
||||
)
|
||||
|
||||
@ -42,6 +43,7 @@ export const saveHighlight = async (
|
||||
export const mergeHighlights = async (
|
||||
highlightsToRemove: string[],
|
||||
highlightToAdd: DeepPartial<Highlight>,
|
||||
libraryItemId: string,
|
||||
userId: string,
|
||||
pubsub = createPubSubClient()
|
||||
) => {
|
||||
@ -50,18 +52,46 @@ export const mergeHighlights = async (
|
||||
|
||||
await highlightRepo.delete(highlightsToRemove)
|
||||
|
||||
return highlightRepo.createAndSave(highlightToAdd, userId)
|
||||
return highlightRepo.createAndSave(highlightToAdd, libraryItemId, userId)
|
||||
})
|
||||
|
||||
await pubsub.entityCreated<HighlightEvent>(
|
||||
EntityType.HIGHLIGHT,
|
||||
{ ...newHighlight, pageId: newHighlight.libraryItem.id },
|
||||
{ ...newHighlight, pageId: libraryItemId },
|
||||
userId
|
||||
)
|
||||
|
||||
return newHighlight
|
||||
}
|
||||
|
||||
export const updateHighlight = async (
|
||||
highlightId: string,
|
||||
highlight: DeepPartial<Highlight>,
|
||||
userId: string,
|
||||
pubsub = createPubSubClient()
|
||||
) => {
|
||||
const updatedHighlight = await authTrx(async (tx) => {
|
||||
await tx.withRepository(highlightRepository).save({
|
||||
...highlight,
|
||||
id: highlightId,
|
||||
})
|
||||
|
||||
return tx.withRepository(highlightRepository).findById(highlightId)
|
||||
})
|
||||
|
||||
if (!updatedHighlight) {
|
||||
throw new Error(`Highlight ${highlightId} not found`)
|
||||
}
|
||||
|
||||
await pubsub.entityUpdated<HighlightEvent>(
|
||||
EntityType.HIGHLIGHT,
|
||||
{ ...highlight, id: highlightId, pageId: updatedHighlight.libraryItem.id },
|
||||
userId
|
||||
)
|
||||
|
||||
return updatedHighlight
|
||||
}
|
||||
|
||||
export const deleteHighlightById = async (highlightId: string) => {
|
||||
return authTrx(async (tx) => {
|
||||
const highlightRepo = tx.withRepository(highlightRepository)
|
||||
@ -70,6 +100,8 @@ export const deleteHighlightById = async (highlightId: string) => {
|
||||
throw new Error(`Highlight ${highlightId} not found`)
|
||||
}
|
||||
|
||||
return highlightRepo.remove(highlight)
|
||||
await highlightRepo.remove(highlight)
|
||||
|
||||
return highlight
|
||||
})
|
||||
}
|
||||
|
||||
@ -57,7 +57,7 @@ export const saveLabelsInLibraryItem = async (
|
||||
await authTrx(async (tx) => {
|
||||
await tx
|
||||
.withRepository(libraryItemRepository)
|
||||
.update(libraryItemId, { labels })
|
||||
.save({ id: libraryItemId, labels })
|
||||
})
|
||||
|
||||
// create pubsub event
|
||||
@ -98,7 +98,9 @@ export const saveLabelsInHighlight = async (
|
||||
pubsub = createPubSubClient()
|
||||
) => {
|
||||
await authTrx(async (tx) => {
|
||||
await tx.withRepository(highlightRepository).update(highlightId, { labels })
|
||||
await tx
|
||||
.withRepository(highlightRepository)
|
||||
.save({ id: highlightId, labels })
|
||||
})
|
||||
|
||||
// create pubsub event
|
||||
|
||||
@ -242,6 +242,8 @@ export const searchLibraryItems = async (
|
||||
.createQueryBuilder(LibraryItem, 'library_item')
|
||||
.leftJoinAndSelect('library_item.labels', 'labels')
|
||||
.leftJoinAndSelect('library_item.highlights', 'highlights')
|
||||
.leftJoinAndSelect('highlights.user', 'user')
|
||||
.leftJoinAndSelect('user.profile', 'profile')
|
||||
.where('library_item.user_id = :userId', { userId })
|
||||
|
||||
// build the where clause
|
||||
|
||||
@ -29,7 +29,7 @@ import {
|
||||
import { logger } from '../utils/logger'
|
||||
import { parsePreparedContent } from '../utils/parser'
|
||||
import { createPageSaveRequest } from './create_page_save_request'
|
||||
import { saveHighlight } from './highlights'
|
||||
import { createHighlight } from './highlights'
|
||||
import { findOrCreateLabels } from './labels'
|
||||
import { createLibraryItem, updateLibraryItem } from './library_item'
|
||||
|
||||
@ -174,7 +174,7 @@ export const savePage = async (
|
||||
type: HighlightType.Highlight,
|
||||
}
|
||||
|
||||
if (!(await saveHighlight(highlight, user.id))) {
|
||||
if (!(await createHighlight(highlight, clientRequestId, user.id))) {
|
||||
return {
|
||||
errorCodes: [SaveErrorCode.EmbeddedHighlightFailed],
|
||||
message: 'Failed to save highlight',
|
||||
|
||||
@ -10,10 +10,12 @@ import { LibraryItem, LibraryItemState } from '../entity/library_item'
|
||||
import { Recommendation as RecommendationData } from '../entity/recommendation'
|
||||
import { RegistrationType, User } from '../entity/user'
|
||||
import {
|
||||
Article,
|
||||
ArticleSavingRequest,
|
||||
ArticleSavingRequestStatus,
|
||||
ContentReader,
|
||||
CreateArticleError,
|
||||
CreateArticleSuccess,
|
||||
FeedArticle,
|
||||
Highlight,
|
||||
HighlightType,
|
||||
@ -24,7 +26,6 @@ import {
|
||||
SearchItem,
|
||||
} from '../generated/graphql'
|
||||
import { createPubSubClient } from '../pubsub'
|
||||
import { CreateArticlesSuccessPartial, PartialArticle } from '../resolvers'
|
||||
import { Claims, WithDataSourcesContext } from '../resolvers/types'
|
||||
import { validateUrl } from '../services/create_page_save_request'
|
||||
import { updateLibraryItem } from '../services/library_item'
|
||||
@ -182,7 +183,7 @@ export const pageError = async (
|
||||
userId: string,
|
||||
pageId?: string | null,
|
||||
pubsub = createPubSubClient()
|
||||
): Promise<CreateArticleError | CreateArticlesSuccessPartial> => {
|
||||
): Promise<CreateArticleError | CreateArticleSuccess> => {
|
||||
if (!pageId) return result
|
||||
|
||||
await updateLibraryItem(
|
||||
@ -225,9 +226,7 @@ export const libraryItemToArticleSavingRequest = (
|
||||
userId: user.id,
|
||||
})
|
||||
|
||||
export const libraryItemToPartialArticle = (
|
||||
item: LibraryItem
|
||||
): PartialArticle => ({
|
||||
export const libraryItemToArticle = (item: LibraryItem): Article => ({
|
||||
...item,
|
||||
url: item.originalUrl,
|
||||
state: item.state as unknown as ArticleSavingRequestStatus,
|
||||
@ -239,6 +238,10 @@ export const libraryItemToPartialArticle = (
|
||||
),
|
||||
subscription: item.subscription?.name,
|
||||
image: item.thumbnail,
|
||||
contentReader: item.contentReader as unknown as ContentReader,
|
||||
readingProgressAnchorIndex: item.readingProgressHighestReadAnchor,
|
||||
readingProgressPercent: item.readingProgressTopPercent,
|
||||
highlights: item.highlights?.map(highlightDataToHighlight) || [],
|
||||
})
|
||||
|
||||
export const libraryItemToSearchItem = (item: LibraryItem): SearchItem => ({
|
||||
|
||||
@ -27,7 +27,7 @@ CREATE TABLE omnivore.library_item (
|
||||
archived_at timestamptz,
|
||||
deleted_at timestamptz,
|
||||
read_at timestamptz,
|
||||
updated_at timestamptz NOT NULL DEFAULT current_timestamp,
|
||||
updated_at timestamptz,
|
||||
item_language text,
|
||||
word_count integer,
|
||||
site_name text,
|
||||
|
||||
@ -8,7 +8,9 @@ CREATE TABLE omnivore.entity_labels (
|
||||
id uuid PRIMARY KEY DEFAULT uuid_generate_v1mc(),
|
||||
library_item_id uuid REFERENCES omnivore.library_item(id) ON DELETE CASCADE,
|
||||
highlight_id uuid REFERENCES omnivore.highlight(id) ON DELETE CASCADE,
|
||||
label_id uuid NOT NULL REFERENCES omnivore.labels(id) ON DELETE CASCADE
|
||||
label_id uuid NOT NULL REFERENCES omnivore.labels(id) ON DELETE CASCADE,
|
||||
unique(library_item_id, label_id),
|
||||
unique(highlight_id, label_id)
|
||||
);
|
||||
|
||||
GRANT SELECT, INSERT, DELETE ON omnivore.entity_labels TO omnivore_user;
|
||||
@ -36,7 +38,7 @@ BEGIN
|
||||
)
|
||||
-- Update label_names on library_item
|
||||
UPDATE omnivore.library_item li
|
||||
SET label_names = l.names_agg
|
||||
SET label_names = coalesce(l.names_agg, array[]::text[])
|
||||
FROM labels_agg l
|
||||
WHERE li.id = current_library_item_id;
|
||||
ELSIF current_highlight_id IS NOT NULL THEN
|
||||
@ -51,7 +53,7 @@ BEGIN
|
||||
)
|
||||
-- Update highlight_labels on library_item
|
||||
UPDATE omnivore.library_item li
|
||||
SET highlight_labels = l.names_agg
|
||||
SET highlight_labels = coalesce(l.names_agg, array[]::text[])
|
||||
FROM labels_agg l
|
||||
WHERE li.id = current_library_item_id;
|
||||
END IF;
|
||||
|
||||
17
packages/db/migrations/0120.do.alter_labels_table.sql
Executable file
17
packages/db/migrations/0120.do.alter_labels_table.sql
Executable file
@ -0,0 +1,17 @@
|
||||
-- Type: DO
|
||||
-- Name: alter_labels_table
|
||||
-- Description: Alter labels table
|
||||
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE omnivore.labels ADD COLUMN updated_at timestamptz;
|
||||
|
||||
CREATE TRIGGER update_labels_modtime BEFORE UPDATE ON omnivore.labels
|
||||
FOR EACH ROW EXECUTE PROCEDURE update_updated_at_column();
|
||||
|
||||
ALTER TABLE omnivore.abuse_report DROP COLUMN page_id;
|
||||
ALTER TABLE omnivore.abuse_report RENAME COLUMN elastic_page_id TO library_item_id;
|
||||
ALTER TABLE omnivore.content_display_report DROP COLUMN page_id;
|
||||
ALTER TABLE omnivore.content_display_report RENAME COLUMN elastic_page_id TO library_item_id;
|
||||
|
||||
COMMIT;
|
||||
@ -1,23 +0,0 @@
|
||||
-- Type: DO
|
||||
-- Name: library_item_preview
|
||||
-- Description: Create library_item_preview table
|
||||
|
||||
BEGIN;
|
||||
|
||||
CREATE TABLE omnivore.library_item_preview (
|
||||
id uuid PRIMARY KEY DEFAULT uuid_generate_v1mc(),
|
||||
sender_id uuid NOT NULL REFERENCES omnivore.user ON DELETE CASCADE,
|
||||
recipient_ids uuid[] NOT NULL, -- Array of user ids
|
||||
library_item_id uuid NOT NULL REFERENCES omnivore.library_item(id) ON DELETE CASCADE,
|
||||
thumbnail text,
|
||||
includes_note bool NOT NULL DEFAULT false,
|
||||
includes_highlight bool NOT NULL DEFAULT false,
|
||||
created_at timestamptz NOT NULL DEFAULT current_timestamp,
|
||||
updated_at timestamptz NOT NULL DEFAULT current_timestamp
|
||||
);
|
||||
|
||||
GRANT SELECT, INSERT ON omnivore.library_item_preview TO omnivore_user;
|
||||
|
||||
CREATE TRIGGER update_library_item_preview_modtime BEFORE UPDATE ON omnivore.library_item_preview FOR EACH ROW EXECUTE PROCEDURE update_updated_at_column();
|
||||
|
||||
COMMIT;
|
||||
16
packages/db/migrations/0120.undo.alter_labels_table.sql
Executable file
16
packages/db/migrations/0120.undo.alter_labels_table.sql
Executable file
@ -0,0 +1,16 @@
|
||||
-- Type: UNDO
|
||||
-- Name: alter_labels_table
|
||||
-- Description: Alter labels table
|
||||
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE omnivore.abuse_report RENAME COLUMN library_item_id TO elastic_page_id;
|
||||
ALTER TABLE omnivore.abuse_report ADD COLUMN page_id text;
|
||||
ALTER TABLE omnivore.content_display_report RENAME COLUMN library_item_id TO elastic_page_id;
|
||||
ALTER TABLE omnivore.content_display_report ADD COLUMN page_id text;
|
||||
|
||||
DROP TRIGGER update_labels_modtime ON omnivore.labels;
|
||||
|
||||
ALTER TABLE omnivore.labels DROP COLUMN updated_at;
|
||||
|
||||
COMMIT;
|
||||
@ -1,11 +0,0 @@
|
||||
-- Type: UNDO
|
||||
-- Name: library_item_preview
|
||||
-- Description: Create library_item_preview table
|
||||
|
||||
BEGIN;
|
||||
|
||||
DROP TRIGGER update_library_item_preview_modtime ON omnivore.library_item_preview;
|
||||
|
||||
DROP TABLE omnivore.library_item_preview;
|
||||
|
||||
COMMIT;
|
||||
@ -51,7 +51,7 @@ BEGIN
|
||||
WHERE library_item_id = current_library_item_id
|
||||
)
|
||||
UPDATE omnivore.library_item li
|
||||
SET highlight_annotations = h.annotation_agg
|
||||
SET highlight_annotations = coalesce(h.annotation_agg, array[]::text[])
|
||||
FROM highlight_agg h
|
||||
WHERE li.id = current_library_item_id;
|
||||
|
||||
|
||||
@ -61,9 +61,6 @@ CREATE POLICY search_history_policy on omnivore.search_history
|
||||
WITH CHECK (user_id = omnivore.get_current_user_id());
|
||||
GRANT SELECT, INSERT, DELETE ON omnivore.search_history TO omnivore_user;
|
||||
|
||||
ALTER TABLE omnivore.abuse_report DROP COLUMN page_id;
|
||||
ALTER TABLE omnivore.abuse_report RENAME COLUMN elastic_page_id TO library_item_id;
|
||||
ALTER TABLE omnivore.content_display_report DROP COLUMN page_id;
|
||||
ALTER TABLE omnivore.content_display_report RENAME COLUMN elastic_page_id TO library_item_id;
|
||||
|
||||
|
||||
COMMIT;
|
||||
|
||||
@ -32,9 +32,4 @@ DROP POLICY user_device_tokens_policy on omnivore.user_device_tokens;
|
||||
ALTER TABLE omnivore.search_history DISABLE ROW LEVEL SECURITY;
|
||||
DROP POLICY search_history_policy on omnivore.search_history;
|
||||
|
||||
ALTER TABLE omnivore.abuse_report RENAME COLUMN library_item_id TO elastic_page_id;
|
||||
ALTER TABLE omnivore.abuse_report ADD COLUMN page_id text;
|
||||
ALTER TABLE omnivore.content_display_report RENAME COLUMN library_item_id TO elastic_page_id;
|
||||
ALTER TABLE omnivore.content_display_report ADD COLUMN page_id text;
|
||||
|
||||
COMMIT;
|
||||
|
||||
@ -10725,9 +10725,9 @@ camelcase@^6.0.0, camelcase@^6.2.0:
|
||||
integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==
|
||||
|
||||
caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001251, caniuse-lite@^1.0.30001286, caniuse-lite@^1.0.30001317, caniuse-lite@^1.0.30001332:
|
||||
version "1.0.30001462"
|
||||
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001462.tgz"
|
||||
integrity sha512-PDd20WuOBPiasZ7KbFnmQRyuLE7cFXW2PVd7dmALzbkUXEP46upAuCDm9eY9vho8fgNMGmbAX92QBZHzcnWIqw==
|
||||
version "1.0.30001527"
|
||||
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001527.tgz"
|
||||
integrity sha512-YkJi7RwPgWtXVSgK4lG9AHH57nSzvvOp9MesgXmw4Q7n0C3H04L0foHqfxcmSAm5AcWb8dW9AYj2tR7/5GnddQ==
|
||||
|
||||
capital-case@^1.0.4:
|
||||
version "1.0.4"
|
||||
|
||||
Reference in New Issue
Block a user