add following handler to save following item

This commit is contained in:
Hongbo Wu
2023-11-09 15:53:44 +08:00
parent a71181982f
commit b7496db56c
12 changed files with 158 additions and 271 deletions

View File

@ -23,8 +23,8 @@ export type AddFollowingToLibraryError = {
};
export enum AddFollowingToLibraryErrorCode {
AlreadyExists = 'ALREADY_EXISTS',
BadRequest = 'BAD_REQUEST',
NotFound = 'NOT_FOUND',
Unauthorized = 'UNAUTHORIZED'
}
@ -855,26 +855,6 @@ export type FiltersSuccess = {
filters: Array<Filter>;
};
export type Following = {
__typename?: 'Following';
SharedAt: Scalars['Date'];
author?: Maybe<Scalars['String']>;
createdAt: Scalars['Date'];
description?: Maybe<Scalars['String']>;
hiddenAt?: Maybe<Scalars['Date']>;
id: Scalars['ID'];
image?: Maybe<Scalars['String']>;
links?: Maybe<Scalars['JSON']>;
previewContent?: Maybe<Scalars['String']>;
publishedAt?: Maybe<Scalars['Date']>;
seenAt?: Maybe<Scalars['Date']>;
sharedBy: Scalars['String'];
sharedSource: Scalars['String'];
title: Scalars['String'];
updatedAt: Scalars['Date'];
url: Scalars['String'];
};
export type GenerateApiKeyError = {
__typename?: 'GenerateApiKeyError';
errorCodes: Array<GenerateApiKeyErrorCode>;
@ -1359,7 +1339,6 @@ export type Mutation = {
saveArticleReadingProgress: SaveArticleReadingProgressResult;
saveFile: SaveResult;
saveFilter: SaveFilterResult;
saveFollowing: SaveFollowingResult;
savePage: SaveResult;
saveUrl: SaveResult;
setBookmarkArticle: SetBookmarkArticleResult;
@ -1561,11 +1540,6 @@ export type MutationSaveFilterArgs = {
};
export type MutationSaveFollowingArgs = {
input: SaveFollowingInput;
};
export type MutationSavePageArgs = {
input: SavePageInput;
};
@ -2261,36 +2235,6 @@ export type SaveFilterSuccess = {
filter: Filter;
};
export type SaveFollowingError = {
__typename?: 'SaveFollowingError';
errorCodes: Array<SaveFollowingErrorCode>;
};
export enum SaveFollowingErrorCode {
BadRequest = 'BAD_REQUEST',
Unauthorized = 'UNAUTHORIZED'
}
export type SaveFollowingInput = {
author?: InputMaybe<Scalars['String']>;
description?: InputMaybe<Scalars['String']>;
links?: InputMaybe<Scalars['JSON']>;
previewContent?: InputMaybe<Scalars['String']>;
publishedAt?: InputMaybe<Scalars['Date']>;
sharedAt: Scalars['Date'];
sharedBy: Scalars['String'];
sharedSource: Scalars['String'];
title: Scalars['String'];
url: Scalars['String'];
};
export type SaveFollowingResult = SaveFollowingError | SaveFollowingSuccess;
export type SaveFollowingSuccess = {
__typename?: 'SaveFollowingSuccess';
following: Following;
};
export type SavePageInput = {
clientRequestId: Scalars['ID'];
labels?: InputMaybe<Array<CreateLabelInput>>;
@ -3616,7 +3560,6 @@ export type ResolversTypes = {
FiltersResult: ResolversTypes['FiltersError'] | ResolversTypes['FiltersSuccess'];
FiltersSuccess: ResolverTypeWrapper<FiltersSuccess>;
Float: ResolverTypeWrapper<Scalars['Float']>;
Following: ResolverTypeWrapper<Following>;
GenerateApiKeyError: ResolverTypeWrapper<GenerateApiKeyError>;
GenerateApiKeyErrorCode: GenerateApiKeyErrorCode;
GenerateApiKeyInput: GenerateApiKeyInput;
@ -3782,11 +3725,6 @@ export type ResolversTypes = {
SaveFilterInput: SaveFilterInput;
SaveFilterResult: ResolversTypes['SaveFilterError'] | ResolversTypes['SaveFilterSuccess'];
SaveFilterSuccess: ResolverTypeWrapper<SaveFilterSuccess>;
SaveFollowingError: ResolverTypeWrapper<SaveFollowingError>;
SaveFollowingErrorCode: SaveFollowingErrorCode;
SaveFollowingInput: SaveFollowingInput;
SaveFollowingResult: ResolversTypes['SaveFollowingError'] | ResolversTypes['SaveFollowingSuccess'];
SaveFollowingSuccess: ResolverTypeWrapper<SaveFollowingSuccess>;
SavePageInput: SavePageInput;
SaveResult: ResolversTypes['SaveError'] | ResolversTypes['SaveSuccess'];
SaveSuccess: ResolverTypeWrapper<SaveSuccess>;
@ -4109,7 +4047,6 @@ export type ResolversParentTypes = {
FiltersResult: ResolversParentTypes['FiltersError'] | ResolversParentTypes['FiltersSuccess'];
FiltersSuccess: FiltersSuccess;
Float: Scalars['Float'];
Following: Following;
GenerateApiKeyError: GenerateApiKeyError;
GenerateApiKeyInput: GenerateApiKeyInput;
GenerateApiKeyResult: ResolversParentTypes['GenerateApiKeyError'] | ResolversParentTypes['GenerateApiKeySuccess'];
@ -4239,10 +4176,6 @@ export type ResolversParentTypes = {
SaveFilterInput: SaveFilterInput;
SaveFilterResult: ResolversParentTypes['SaveFilterError'] | ResolversParentTypes['SaveFilterSuccess'];
SaveFilterSuccess: SaveFilterSuccess;
SaveFollowingError: SaveFollowingError;
SaveFollowingInput: SaveFollowingInput;
SaveFollowingResult: ResolversParentTypes['SaveFollowingError'] | ResolversParentTypes['SaveFollowingSuccess'];
SaveFollowingSuccess: SaveFollowingSuccess;
SavePageInput: SavePageInput;
SaveResult: ResolversParentTypes['SaveError'] | ResolversParentTypes['SaveSuccess'];
SaveSuccess: SaveSuccess;
@ -5010,26 +4943,6 @@ export type FiltersSuccessResolvers<ContextType = ResolverContext, ParentType ex
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type FollowingResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['Following'] = ResolversParentTypes['Following']> = {
SharedAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
author?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
createdAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
description?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
hiddenAt?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
image?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
links?: Resolver<Maybe<ResolversTypes['JSON']>, ParentType, ContextType>;
previewContent?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
publishedAt?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
seenAt?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
sharedBy?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
sharedSource?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
title?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
updatedAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
url?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type GenerateApiKeyErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['GenerateApiKeyError'] = ResolversParentTypes['GenerateApiKeyError']> = {
errorCodes?: Resolver<Array<ResolversTypes['GenerateApiKeyErrorCode']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
@ -5396,7 +5309,6 @@ export type MutationResolvers<ContextType = ResolverContext, ParentType extends
saveArticleReadingProgress?: Resolver<ResolversTypes['SaveArticleReadingProgressResult'], ParentType, ContextType, RequireFields<MutationSaveArticleReadingProgressArgs, 'input'>>;
saveFile?: Resolver<ResolversTypes['SaveResult'], ParentType, ContextType, RequireFields<MutationSaveFileArgs, 'input'>>;
saveFilter?: Resolver<ResolversTypes['SaveFilterResult'], ParentType, ContextType, RequireFields<MutationSaveFilterArgs, 'input'>>;
saveFollowing?: Resolver<ResolversTypes['SaveFollowingResult'], ParentType, ContextType, RequireFields<MutationSaveFollowingArgs, 'input'>>;
savePage?: Resolver<ResolversTypes['SaveResult'], ParentType, ContextType, RequireFields<MutationSavePageArgs, 'input'>>;
saveUrl?: Resolver<ResolversTypes['SaveResult'], ParentType, ContextType, RequireFields<MutationSaveUrlArgs, 'input'>>;
setBookmarkArticle?: Resolver<ResolversTypes['SetBookmarkArticleResult'], ParentType, ContextType, RequireFields<MutationSetBookmarkArticleArgs, 'input'>>;
@ -5756,20 +5668,6 @@ export type SaveFilterSuccessResolvers<ContextType = ResolverContext, ParentType
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type SaveFollowingErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['SaveFollowingError'] = ResolversParentTypes['SaveFollowingError']> = {
errorCodes?: Resolver<Array<ResolversTypes['SaveFollowingErrorCode']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type SaveFollowingResultResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['SaveFollowingResult'] = ResolversParentTypes['SaveFollowingResult']> = {
__resolveType: TypeResolveFn<'SaveFollowingError' | 'SaveFollowingSuccess', ParentType, ContextType>;
};
export type SaveFollowingSuccessResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['SaveFollowingSuccess'] = ResolversParentTypes['SaveFollowingSuccess']> = {
following?: Resolver<ResolversTypes['Following'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type SaveResultResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['SaveResult'] = ResolversParentTypes['SaveResult']> = {
__resolveType: TypeResolveFn<'SaveError' | 'SaveSuccess', ParentType, ContextType>;
};
@ -6554,7 +6452,6 @@ export type Resolvers<ContextType = ResolverContext> = {
FiltersError?: FiltersErrorResolvers<ContextType>;
FiltersResult?: FiltersResultResolvers<ContextType>;
FiltersSuccess?: FiltersSuccessResolvers<ContextType>;
Following?: FollowingResolvers<ContextType>;
GenerateApiKeyError?: GenerateApiKeyErrorResolvers<ContextType>;
GenerateApiKeyResult?: GenerateApiKeyResultResolvers<ContextType>;
GenerateApiKeySuccess?: GenerateApiKeySuccessResolvers<ContextType>;
@ -6665,9 +6562,6 @@ export type Resolvers<ContextType = ResolverContext> = {
SaveFilterError?: SaveFilterErrorResolvers<ContextType>;
SaveFilterResult?: SaveFilterResultResolvers<ContextType>;
SaveFilterSuccess?: SaveFilterSuccessResolvers<ContextType>;
SaveFollowingError?: SaveFollowingErrorResolvers<ContextType>;
SaveFollowingResult?: SaveFollowingResultResolvers<ContextType>;
SaveFollowingSuccess?: SaveFollowingSuccessResolvers<ContextType>;
SaveResult?: SaveResultResolvers<ContextType>;
SaveSuccess?: SaveSuccessResolvers<ContextType>;
SearchError?: SearchErrorResolvers<ContextType>;

View File

@ -5,8 +5,8 @@ type AddFollowingToLibraryError {
}
enum AddFollowingToLibraryErrorCode {
ALREADY_EXISTS
BAD_REQUEST
NOT_FOUND
UNAUTHORIZED
}
@ -758,25 +758,6 @@ type FiltersSuccess {
filters: [Filter!]!
}
type Following {
SharedAt: Date!
author: String
createdAt: Date!
description: String
hiddenAt: Date
id: ID!
image: String
links: JSON
previewContent: String
publishedAt: Date
seenAt: Date
sharedBy: String!
sharedSource: String!
title: String!
updatedAt: Date!
url: String!
}
type GenerateApiKeyError {
errorCodes: [GenerateApiKeyErrorCode!]!
}
@ -1221,7 +1202,6 @@ type Mutation {
saveArticleReadingProgress(input: SaveArticleReadingProgressInput!): SaveArticleReadingProgressResult!
saveFile(input: SaveFileInput!): SaveResult!
saveFilter(input: SaveFilterInput!): SaveFilterResult!
saveFollowing(input: SaveFollowingInput!): SaveFollowingResult!
savePage(input: SavePageInput!): SaveResult!
saveUrl(input: SaveUrlInput!): SaveResult!
setBookmarkArticle(input: SetBookmarkArticleInput!): SetBookmarkArticleResult!
@ -1715,34 +1695,6 @@ type SaveFilterSuccess {
filter: Filter!
}
type SaveFollowingError {
errorCodes: [SaveFollowingErrorCode!]!
}
enum SaveFollowingErrorCode {
BAD_REQUEST
UNAUTHORIZED
}
input SaveFollowingInput {
author: String
description: String
links: JSON
previewContent: String
publishedAt: Date
sharedAt: Date!
sharedBy: String!
sharedSource: String!
title: String!
url: String!
}
union SaveFollowingResult = SaveFollowingError | SaveFollowingSuccess
type SaveFollowingSuccess {
following: Following!
}
input SavePageInput {
clientRequestId: ID!
labels: [CreateLabelInput!]

View File

@ -9,16 +9,16 @@ import {
FeedsErrorCode,
FeedsSuccess,
MutationAddFollowingToLibraryArgs,
MutationSaveFollowingArgs,
QueryFeedsArgs,
SaveFollowingError,
SaveFollowingSuccess,
} from '../../generated/graphql'
import { feedRepository } from '../../repository/feed'
import { createPageSaveRequest } from '../../services/create_page_save_request'
import { createFollowing } from '../../services/library_item'
import { updateLibraryItem } from '../../services/library_item'
import { analytics } from '../../utils/analytics'
import { authorized } from '../../utils/helpers'
import {
authorized,
libraryItemToArticleSavingRequest,
} from '../../utils/helpers'
export const feedsResolve = authorized<
FeedsSuccess,
@ -72,33 +72,6 @@ export const feedsResolve = authorized<
}
})
export const saveFollowingResolver = authorized<
SaveFollowingSuccess,
SaveFollowingError,
MutationSaveFollowingArgs
>(async (_, { input }, { uid }) => {
analytics.track({
userId: uid,
event: 'save_following',
properties: {
url: input.url,
},
})
const newItem = await createFollowing(input, uid)
return {
__typename: 'SaveFollowingSuccess',
following: {
...newItem,
url: newItem.originalUrl,
SharedAt: new Date(input.sharedAt),
sharedBy: input.sharedBy,
sharedSource: input.sharedSource,
},
}
})
export const addFollowingToLibraryResolver = authorized<
AddFollowingToLibrarySuccess,
AddFollowingToLibraryError,
@ -117,7 +90,6 @@ export const addFollowingToLibraryResolver = authorized<
where: {
id,
sharedAt: Not(IsNull()),
isInLibrary: false,
},
relations: ['user'],
})
@ -125,22 +97,48 @@ export const addFollowingToLibraryResolver = authorized<
if (!item) {
return {
errorCodes: [AddFollowingToLibraryErrorCode.NotFound],
errorCodes: [AddFollowingToLibraryErrorCode.Unauthorized],
}
}
const articleSavingRequest = await createPageSaveRequest({
userId: uid,
url: item.originalUrl,
articleSavingRequestId: id,
priority: 'high',
publishedAt: item.publishedAt || undefined,
savedAt: item.savedAt || undefined,
pubsub,
})
if (item.isInLibrary) {
return {
errorCodes: [AddFollowingToLibraryErrorCode.AlreadyExists],
}
}
// if the content is not fetched yet, create a page save request
if (!item.readableContent) {
const articleSavingRequest = await createPageSaveRequest({
userId: uid,
url: item.originalUrl,
articleSavingRequestId: id,
priority: 'high',
publishedAt: item.publishedAt || undefined,
pubsub,
})
return {
__typename: 'AddFollowingToLibrarySuccess',
articleSavingRequest,
}
}
const updatedItem = await updateLibraryItem(
item.id,
{
isInLibrary: true,
savedAt: new Date(),
},
uid,
pubsub
)
return {
__typename: 'AddFollowingToLibrarySuccess',
articleSavingRequest,
articleSavingRequest: libraryItemToArticleSavingRequest(
item.user,
updatedItem
),
}
})

View File

@ -0,0 +1,71 @@
/* eslint-disable @typescript-eslint/no-misused-promises */
import express from 'express'
import { saveFeedItemInFollowing } from '../../services/library_item'
import { logger } from '../../utils/logger'
type SharedSource = 'feed' | 'newsletter' | 'user'
export interface SaveFollowingItemRequest {
userIds: string[]
title: string
url: string
itemId: string
sharedAt: Date
sharedBy: string
sharedSource: SharedSource
author?: string
description?: string
links?: any
previewContent?: string
publishedAt?: Date
savedAt?: Date
}
function isSaveFollowingItemRequest(
body: any
): body is SaveFollowingItemRequest {
return (
'userIds' in body &&
'sharedAt' in body &&
'sharedBy' in body &&
'sharedSource' in body &&
'url' in body &&
'itemId' in body &&
'title' in body
)
}
export function followingServiceRouter() {
const router = express.Router()
router.post('/save', async (req, res) => {
logger.info('save following item request', req.body)
if (req.query.token !== process.env.PUBSUB_VERIFICATION_TOKEN) {
console.log('query does not include valid token')
return res.sendStatus(403)
}
if (!isSaveFollowingItemRequest(req.body)) {
console.error('Invalid request body', req.body)
return res.status(400).send('INVALID_REQUEST_BODY')
}
if (req.body.sharedSource === 'feed') {
logger.info('saving feed item')
const result = await saveFeedItemInFollowing(req.body)
if (result.identifiers.length === 0) {
logger.error('error saving feed item in following')
return res.status(500).send('ERROR_SAVING_FEED_ITEM')
}
logger.info('feed item saved in following')
return res.sendStatus(200)
}
res.sendStatus(200)
})
return router
}

View File

@ -2649,53 +2649,6 @@ const schema = gql`
author: String
}
union SaveFollowingResult = SaveFollowingSuccess | SaveFollowingError
type SaveFollowingSuccess {
following: Following!
}
type Following {
id: ID!
title: String!
url: String!
author: String
image: String
description: String
seenAt: Date
createdAt: Date!
updatedAt: Date!
publishedAt: Date
hiddenAt: Date
SharedAt: Date!
sharedBy: String!
links: JSON
previewContent: String
sharedSource: String!
}
type SaveFollowingError {
errorCodes: [SaveFollowingErrorCode!]!
}
enum SaveFollowingErrorCode {
UNAUTHORIZED
BAD_REQUEST
}
input SaveFollowingInput {
url: String!
title: String!
author: String
description: String
publishedAt: Date
sharedSource: String!
links: JSON
previewContent: String
sharedBy: String!
sharedAt: Date!
}
union AddFollowingToLibraryResult =
AddFollowingToLibrarySuccess
| AddFollowingToLibraryError
@ -2711,7 +2664,7 @@ const schema = gql`
enum AddFollowingToLibraryErrorCode {
UNAUTHORIZED
BAD_REQUEST
NOT_FOUND
ALREADY_EXISTS
}
# Mutations
@ -2817,7 +2770,6 @@ const schema = gql`
updateSubscription(
input: UpdateSubscriptionInput!
): UpdateSubscriptionResult!
saveFollowing(input: SaveFollowingInput!): SaveFollowingResult!
addFollowingToLibrary(id: ID!): AddFollowingToLibraryResult!
}

View File

@ -25,6 +25,7 @@ import { pageRouter } from './routers/page_router'
import { contentServiceRouter } from './routers/svc/content'
import { emailsServiceRouter } from './routers/svc/emails'
import { emailAttachmentRouter } from './routers/svc/email_attachment'
import { followingServiceRouter } from './routers/svc/following'
import { integrationsServiceRouter } from './routers/svc/integrations'
import { linkServiceRouter } from './routers/svc/links'
import { newsletterServiceRouter } from './routers/svc/newsletters'
@ -125,6 +126,7 @@ export const createApp = (): {
app.use('/svc/pubsub/user', userServiceRouter())
// app.use('/svc/reminders', remindersServiceRouter())
app.use('/svc/email-attachment', emailAttachmentRouter())
app.use('/svc/following', followingServiceRouter())
if (env.dev.isLocal) {
app.use('/local/debug', localDebugRouter())

View File

@ -130,8 +130,11 @@ export const createPageSaveRequest = async ({
pubsub
)
}
// reset state to processing
if (libraryItem.state !== LibraryItemState.Processing) {
// reset state to processing if in following
if (
libraryItem.state !== LibraryItemState.Processing &&
!libraryItem.sharedAt
) {
libraryItem = await updateLibraryItem(
libraryItem.id,
{

View File

@ -4,10 +4,12 @@ import { EntityLabel } from '../entity/entity_label'
import { Highlight } from '../entity/highlight'
import { Label } from '../entity/label'
import { LibraryItem, LibraryItemState } from '../entity/library_item'
import { BulkActionType, SaveFollowingInput } from '../generated/graphql'
import { BulkActionType } from '../generated/graphql'
import { createPubSubClient, EntityType } from '../pubsub'
import { authTrx, getColumns } from '../repository'
import { libraryItemRepository } from '../repository/library_item'
import { SaveFollowingItemRequest } from '../routers/svc/following'
import { SetClaimsRole } from '../utils/dictionary'
import { wordsCount } from '../utils/helpers'
import {
DateFilter,
@ -567,25 +569,30 @@ export const createLibraryItem = async (
return newLibraryItem
}
export const createFollowing = async (
input: SaveFollowingInput,
userId: string
): Promise<LibraryItem> => {
return createLibraryItem(
{
...input,
originalUrl: input.url,
isInLibrary: false,
state: LibraryItemState.Succeeded,
wordCount: 0,
user: { id: userId },
sharedAt: new Date(input.sharedAt),
sharedSource: input.sharedSource,
sharedBy: input.sharedBy,
export const saveFeedItemInFollowing = (input: SaveFollowingItemRequest) => {
return authTrx(
async (tx) => {
const libraryItems: QueryDeepPartialEntity<LibraryItem>[] =
input.userIds.map((userId) => ({
...input,
user: { id: userId },
isInLibrary: false,
originalUrl: input.url,
subscription: input.sharedBy,
}))
return tx
.getRepository(LibraryItem)
.createQueryBuilder()
.insert()
.values(libraryItems)
.orIgnore() // ignore if the item already exists
.returning('*')
.execute()
},
userId,
undefined,
true
undefined,
SetClaimsRole.ADMIN
)
}

View File

@ -144,6 +144,7 @@ export const savePage = async (
...itemToSave,
id: undefined,
slug: undefined,
isInLibrary: true,
} as QueryDeepPartialEntity<LibraryItem>,
user.id
)

View File

@ -18,6 +18,11 @@ ALTER TABLE omnivore.library_item
ADD COLUMN shared_source text,
ADD COLUMN is_in_library boolean NOT NULL DEFAULT true;
CREATE POLICY library_item_admin_policy on omnivore.library_item
FOR ALL
TO omnivore_admin
USING (true);
CREATE TABLE omnivore.feed (
id uuid PRIMARY KEY DEFAULT uuid_generate_v1mc(),
title text NOT NULL,

View File

@ -6,6 +6,8 @@ BEGIN;
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 shared_at,

View File

@ -161,12 +161,12 @@ const createFollowingTask = async (
item: Item
) => {
const input = {
userId,
userIds: [userId],
url: item.link,
title: item.title,
author: item.creator,
description: item.summary,
sharedSource: 'rss-feeder',
sharedSource: 'feed',
previewContent: item.content,
sharedBy: feedUrl,
savedAt: item.isoDate,