From acec3b1acb40ad7777b09a46642966cfffea5792 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Tue, 22 Mar 2022 18:08:08 +0800 Subject: [PATCH 001/121] Use lower priority queue for large imports * add a count query in elastic * if priority is not set, check the rate limit of user's libary - if 5 articles added in the last minute: use low queue - default: use normal queue --- packages/api/src/elastic/index.ts | 39 ++++++++++++++++++- packages/api/src/routers/article_router.ts | 8 +--- packages/api/src/routers/svc/links.ts | 7 +--- .../src/services/create_page_save_request.ts | 17 +++++++- packages/api/src/services/save_url.ts | 1 - packages/api/src/utils/createTask.ts | 1 + packages/api/test/elastic/index.test.ts | 28 +++++++++++++ 7 files changed, 84 insertions(+), 17 deletions(-) diff --git a/packages/api/src/elastic/index.ts b/packages/api/src/elastic/index.ts index 5844270fb..1f61398f3 100644 --- a/packages/api/src/elastic/index.ts +++ b/packages/api/src/elastic/index.ts @@ -170,7 +170,6 @@ export const createPage = async ( body: { ...page, updatedAt: new Date(), - createdAt: new Date(), savedAt: new Date(), }, refresh: ctx.refresh, @@ -485,6 +484,44 @@ export const searchPages = async ( } } +export const countByCreatedAt = async ( + userId: string, + from?: number, + to?: number +): Promise => { + try { + const { body } = await client.count({ + index: INDEX_ALIAS, + body: { + query: { + bool: { + filter: [ + { + term: { + userId, + }, + }, + { + range: { + createdAt: { + gte: from, + lte: to, + }, + }, + }, + ], + }, + }, + }, + }) + + return body.count as number + } catch (e) { + console.error('failed to count pages in elastic', e) + return 0 + } +} + export const initElasticsearch = async (): Promise => { try { const response = await client.info() diff --git a/packages/api/src/routers/article_router.ts b/packages/api/src/routers/article_router.ts index b66672ae4..396319222 100644 --- a/packages/api/src/routers/article_router.ts +++ b/packages/api/src/routers/article_router.ts @@ -47,13 +47,7 @@ export function articleRouter() { const requestId = uuidv4() const models = initModels(kx, false) - const result = await createPageSaveRequest( - uid, - url, - models, - 'high', - requestId - ) + const result = await createPageSaveRequest(uid, url, models, requestId) if (isSiteBlockedForParse(url)) { return res diff --git a/packages/api/src/routers/svc/links.ts b/packages/api/src/routers/svc/links.ts index 93ffd189a..b656203c0 100644 --- a/packages/api/src/routers/svc/links.ts +++ b/packages/api/src/routers/svc/links.ts @@ -42,12 +42,7 @@ export function linkServiceRouter() { const models = initModels(kx, false) try { - const request = await createPageSaveRequest( - msg.userId, - msg.url, - models, - 'low' - ) + const request = await createPageSaveRequest(msg.userId, msg.url, models) console.log('create link request', request) res.status(200).send(request) diff --git a/packages/api/src/services/create_page_save_request.ts b/packages/api/src/services/create_page_save_request.ts index 0120155bf..be4a81b16 100644 --- a/packages/api/src/services/create_page_save_request.ts +++ b/packages/api/src/services/create_page_save_request.ts @@ -9,9 +9,19 @@ import { } from '../generated/graphql' import { articleSavingRequestDataToArticleSavingRequest } from '../utils/helpers' import * as privateIpLib from 'private-ip' +import { countByCreatedAt } from '../elastic' const isPrivateIP = privateIpLib.default +// 5 articles added in the last minute: use low queue +// default: use normal queue +const getPriorityByRateLimit = async ( + userId: string +): Promise<'low' | 'high'> => { + const count = await countByCreatedAt(userId, Date.now() - 60 * 1000) + return count >= 5 ? 'low' : 'high' +} + export const validateUrl = (url: string): URL => { const u = new URL(url) // Make sure the URL is http or https @@ -43,8 +53,8 @@ export const createPageSaveRequest = async ( userId: string, url: string, models: DataModels, - priority: 'low' | 'high' = 'high', - articleSavingRequestId = uuidv4() + articleSavingRequestId = uuidv4(), + priority?: 'low' | 'high' ): Promise => { try { validateUrl(url) @@ -63,6 +73,9 @@ export const createPageSaveRequest = async ( }) } + // get priority by checking rate limit if not specified + priority = priority || (await getPriorityByRateLimit(userId)) + const createdTaskName = await enqueueParseRequest( url, userId, diff --git a/packages/api/src/services/save_url.ts b/packages/api/src/services/save_url.ts index cf382f187..2df37cc97 100644 --- a/packages/api/src/services/save_url.ts +++ b/packages/api/src/services/save_url.ts @@ -20,7 +20,6 @@ export const saveUrl = async ( saver.id, input.url, ctx.models, - 'high', input.clientRequestId ) } catch (error) { diff --git a/packages/api/src/utils/createTask.ts b/packages/api/src/utils/createTask.ts index 75eed7395..5b37f604b 100644 --- a/packages/api/src/utils/createTask.ts +++ b/packages/api/src/utils/createTask.ts @@ -204,6 +204,7 @@ export const deleteTask = async ( * @param url - URL address of the article to parse * @param userId - Id of the user authorized * @param saveRequestId - Id of the article_saving_request table record + * @param priority - Priority of the task * @returns Name of the task created */ export const enqueueParseRequest = async ( diff --git a/packages/api/test/elastic/index.test.ts b/packages/api/test/elastic/index.test.ts index 22ac48787..2d6983114 100644 --- a/packages/api/test/elastic/index.test.ts +++ b/packages/api/test/elastic/index.test.ts @@ -1,6 +1,7 @@ import 'mocha' import { addLabelInPage, + countByCreatedAt, createPage, deletePage, getPageById, @@ -178,4 +179,31 @@ describe('elastic api', () => { }) }) }) + + describe('countByCreatedAt', () => { + const createdAt = Date.now() - 60 * 60 * 24 * 1000 + + before(async () => { + const newPageData: Page = { + id: '', + hash: 'hash', + userId: userId, + pageType: PageType.Article, + title: 'test', + content: 'test', + slug: 'test', + createdAt: new Date(createdAt), + readingProgressPercent: 0, + readingProgressAnchorIndex: 0, + url: 'https://blog.omnivore.app/testCount', + } + + await createPage(newPageData, ctx) + }) + + it('counts pages by createdAt', async () => { + const count = await countByCreatedAt(userId, createdAt, createdAt) + expect(count).to.eq(1) + }) + }) }) From f6b780d2ddafafdc88faba4cdec132c3f75cd4a1 Mon Sep 17 00:00:00 2001 From: gitstart-omnivore Date: Wed, 23 Mar 2022 19:33:32 +0000 Subject: [PATCH 002/121] fix props naming --- .../components/elements/images/EdgeIcon.tsx | 36 +++++------ .../elements/images/FirefoxIcon.tsx | 62 +++++++++---------- packages/web/pages/_app.tsx | 2 +- 3 files changed, 50 insertions(+), 50 deletions(-) diff --git a/packages/web/components/elements/images/EdgeIcon.tsx b/packages/web/components/elements/images/EdgeIcon.tsx index 36633c38d..991b73d2d 100644 --- a/packages/web/components/elements/images/EdgeIcon.tsx +++ b/packages/web/components/elements/images/EdgeIcon.tsx @@ -11,7 +11,7 @@ export function EdgeIcon(props: EdgeIconProps): JSX.Element { fill="none" xmlns="http://www.w3.org/2000/svg" > - + - - + + - - + + - - - - + + + + - - + + - - - - - + + + + + - - + + - + - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + + - - - + + + - - + + - - - + + + - + From 69ad032f4c74f59cb2bcb8f29557b5a185e905e2 Mon Sep 17 00:00:00 2001 From: gitstart-omnivore Date: Wed, 23 Mar 2022 19:49:35 +0000 Subject: [PATCH 003/121] fix props naming issue --- .../components/elements/images/ChromeIcon.tsx | 34 +++++++++++++++---- .../components/elements/images/SafariIcon.tsx | 6 ++-- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/packages/web/components/elements/images/ChromeIcon.tsx b/packages/web/components/elements/images/ChromeIcon.tsx index d04634f9e..234c823b7 100644 --- a/packages/web/components/elements/images/ChromeIcon.tsx +++ b/packages/web/components/elements/images/ChromeIcon.tsx @@ -4,13 +4,33 @@ type ChromeIconProps = { export function ChromeIcon(props: ChromeIconProps): JSX.Element { return ( - - - - - - + + + + + + - ) } diff --git a/packages/web/components/elements/images/SafariIcon.tsx b/packages/web/components/elements/images/SafariIcon.tsx index f425e438d..c40b04861 100644 --- a/packages/web/components/elements/images/SafariIcon.tsx +++ b/packages/web/components/elements/images/SafariIcon.tsx @@ -30,7 +30,7 @@ export function SafariIcon(props: SafariIconProps): JSX.Element { - - + + From ca0897edab8ae6b84c56cb2724447085db79ed4b Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Fri, 25 Mar 2022 20:54:07 -0700 Subject: [PATCH 004/121] Pass createdAt, savedAt, and publishedAt into the appreader --- .../App/Views/WebReader/WebReaderContent.swift | 11 ++++++++--- apple/OmnivoreKit/Sources/Models/FeedItem.swift | 10 +++++++++- .../DataService/Queries/LibraryItemsQuery.swift | 2 ++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContent.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContent.swift index 8be76c50d..87d242aaf 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContent.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContent.swift @@ -22,7 +22,11 @@ struct WebReaderContent { // swiftlint:disable line_length var styledContent: String { - """ + let savedAt = "new Date(\(item.savedAt.timeIntervalSince1970 * 1000)).toISOString()" + let createdAt = "new Date(\(item.createdAt.timeIntervalSince1970 * 1000)).toISOString()" + let publishAt = item.publishDate != nil ? "new Date(\(item.publishDate!.timeIntervalSince1970 * 1000)).toISOString()" : "undefined" + + return """ @@ -52,8 +56,9 @@ struct WebReaderContent { id: "\(item.id)", linkId: "\(item.id)", slug: "\(item.slug)", - createdAt: new Date().toISOString(), - savedAt: new Date().toISOString(), + createdAt: \(createdAt), + savedAt: \(savedAt), + publishedAt: \(publishAt), url: `\(item.pageURLString)`, title: document.getElementById('_omnivore-title').innerHTML, content: document.getElementById('_omnivore-htmlContent').innerHTML, diff --git a/apple/OmnivoreKit/Sources/Models/FeedItem.swift b/apple/OmnivoreKit/Sources/Models/FeedItem.swift index bd64ec39b..c8f5a418e 100644 --- a/apple/OmnivoreKit/Sources/Models/FeedItem.swift +++ b/apple/OmnivoreKit/Sources/Models/FeedItem.swift @@ -14,6 +14,8 @@ public struct FeedItem: Identifiable, Hashable, Decodable { public let id: String public let renderID = UUID() public let title: String + public let createdAt: Date + public let savedAt: Date public var readingProgress: Double public var readingProgressAnchor: Int public let imageURLString: String? @@ -31,6 +33,8 @@ public struct FeedItem: Identifiable, Hashable, Decodable { public init( id: String, title: String, + createdAt: Date, + savedAt: Date, readingProgress: Double, readingProgressAnchor: Int, imageURLString: String?, @@ -47,6 +51,8 @@ public struct FeedItem: Identifiable, Hashable, Decodable { ) { self.id = id self.title = title + self.createdAt = createdAt + self.savedAt = savedAt self.readingProgress = readingProgress self.readingProgressAnchor = readingProgressAnchor self.imageURLString = imageURLString @@ -63,13 +69,15 @@ public struct FeedItem: Identifiable, Hashable, Decodable { } enum CodingKeys: String, CodingKey { - case id, title, image, isArchived, readingProgressPercent, readingProgressAnchorIndex, slug, contentReader, url + case id, title, createdAt, savedAt, image, isArchived, readingProgressPercent, readingProgressAnchorIndex, slug, contentReader, url } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decode(String.self, forKey: .id) title = try container.decode(String.self, forKey: .title) + createdAt = try container.decode(Date.self, forKey: .createdAt) + savedAt = try container.decode(Date.self, forKey: .savedAt) description = try container.decode(String?.self, forKey: .title) imageURLString = try container.decode(String?.self, forKey: .image) readingProgress = try container.decode(Double.self, forKey: .readingProgressPercent) diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Queries/LibraryItemsQuery.swift b/apple/OmnivoreKit/Sources/Services/DataService/Queries/LibraryItemsQuery.swift index d78408487..7419e2649 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/Queries/LibraryItemsQuery.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/Queries/LibraryItemsQuery.swift @@ -129,6 +129,8 @@ let homeFeedItemSelection = Selection.Article { FeedItem( id: try $0.id(), title: try $0.title(), + createdAt: try $0.createdAt().value ?? Date(), + savedAt: try $0.savedAt().value ?? Date(), readingProgress: try $0.readingProgressPercent(), readingProgressAnchor: try $0.readingProgressAnchorIndex(), imageURLString: try $0.image(), From 228745f3ea335cebc17e7e1b0f0d41621b7eacb7 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Fri, 25 Mar 2022 20:56:17 -0700 Subject: [PATCH 005/121] Fix variable name to match attribute --- .../Sources/App/Views/WebReader/WebReaderContent.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContent.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContent.swift index 87d242aaf..4801a79be 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContent.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContent.swift @@ -24,7 +24,7 @@ struct WebReaderContent { var styledContent: String { let savedAt = "new Date(\(item.savedAt.timeIntervalSince1970 * 1000)).toISOString()" let createdAt = "new Date(\(item.createdAt.timeIntervalSince1970 * 1000)).toISOString()" - let publishAt = item.publishDate != nil ? "new Date(\(item.publishDate!.timeIntervalSince1970 * 1000)).toISOString()" : "undefined" + let publishedAt = item.publishDate != nil ? "new Date(\(item.publishDate!.timeIntervalSince1970 * 1000)).toISOString()" : "undefined" return """ @@ -58,7 +58,7 @@ struct WebReaderContent { slug: "\(item.slug)", createdAt: \(createdAt), savedAt: \(savedAt), - publishedAt: \(publishAt), + publishedAt: \(publishedAt), url: `\(item.pageURLString)`, title: document.getElementById('_omnivore-title').innerHTML, content: document.getElementById('_omnivore-htmlContent').innerHTML, From b6c04f3508a841cd00974c2f5985a0aa4b80d624 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Mon, 28 Mar 2022 20:38:54 +0800 Subject: [PATCH 006/121] fix saving report item by linking to elastic page --- .../api/src/entity/reports/abuse_report.ts | 9 ++- .../entity/reports/content_display_report.ts | 9 ++- packages/api/src/resolvers/report/index.ts | 4 +- packages/api/src/services/reports.ts | 29 +++---- packages/api/test/resolvers/report.test.ts | 80 +++++++++++++++++++ .../0076.do.drop_not_null_on_page_id.sql | 10 +++ .../0076.undo.drop_not_null_on_page_id.sql | 10 +++ 7 files changed, 126 insertions(+), 25 deletions(-) create mode 100644 packages/api/test/resolvers/report.test.ts create mode 100755 packages/db/migrations/0076.do.drop_not_null_on_page_id.sql create mode 100755 packages/db/migrations/0076.undo.drop_not_null_on_page_id.sql diff --git a/packages/api/src/entity/reports/abuse_report.ts b/packages/api/src/entity/reports/abuse_report.ts index 6f68b1982..a38b0b939 100644 --- a/packages/api/src/entity/reports/abuse_report.ts +++ b/packages/api/src/entity/reports/abuse_report.ts @@ -1,9 +1,9 @@ import { - Entity, BaseEntity, Column, - PrimaryGeneratedColumn, CreateDateColumn, + Entity, + PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm' import { ReportType } from '../../generated/graphql' @@ -14,7 +14,7 @@ export class AbuseReport extends BaseEntity { id?: string @Column('text') - pageId!: string + pageId?: string @Column('text') sharedBy!: string @@ -36,4 +36,7 @@ export class AbuseReport extends BaseEntity { @UpdateDateColumn() updatedAt?: Date + + @Column('text') + elasticPageId?: string } diff --git a/packages/api/src/entity/reports/content_display_report.ts b/packages/api/src/entity/reports/content_display_report.ts index fe59e535d..d5396d010 100644 --- a/packages/api/src/entity/reports/content_display_report.ts +++ b/packages/api/src/entity/reports/content_display_report.ts @@ -1,9 +1,9 @@ import { - Entity, BaseEntity, Column, - PrimaryGeneratedColumn, CreateDateColumn, + Entity, + PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm' @@ -16,7 +16,7 @@ export class ContentDisplayReport extends BaseEntity { userId!: string @Column('text') - pageId!: string + pageId?: string @Column('text') content!: string @@ -35,4 +35,7 @@ export class ContentDisplayReport extends BaseEntity { @UpdateDateColumn() updatedAt?: Date + + @Column('text') + elasticPageId?: string } diff --git a/packages/api/src/resolvers/report/index.ts b/packages/api/src/resolvers/report/index.ts index 264fd59d9..f38b7b5c2 100644 --- a/packages/api/src/resolvers/report/index.ts +++ b/packages/api/src/resolvers/report/index.ts @@ -45,7 +45,7 @@ export const reportItemResolver: ResolverFn< const uid = ctx.claims?.uid || '' const value = { sharedBy: uid, reportedBy: uid, ...args.input } - const report = await saveAbuseReport(ctx.kx, uid, value) + const report = await saveAbuseReport(uid, value) const message = report ? SUCCESS_MESSAGE : FAILURE_MESSAGE return { message: message, @@ -58,7 +58,7 @@ export const reportItemResolver: ResolverFn< message: FAILURE_MESSAGE, } } - const report = await saveContentDisplayReport(ctx.kx, uid, args.input) + const report = await saveContentDisplayReport(uid, args.input) const message = report ? SUCCESS_MESSAGE : FAILURE_MESSAGE return { message: message, diff --git a/packages/api/src/services/reports.ts b/packages/api/src/services/reports.ts index 7cad6226e..f58e7e74b 100644 --- a/packages/api/src/services/reports.ts +++ b/packages/api/src/services/reports.ts @@ -1,22 +1,19 @@ import { getRepository } from 'typeorm' import { ReportItemInput, ReportType } from '../generated/graphql' import { ContentDisplayReport } from '../entity/reports/content_display_report' -import ArticleModel from '../datalayer/article' -import Knex from 'knex' import { AbuseReport } from '../entity/reports/abuse_report' +import { getPageById } from '../elastic' export const saveContentDisplayReport = async ( - kx: Knex, uid: string, input: ReportItemInput ): Promise => { const repo = getRepository(ContentDisplayReport) - const am = new ArticleModel(kx) - const article = await am.get(input.pageId) + const page = await getPageById(input.pageId) - if (!article) { - console.log('unable to submit report, article not found', input) + if (!page) { + console.log('unable to submit report, page not found', input) return false } @@ -26,10 +23,10 @@ export const saveContentDisplayReport = async ( const result = await repo .create({ userId: uid, - pageId: input.pageId, - content: article.content, - originalHtml: article.originalHtml || undefined, - originalUrl: article.url, + elasticPageId: input.pageId, + content: page.content, + originalHtml: page.originalHtml || undefined, + originalUrl: page.url, reportComment: input.reportComment, }) .save() @@ -38,17 +35,15 @@ export const saveContentDisplayReport = async ( } export const saveAbuseReport = async ( - kx: Knex, uid: string, input: ReportItemInput ): Promise => { const repo = getRepository(AbuseReport) - const am = new ArticleModel(kx) - const article = await am.get(input.pageId) + const page = await getPageById(input.pageId) - if (!article) { - console.log('unable to submit report, article not found', input) + if (!page) { + console.log('unable to submit report, page not found', input) return false } @@ -64,7 +59,7 @@ export const saveAbuseReport = async ( .create({ reportedBy: uid, sharedBy: input.sharedBy, - pageId: input.pageId, + elasticPageId: input.pageId, itemUrl: input.itemUrl, reportTypes: [ReportType.Abusive], reportComment: input.reportComment, diff --git a/packages/api/test/resolvers/report.test.ts b/packages/api/test/resolvers/report.test.ts new file mode 100644 index 000000000..148eb3566 --- /dev/null +++ b/packages/api/test/resolvers/report.test.ts @@ -0,0 +1,80 @@ +import { User } from '../../src/entity/user' +import { Page } from '../../src/elastic/types' +import { createTestUser, deleteTestUser } from '../db' +import { createTestElasticPage, graphqlRequest, request } from '../util' +import { ReportType } from '../../src/generated/graphql' +import { getRepository } from 'typeorm' +import { ContentDisplayReport } from '../../src/entity/reports/content_display_report' +import { expect } from 'chai' + +describe('Report API', () => { + const username = 'fakeUser' + + let user: User + let authToken: string + let page: Page + + before(async () => { + // create test user and login + user = await createTestUser(username) + const res = await request + .post('/local/debug/fake-user-login') + .send({ fakeEmail: user.email }) + + authToken = res.body.authToken + + // create a page + page = await createTestElasticPage(user) + }) + + after(async () => { + // clean up + await deleteTestUser(username) + }) + + describe('reportItem', () => { + let pageId: string + let reportTypes: ReportType[] + let query: string + + beforeEach(() => { + query = ` + mutation { + reportItem( + input: { + pageId: "${pageId}", + itemUrl: "test url" + reportTypes: [${reportTypes}], + reportComment: "test comment" + } + ) { + message + } + } + ` + }) + + context('when page exists and report is content display', () => { + before(() => { + pageId = page.id + reportTypes = [ReportType.ContentDisplay] + }) + + after(async () => { + await getRepository(ContentDisplayReport).delete({ + elasticPageId: pageId, + }) + }) + + it('should report an item', async () => { + await graphqlRequest(query, authToken).expect(200) + + expect( + await getRepository(ContentDisplayReport).find({ + elasticPageId: pageId, + }) + ).to.exist + }) + }) + }) +}) diff --git a/packages/db/migrations/0076.do.drop_not_null_on_page_id.sql b/packages/db/migrations/0076.do.drop_not_null_on_page_id.sql new file mode 100755 index 000000000..662df1f9f --- /dev/null +++ b/packages/db/migrations/0076.do.drop_not_null_on_page_id.sql @@ -0,0 +1,10 @@ +-- Type: DO +-- Name: drop_not_null_on_page_id +-- Description: Drop not null constraint on page_id in content_display_report table + +BEGIN; + +ALTER TABLE omnivore.content_display_report ALTER COLUMN page_id DROP NOT NULL; +ALTER TABLE omnivore.abuse_report ALTER COLUMN page_id DROP NOT NULL; + +COMMIT; diff --git a/packages/db/migrations/0076.undo.drop_not_null_on_page_id.sql b/packages/db/migrations/0076.undo.drop_not_null_on_page_id.sql new file mode 100755 index 000000000..136006069 --- /dev/null +++ b/packages/db/migrations/0076.undo.drop_not_null_on_page_id.sql @@ -0,0 +1,10 @@ +-- Type: UNDO +-- Name: drop_not_null_on_page_id +-- Description: Drop not null constraint on page_id in content_display_report table + +BEGIN; + +ALTER TABLE omnivore.content_display_report ALTER COLUMN page_id SET NOT NULL; +ALTER TABLE omnivore.abuse_report ALTER COLUMN page_id SET NOT NULL; + +COMMIT; From 7786a3f321a33071809a8f375b38e9c3964b0eee Mon Sep 17 00:00:00 2001 From: gitstart-omnivore Date: Mon, 28 Mar 2022 12:57:28 +0000 Subject: [PATCH 007/121] effected review changes --- .../elements/ExtensionsInstallHelp.tsx | 126 +++-- .../components/elements/MobileInstallHelp.tsx | 173 ++++--- .../web/components/elements/StyledText.tsx | 4 + packages/web/components/elements/Tooltip.tsx | 6 +- .../elements/images/InstallationIcon.tsx | 458 ++++++++++++++++++ .../elements/images/SaveArticleIcon.tsx | 41 ++ .../images/about/save-article-mobile.png | Bin 695763 -> 0 bytes .../static/images/about/save-article.png | Bin 70888 -> 0 bytes .../web/public/static/images/installation.png | Bin 11870 -> 0 bytes .../media/about/save-article-mobile.png | Bin 695763 -> 0 bytes .../static/media/about/save-article.png | Bin 70888 -> 0 bytes .../web/public/static/media/appStoreBadge.png | Bin 2458 -> 3938 bytes 12 files changed, 688 insertions(+), 120 deletions(-) create mode 100644 packages/web/components/elements/images/InstallationIcon.tsx create mode 100644 packages/web/components/elements/images/SaveArticleIcon.tsx delete mode 100644 packages/web/public/static/images/about/save-article-mobile.png delete mode 100644 packages/web/public/static/images/about/save-article.png delete mode 100644 packages/web/public/static/images/installation.png delete mode 100644 packages/web/public/static/media/about/save-article-mobile.png delete mode 100644 packages/web/public/static/media/about/save-article.png diff --git a/packages/web/components/elements/ExtensionsInstallHelp.tsx b/packages/web/components/elements/ExtensionsInstallHelp.tsx index f19495973..a7a8c82af 100644 --- a/packages/web/components/elements/ExtensionsInstallHelp.tsx +++ b/packages/web/components/elements/ExtensionsInstallHelp.tsx @@ -1,6 +1,6 @@ import React from 'react' import { Box, HStack } from '../elements/LayoutPrimitives' -import { StyledImg, StyledText } from '../elements/StyledText' +import { StyledText, StyledAnchor } from '../elements/StyledText' import { AngleDownIcon } from '../tokens/icons/AngleDownIcon' import { Button } from './Button' import { Dropdown, DropdownOption, DropdownSeparator } from './DropdownElements' @@ -8,6 +8,8 @@ import { ChromeIcon } from './images/ChromeIcon' import { EdgeIcon } from './images/EdgeIcon' import { FirefoxIcon } from './images/FirefoxIcon' import { SafariIcon } from './images/SafariIcon' +import Link from 'next/link' +import { SaveArticleIcon } from './images/SaveArticleIcon' const icons = { 'Google Chrome': , @@ -16,6 +18,13 @@ const icons = { Safari: , } +const extensionDownloadLinks = { + 'Google Chrome': 'https://omnivore.app/install/chrome', + Safari: 'https://omnivore.app/install/mac', + 'Microsoft Edge': 'https://omnivore.app/install/edge', + Firefox: 'https://omnivore.app/install/firefox', +} + const browserOptions = [ 'Google Chrome', 'Firefox', @@ -55,9 +64,7 @@ export default function ExtensionsInstallHelp(): JSX.Element { const [browserValue, setBrowserValue] = React.useState( browserOptions[0] ) - const handleBrowserUpdate = (e: any) => { - setBrowserValue(e) - } + return ( - + > + + + + - - Learn more - + + + Learn more + + - - Learn more about the browser extension here. - + + + Learn more about the browser extension here. + + @@ -244,10 +268,14 @@ export default function ExtensionsInstallHelp(): JSX.Element { ))}