Merge commit '217b9e8835970e0f2c5b9fe2904af77f5caa8695' into OMN-134

This commit is contained in:
gitstart-omnivore
2022-03-28 13:53:45 +00:00
49 changed files with 1105 additions and 1099 deletions

View File

@ -1454,7 +1454,7 @@
CODE_SIGN_ENTITLEMENTS = Entitlements/Omnivore.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 62;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = QJF2XZ86HB;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = InfoPlists/Omnivore.plist;
@ -1463,7 +1463,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.4.1;
MARKETING_VERSION = 1.4.2;
PRODUCT_BUNDLE_IDENTIFIER = app.omnivore.app;
PRODUCT_NAME = Omnivore;
PROVISIONING_PROFILE_SPECIFIER = "";
@ -1483,7 +1483,7 @@
CODE_SIGN_ENTITLEMENTS = "SafariExtension (iOS).entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 62;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = QJF2XZ86HB;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "InfoPlists/SafariExtension-iOS.plist";
@ -1495,7 +1495,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.4.1;
MARKETING_VERSION = 1.4.2;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
OTHER_LDFLAGS = (
@ -1522,7 +1522,7 @@
CODE_SIGN_ENTITLEMENTS = "SafariExtension (iOS)Release.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 62;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = QJF2XZ86HB;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "InfoPlists/SafariExtension-iOS.plist";
@ -1534,7 +1534,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.4.1;
MARKETING_VERSION = 1.4.2;
MTL_FAST_MATH = YES;
OTHER_LDFLAGS = (
"-framework",
@ -1687,7 +1687,7 @@
buildSettings = {
CODE_SIGN_ENTITLEMENTS = Entitlements/ShareExtension.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CURRENT_PROJECT_VERSION = 62;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = QJF2XZ86HB;
INFOPLIST_FILE = InfoPlists/ShareExtension.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
@ -1696,7 +1696,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.4.1;
MARKETING_VERSION = 1.4.2;
PRODUCT_BUNDLE_IDENTIFIER = "app.omnivore.app.share-extension";
PRODUCT_NAME = ShareExtension;
SDKROOT = iphoneos;
@ -1741,7 +1741,7 @@
CODE_SIGN_ENTITLEMENTS = Entitlements/Omnivore.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 62;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = QJF2XZ86HB;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = InfoPlists/Omnivore.plist;
@ -1750,7 +1750,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.4.1;
MARKETING_VERSION = 1.4.2;
PRODUCT_BUNDLE_IDENTIFIER = app.omnivore.app;
PRODUCT_NAME = Omnivore;
PROVISIONING_PROFILE_SPECIFIER = "";
@ -1769,7 +1769,7 @@
buildSettings = {
CODE_SIGN_ENTITLEMENTS = Entitlements/ShareExtension.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CURRENT_PROJECT_VERSION = 62;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = QJF2XZ86HB;
INFOPLIST_FILE = InfoPlists/ShareExtension.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
@ -1778,7 +1778,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.4.1;
MARKETING_VERSION = 1.4.2;
PRODUCT_BUNDLE_IDENTIFIER = "app.omnivore.app.share-extension";
PRODUCT_NAME = ShareExtension;
SDKROOT = iphoneos;

View File

@ -56,7 +56,9 @@ struct WebReader: UIViewRepresentable {
webView.configuration.userContentController.add(webView, name: "viewerAction")
webView.configuration.userContentController.addScriptMessageHandler(context.coordinator, contentWorld: .page, name: "articleAction")
webView.configuration.userContentController.addScriptMessageHandler(
context.coordinator, contentWorld: .page, name: "articleAction"
)
context.coordinator.linkHandler = openLinkAction
context.coordinator.webViewActionHandler = webViewActionHandler

View File

@ -9,6 +9,14 @@ struct SafariWebLink: Identifiable {
let url: URL
}
func encodeHighlightResult(_ highlight: Highlight) -> [String: Any]? {
let data = try? JSONEncoder().encode(highlight)
if let data = data, let dictionary = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] {
return dictionary
}
return nil
}
final class WebReaderViewModel: ObservableObject {
@Published var isLoading = false
@Published var articleContent: ArticleContent?
@ -42,13 +50,18 @@ final class WebReaderViewModel: ObservableObject {
highlightID: messageBody["id"] as? String ?? "",
quote: messageBody["quote"] as? String ?? "",
patch: messageBody["patch"] as? String ?? "",
articleId: messageBody["articleId"] as? String ?? ""
articleId: messageBody["articleId"] as? String ?? "",
annotation: messageBody["annotation"] as? String ?? ""
)
.sink { completion in
guard case .failure = completion else { return }
replyHandler(["result": false], nil)
} receiveValue: { _ in
replyHandler(["result": true], nil)
replyHandler([], "createHighlight: Error encoding response")
} receiveValue: { highlight in
if let highlight = encodeHighlightResult(highlight) {
replyHandler(["result": highlight], nil)
} else {
replyHandler([], "createHighlight: Error encoding response")
}
}
.store(in: &subscriptions)
}
@ -85,9 +98,13 @@ final class WebReaderViewModel: ObservableObject {
)
.sink { completion in
guard case .failure = completion else { return }
replyHandler(["result": false], nil)
} receiveValue: { _ in
replyHandler(["result": true], nil)
replyHandler([], "mergeHighlight: Error encoding response")
} receiveValue: { highlight in
if let highlight = encodeHighlightResult(highlight) {
replyHandler(["result": highlight], nil)
} else {
replyHandler([], "mergeHighlight: Error encoding response")
}
}
.store(in: &subscriptions)
}
@ -104,9 +121,10 @@ final class WebReaderViewModel: ObservableObject {
)
.sink { completion in
guard case .failure = completion else { return }
replyHandler(["result": false], nil)
} receiveValue: { _ in
replyHandler(["result": true], nil)
replyHandler([], "updateHighlight: Error encoding response")
} receiveValue: { highlight in
// Update highlight JS code just expects the highlight ID back
replyHandler(["result": highlight.id], nil)
}
.store(in: &subscriptions)
}

View File

@ -11,16 +11,16 @@ public extension DataService {
patch: String,
articleId: String,
annotation: String? = nil
) -> AnyPublisher<String, BasicError> {
) -> AnyPublisher<Highlight, BasicError> {
enum MutationResult {
case saved(id: String)
case saved(highlight: Highlight)
case error(errorCode: Enums.CreateHighlightErrorCode)
}
let selection = Selection<MutationResult, Unions.CreateHighlightResult> {
try $0.on(
createHighlightSuccess: .init {
.saved(id: try $0.highlight(selection: Selection.Highlight { try $0.id() }))
.saved(highlight: try $0.highlight(selection: highlightSelection))
},
createHighlightError: .init { .error(errorCode: try $0.errorCodes().first ?? .badData) }
)
@ -53,8 +53,8 @@ public extension DataService {
}
switch payload.data {
case let .saved(id: id):
promise(.success(id))
case let .saved(highlight: highlight):
promise(.success(highlight))
case let .error(errorCode: errorCode):
promise(.failure(.message(messageText: errorCode.rawValue)))
}

View File

@ -12,16 +12,16 @@ public extension DataService {
patch: String,
articleId: String,
overlapHighlightIdList: [String]
) -> AnyPublisher<String, BasicError> {
) -> AnyPublisher<Highlight, BasicError> {
enum MutationResult {
case saved(id: String)
case saved(highlight: Highlight)
case error(errorCode: Enums.MergeHighlightErrorCode)
}
let selection = Selection<MutationResult, Unions.MergeHighlightResult> {
try $0.on(
mergeHighlightSuccess: .init {
.saved(id: try $0.highlight(selection: Selection.Highlight { try $0.id() }))
.saved(highlight: try $0.highlight(selection: highlightSelection))
},
mergeHighlightError: .init { .error(errorCode: try $0.errorCodes().first ?? .badData) }
)
@ -57,8 +57,8 @@ public extension DataService {
}
switch payload.data {
case let .saved(id: id):
promise(.success(id))
case let .saved(highlight: highlight):
promise(.success(highlight))
case let .error(errorCode: errorCode):
promise(.failure(.message(messageText: errorCode.rawValue)))
}

View File

@ -8,16 +8,16 @@ public extension DataService {
highlightID: String,
annotation: String?,
sharedAt: Date?
) -> AnyPublisher<String, BasicError> {
) -> AnyPublisher<Highlight, BasicError> {
enum MutationResult {
case saved(id: String)
case saved(highlight: Highlight)
case error(errorCode: Enums.UpdateHighlightErrorCode)
}
let selection = Selection<MutationResult, Unions.UpdateHighlightResult> {
try $0.on(
updateHighlightSuccess: .init {
.saved(id: try $0.highlight(selection: Selection.Highlight { try $0.id() }))
.saved(highlight: try $0.highlight(selection: highlightSelection))
},
updateHighlightError: .init { .error(errorCode: try $0.errorCodes().first ?? .badData) }
)
@ -47,8 +47,8 @@ public extension DataService {
}
switch payload.data {
case let .saved(id: id):
promise(.success(id))
case let .saved(highlight: highlight):
promise(.success(highlight))
case let .error(errorCode: errorCode):
promise(.failure(.message(messageText: errorCode.rawValue)))
}

View File

@ -11,19 +11,6 @@ public extension DataService {
case error(error: String)
}
let highlightSelection = Selection.Highlight {
Highlight(
id: try $0.id(),
shortId: try $0.shortId(),
quote: try $0.quote(),
prefix: try $0.prefix(),
suffix: try $0.suffix(),
patch: try $0.patch(),
annotation: try $0.annotation(),
createdByMe: try $0.createdByMe()
)
}
let articleSelection = Selection.Article {
ArticleContent(
htmlContent: try $0.content(),

View File

@ -0,0 +1,15 @@
import Models
import SwiftGraphQL
let highlightSelection = Selection.Highlight {
Highlight(
id: try $0.id(),
shortId: try $0.shortId(),
quote: try $0.quote(),
prefix: try $0.prefix(),
suffix: try $0.suffix(),
patch: try $0.patch(),
annotation: try $0.annotation(),
createdByMe: try $0.createdByMe()
)
}

File diff suppressed because one or more lines are too long

View File

@ -78,6 +78,8 @@ UPDATE_HIGHLIGHT_SQL = f'''
def assertData(conn, client):
# get all users from postgres
try:
success = 0
failure = 0
cursor = conn.cursor(cursor_factory=RealDictCursor)
cursor.execute('''SELECT id FROM omnivore.user''')
result = cursor.fetchall()
@ -90,11 +92,14 @@ def assertData(conn, client):
index='pages', body={'query': {'term': {'userId': userId}}})['count']
if countInPostgres == countInElastic:
success += 1
print(f'User {userId} OK')
else:
failure += 1
print(
f'User {userId} ERROR: postgres: {countInPostgres}, elastic: {countInElastic}')
cursor.close()
print(f'Asserted data, success: {success}, failure: {failure}')
except Exception as err:
print('Assert data ERROR:', err)
exit(1)

View File

@ -77,7 +77,7 @@
"highlightjs": "^9.16.2",
"html-entities": "^2.3.2",
"intercom-client": "^3.1.4",
"jsdom": "^16.4.0",
"jsdom": "^19.0.0",
"jsonwebtoken": "^8.5.1",
"jwks-rsa": "^2.0.3",
"knex": "0.21.12",

View File

@ -37,9 +37,9 @@ export const createPubSubClient = (): PubsubClient => {
Buffer.from(JSON.stringify({ userId, email, name, username }))
)
},
pageSaved: (page: Partial<Page>, userId: string): Promise<void> => {
pageUpdated: (page: Partial<Page>, userId: string): Promise<void> => {
return publish(
'pageSaved',
'pageUpdated',
Buffer.from(JSON.stringify({ ...page, userId }))
)
},
@ -73,7 +73,7 @@ export interface PubsubClient {
username: string
) => Promise<void>
pageCreated: (page: Page) => Promise<void>
pageSaved: (page: Partial<Page>, userId: string) => Promise<void>
pageUpdated: (page: Partial<Page>, userId: string) => Promise<void>
pageDeleted: (id: string, userId: string) => Promise<void>
reportSubmitted(
submitterId: string | undefined,

View File

@ -206,7 +206,7 @@ export const updatePage = async (
if (body.result !== 'updated') return false
await ctx.pubsub.pageSaved(page, ctx.uid)
await ctx.pubsub.pageUpdated({ ...page, id }, ctx.uid)
return true
} catch (e) {

View File

@ -1,10 +1,12 @@
import {
Entity,
BaseEntity,
Column,
ManyToOne,
CreateDateColumn,
Entity,
JoinColumn,
ManyToOne,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm'
import { User } from './user'
@ -22,4 +24,10 @@ export class NewsletterEmail extends BaseEntity {
@Column('varchar', { nullable: true })
confirmationCode?: string
@CreateDateColumn()
createdAt!: Date
@UpdateDateColumn()
updatedAt!: Date
}

View File

@ -306,6 +306,9 @@ export const functionResolvers = {
}
return article.url
},
async originalArticleUrl(article: { url: string }) {
return article.url
},
async savedByViewer(
article: { id: string; savedByViewer?: boolean },
__: unknown,

View File

@ -30,7 +30,10 @@ export const createNewsletterEmail = async (
export const getNewsletterEmails = async (
userId: string
): Promise<NewsletterEmail[]> => {
return getRepository(NewsletterEmail).find({ where: { user: userId } })
return getRepository(NewsletterEmail).find({
where: { user: userId },
order: { createdAt: 'DESC' },
})
}
export const deleteNewsletterEmail = async (id: string): Promise<boolean> => {

View File

@ -104,6 +104,7 @@ const articlesQuery = (after = '', order = 'ASCENDING') => {
id
url
linkId
originalArticleUrl
labels {
id
name
@ -360,6 +361,8 @@ describe('Article API', () => {
})
describe('GetArticles', () => {
const url = 'https://blog.omnivore.app/p/getting-started-with-omnivore'
let query = ''
let after = ''
let pages: Page[] = []
@ -380,7 +383,7 @@ describe('Article API', () => {
updatedAt: new Date(),
readingProgressPercent: 100,
readingProgressAnchorIndex: 0,
url: 'https://blog.omnivore.app/p/getting-started-with-omnivore',
url: url,
savedAt: new Date(),
} as Page
const pageId = await createPage(page, ctx)
@ -406,6 +409,14 @@ describe('Article API', () => {
query = articlesQuery(after)
})
it('should return originalArticleUrl', async () => {
const res = await graphqlRequest(query, authToken).expect(200)
expect(res.body.data.articles.edges[0].node.originalArticleUrl).to.eql(
url
)
})
context('when there are pages with labels', () => {
before(() => {
// get the last page

View File

@ -61,16 +61,18 @@ describe('Newsletters API', () => {
}
`
it('responds with newsletter emails', async () => {
it('responds with newsletter emails sort by created_at desc', async () => {
const response = await graphqlRequest(query, authToken).expect(200)
expect(response.body.data.newsletterEmails.newsletterEmails).to.eqls(
newsletterEmails.map((value) => {
return {
id: value.id,
address: value.address,
confirmationCode: value.confirmationCode,
}
})
newsletterEmails
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
.map((value) => {
return {
id: value.id,
address: value.address,
confirmationCode: value.confirmationCode,
}
})
)
})

View File

@ -33,7 +33,6 @@ const App = () => {
className="disable-webkit-callout"
>
<ArticleContainer
viewerUsername="test"
article={window.omnivoreArticle}
scrollElementRef={React.createRef()}
isAppleAppEmbed={true}

View File

@ -0,0 +1,13 @@
-- Type: DO
-- Name: add_created_at_to_newsletter_emails
-- Description: Add created_at and updated_at fields to newsletter_emails table
BEGIN;
ALTER TABLE omnivore.newsletter_emails
ADD COLUMN created_at timestamptz NOT NULL default current_timestamp,
ADD COLUMN updated_at timestamptz NOT NULL default current_timestamp;
CREATE TRIGGER newsletter_emails_modtime BEFORE UPDATE ON omnivore.newsletter_emails FOR EACH ROW EXECUTE PROCEDURE update_updated_at_column();
COMMIT;

View File

@ -0,0 +1,11 @@
-- Type: UNDO
-- Name: add_created_at_to_newsletter_emails
-- Description: Add created_at and updated_at fields to newsletter_emails table
BEGIN;
ALTER TABLE omnivore.newsletter_emails
DROP COLUMN created_at,
DROP COLUMN updated_at;
COMMIT;

View File

@ -10,7 +10,7 @@
"axios": "^0.26.0",
"chrome-aws-lambda": "^7.0.0",
"dotenv": "^8.2.0",
"jsdom": "^17.0.0",
"jsdom": "^19.0.0",
"jsonwebtoken": "^8.5.1",
"luxon": "^1.26.0",
"puppeteer-core": "^7.1.0",

View File

@ -27,7 +27,7 @@
"chai": "^2.1.*",
"htmltidy2": "^0.3.0",
"js-beautify": "^1.13.0",
"jsdom": "^13.1",
"jsdom": "^19.0",
"mocha": "^8.2.0",
"puppeteer": "^10.1.0",
"sinon": "^7.3.2"

View File

@ -1,11 +1,11 @@
import { StyledText } from './StyledText'
type LabelProps = {
type LabelChipProps = {
text: string
color: string // expected to be a RGB hex color string
}
export function Label(props: LabelProps): JSX.Element {
export function LabelChip(props: LabelChipProps): JSX.Element {
const hexToRgb = (hex: string) => {
const bigint = parseInt(hex.substring(1), 16)
const r = (bigint >> 16) & 255

View File

@ -69,10 +69,10 @@ function LoadedContent(props: LoadedContentProps): JSX.Element {
return props.publicArticle.highlights.length - 1
}, [props.publicArticle.highlights])
const sharedBy = useMemo(() => {
if (moreHighlightsCount < 1) return undefined
return props.publicArticle.highlights[0].user
}, [moreHighlightsCount, props.publicArticle.highlights])
// const sharedBy = useMemo(() => {
// if (moreHighlightsCount < 1) return undefined
// return props.publicArticle.highlights[0].user
// }, [moreHighlightsCount, props.publicArticle.highlights])
const articleSite = useMemo(() => {
try {
@ -124,7 +124,7 @@ function LoadedContent(props: LoadedContentProps): JSX.Element {
site={articleSite}
/>
{!showAllHighlights && moreHighlightsCount > 0 && sharedBy && (
{!showAllHighlights && moreHighlightsCount > 0 && (
<Button onClick={() => setShowAllHighlights(true)}>
<Box
css={{ width: '8px', color: '$grayBackground', mr: '4px' }}
@ -132,7 +132,7 @@ function LoadedContent(props: LoadedContentProps): JSX.Element {
/>
Read {moreHighlightsCount} more highlight
{moreHighlightsCount > 1 ? 's ' : ' '}
from {sharedBy.name}
{/* from {sharedBy.name} */}
</Button>
)}

View File

@ -15,7 +15,7 @@ import {
import { Tweet } from 'react-twitter-widgets'
import { render } from 'react-dom'
import { isDarkTheme } from '../../../lib/themeUpdater'
import { debounce } from 'lodash'
import debounce from 'lodash/debounce'
import { ArticleMutations } from '../../../lib/articleActions'
export type ArticleProps = {

View File

@ -10,19 +10,11 @@ import { MutableRefObject, useEffect, useState } from 'react'
import { ReportIssuesModal } from './ReportIssuesModal'
import { reportIssueMutation } from '../../../lib/networking/mutations/reportIssueMutation'
import { ArticleHeaderToolbar } from './ArticleHeaderToolbar'
import { articleKeyboardCommands } from '../../../lib/keyboardShortcuts/navigationShortcuts'
import { useKeyboardShortcuts } from '../../../lib/keyboardShortcuts/useKeyboardShortcuts'
import { ShareArticleModal } from './ShareArticleModal'
import { userPersonalizationMutation } from '../../../lib/networking/mutations/userPersonalizationMutation'
import { webBaseURL } from '../../../lib/appConfig'
import { updateThemeLocally } from '../../../lib/themeUpdater'
import { EditLabelsModal } from './EditLabelsModal'
import Script from 'next/script'
import { useRouter } from 'next/router'
import { ArticleMutations } from '../../../lib/articleActions'
type ArticleContainerProps = {
viewerUsername: string
article: ArticleAttributes
articleMutations: ArticleMutations
scrollElementRef: MutableRefObject<HTMLDivElement | null>
@ -35,7 +27,6 @@ type ArticleContainerProps = {
}
export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
const router = useRouter()
const [showShareModal, setShowShareModal] = useState(false)
const [showLabelsModal, setShowLabelsModal] = useState(false)
const [showNotesSidebar, setShowNotesSidebar] = useState(false)
@ -50,28 +41,6 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
await userPersonalizationMutation({ fontSize: newFontSize })
}
useKeyboardShortcuts(
articleKeyboardCommands(router, async (action) => {
switch (action) {
case 'openOriginalArticle':
const url = props.article.url
if (url) {
window.open(url, '_blank')
}
break
case 'incrementFontSize':
await updateFontSize(Math.min(fontSize + 2, 28))
break
case 'decrementFontSize':
await updateFontSize(Math.max(fontSize - 2, 10))
break
case 'editLabels':
setShowLabelsModal(true)
break
}
})
)
// Listen for font size and color mode change events sent from host apps (ios, macos...)
useEffect(() => {
const increaseFontSize = async () => {
@ -123,16 +92,6 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
return (
<>
{!props.isAppleAppEmbed && (
<>
<Script async src="/static/scripts/mathJaxConfiguration.js" />
<Script
async
id="MathJax-script"
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"
/>
</>
)}
<Box
id="article-container"
css={{
@ -213,7 +172,6 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
<Box css={{ height: '100px' }} />
</Box>
<HighlightsLayer
viewerUsername={props.viewerUsername}
highlights={props.article.highlights}
articleTitle={props.article.title}
articleAuthor={props.article.author ?? ''}
@ -238,7 +196,7 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
onOpenChange={(open: boolean) => setShowReportIssuesModal(open)}
/>
) : null}
{showShareModal && (
{/* {showShareModal && (
<ShareArticleModal
url={`${webBaseURL}/${props.viewerUsername}/${props.article.slug}/highlights?r=true`}
title={props.article.title}
@ -249,19 +207,7 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
originalArticleUrl={props.article.originalArticleUrl}
onOpenChange={(open: boolean) => setShowShareModal(open)}
/>
)}
{showLabelsModal && (
<EditLabelsModal
labels={labels}
article={props.article}
onOpenChange={() => {
setShowLabelsModal(false)
}}
setLabels={(labels: string[]) => {
setLabels(labels)
}}
/>
)}
)} */}
</>
)
}

View File

@ -10,15 +10,16 @@ import { CrossIcon } from '../../elements/images/CrossIcon'
import { theme } from '../../tokens/stitches.config'
import { useGetLabelsQuery } from '../../../lib/networking/queries/useGetLabelsQuery'
import { ChangeEvent, useCallback, useState } from 'react'
import { Label } from '../../elements/Label'
import { setLabelsMutation } from '../../../lib/networking/mutations/setLabelsMutation'
import { ArticleAttributes } from '../../../lib/networking/queries/useGetArticleQuery'
import { Label } from '../../../lib/networking/fragments/labelFragment'
import { LabelChip } from '../../elements/LabelChip'
type EditLabelsModalProps = {
labels: string[]
labels: Label[]
article: ArticleAttributes
onOpenChange: (open: boolean) => void
setLabels: (labels: string[]) => void
setLabels: (labels: Label[]) => void
}
export function EditLabelsModal(props: EditLabelsModalProps): JSX.Element {
@ -26,7 +27,7 @@ export function EditLabelsModal(props: EditLabelsModalProps): JSX.Element {
const { labels } = useGetLabelsQuery()
const saveAndExit = useCallback(async () => {
const result = await setLabelsMutation(props.article.id, selectedLabels)
const result = await setLabelsMutation(props.article.id, selectedLabels.map((l) => l.id))
console.log('result of setting labels', result)
props.onOpenChange(false)
props.setLabels(selectedLabels)
@ -34,12 +35,12 @@ export function EditLabelsModal(props: EditLabelsModalProps): JSX.Element {
const handleChange = useCallback(
(event: ChangeEvent<HTMLInputElement>) => {
const label = event.target.value
if (event.target.checked) {
setSelectedLabels([...selectedLabels, label])
} else {
setSelectedLabels(selectedLabels.filter((l) => l !== label))
}
// const label = event.target.value
// if (event.target.checked) {
// setSelectedLabels([...selectedLabels, label])
// } else {
// setSelectedLabels(selectedLabels.filter((l) => l !== label))
// }
},
[selectedLabels]
)
@ -81,21 +82,21 @@ export function EditLabelsModal(props: EditLabelsModalProps): JSX.Element {
key={label.id}
css={{ height: '50px', verticalAlign: 'middle' }}
onClick={() => {
if (selectedLabels.includes(label.id)) {
setSelectedLabels(
selectedLabels.filter((id) => id !== label.id)
)
} else {
setSelectedLabels([...selectedLabels, label.id])
}
// if (selectedLabels.includes(label.id)) {
// setSelectedLabels(
// selectedLabels.filter((id) => id !== label.id)
// )
// } else {
// setSelectedLabels([...selectedLabels, label.id])
// }
}}
>
<Label color={label.color} text={label.name} />
<LabelChip color={label.color} text={label.name} />
<input
type="checkbox"
value={label.id}
onChange={handleChange}
checked={selectedLabels.includes(label.id)}
checked={selectedLabels.includes(label)}
/>
</HStack>
))}

View File

@ -17,7 +17,7 @@ import { readableUpdatedAtMessage } from './../../../lib/dateFormatting'
import { useConfirmListener } from '../../../lib/keyboardShortcuts/useKeyboardShortcuts'
import { createHighlight } from '../../../lib/highlights/createHighlight'
import { createHighlightMutation } from '../../../lib/networking/mutations/createHighlightMutation'
import toast from 'react-hot-toast'
import { showErrorToast } from '../../../lib/toastHelpers'
type HighlightNoteModalProps = {
author: string
@ -59,14 +59,14 @@ export function HighlightNoteModal(
props.onUpdate({ ...props.highlight, annotation: noteContent })
props.onOpenChange(false)
} else {
toast.error('Error updating your note', { position: 'bottom-right' })
showErrorToast('Error updating your note', { position: 'bottom-right' })
}
} if (!props.highlight && props.createHighlightForNote) {
const result = await props.createHighlightForNote(noteContent)
if (result) {
props.onOpenChange(true)
} else {
toast.error('Error saving highlight', { position: 'bottom-right' })
showErrorToast('Error saving highlight', { position: 'bottom-right' })
}
} else {
props.onOpenChange(false)

View File

@ -3,8 +3,6 @@ import { makeHighlightStartEndOffset } from '../../../lib/highlights/highlightGe
import type { HighlightLocation } from '../../../lib/highlights/highlightGenerator'
import { useSelection } from '../../../lib/highlights/useSelection'
import type { Highlight } from '../../../lib/networking/fragments/highlightFragment'
import { shareHighlightToFeedMutation } from '../../../lib/networking/mutations/shareHighlightToFeedMutation'
import { shareHighlightCommentMutation } from '../../../lib/networking/mutations/updateShareHighlightCommentMutation'
import {
highlightIdAttribute,
highlightNoteIdAttribute,
@ -15,14 +13,12 @@ import { removeHighlights } from '../../../lib/highlights/deleteHighlight'
import { createHighlight } from '../../../lib/highlights/createHighlight'
import { HighlightNoteModal } from './HighlightNoteModal'
import { ShareHighlightModal } from './ShareHighlightModal'
import { HighlightPostToFeedModal } from './HighlightPostToFeedModal'
import { HighlightsModal } from './HighlightsModal'
import { useCanShareNative } from '../../../lib/hooks/useCanShareNative'
import toast from 'react-hot-toast'
import { showErrorToast } from '../../../lib/toastHelpers'
import { ArticleMutations } from '../../../lib/articleActions'
type HighlightsLayerProps = {
viewerUsername: string
highlights: Highlight[]
articleId: string
articleTitle: string
@ -35,7 +31,7 @@ type HighlightsLayerProps = {
articleMutations: ArticleMutations
}
type HighlightModalAction = 'none' | 'addComment' | 'postToFeed' | 'share'
type HighlightModalAction = 'none' | 'addComment' | 'share'
type HighlightActionProps = {
highlight?: Highlight
@ -113,32 +109,6 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
[highlights, highlightLocations]
)
const postToFeedCallback = useCallback(
async (highlight: Highlight, annotation: string | undefined) => {
await shareHighlightToFeedMutation({
id: highlight.id,
share: highlight.sharedAt == undefined,
})
await shareHighlightCommentMutation({
highlightId: highlight.id,
annotation,
})
// Toggle the sharedAt field after mutating the highlight
const mutatedHighlight = highlight
mutatedHighlight.sharedAt = highlight.sharedAt
? undefined
: new Date().toISOString()
mutatedHighlight.annotation = annotation
const unmutatedHighlights = highlights.filter(
($0) => $0.id !== highlight.id
)
setHighlights([...unmutatedHighlights, mutatedHighlight])
},
[highlights]
)
const handleNativeShare = useCallback(
(highlightID: string) => {
navigator
@ -220,7 +190,7 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
annotation
)
if (!result) {
toast.error('Error saving highlight', { position: 'bottom-right' })
showErrorToast('Error saving highlight', { position: 'bottom-right' })
}
// if (successAction === 'share' && canShareNative) {
// handleNativeShare(highlight.shortId)
@ -348,16 +318,6 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
createHighlightCallback('share')
}
break
case 'post':
if (focusedHighlight) {
setHighlightModalAction({
highlight: focusedHighlight,
highlightModalAction: 'postToFeed',
})
} else {
createHighlightCallback('postToFeed')
}
break
case 'unshare':
console.log('unshare')
break // TODO: implement -- need to show confirmation dialog
@ -460,23 +420,6 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
)
}
if (
highlightModalAction?.highlightModalAction == 'postToFeed' &&
highlightModalAction.highlight
) {
return (
<HighlightPostToFeedModal
highlight={highlightModalAction.highlight}
author={props.articleAuthor}
title={props.articleTitle}
onCommit={postToFeedCallback}
onOpenChange={() =>
setHighlightModalAction({ highlightModalAction: 'none' })
}
/>
)
}
if (
highlightModalAction?.highlightModalAction == 'share' &&
highlightModalAction.highlight

View File

@ -4,7 +4,7 @@ import { Button } from '../../elements/Button'
import { StyledText } from '../../elements/StyledText'
import { X, Check } from 'phosphor-react'
import { useState } from 'react'
import toast from 'react-hot-toast'
import { showErrorToast } from '../../../lib/toastHelpers'
type ShareArticleModalProps = {
onOpenChange: (open: boolean) => void
@ -149,7 +149,7 @@ export function SnoozeLinkModal(
props.submit(snoozeOption, sendReminder, msg)
props.onOpenChange(false)
} else {
toast.error('No option selected', { position: 'bottom-right' })
showErrorToast('No option selected', { position: 'bottom-right' })
}
}}
>Save</Button>
@ -158,4 +158,4 @@ export function SnoozeLinkModal(
</ModalContent>
</ModalRoot>
)
}
}

View File

@ -13,6 +13,7 @@ import { useState, useCallback } from 'react'
import { createArticleFromURLMutation } from '../../../lib/networking/mutations/createArticleFromURLMutation'
import { saveUrlMutation } from '../../../lib/networking/mutations/saveUrlMutation'
import toast from 'react-hot-toast'
import { showErrorToast } from '../../../lib/toastHelpers'
type AddLinkModalProps = {
onOpenChange: (open: boolean) => void
@ -42,7 +43,7 @@ export function AddLinkModal(props: AddLinkModalProps): JSX.Element {
</Box>
), { position: 'bottom-right' })
} else {
toast.error('Error saving link', { position: 'bottom-right' })
showErrorToast('Error saving link', { position: 'bottom-right' })
}
}, [link])
@ -100,7 +101,7 @@ export function AddLinkModal(props: AddLinkModalProps): JSX.Element {
// `https` to give the link a protocol.
const newLink = `https://${link}`
if (!validateLink(newLink)) {
toast.error('Invalid link', { position: 'bottom-right' })
showErrorToast('Invalid link', { position: 'bottom-right' })
return
}
setLink(newLink)

View File

@ -27,7 +27,7 @@ import { ShareArticleModal } from '../article/ShareArticleModal'
import { userPersonalizationMutation } from '../../../lib/networking/mutations/userPersonalizationMutation'
import { useGetUserPreferences } from '../../../lib/networking/queries/useGetUserPreferences'
import { webBaseURL } from '../../../lib/appConfig'
import toast, { Toaster } from 'react-hot-toast'
import { Toaster } from 'react-hot-toast'
import { SnoozeLinkModal } from '../article/SnoozeLinkModal'
import {
createReminderMutation,
@ -35,6 +35,7 @@ import {
} from '../../../lib/networking/mutations/createReminderMutation'
import { useFetchMoreScroll } from '../../../lib/hooks/useFetchMoreScroll'
import { usePersistedState } from '../../../lib/hooks/usePersistedState'
import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers'
export type LayoutType = 'LIST_LAYOUT' | 'GRID_LAYOUT'
@ -654,10 +655,10 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
return props.actionHandler('archive', props.snoozeTarget)
})
.then(() => {
toast.success(msg, { position: 'bottom-right' })
showSuccessToast(msg, { position: 'bottom-right' })
})
.catch((error) => {
toast.error('There was an error snoozing your link.', {
showErrorToast('There was an error snoozing your link.', {
position: 'bottom-right',
})
})

View File

@ -8,7 +8,7 @@ import { UpdateHighlightInput } from "./networking/mutations/updateHighlightMuta
export type ArticleMutations = {
createHighlightMutation: (input: CreateHighlightInput) => Promise<Highlight | undefined>
deleteHighlightMutation: (highlightId: string) => Promise<boolean>
mergeHighlightMutation: (input: MergeHighlightInput) => Promise<MergeHighlightOutput | undefined>
mergeHighlightMutation: (input: MergeHighlightInput) => Promise<Highlight | undefined>
updateHighlightMutation: (input: UpdateHighlightInput) => Promise<string | undefined>
articleReadingProgressMutation: (input: ArticleReadingProgressMutationInput) => Promise<boolean>
}

View File

@ -89,12 +89,11 @@ export async function createHighlight(
let keptHighlights = input.existingHighlights
if (shouldMerge) {
const result = await articleMutations.mergeHighlightMutation({
highlight = await articleMutations.mergeHighlightMutation({
...newHighlightAttributes,
overlapHighlightIdList: input.selection.overlapHighlights,
})
highlight = result?.mergeHighlight.highlight
keptHighlights = input.existingHighlights.filter(
($0) => !input.selection.overlapHighlights.includes($0.id)
)

View File

@ -67,12 +67,16 @@ function nodeAttributesFromHighlight(
const patch = highlight.patch
const id = highlight.id
const withNote = !!highlight.annotation
const customColor = !highlight.createdByMe
? stringToColour(highlight.user.profile.username)
: undefined
const tooltip = !highlight.createdByMe
? `Created by: @${highlight.user.profile.username}`
: undefined
const customColor = undefined
const tooltip = undefined
// We've disabled shared highlights, so passing undefined
// here now, and removing the user object from highlights
// !highlight.createdByMe
// ? stringToColour(highlight.user.profile.username)
// : undefined
// const tooltip = !highlight.createdByMe
// ? `Created by: @${highlight.user.profile.username}`
// : undefined
return makeHighlightNodeAttributes(patch, id, withNote, customColor, tooltip)
}

View File

@ -9,19 +9,9 @@ export const highlightFragment = gql`
suffix
patch
annotation
createdAt
createdByMe
updatedAt
sharedAt
user {
id
name
profile {
id
pictureUrl
username
}
}
createdByMe
}
`
@ -33,11 +23,9 @@ export type Highlight = {
suffix?: string
patch: string
annotation?: string
createdAt: string
updatedAt: string
user: User
createdByMe: boolean
sharedAt?: string
updatedAt: string
sharedAt: string
}
export type User = {

View File

@ -6,6 +6,7 @@ export const labelFragment = gql`
name
color
description
createdAt
}
`

View File

@ -1,6 +1,6 @@
import { gql } from 'graphql-request'
import { gqlFetcher } from '../networkHelpers'
import { Highlight } from './../fragments/highlightFragment'
import { Highlight, highlightFragment } from './../fragments/highlightFragment'
export type CreateHighlightInput = {
prefix: string
@ -28,7 +28,7 @@ export async function createHighlightMutation(
createHighlight(input: $input) {
... on CreateHighlightSuccess {
highlight {
...NewHighlight
...HighlightFields
}
}
@ -37,7 +37,7 @@ export async function createHighlightMutation(
}
}
}
${NewHighlightFragment}
${highlightFragment}
`
try {
@ -48,28 +48,3 @@ export async function createHighlightMutation(
return undefined
}
}
const NewHighlightFragment = gql`
fragment NewHighlight on Highlight {
id
shortId
quote
prefix
suffix
patch
createdAt
updatedAt
annotation
sharedAt
user {
id
name
profile {
id
pictureUrl
username
}
}
createdByMe
}
`

View File

@ -25,7 +25,7 @@ type InnerMergeHighlightOutput = {
export async function mergeHighlightMutation(
input: MergeHighlightInput
): Promise<MergeHighlightOutput | undefined> {
): Promise<Highlight | undefined> {
const mutation = gql`
mutation MergeHighlight($input: MergeHighlightInput!) {
mergeHighlight(input: $input) {
@ -41,15 +41,6 @@ export async function mergeHighlightMutation(
updatedAt
annotation
sharedAt
user {
id
name
profile {
id
pictureUrl
username
}
}
createdByMe
}
overlapHighlightIdList
@ -63,7 +54,8 @@ export async function mergeHighlightMutation(
try {
const data = await gqlFetcher(mutation, { input })
return data as MergeHighlightOutput | undefined
const output = data as MergeHighlightOutput | undefined
return output?.mergeHighlight.highlight
} catch {
return undefined
}

View File

@ -1,5 +1,6 @@
import { gql } from 'graphql-request'
import useSWR from 'swr'
import { Label, labelFragment } from '../fragments/labelFragment'
import { publicGqlFetcher } from '../networkHelpers'
type LabelsQueryResponse = {
@ -16,25 +17,13 @@ type LabelsData = {
labels?: unknown
}
export type Label = {
id: string
name: string
color: string
description?: string
createdAt: Date
}
export function useGetLabelsQuery(): LabelsQueryResponse {
const query = gql`
query GetLabels {
labels {
... on LabelsSuccess {
labels {
id
name
color
description
createdAt
...LabelFields
}
}
... on LabelsError {
@ -42,6 +31,7 @@ export function useGetLabelsQuery(): LabelsQueryResponse {
}
}
}
${labelFragment}
`
const { data, mutate, error, isValidating } = useSWR(query, publicGqlFetcher)

View File

@ -6,7 +6,8 @@ import { articleFragment } from '../fragments/articleFragment'
import { setLinkArchivedMutation } from '../mutations/setLinkArchivedMutation'
import { deleteLinkMutation } from '../mutations/deleteLinkMutation'
import { articleReadingProgressMutation } from '../mutations/articleReadingProgressMutation'
import { Label } from './useGetLabelsQuery'
import { Label } from './../fragments/labelFragment'
import { showErrorToast, showSuccessToast } from '../../toastHelpers'
export type LibraryItemsQueryInput = {
limit: number
@ -206,6 +207,12 @@ export function useGetLibraryItemsQuery({
setLinkArchivedMutation({
linkId: item.node.id,
archived: true,
}).then((res) => {
if (res) {
showSuccessToast('Link archived', { position: 'bottom-right' })
} else {
showErrorToast('Error archiving link', { position: 'bottom-right' })
}
})
break
@ -225,11 +232,24 @@ export function useGetLibraryItemsQuery({
setLinkArchivedMutation({
linkId: item.node.id,
archived: false,
}).then((res) => {
if (res) {
showSuccessToast('Link unarchived', { position: 'bottom-right' })
} else {
showErrorToast('Error unarchiving link', { position: 'bottom-right' })
}
})
break
case 'delete':
updateData(undefined)
deleteLinkMutation(item.node.id)
.then((res) => {
if (res) {
showSuccessToast('Link removed', { position: 'bottom-right' })
} else {
showErrorToast('Error removing link', { position: 'bottom-right' })
}
})
break
case 'mark-read':
updateData({

View File

@ -0,0 +1,65 @@
import { toast, ToastOptions } from 'react-hot-toast'
import { CheckCircle, WarningCircle, X } from 'phosphor-react'
import { Box, HStack } from '../components/elements/LayoutPrimitives'
import { styled } from '@stitches/react'
const toastStyles = {
minWidth: 265,
height: 56,
borderRadius: 4,
boxShadow: '0px 2px 8px rgba(32, 31, 29, 0.33)',
margin: 0,
paddingLeft: 6,
paddingRight: 6,
paddingTop: 12,
paddingBottom: 12,
}
const messageStyles = {
fontSize: 14,
fontWeight: 'bolder',
color: 'white',
flex: 1,
marginLeft: 16,
}
const MessageContainer = styled(Box, messageStyles)
const FullWidthContainer = styled(HStack, {
width: '100%',
})
type ToastType = 'success' | 'error'
const showToast = (
message: string,
background: string,
type: ToastType,
options?: ToastOptions
) => {
return toast(
({ id }) => (
<FullWidthContainer alignment='center'>
{type === 'success' ? <CheckCircle size={24} color='white' /> : <WarningCircle size={24} color='white' />}
<MessageContainer>{message}</MessageContainer>
<HStack distribution='end' css={{marginLeft: 16}}>
<X size={18} style={{cursor: 'pointer'}} color='#CCEAC4' weight='bold' onClick={() => toast.dismiss(id)} />
</HStack>
</FullWidthContainer>
),
{
style: {
...toastStyles,
background: background,
},
duration: 2000,
...options,
})
}
export const showSuccessToast = (message: string, options?: ToastOptions) => {
return showToast(message, '#55B938', 'success', options)
}
export const showErrorToast = (message: string, options?: ToastOptions) => {
return showToast(message, '#cc0000', 'error', options)
}

View File

@ -6,9 +6,9 @@ import { useRouter } from 'next/router'
import { VStack } from './../../../components/elements/LayoutPrimitives'
import { ArticleContainer } from './../../../components/templates/article/ArticleContainer'
import { PdfArticleContainerProps } from './../../../components/templates/article/PdfArticleContainer'
import { useRef } from 'react'
import { useRef, useState } from 'react'
import { useKeyboardShortcuts } from '../../../lib/keyboardShortcuts/useKeyboardShortcuts'
import { navigationCommands } from '../../../lib/keyboardShortcuts/navigationShortcuts'
import { articleKeyboardCommands, navigationCommands } from '../../../lib/keyboardShortcuts/navigationShortcuts'
import dynamic from 'next/dynamic'
import { useGetUserPreferences } from '../../../lib/networking/queries/useGetUserPreferences'
import { webBaseURL } from '../../../lib/appConfig'
@ -18,6 +18,11 @@ import { deleteHighlightMutation } from '../../../lib/networking/mutations/delet
import { mergeHighlightMutation } from '../../../lib/networking/mutations/mergeHighlightMutation'
import { articleReadingProgressMutation } from '../../../lib/networking/mutations/articleReadingProgressMutation'
import { updateHighlightMutation } from '../../../lib/networking/mutations/updateHighlightMutation'
import { userPersonalizationMutation } from '../../../lib/networking/mutations/userPersonalizationMutation'
import Script from 'next/script'
import { EditLabelsModal } from '../../../components/templates/article/EditLabelsModal'
import { Label } from '../../../lib/networking/fragments/labelFragment'
import { isVipUser } from '../../../lib/featureFlag'
const PdfArticleContainerNoSSR = dynamic<PdfArticleContainerProps>(
() => import('./../../../components/templates/article/PdfArticleContainer'),
@ -28,6 +33,7 @@ export default function Home(): JSX.Element {
const router = useRouter()
const scrollRef = useRef<HTMLDivElement | null>(null)
const { slug } = router.query
const [showLabelsModal, setShowLabelsModal] = useState(false)
// Populate data cache
const { viewerData } = useGetViewerQuery()
@ -38,9 +44,39 @@ export default function Home(): JSX.Element {
})
const { preferencesData } = useGetUserPreferences()
const article = articleData?.article.article
const [fontSize, setFontSize] = useState(preferencesData?.fontSize ?? 20)
useKeyboardShortcuts(navigationCommands(router))
const updateFontSize = async (newFontSize: number) => {
setFontSize(newFontSize)
await userPersonalizationMutation({ fontSize: newFontSize })
}
useKeyboardShortcuts(
articleKeyboardCommands(router, async (action) => {
switch (action) {
case 'openOriginalArticle':
const url = article?.url
if (url) {
window.open(url, '_blank')
}
break
case 'incrementFontSize':
await updateFontSize(Math.min(fontSize + 2, 28))
break
case 'decrementFontSize':
await updateFontSize(Math.max(fontSize - 2, 10))
break
case 'editLabels':
if (viewerData?.me && isVipUser(viewerData?.me)) {
setShowLabelsModal(true)
}
break
}
})
)
if (article && viewerData?.me) {
return (
<PrimaryLayout
@ -53,6 +89,12 @@ export default function Home(): JSX.Element {
description: article.description,
}}
>
<Script async src="/static/scripts/mathJaxConfiguration.js" />
<Script
async
id="MathJax-script"
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"
/>
<Toaster />
{article.contentReader == 'PDF' ? (
@ -72,9 +114,8 @@ export default function Home(): JSX.Element {
scrollElementRef={scrollRef}
isAppleAppEmbed={false}
highlightBarDisabled={false}
viewerUsername={viewerData.me?.profile?.username}
highlightsBaseURL={`${webBaseURL}/${viewerData.me?.profile?.username}/${slug}/highlights`}
fontSize={preferencesData?.fontSize}
fontSize={fontSize}
articleMutations={{
createHighlightMutation,
deleteHighlightMutation,
@ -83,6 +124,18 @@ export default function Home(): JSX.Element {
articleReadingProgressMutation,
}}
/>
{showLabelsModal && (
<EditLabelsModal
labels={article.labels || []}
article={article}
onOpenChange={() => {
setShowLabelsModal(false)
}}
setLabels={(labels: Label[]) => {
// setLabels(labels)
}}
/>
)}
</VStack>
)}
</PrimaryLayout>

View File

@ -12,6 +12,7 @@ import { deleteHighlightMutation } from '../../../../lib/networking/mutations/de
import { mergeHighlightMutation } from '../../../../lib/networking/mutations/mergeHighlightMutation'
import { updateHighlightMutation } from '../../../../lib/networking/mutations/updateHighlightMutation'
import { articleReadingProgressMutation } from '../../../../lib/networking/mutations/articleReadingProgressMutation'
import Script from 'next/script'
type AppArticleEmbedContentProps = {
slug: string
@ -78,6 +79,12 @@ function AppArticleEmbedContent(
width: '100vw',
}}
>
<Script async src="/static/scripts/mathJaxConfiguration.js" />
<Script
async
id="MathJax-script"
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"
/>
<VStack
alignment="center"
distribution="center"
@ -88,7 +95,6 @@ function AppArticleEmbedContent(
scrollElementRef={scrollRef}
isAppleAppEmbed={true}
highlightBarDisabled={props.highlightBarDisabled}
viewerUsername={props.username}
highlightsBaseURL={`${webBaseURL}/${props.username}/${props.slug}/highlights`}
fontSize={props.fontSize}
margin={props.margin}

View File

@ -22,6 +22,7 @@ import { toast, Toaster } from 'react-hot-toast'
import { useCallback } from 'react'
import { StyledText } from '../../components/elements/StyledText'
import { applyStoredTheme } from '../../lib/themeUpdater'
import { showSuccessToast } from '../../lib/toastHelpers'
import Link from 'next/link'
enum TextType {
@ -165,11 +166,7 @@ function CopyTextButton(props: CopyTextButtonProps): JSX.Element {
const copy = useCallback(() => {
copyLink()
toast(
props.type == TextType.EmailAddress
? 'Email Address Copied'
: 'Confirmation Code Copied'
)
showSuccessToast(props.type == TextType.EmailAddress ? 'Email Address Copied' : 'Confirmation Code Copied');
}, [])
return (
@ -186,14 +183,14 @@ export default function EmailsPage(): JSX.Element {
applyStoredTheme(false)
async function createEmail(): Promise<void> {
showSuccessToast('Email Created')
await createNewsletterEmailMutation()
revalidate()
toast.success('Email Created')
}
async function deleteEmail(id: string): Promise<void> {
await deleteNewsletterEmailMutation(id)
revalidate()
toast.success('Email Deleted!')
showSuccessToast('Email Deleted')
}
return (
<PrimaryLayout pageTestId="settings-emails-tag">

View File

@ -1,12 +1,13 @@
import { PrimaryLayout } from '../../components/templates/PrimaryLayout'
import { Button } from '../../components/elements/Button'
import { Box, VStack } from '../../components/elements/LayoutPrimitives'
import { toast, Toaster } from 'react-hot-toast'
import { Toaster } from 'react-hot-toast'
import { useGetLabelsQuery } from '../../lib/networking/queries/useGetLabelsQuery'
import { createLabelMutation } from '../../lib/networking/mutations/createLabelMutation'
import { deleteLabelMutation } from '../../lib/networking/mutations/deleteLabelMutation'
import { useState } from 'react'
import { applyStoredTheme } from '../../lib/themeUpdater'
import { showErrorToast, showSuccessToast } from '../../lib/toastHelpers'
export default function LabelsPage(): JSX.Element {
const { labels, revalidate, isValidating } = useGetLabelsQuery()
@ -20,16 +21,16 @@ export default function LabelsPage(): JSX.Element {
const res = await createLabelMutation(name, color, description)
if (res) {
if (res.createLabel.errorCodes && res.createLabel.errorCodes.length > 0) {
toast.error(res.createLabel.errorCodes[0])
showErrorToast(res.createLabel.errorCodes[0])
} else {
toast.success('Label created')
showSuccessToast('Label created')
setName('')
setColor('')
setDescription('')
revalidate()
}
} else {
toast.error('Failed to create label')
showErrorToast('Failed to create label')
}
}

File diff suppressed because it is too large Load Diff

392
yarn.lock
View File

@ -4433,6 +4433,27 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs" "0.1.0"
"@radix-ui/react-tooltip@^0.1.7":
version "0.1.7"
resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-0.1.7.tgz#6f8c00d6e489565d14abf209ce0fb8853c8c8ee3"
integrity sha512-eiBUsVOHenZ0JR16tl970bB0DafJBz6mFgSGfIGIVpflFj0LIsIDiLMsYyvYdx1KwwsIUDTEZtxcPm/sWjPzqA==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/primitive" "0.1.0"
"@radix-ui/react-compose-refs" "0.1.0"
"@radix-ui/react-context" "0.1.1"
"@radix-ui/react-id" "0.1.5"
"@radix-ui/react-popper" "0.1.4"
"@radix-ui/react-portal" "0.1.4"
"@radix-ui/react-presence" "0.1.2"
"@radix-ui/react-primitive" "0.1.4"
"@radix-ui/react-slot" "0.1.2"
"@radix-ui/react-use-controllable-state" "0.1.0"
"@radix-ui/react-use-escape-keydown" "0.1.0"
"@radix-ui/react-use-previous" "0.1.1"
"@radix-ui/react-use-rect" "0.1.1"
"@radix-ui/react-visually-hidden" "0.1.4"
"@radix-ui/react-use-body-pointer-events@0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-body-pointer-events/-/react-use-body-pointer-events-0.1.0.tgz#29b211464493f8ca5149ce34b96b95abbc97d741"
@ -4606,17 +4627,17 @@
tiny-hashes "^1.0.1"
"@sendgrid/client@^7.6.0":
version "7.6.0"
resolved "https://registry.yarnpkg.com/@sendgrid/client/-/client-7.6.0.tgz#f90cb8759c96e1d90224f29ad98f8fdc2be287f3"
integrity sha512-cpBVZKLlMTO+vpE18krTixubYmZa98oTbLkqBDuTiA3zRkW+urrxg7pDR24TkI35Mid0Zru8jDHwnOiqrXu0TA==
version "7.6.2"
resolved "https://registry.yarnpkg.com/@sendgrid/client/-/client-7.6.2.tgz#5d08949120dad679f34260f1b875b4f57a8d688e"
integrity sha512-Yw3i3vPBBwfiIi+4i7+1f1rwQoLlLsu3qW16d1UuRp6RgX6H6yHYb2/PfqwNyCC0qzqIWGUKPWwYe5ggcr5Guw==
dependencies:
"@sendgrid/helpers" "^7.6.0"
axios "^0.21.4"
"@sendgrid/helpers" "^7.6.2"
axios "^0.26.0"
"@sendgrid/helpers@^7.6.0":
version "7.6.0"
resolved "https://registry.yarnpkg.com/@sendgrid/helpers/-/helpers-7.6.0.tgz#b381bfab391bcd66c771811b22bb6bb2d5c1dfc6"
integrity sha512-0uWD+HSXLl4Z/X3cN+UMQC20RE7xwAACgppnfjDyvKG0KvJcUgDGz7HDdQkiMUdcVWfmyk6zKSg7XKfKzBjTwA==
"@sendgrid/helpers@^7.6.0", "@sendgrid/helpers@^7.6.2":
version "7.6.2"
resolved "https://registry.yarnpkg.com/@sendgrid/helpers/-/helpers-7.6.2.tgz#e4abdd4e259611ed549ae8e0f4a46cd4f587e5d1"
integrity sha512-kGW0kM2AOHfXjcvB6Lgwa/nMv8IALu0KyNY9X4HSa3MtLohymuhbG9HgjrOh66+BkbsfA03H3bcT0+sPVJ0GKQ==
dependencies:
deepmerge "^4.2.2"
@ -6969,7 +6990,7 @@ JSONStream@^1.0.4:
jsonparse "^1.2.0"
through ">=2.2.7 <3"
abab@^2.0.0, abab@^2.0.3, abab@^2.0.5:
abab@^2.0.3, abab@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a"
integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==
@ -7002,14 +7023,6 @@ accepts@~1.3.4, accepts@~1.3.5:
mime-types "~2.1.34"
negotiator "0.6.3"
acorn-globals@^4.3.0:
version "4.3.4"
resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7"
integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==
dependencies:
acorn "^6.0.1"
acorn-walk "^6.0.1"
acorn-globals@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45"
@ -7028,11 +7041,6 @@ acorn-jsx@^5.3.1:
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
acorn-walk@^6.0.1:
version "6.2.0"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c"
integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==
acorn-walk@^7.1.1:
version "7.2.0"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
@ -7048,26 +7056,16 @@ acorn-walk@^8.1.1:
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.1.1.tgz#3ddab7f84e4a7e2313f6c414c5b7dac85f4e3ebc"
integrity sha512-FbJdceMlPHEAWJOILDk1fXD8lnTlEIWFkqtfk+MvmL5q/qlHfN7GEHcsFZWt/Tea9jRNPWUZG4G976nqAAmU9w==
acorn@^6.0.1, acorn@^6.0.4:
version "6.4.2"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6"
integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==
acorn@^7.1.1:
version "7.4.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
acorn@^8.0.4, acorn@^8.5.0, acorn@^8.7.0:
acorn@^8.0.4, acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.0:
version "8.7.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf"
integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==
acorn@^8.2.4, acorn@^8.4.1:
version "8.5.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2"
integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==
add-stream@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa"
@ -7487,11 +7485,6 @@ array-each@^1.0.1:
resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f"
integrity sha1-p5SvDAWrF1KEbudTofIRoFugxE8=
array-equal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=
array-flatten@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
@ -7614,11 +7607,6 @@ ast-types-flow@^0.0.7:
resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad"
integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0=
async-limiter@~1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
async-retry@^1.2.1, async-retry@^1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.3.tgz#0e7f36c04d8478e7a58bdbed80cedf977785f280"
@ -9171,11 +9159,6 @@ cssfilter@0.0.10:
resolved "https://registry.yarnpkg.com/cssfilter/-/cssfilter-0.0.10.tgz#c6d2672632a2e5c83e013e6864a42ce8defd20ae"
integrity sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4=
cssom@0.3.x, cssom@^0.3.4, cssom@~0.3.6:
version "0.3.8"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a"
integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==
cssom@^0.4.4:
version "0.4.4"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10"
@ -9186,12 +9169,10 @@ cssom@^0.5.0:
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36"
integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==
cssstyle@^1.1.1:
version "1.4.0"
resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.4.0.tgz#9d31328229d3c565c61e586b02041a28fccdccf1"
integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==
dependencies:
cssom "0.3.x"
cssom@~0.3.6:
version "0.3.8"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a"
integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==
cssstyle@^2.3.0:
version "2.3.0"
@ -9229,15 +9210,6 @@ dashdash@^1.12.0:
dependencies:
assert-plus "^1.0.0"
data-urls@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe"
integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==
dependencies:
abab "^2.0.0"
whatwg-mimetype "^2.2.0"
whatwg-url "^7.0.0"
data-urls@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b"
@ -9247,14 +9219,14 @@ data-urls@^2.0.0:
whatwg-mimetype "^2.3.0"
whatwg-url "^8.0.0"
data-urls@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.0.tgz#3ff551c986d7c6234a0ac4bbf20a269e1cd6b378"
integrity sha512-4AefxbTTdFtxDUdh0BuMBs2qJVL25Mow2zlcuuePegQwgD6GEmQao42LLEeksOui8nL4RcNEugIpFP7eRd33xg==
data-urls@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.1.tgz#597fc2ae30f8bc4dbcf731fcd1b1954353afc6f8"
integrity sha512-Ds554NeT5Gennfoo9KN50Vh6tpgtvYEwraYjejXnyTpu1C7oXKxdFk75REooENHE8ndTVOJuv+BEs4/J/xcozw==
dependencies:
abab "^2.0.3"
whatwg-mimetype "^2.3.0"
whatwg-url "^9.0.0"
whatwg-mimetype "^3.0.0"
whatwg-url "^10.0.0"
dataloader@2.0.0, dataloader@^2.0.0:
version "2.0.0"
@ -9682,13 +9654,6 @@ domelementtype@^2.0.1, domelementtype@^2.2.0:
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57"
integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==
domexception@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90"
integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==
dependencies:
webidl-conversions "^4.0.2"
domexception@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304"
@ -9696,6 +9661,13 @@ domexception@^2.0.1:
dependencies:
webidl-conversions "^5.0.0"
domexception@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673"
integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==
dependencies:
webidl-conversions "^7.0.0"
domhandler@^3.0.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.3.0.tgz#6db7ea46e4617eb15cf875df68b2b8524ce0037a"
@ -10070,18 +10042,6 @@ escape-string-regexp@^2.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
escodegen@^1.11.0:
version "1.14.3"
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503"
integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==
dependencies:
esprima "^4.0.1"
estraverse "^4.2.0"
esutils "^2.0.2"
optionator "^0.8.1"
optionalDependencies:
source-map "~0.6.1"
escodegen@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd"
@ -10329,7 +10289,7 @@ esrecurse@^4.3.0:
dependencies:
estraverse "^5.2.0"
estraverse@^4.1.1, estraverse@^4.2.0:
estraverse@^4.1.1:
version "4.3.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
@ -11764,13 +11724,6 @@ hpagent@^0.1.1:
resolved "https://registry.yarnpkg.com/hpagent/-/hpagent-0.1.2.tgz#cab39c66d4df2d4377dbd212295d878deb9bdaa9"
integrity sha512-ePqFXHtSQWAFXYmj+JtOTHr84iNrII4/QRlAAPPE+zqnKy4xJo7Ie1Y4kC7AdB+LxLxSTTzBMASsEcy0q8YyvQ==
html-encoding-sniffer@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8"
integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==
dependencies:
whatwg-encoding "^1.0.1"
html-encoding-sniffer@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3"
@ -11778,6 +11731,13 @@ html-encoding-sniffer@^2.0.1:
dependencies:
whatwg-encoding "^1.0.5"
html-encoding-sniffer@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9"
integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==
dependencies:
whatwg-encoding "^2.0.0"
html-entities@^2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.2.tgz#760b404685cb1d794e4f4b744332e3b00dcfe488"
@ -11950,6 +11910,13 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24:
dependencies:
safer-buffer ">= 2.1.2 < 3"
iconv-lite@0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
dependencies:
safer-buffer ">= 2.1.2 < 3.0.0"
iconv-lite@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01"
@ -13307,39 +13274,7 @@ jsbn@~0.1.0:
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
jsdom@^13.1:
version "13.2.0"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-13.2.0.tgz#b1a0dbdadc255435262be8ea3723d2dba0d7eb3a"
integrity sha512-cG1NtMWO9hWpqRNRR3dSvEQa8bFI6iLlqU2x4kwX51FQjp0qus8T9aBaAO6iGp3DeBrhdwuKxckknohkmfvsFw==
dependencies:
abab "^2.0.0"
acorn "^6.0.4"
acorn-globals "^4.3.0"
array-equal "^1.0.0"
cssom "^0.3.4"
cssstyle "^1.1.1"
data-urls "^1.1.0"
domexception "^1.0.1"
escodegen "^1.11.0"
html-encoding-sniffer "^1.0.2"
nwsapi "^2.0.9"
parse5 "5.1.0"
pn "^1.1.0"
request "^2.88.0"
request-promise-native "^1.0.5"
saxes "^3.1.5"
symbol-tree "^3.2.2"
tough-cookie "^2.5.0"
w3c-hr-time "^1.0.1"
w3c-xmlserializer "^1.0.1"
webidl-conversions "^4.0.2"
whatwg-encoding "^1.0.5"
whatwg-mimetype "^2.3.0"
whatwg-url "^7.0.0"
ws "^6.1.2"
xml-name-validator "^3.0.0"
jsdom@^16.4.0, jsdom@^16.6.0:
jsdom@^16.6.0:
version "16.7.0"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710"
integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==
@ -13372,23 +13307,23 @@ jsdom@^16.4.0, jsdom@^16.6.0:
ws "^7.4.6"
xml-name-validator "^3.0.0"
jsdom@^17.0.0:
version "17.0.0"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-17.0.0.tgz#3ec82d1d30030649c8defedc45fff6aa3e5d06ae"
integrity sha512-MUq4XdqwtNurZDVeKScENMPHnkgmdIvMzZ1r1NSwHkDuaqI6BouPjr+17COo4/19oLNnmdpFDPOHVpgIZmZ+VA==
jsdom@^19.0, jsdom@^19.0.0:
version "19.0.0"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-19.0.0.tgz#93e67c149fe26816d38a849ea30ac93677e16b6a"
integrity sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A==
dependencies:
abab "^2.0.5"
acorn "^8.4.1"
acorn "^8.5.0"
acorn-globals "^6.0.0"
cssom "^0.5.0"
cssstyle "^2.3.0"
data-urls "^3.0.0"
data-urls "^3.0.1"
decimal.js "^10.3.1"
domexception "^2.0.1"
domexception "^4.0.0"
escodegen "^2.0.0"
form-data "^4.0.0"
html-encoding-sniffer "^2.0.1"
http-proxy-agent "^4.0.1"
html-encoding-sniffer "^3.0.0"
http-proxy-agent "^5.0.0"
https-proxy-agent "^5.0.0"
is-potential-custom-element-name "^1.0.1"
nwsapi "^2.2.0"
@ -13397,13 +13332,13 @@ jsdom@^17.0.0:
symbol-tree "^3.2.4"
tough-cookie "^4.0.0"
w3c-hr-time "^1.0.2"
w3c-xmlserializer "^2.0.0"
webidl-conversions "^6.1.0"
whatwg-encoding "^1.0.5"
whatwg-mimetype "^2.3.0"
whatwg-url "^9.0.0"
ws "^8.0.0"
xml-name-validator "^3.0.0"
w3c-xmlserializer "^3.0.0"
webidl-conversions "^7.0.0"
whatwg-encoding "^2.0.0"
whatwg-mimetype "^3.0.0"
whatwg-url "^10.0.0"
ws "^8.2.3"
xml-name-validator "^4.0.0"
jsesc@^2.5.1:
version "2.5.2"
@ -15283,7 +15218,7 @@ number-is-nan@^1.0.0:
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
nwsapi@^2.0.9, nwsapi@^2.2.0:
nwsapi@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7"
integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==
@ -15906,11 +15841,6 @@ parse5-htmlparser2-tree-adapter@^6.0.0:
dependencies:
parse5 "^6.0.1"
parse5@5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2"
integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==
parse5@6.0.1, parse5@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
@ -16184,11 +16114,6 @@ plop@^2.7.1:
ora "^3.4.0"
v8flags "^2.0.10"
pn@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb"
integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==
portfinder@^1.0.28:
version "1.0.28"
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778"
@ -17092,22 +17017,6 @@ replaceall@^0.1.6:
resolved "https://registry.yarnpkg.com/replaceall/-/replaceall-0.1.6.tgz#81d81ac7aeb72d7f5c4942adf2697a3220688d8e"
integrity sha1-gdgax663LX9cSUKt8ml6MiBojY4=
request-promise-core@1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f"
integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==
dependencies:
lodash "^4.17.19"
request-promise-native@^1.0.5:
version "1.0.9"
resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28"
integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==
dependencies:
request-promise-core "1.1.4"
stealthy-require "^1.1.1"
tough-cookie "^2.3.3"
request@^2.88.0, request@^2.88.2:
version "2.88.2"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
@ -17357,13 +17266,6 @@ sax@>=0.6.0:
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
saxes@^3.1.5:
version "3.1.11"
resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b"
integrity sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==
dependencies:
xmlchars "^2.1.1"
saxes@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d"
@ -17996,11 +17898,6 @@ static-extend@^0.1.1:
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
stealthy-require@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=
stream-events@^1.0.4, stream-events@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/stream-events/-/stream-events-1.0.5.tgz#bbc898ec4df33a4902d892333d47da9bf1c406d5"
@ -18349,7 +18246,7 @@ symbol-observable@^1.0.4, symbol-observable@^1.1.0:
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
symbol-tree@^3.2.2, symbol-tree@^3.2.4:
symbol-tree@^3.2.4:
version "3.2.4"
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
@ -18672,14 +18569,6 @@ touch@^3.1.0:
dependencies:
nopt "~1.0.10"
tough-cookie@^2.3.3, tough-cookie@^2.5.0, tough-cookie@~2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
dependencies:
psl "^1.1.28"
punycode "^2.1.1"
tough-cookie@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4"
@ -18689,12 +18578,13 @@ tough-cookie@^4.0.0:
punycode "^2.1.1"
universalify "^0.1.2"
tr46@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=
tough-cookie@~2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
dependencies:
punycode "^2.1.0"
psl "^1.1.28"
punycode "^2.1.1"
tr46@^2.1.0:
version "2.1.0"
@ -18703,6 +18593,13 @@ tr46@^2.1.0:
dependencies:
punycode "^2.1.1"
tr46@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9"
integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==
dependencies:
punycode "^2.1.1"
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
@ -19362,22 +19259,13 @@ voca@^1.4.0:
resolved "https://registry.yarnpkg.com/voca/-/voca-1.4.0.tgz#e15ac58b38290b72acc0c330366b6cc7984924d7"
integrity sha512-8Xz4H3vhYRGbFupLtl6dHwMx0ojUcjt0HYkqZ9oBCfipd/5mD7Md58m2/dq7uPuZU/0T3Gb1m66KS9jn+I+14Q==
w3c-hr-time@^1.0.1, w3c-hr-time@^1.0.2:
w3c-hr-time@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd"
integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==
dependencies:
browser-process-hrtime "^1.0.0"
w3c-xmlserializer@^1.0.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794"
integrity sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==
dependencies:
domexception "^1.0.1"
webidl-conversions "^4.0.2"
xml-name-validator "^3.0.0"
w3c-xmlserializer@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a"
@ -19385,6 +19273,13 @@ w3c-xmlserializer@^2.0.0:
dependencies:
xml-name-validator "^3.0.0"
w3c-xmlserializer@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz#06cdc3eefb7e4d0b20a560a5a3aeb0d2d9a65923"
integrity sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==
dependencies:
xml-name-validator "^4.0.0"
walkdir@^0.4.0:
version "0.4.1"
resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.4.1.tgz#dc119f83f4421df52e3061e514228a2db20afa39"
@ -19434,11 +19329,6 @@ webidl-conversions@^3.0.0:
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
webidl-conversions@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
webidl-conversions@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff"
@ -19449,6 +19339,11 @@ webidl-conversions@^6.1.0:
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514"
integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==
webidl-conversions@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a"
integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==
webpack-bundle-analyzer@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.5.0.tgz#1b0eea2947e73528754a6f9af3e91b2b6e0f79d5"
@ -19586,23 +19481,43 @@ websocket-extensions@>=0.1.1:
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42"
integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==
whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5:
whatwg-encoding@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0"
integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==
dependencies:
iconv-lite "0.4.24"
whatwg-encoding@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53"
integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==
dependencies:
iconv-lite "0.6.3"
whatwg-fetch@^3.4.1:
version "3.6.2"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c"
integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==
whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0:
whatwg-mimetype@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
whatwg-mimetype@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7"
integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==
whatwg-url@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-10.0.0.tgz#37264f720b575b4a311bd4094ed8c760caaa05da"
integrity sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==
dependencies:
tr46 "^3.0.0"
webidl-conversions "^7.0.0"
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
@ -19611,15 +19526,6 @@ whatwg-url@^5.0.0:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"
whatwg-url@^7.0.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06"
integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==
dependencies:
lodash.sortby "^4.7.0"
tr46 "^1.0.1"
webidl-conversions "^4.0.2"
whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0:
version "8.7.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77"
@ -19629,14 +19535,6 @@ whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0:
tr46 "^2.1.0"
webidl-conversions "^6.1.0"
whatwg-url@^9.0.0:
version "9.1.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-9.1.0.tgz#1b112cf237d72cd64fa7882b9c3f6234a1c3050d"
integrity sha512-CQ0UcrPHyomtlOCot1TL77WyMIm/bCwrJ2D6AOKGwEczU9EpyoqAokfqrf/MioU9kHcMsmJZcg1egXix2KYEsA==
dependencies:
tr46 "^2.1.0"
webidl-conversions "^6.1.0"
which-boxed-primitive@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
@ -19831,29 +19729,12 @@ ws@7.4.6:
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==
"ws@^5.2.0 || ^6.0.0 || ^7.0.0", ws@^7.2.3, ws@^7.4.6:
version "7.5.3"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74"
integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==
ws@^6.1.2:
version "6.2.2"
resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e"
integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==
dependencies:
async-limiter "~1.0.0"
ws@^7.3.1:
"ws@^5.2.0 || ^6.0.0 || ^7.0.0", ws@^7.2.3, ws@^7.3.1, ws@^7.4.6:
version "7.5.7"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67"
integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==
ws@^8.0.0:
version "8.2.3"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba"
integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==
ws@^8.3.0, ws@^8.4.2:
ws@^8.2.3, ws@^8.3.0, ws@^8.4.2:
version "8.5.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f"
integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==
@ -19868,6 +19749,11 @@ xml-name-validator@^3.0.0:
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
xml-name-validator@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835"
integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==
xml2js@^0.4.23:
version "0.4.23"
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"
@ -19881,7 +19767,7 @@ xmlbuilder@~11.0.0:
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
xmlchars@^2.1.1, xmlchars@^2.2.0:
xmlchars@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==