Compare commits

...
321 Commits
Author SHA1 Message Date
Wyatt Johnson a95694dff6 [CORL-867] Version SSL Bypass (#2797)
* fix: move /api/version outside SSL wrap

* chore: bump version
2020-01-15 23:22:37 +00:00
Wyatt Johnson f58f65c7ae [CORL-867] Health SSL Bypass (#2796)
* fix: bypass enforceHTTPS for /api/health

* chore: version bump
2020-01-15 20:11:28 +00:00
Nick FunkandWyatt Johnson 22516457ef Fix text field adornments to use correct font (#2795)
Font styling for text field adornments now
matches the text field input.

CORL-849

Co-authored-by: Wyatt Johnson <accounts+github@wyattjoh.ca>
2020-01-14 22:48:06 +00:00
Nick FunkandWyatt Johnson 533d7633e1 [CORL-846] Restructure the slack comment message format (#2794)
* Restructure the slack comment message format

CORL-846

* feat: removed all comments

Co-authored-by: Wyatt Johnson <accounts+github@wyattjoh.ca>
2020-01-14 22:26:33 +00:00
Tessa ThorntonandWyatt Johnson e8fb234838 [CORL-850] use pngs for logo (#2790)
* use png instead of svg for logo

* update snap

Co-authored-by: Wyatt Johnson <accounts+github@wyattjoh.ca>
2020-01-14 17:26:56 +00:00
Wyatt Johnson 9b8ab6de5f 5.4.0 Release Bug Fixes (#2789)
* fix: addresses CORL-848

Fixed copy for new commenters feature.

* fix: address CORL-847

Revert the line hight changes on select fields for now.

* fix: addressed CORL-851

Changed copy on CSS field.

* fix: addressed CORL-840

Changed deletion window to 24 hours.
Refactored durations to use TIME enum.
2020-01-14 16:39:21 +00:00
Nick FunkandWyatt Johnson a88644d98e [CORL-836] Create activeStories endpoint (#2787)
* Create activeStories GraphQL query endpoint

Set lastCommentedAt on stories when they are
commented upon.
Use lastCommentedAt to retrieve the activeStories.
Create a migration to partial index
lastCommentedAt on stories to make retrieval fast.

CORL-836

* fix: adjusted query to use index, more @auth directives

Co-authored-by: Wyatt Johnson <accounts+github@wyattjoh.ca>
2020-01-10 23:55:22 +00:00
Wyatt Johnson 745fb4056c fix: fixed tag styling due to localization (#2785) 2020-01-10 18:32:15 +00:00
vladimir04andWyatt Johnson a582773e1d I18n ro translation (#2786)
* add Romanian translation

* add further RO translations

* update locales.ts file

* add common.ftl RO translations

* fix: linting

* fix: updated snapshots

Co-authored-by: Wyatt Johnson <accounts+github@wyattjoh.ca>
2020-01-10 18:04:52 +00:00
Wyatt Johnson 6057edcb07 Heroku Build Fix (#2784)
* fix: pinned node and npm versions

* fix: addressed issues with package upgrades
2020-01-10 01:10:15 +00:00
Wyatt JohnsonandKim Gardner 852466e9a1 feat: add additional log messages, added nudge to metadata (#2781)
Co-authored-by: Kim Gardner <kgardnr@gmail.com>
2020-01-08 19:43:10 +00:00
Nick Funk f53db233ce [CORL-829] Bump line-height on select fields (#2779)
* Bump line-height on select to account for hanging font characters

Prevents characters like "g" or "j" being cut off at the
bottom of the select.

CORL-829

* Create new tall line height

Use it for the select boxes to allow for dangling
characters like 'g' and 'j'.

CORL-829
2020-01-08 11:06:16 -07:00
Nick Funk 31cf9a4f7d [CORL-818] Add validation and placeholder for Slack hook url field (#2782)
* Add a placeholder for Slack hook url field

CORL-818

* Validate slack hook url's before saving

CORL-818
2020-01-08 10:49:33 -07:00
Nick Funk 81338d1386 [CORL-825-cp] Allow sending of moderation decisions to Google (#2783)
* Allow sending of moderation decisions to Google

If enabled in the config, moderation decisions can
be sent to Google.

Simplify OnOffField test id's

Consolidate guard clauses

Reconstruct tenant url's when sending perspective feedback

Use the comment revision id when sending perspective feedback

Use retrieveStory instead of findStory

Clarify logging warning levels on perspective feedback

Clean html tags from sent comment body

Fix include to match new graph context refactors

CORL-825

* Handle asynchronous perspective operation more cleanly

CORL-825
2020-01-08 10:32:24 -07:00
Nick FunkandWyatt Johnson 84bbc3d0c3 [CORL-688] Refactor: Consolidate the tenant and common context together (#2780)
* feat: initial impl

* Create preliminary comment moderation slices

CORL-688

* Move slices logic into stacks

CORL-688

* Create user comment counts

CORL-688

* Create naive mutation that initializes user comment counts

CORL-688

* Use bulk updates in user counts migration

CORL-688

* fix: review

* fix: fixed issue with aggregation

* Migrate creating comment into stacks

CORL-688

* Migrate editing a comment to the stacks

CORL-688

* Break publishing comment status out of updateAllCounts

CORL-688

* review: removed variable scoping in favor of export

* revert: feb8e8196cd448f5cd24f1ca2eb0b91fe9bd43c7

* review: simplification of stacks implementation

This simplifies the stacks implementation to better reuse code related
to count management and event publishing. This can be used to great
effect with the upcomming events PR #2738.

* Consolidate the tenant and common context together

CORL-688

* review: removed variable scoping in favor of export

* revert: feb8e8196cd448f5cd24f1ca2eb0b91fe9bd43c7

* review: simplification of stacks implementation

This simplifies the stacks implementation to better reuse code related
to count management and event publishing. This can be used to great
effect with the upcomming events PR #2738.

* fix: check if authorID is null before update user counts

CORL-688

* Consolidate common/tenant context supporting files

CORL-688

* feat: renamed TenantContext -> GraphContext

Co-authored-by: Wyatt Johnson <accounts+github@wyattjoh.ca>
2020-01-08 17:07:25 +00:00
d26e331a4f [CORL-664] Threshold moderate new commenters (#2752)
* add new commenters config

* fix specs and fixtures, add translation strings

* save whether a commenter is new

* fix specs and snaps

* add admin role to new config options

* Update copy

* remvoe unused ref

* feat: initial impl

* Create preliminary comment moderation slices

CORL-688

* Move slices logic into stacks

CORL-688

* Create user comment counts

CORL-688

* Create naive mutation that initializes user comment counts

CORL-688

* Use bulk updates in user counts migration

CORL-688

* fix: review

* fix: fixed issue with aggregation

* Migrate creating comment into stacks

CORL-688

* Migrate editing a comment to the stacks

CORL-688

* Break publishing comment status out of updateAllCounts

CORL-688

* review: removed variable scoping in favor of export

* revert: feb8e8196cd448f5cd24f1ca2eb0b91fe9bd43c7

* review: simplification of stacks implementation

This simplifies the stacks implementation to better reuse code related
to count management and event publishing. This can be used to great
effect with the upcomming events PR #2738.

* Remove un-necessary isNew flags on users

CORL-664

* review: removed variable scoping in favor of export

* revert: feb8e8196cd448f5cd24f1ca2eb0b91fe9bd43c7

* review: simplification of stacks implementation

This simplifies the stacks implementation to better reuse code related
to count management and event publishing. This can be used to great
effect with the upcomming events PR #2738.

* fix: check if authorID is null before update user counts

CORL-688

* fix: addressed bug in shared count retrival

Co-authored-by: Tessa Thornton <tessathornton@gmail.com>
Co-authored-by: Wyatt Johnson <accounts+github@wyattjoh.ca>
2020-01-07 14:38:50 -07:00
Nick FunkandWyatt Johnson e3e2e0f52e [CORL-688] Add user comment count tracking (#2744)
* feat: initial impl

* Create preliminary comment moderation slices

CORL-688

* Move slices logic into stacks

CORL-688

* Create user comment counts

CORL-688

* Create naive mutation that initializes user comment counts

CORL-688

* Use bulk updates in user counts migration

CORL-688

* fix: review

* fix: fixed issue with aggregation

* Migrate creating comment into stacks

CORL-688

* Migrate editing a comment to the stacks

CORL-688

* Break publishing comment status out of updateAllCounts

CORL-688

* review: removed variable scoping in favor of export

* revert: feb8e8196cd448f5cd24f1ca2eb0b91fe9bd43c7

* review: simplification of stacks implementation

This simplifies the stacks implementation to better reuse code related
to count management and event publishing. This can be used to great
effect with the upcomming events PR #2738.

* fix: check if authorID is null before update user counts

CORL-688

Co-authored-by: Wyatt Johnson <accounts+github@wyattjoh.ca>
2020-01-07 21:00:25 +00:00
Wyatt Johnson 0dc3e8968a fix: package upgrade (#2777)
Upgraded some packages to latest. This should resolve #2774. Fixes were
also applied after types upgrades that helped discover other errors.
2020-01-06 16:14:56 +00:00
Hans Sprecher f900050ef4 2772: swapping a Perspective API link from a dead one to the README on their repo. (#2773) 2019-12-23 17:42:45 -05:00
0f456330c5 [CORL-827/828] Scrub downloaded comments of html tags, rename archive (#2767)
* Prefix comment download filename with "coral"

CORL-827

* Scrub html tags from downloaded comment data

CORL-828

Co-authored-by: Kim Gardner <kgardnr@gmail.com>
Co-authored-by: Wyatt Johnson <accounts+github@wyattjoh.ca>
2019-12-20 23:46:24 +00:00
Wyatt Johnson 9d050e8b51 [CORL-800] Fluent Upgrade (#2739)
* feat: upgraded fluent libraries

* fix: adjustments to support fluent upgrade
2019-12-20 23:18:49 +00:00
Wyatt JohnsonandKim Gardner 7581f0201d fix: improved the count management (#2769)
Count management has been adjusted to instead preserve the zero values
of the counts. Old count maintainence functions were also removed
because they were untested and not used.

Co-authored-by: Kim Gardner <kgardnr@gmail.com>
2019-12-20 18:07:29 -05:00
Nick Funk 86b2ec1e56 Fix incorrectly set tabID on moderate card details (#2768)
CORL-826
2019-12-20 17:55:56 -05:00
Nick FunkandKim Gardner fd1ee39db4 Fix story message boxes to wrap text properly (#2761)
Need to set the message content width so that
the text knows how to wrap. Also accounts for
when the icon is present to set width to the
correct value so it doesn't shift past the edge
of the message box container.

CORL-802

Co-authored-by: Kim Gardner <kgardnr@gmail.com>
2019-12-20 17:25:55 -05:00
8c6f12c24d Improve pt-BR translation (#2757)
Update src/locales/pt-BR/admin.ftl

Co-Authored-By: Wyatt Johnson <accounts+github@wyattjoh.ca>

Co-authored-by: Wyatt Johnson <accounts+github@wyattjoh.ca>
Co-authored-by: Kim Gardner <kgardnr@gmail.com>
2019-12-20 17:15:31 -05:00
Nick Funk 11a4a5ebe3 Update logo to match new Vox Media branding (#2766)
The Vox Media text now has reduced spacing.

CORL-804
2019-12-20 17:03:07 -05:00
Wyatt Johnson f3ecbf5dc6 [CORL-824] Feature Flags (#2756)
* feat: implemented feature flag

* fix: support deprecated enums
2019-12-20 18:06:30 +00:00
Tessa Thornton 116d3c0c81 fix jsdoc warnings (#2750) 2019-12-12 11:41:57 -05:00
Tessa Thornton 3358cef433 [CORL-746] updated branding: buttons (#2748)
* new button component

* update specs

* fix up markers

* fix docz so it renders

* clean up buttons implementation

* update snaps

* clean up slack buttons

* update snaps
2019-12-09 18:03:30 -05:00
Nick Funk ec93789cff [CORL-645] Resolve mismanaged branch merging (#2746)
* Create preliminary schema for setting Slack channels

CORL-645

* Implement preliminary slack notification using tenant slack channels

CORL-645

* Very preliminarily get data loading with FieldArray's for slack channels

CORL-645

* Update settings input on schema to allow saving Slack settings to tenant

CORL-645

* Filter off UNMODERATED queue events from slack channels

We don't send these to slack through our filters, we only care
about pending, reported, and featured

CORL-645

* Include a moderation link in comments pushed to Slack

CORL-645

* Hook up proper callback functions for adding/removing slack channels

CORL-645

* Add missing translation for Slack navigation item

CORL-645

* Update snapshots for preliminary Slack configuration changes

CORL-645

* Add some FormField wrappers around slack config elements

Makes the UI appear a little nicer

CORL-645

* Set up slack config to only provide one slack channel

We need to do this until we can get ArrayField's working
in final-form.

CORL-645

* Disable the other trigger checkboxes when "All Comments" is checked

CORL-645

* Clean up the formatting of Slack messages

CORL-645

* Add error handling around sending comments to Slack

CORL-645

* Add links to external Slack setup documentation

CORL-645

* Replace form state with wrapped field element

CORL-645

* Clean up fetch request sending Slack notifications

CORL-645

* Prefer global string replacement over RegEx

CORL-645

* Use URL class to construct comment URL's

CORL-645

* Require slack configuration in schema

CORL-645

* Initialize Slack in fixtures

Also fix up a flaky test that wasn't waiting
on form submission and on-change events.

CORL-645

* Preliminarily fix up styles to match other config pages

CORL-645

* Create placeholder add/remove buttons

* Convert SlackConfigContainer to FunctionalComponent

CORL-645

* Add name field to slack channels

CORL-645

* Disable inner fields on Slack channel when not enabled

CORL-645

* Improve the delete channel button

CORL-645

* Use pureMerge to extract slack channel settings

CORL-645

* Do a bit of cleanup on the add channel button

CORL-645

* Update the styling of the Slack config

- Use sub header for channel headings
- Update add channel button text
- Move the remove channel button down
- Other minor spacing tweaks

CORL-645
2019-12-09 16:29:57 -05:00
Tessa Thornton f60fac651b use correct timestamp component (#2747) 2019-12-09 16:20:24 -05:00
Tessa Thornton 514a549a77 update markers to make sure they're legible (#2745) 2019-12-09 16:11:12 -05:00
Wyatt Johnson 88f6cad840 fix: resolved issue with incorrect moderation url being generated (#2743) 2019-12-06 18:46:54 +00:00
Vinh 7615dc2aaf [CORL-720] Integratejest-axe (#2741)
* feat: axe checks for tests

* test: add another axe check

* fix: tests
2019-12-05 16:44:16 -05:00
Wyatt Johnson 51bfde8cf8 feat: added toxicity multi-language support (#2737) 2019-12-05 13:17:07 -05:00
Wyatt Johnson 5ebc39a2e1 chore: bump 5.4.0 (#2742) 2019-12-03 22:12:30 +00:00
Nick Funk d5020efa70 [CORL-758] Show new comments in stream when sorting by oldest (#2740)
* Handle comment insertion in stream when sort by oldest

Push the comment to the end of current cursored
comment stream so it shows up.

CORL-758

* Make commenstOrderBy a client only mutation parameter

CORL-758

* fix: addressed linting issue
2019-12-03 21:57:41 +00:00
Wyatt Johnson a396efef0d Security Headers (#2736)
* feat: added more security headers

* fix: added option to disable forcing SSL

* fix: linting
2019-12-03 21:47:36 +00:00
Tessa Thornton 9618e5dab1 show a message to user if comment is rejected on submission (#2733) 2019-12-03 21:35:57 +00:00
Nick Funk 54296fa484 [CORL-645] Add Slack support for v5 (#2713)
* Create preliminary schema for setting Slack channels

CORL-645

* Implement preliminary slack notification using tenant slack channels

CORL-645

* Very preliminarily get data loading with FieldArray's for slack channels

CORL-645

* Update settings input on schema to allow saving Slack settings to tenant

CORL-645

* Filter off UNMODERATED queue events from slack channels

We don't send these to slack through our filters, we only care
about pending, reported, and featured

CORL-645

* Include a moderation link in comments pushed to Slack

CORL-645

* Hook up proper callback functions for adding/removing slack channels

CORL-645

* Add missing translation for Slack navigation item

CORL-645

* Update snapshots for preliminary Slack configuration changes

CORL-645

* Add some FormField wrappers around slack config elements

Makes the UI appear a little nicer

CORL-645

* Set up slack config to only provide one slack channel

We need to do this until we can get ArrayField's working
in final-form.

CORL-645

* Disable the other trigger checkboxes when "All Comments" is checked

CORL-645

* Clean up the formatting of Slack messages

CORL-645

* Add error handling around sending comments to Slack

CORL-645

* Add links to external Slack setup documentation

CORL-645

* Replace form state with wrapped field element

CORL-645

* Clean up fetch request sending Slack notifications

CORL-645

* Prefer global string replacement over RegEx

CORL-645

* Use URL class to construct comment URL's

CORL-645

* Require slack configuration in schema

CORL-645

* Initialize Slack in fixtures

Also fix up a flaky test that wasn't waiting
on form submission and on-change events.

CORL-645

* Preliminarily fix up styles to match other config pages

CORL-645

* Create placeholder add/remove buttons

* Convert SlackConfigContainer to FunctionalComponent

CORL-645

* Add name field to slack channels

CORL-645

* Disable inner fields on Slack channel when not enabled

CORL-645

* Improve the delete channel button

CORL-645

* Use pureMerge to extract slack channel settings

CORL-645

* Do a bit of cleanup on the add channel button

CORL-645
2019-12-03 16:06:38 -05:00
Tessa Thornton c109188efe better unsaved changes error message (#2734) 2019-12-02 19:03:10 -05:00
Dzarr 08ead49629 adding french translations to all of src/locales files (#2720)
* adding french translations to all of src/locales files

* Fix formatting and update snapshots

* Update snapshots
2019-12-02 18:11:03 -05:00
Vinh bc27d7fbec [CORL-729] Upgrade Final Form & improve tests (#2735)
* CORL-729 Upgrade final form, fix and improve tests

This is a squashed rebase from these commits:
a300b31c23ab11e5e6f0668bc03ece7697360aaa feat: error on optimisic response warnings during test
dd8a9776865ec41d346e23ae0743d0d4fb0caa21 fix: turn off @typescript-eslint/prefer-regexp-exec rule
b995daacf1722cace60d755e672cb6a3a20d6bc2 feat: mute false warnings in test
e44f9e28307cd63a82c1fb7ac013667dd7b7bc46 fix: wrap remaining tests with act
afbd4329b97f3dbc9f873ea4ff234d98bb651ccf feat: fail when act(async () => ...) without await warnings
51dfb60b7d75411ba2e1a28db33f4aba5cf84de1 feat: fail tests with act warnings
97f93546ed8113e207882411eb4cdb7675b0796c fix: mock window.resizeTo globally in tests
850958b8c4d2fc0aca67ae580296433af223f8ab fix: more tests with act
24c05ab88e9a416e4962acc3f20f2c764ba07657 fix: wrap charCountEditComment test in acts
ed590b82d147470bba74055dc682e6b70d2e76fb fix signInWithEmail
4a2b9402cb6ce9565d99ae1a950eaa422ff603c3 fix: PostCommentFormContainer
815ebe6ef364d954d4bd0a35495934c9d014170b fix: use final form initialize instead of reset and remove obsolete
d3101f2112ed3ffe8d06609620e31e6655d2cf6d Merge branch 'feature/CORL-729-final-form' of https://github.com/coralproject/talk into feature/CORL-729-final-form
a0658da610a5f39b6fae78ffb8dd291b22d54e50 fix: addEmailAddress
60f7fc99a44dfa49dfd401a0ef49c60973b3e8e3 fix: use proper act pattern in renderConfigure.spec.tsx
d66bdfc2245c2b1ee03a1b3a3a56f1d5ba14ddc4 Stop propagation of Modal content click events (#2706)
ec6689594136e22a5b9f05ea284162702dc4955d fix: use proper act pattern in createUsername.spec.tsx
ef239496964a5f9a91ee1c4424ad81537c4f47fe fix: stream configuration
c7e06a0c6aef6b299c41392af81f8a20855028bf fix: user proper act pattern in streamConfiguration
9712e659e394a898500ed649464ff14d4870e589 Merge branch 'feature/CORL-729-final-form' of https://github.com/coralproject/talk into feature/CORL-729-final-form
9e5cfbaf3593615b457055de23f187fa07edd4c4 fix: signIn test
99b44a4a1bbb7ff2cd44c6821ad33b80f90c4a99 fix: user proper act pattern in stories.spec.tsx
ed7c1a92f93ab9aaa85ff92837b0ed21560cb358 fix: user proper act pattern in addEmailAddress
a04b392cb2148b9a24791b062027796c409d053e fix: remove obsolete snapshots
59df67c0f9b9d26c74e2cca7d333f5868b0b202d fix: signUp test
7656f179df95b4cd96b37afbc88a1c3a2944fdf4 fix: use proper act pattern on createPassword
85246fbf1f9ab49ad3a09c11ab79bf537059b548 fix: use correct act() pattern in createUsername.spec.tsx
d5239373a2d1bbed0bfe8c1ca62ef6a70ef5c7d9 fix: the correct way to use act on form submit :-)
d84ecd168354f4acb422a5ddb725fb8faf9c3184 fix: moderation test
d8df62ab1a6486144684ff917c47e6e375ffbe03 fix: reportComment test
2756e3184bb292168e8d34e201f340c3799941e6 fix: auth tests
a28695dbdd313a7bc3dade9ac1f92d6ef0061526 Properly handle final-form actions in tests
2fafc8ea3458c5b15b66f3d65f0947672dd1a516 Update snapshots now that final form isn't overwriting props
1f9bbaec8678a7653124898ba4a2e84ddc1ef243 fix: prevent final form from overwriting field props
f6c66c003d1917db2dcb3f757e8a303266c381fa fix: prevent final form fields overwrite out props
48d1fc7318ee4ba7bf72839127e9a0b1487c1729 chore: rename translation string
728373da5728a4e7c039bd0c3a3cf0037e9f5177 fix: purge relay metadata from request
7cdea925087a6b9b6e318bbb1b31b798be87dc2f fix: radiobuttons
9735bae79222219a81a28d458976a596201b650b chore: revert obsolete checks
0b556e1693584430a5814e81d87b0f233efd1a30 fix: refactor admin configure
b245afc7b196035bcb454e031c966e63c77ce522 fix: implement withForm HOC
5787400051211f5d2e1773d7207f32b66b02a2a1 Update the Configure page form state to properly load form values
8c2af3e22a96a3d2e50e7f06fb45d1fb79cf0c8e Replace form.reset() with setTimeout(form.reset)
27d9c90e3f0166cc2db45db461619be15a3cb950 Update radio buttons and on/off fields to work with final-form updates
b852dd14af85b14ff8e0d2823e1e83bf278b29b9 Replace any on OnSubmit with typed form submission interfaces
f049a70aaf4872825ac3b2aa62dc5cb7f945290a (f) Preliminarily get Coral compiling with latest final-form libraries

* feat: act is now actAndReturn

* fix: print original filename and line number in custom console impl

* feat: trace process warnings

* fix: server warnings about potentially memory leak with too many listeners
2019-12-03 01:07:50 +07:00
Tessa Thornton 4ee8cf7c96 new admin branding (#2692)
* add comment revisions

* add strings

* update fixtures

* fix fixtures

* [CORL-629] Update admin moderation containers to use new branding tokens (#2600)

* Convert the moderation card components to use new styles

CORL-629

* Style the details area in the moderate card using new font and color styles

CORL-629

* Update FlagDetails on Moderate Card to match new branding styles

CORL-629

* Remove typography from ModerateCard Username

CORL-629

* Remove unnecessary new typography tokens created while experimenting

CORL-629

* Update background colors of username buttons on moderate card

CORL-629

* Update approve reject buttons to use new colors

CORL-629

* Fix alignment of username and "Reply to" in InReplyTo component

CORL-629

* Update moderated by on moderate card to match new branding

CORL-629

* Create distinct admin marker and counter components with new branding styles

Update moderate card components to use these distinct newly branded styles.

CORL-629

* Create independent Tabs control with new styling for admin area

Use it on the moderate card details area.

CORL-629

* Remove letter spacing from moderate card styles

CORL-629

* Update "DETAILS" tab on moderate card to be named "INFO"

CORL-629

* Update some missing v2 css variables

CORL-629

* Tweak the spacing of the COMMENT link and title on the moderate card

CORL-629

* Update details font color on moderate card

CORL-629

* Update spacing on newly styled Admin Tab component

CORL-629

* Update spacing of reply to username on the moderate card

CORL-629

* Update moderate card comment text area minimum width and height

CORL-629

* Update vertical divider on moderate card to updated branding

CORL-629

* Introduce new bold open sans font face

Downgrades all existing bold's to semi-bold as they were equivalent before.

CORL-629

* Update moderate card details tabs to use new bold open sans font

CORL-629

* Create an admin specific timestamp control that implements new branding

CORL-629

* Migrate shared moderate card styles into child component files

CORL-629

* [CORL-631] Update admin side main navigation to new branding styles (#2625)

* Convert the moderation card components to use new styles

CORL-629

* Style the details area in the moderate card using new font and color styles

CORL-629

* Update FlagDetails on Moderate Card to match new branding styles

CORL-629

* Remove typography from ModerateCard Username

CORL-629

* Remove unnecessary new typography tokens created while experimenting

CORL-629

* Update background colors of username buttons on moderate card

CORL-629

* Update approve reject buttons to use new colors

CORL-629

* Fix alignment of username and "Reply to" in InReplyTo component

CORL-629

* Update moderated by on moderate card to match new branding

CORL-629

* Create distinct admin marker and counter components with new branding styles

Update moderate card components to use these distinct newly branded styles.

CORL-629

* Create independent Tabs control with new styling for admin area

Use it on the moderate card details area.

CORL-629

* Remove letter spacing from moderate card styles

CORL-629

* Update "DETAILS" tab on moderate card to be named "INFO"

CORL-629

* Update some missing v2 css variables

CORL-629

* Tweak the spacing of the COMMENT link and title on the moderate card

CORL-629

* Update details font color on moderate card

CORL-629

* Update spacing on newly styled Admin Tab component

CORL-629

* Update spacing of reply to username on the moderate card

CORL-629

* Update moderate card comment text area minimum width and height

CORL-629

* Update vertical divider on moderate card to updated branding

CORL-629

* Introduce new bold open sans font face

Downgrades all existing bold's to semi-bold as they were equivalent before.

CORL-629

* Update moderate card details tabs to use new bold open sans font

CORL-629

* Create an admin specific timestamp control that implements new branding

CORL-629

* Migrate shared moderate card styles into child component files

CORL-629

* Update main navigation admin side to use new branding styles

CORL-631

* Bring in some preliminary color and font styles for the search bar

This helps it match navigation, but does not update all the sub-styles of
the search bar's many child components.

CORL-631

* [CORL-694] use new styles for decision history (#2644)

* use new styles for decision history

* remove reference to nonexistent variable

* update snaps

* [CORL 695] admin branding - user menu (#2650)

* update user menu, popover, and dropdown to new design tokens

* fix feature button colours

* use v2 spacing vars

* admin branding: tables (#2651)

* update user menu, popover, and dropdown to new design tokens

* fix feature button colours

* use v2 spacing vars

* style stories and users table

* update snap

* update user status font weight

* remove duplicate declaration

* fix line height references

* update design tokens for text inputs and selects (#2658)

* use box shadow instead of outline for focus states

* Update snapshots to adjust for resolved merge conflicts

* Bring new branding styles to button components (#2667)

* Create preliminary filled and outline buttons that match branding

* Add adornment button variant to newly branded button

* Add the ghost variant to the newly branded button component

* Add underlined variant to the newly branded button component

* Remove external Icon composes property

* Convert camel-case button variables to kebab-case

* [CORL-672] Configuration pages new branding (#2673)

* style config side nav

* general configuration tab branding

* organization tab branding

* moderation configuration branding

* banned/suspect words config branding

* email config branding

* advanced settings branding

* auth config branding

* move helpertext and fieldset components

* add textarea component

* add back gutters on table search

* update snaps

* fix header component to accept labels

* use new spacing form components

* use spacing components for advanced config

* fix tests

* fix line height of radio buttons

* update validation message branding

* update snaps

* fix paths casing

* try lowercase then

* add docs for new form components

* move description component

* update snaps

* move components to /ui/v2

* [CORL-673] Update the moderate area search menu to use new branding styles (#2672)

* Create preliminary filled and outline buttons that match branding

* Add adornment button variant to newly branded button

* Add the ghost variant to the newly branded button component

* Add underlined variant to the newly branded button component

* Remove external Icon composes property

* Convert camel-case button variables to kebab-case

* Preliminarily migrate raw components into v2 to prep for branding updates

CORL-673

* Preliminarily update the search results bar to use new branding tokens

CORL-673

* Update styles to new branding on migrated v2 client-ui components

CORL-673

* Update see all and moderate all search option icons

CORL-673

* style config side nav

* general configuration tab branding

* organization tab branding

* moderation configuration branding

* banned/suspect words config branding

* email config branding

* advanced settings branding

* auth config branding

* move helpertext and fieldset components

* add textarea component

* add back gutters on table search

* update snaps

* fix header component to accept labels

* use new spacing form components

* use spacing components for advanced config

* fix tests

* fix line height of radio buttons

* update validation message branding

* update snaps

* fix paths casing

* try lowercase then

* add docs for new form components

* move description component

* update snaps

* move components to /ui/v2

* Set size="md" on the Icon component and not the Localized component

CORL-673

* tag component

* [CORL-669] user drawer + misc branding (#2686)

* tag component

* top of user drawer branding

* recent history in drawer

* add tooltips component

* use correct spacing prop

* history tabs

* user history and rejected comments

* move callout component'

* moderator notes components

* account history table

* table user row

* swap in tabs components

* remove overflow:hidden on buttons

* update snaps

* replace overflow scroll with auto

* clean up from rebasing vs master

* fix ts

* sort exports

* fix confusing linting errors

* update snaps

* clean up nav bar design

* adjust nav hover colors

* comment content component

* update nav hover styles

* fix search bar border

* clean up moderate card components

* more user drawer cleanup

* user role switcher

* update textfield vars and variants

* update components in community page

* move clickoutside component

* clean up stories table

* moderate route components

* single moderate view

* queue components

* delay component

* relative time component

* decision history components

* user drawer components cleanup

* configure components

* clean up buttons

* update snaps

* add all exports

* [CORL-674] admin branding: modals (#2695)

* update hotkeys modal components

* hotkeys modal

* user status change modals

* user status change modals

* invite users modal

* fix tests

* fix incorrect i18n string

* resolve merge conflicts

* move fonts to devdeps

* Fix username props interface

* fix dom nesting in components

* update formatting in strings

* rewrite delay component as functional component

* remove tslint comments

* change configure header component props
2019-11-26 11:26:53 -05:00
Tessa ThorntonandWyatt Johnson 0e15bc46d2 [CORL-353] show error if network request fails (#2731)
* show error if network request fails

* remove commented code

* Update src/core/client/admin/routes/AuthCheck/NetworkError.tsx

Co-Authored-By: Wyatt Johnson <wyattjoh@gmail.com>

* spell occurred correctly
2019-11-26 15:46:56 +00:00
Tessa Thornton 9bc41ad020 [CORL-575] allow line breaks in stream tabs (#2726)
* allow line breaks in stream tabs

* update snaps

* fix specs

* rearrange stream tabs for better mutliline support
2019-11-26 10:01:05 -05:00
Nick Funk f1cdcb149b [CORL-625] Create link details in the moderation card details tabs (#2721)
* Create link details in the moderation card details tabs

CORL-625

* Use getURLWithCommentID and getLocationOrigin to generate URLs

CORL-625
2019-11-22 23:50:52 +00:00
Wyatt Johnson bf89fc2b3e [chore] NodeJS 12 (#2722)
* chore: upgraded node 12, more log lines

- upgraded from 10 LTS to 12 LTS
- added log line with userID to GraphQL
  log entry

* fix: adjusted some intl formats
2019-11-22 22:46:16 +00:00
Vinh 7c2e56b751 [CORL-719] A11y runtime checks using react-axe (#2725)
* feature: use react-axe and fix some a11y issues in stream

* fix: adapt snapshots

* chore: fix axe warnings in auth and account bundle

* chore: improve accessibility in install bundle

* chore: add some comments
2019-11-22 19:10:51 +00:00
Vinh 4542dbdb43 feat: hide deprecated events behind a flag (#2728) 2019-11-22 18:48:33 +00:00
Wyatt Johnson 787f1c68e7 fix: introduced scraper timeout (#2724)
- fixes #2716
2019-11-22 18:16:41 +00:00
Tessa Thornton 09cf8c5490 [CORL-732] allow navigation via j/k in single comment view (#2712)
* allow navigation via j/k in single comment view

* update snaps
2019-11-22 17:59:46 +00:00
Wyatt Johnson ddedf4dc7e [CORL-797] Moderate this stream (#2729)
* feat: added moderate this stream button

* fix: removed origin

* fix: revised based on feedback from design
2019-11-22 17:50:09 +00:00
Vinh ca811a3496 Bring back deprecated events temporarily (#2727)
* fix: bring back deprecated events

* chore: update version number
2019-11-21 20:04:42 +00:00
Wyatt Johnson 10719bd874 [CORL-793] Scheduled Indexes (#2723)
* fix: modified digesting to use index

* fix: added index for deletion

* review: cleaning up

* review: fixed log copy
2019-11-21 17:24:09 +00:00
Wyatt Johnson bb23c80004 feat: generalized rate limiting for graphql (#2709) 2019-11-19 14:20:36 -07:00
Wyatt Johnson 2491445579 feat: improved logging (#2719) 2019-11-19 19:36:46 +00:00
Wyatt Johnson 3642c642d6 fix: fixed migration script bugs (#2718) 2019-11-19 19:18:28 +00:00
Wyatt Johnson fce7b08ca0 chore: version bump (#2717) 2019-11-18 23:27:25 +00:00
Wyatt Johnson aa9dcb4e04 [CORL-750] SSO Migration Script Bug (#2715)
* fix: fixed bug with sso migration script

* feat: refactored sso schema

* fix: resolved issue with data race and migration/install ordering
2019-11-18 23:11:20 +00:00
Wyatt Johnson 9e9c016f4b fix: adjusted i18n keys (#2708) 2019-11-13 17:50:40 +00:00
Nick Funk 40b247350b [CORL-766] Update copy on SSO key regeneration and account deletion (#2707)
* Update acknowledgment copy for downloading user comment data

CORL-766

* Update copy for 30 day leniency on SSO tokens

CORL-766
2019-11-13 09:50:56 -07:00
Nick Funk 2945184b07 Stop propagation of Modal content click events (#2706)
This prevents the click event from propagating out to
the backdrop parent and auto-closing the modal on any
content click.

CORL-765
2019-11-13 11:00:55 -05:00
Wyatt Johnson a55472e086 fix: adjusted onsubmit handlers (#2705) 2019-11-13 10:42:32 -05:00
Wyatt Johnson 033433549a fix: fixed docs, graphql type (#2704) 2019-11-12 14:00:51 -05:00
Wyatt Johnson 2ef07429c2 fix: improvements to scraping (#2702)
- fixes #2690
2019-11-12 17:25:41 +00:00
Wyatt Johnson b83d737530 chore: version bump (#2700) 2019-11-08 17:00:41 -05:00
Vinh 18346d1683 [CORL-666] Viewer Events (#2681)
* feat: viewer event system

* feat: more events

* feat: MORE events

* fix: tests

* fix: rte focus events

* chore: add comments

* fix: remove listening to events

* chore: update RTE

* fix: tests

* feature: generate event docs

* fix: remove obsolete line in docs

* chore: improve docs

* chore: improve formatting

* feature: protect events.md from getting out of sync

* chore: small improvements

* fix: removing redundant lambda
2019-11-08 17:17:01 +00:00
Wyatt Johnson ce4a3408fc [CORL-628] CLI Support (#2646)
* feat: improved api for reloading tenants for cli

* fix: cleaned up merge beheviour

* feat: added support for stream/story edge

* feat: support tenant install

* fix: updated snapshot

* fix: fixed tests
2019-11-08 00:40:56 +00:00
Wyatt Johnson 2dbba52fbd feat: added initial server sso key rotation semantics (#2696) 2019-11-07 16:53:28 -05:00
Wyatt Johnson 59b8dfccda fix: improved search field serialization (#2697) 2019-11-07 16:45:57 -05:00
Wyatt Johnson 3394628f27 [CORL-744] Index Optimizations (#2694)
* fix: added comment moderation index

* fix: improved user indexing
2019-11-07 16:32:38 -05:00
Vinh 8ee74b7d24 [CORL-743] Fix sort menu on small screens (#2698)
* fix: sort menu on small screens

* chore: add comments
2019-11-07 15:32:49 -05:00
Vinh f85af8e88f [CORL-684] Fix scrolling bug in overflowing modal (#2682)
* fix: scrolling bug in overflowing modal

* fix: tests
2019-11-07 13:44:10 -05:00
Kim Gardner 9d6592feef Update PR template (#2685) 2019-11-01 14:00:04 -04:00
Kim Gardner 53c87118db Update Feature Request template (#2684) 2019-11-01 13:53:08 -04:00
Kim Gardner bdc9c5f05c Update Bug Report template (#2683) 2019-11-01 13:31:18 -04:00
Tessa Thornton e334c3266a update hotkeys legend to match implemented hotkeys (#2679) 2019-10-29 10:13:34 -04:00
Wyatt Johnson a31ee3bc96 Version Bumps (#2677)
* chore: upgraded versions

* chore: linting

* fix: updated snap
2019-10-28 23:45:21 +00:00
Wyatt Johnson a7eb757be7 fix: fixed issue with directive resolver (#2674) 2019-10-25 15:53:30 +00:00
Wyatt Johnson 4038fcbcd6 fix: handle redirection loop (#2662) 2019-10-24 17:15:53 +00:00
Wyatt Johnson 5714f441bb [CORL-735] Scraper support for JSON Linked Data (#2671)
* feat: add support for application/json+ld parsing

- upgraded scraper packages to use new jsonld parser
- waiting on https://github.com/microlinkhq/metascraper/pull/225
  for merging
- fixes #2670

* chore: metascraper upgrades
2019-10-24 16:08:15 +00:00
Wyatt Johnson ac96e5e312 chore: bump version (#2668) 2019-10-23 11:59:39 -04:00
Wyatt Johnson e91da1c9a3 fix: check if auth params exist in hash before clearing it (#2666) 2019-10-22 17:25:47 -04:00
Wyatt Johnson e816f8e019 fix: adjusted copy, added translation support (#2664) 2019-10-22 13:33:15 -04:00
Wyatt Johnson 121469ade5 feat: readme simplication (#2649) 2019-10-22 17:09:49 +00:00
Cristian Dean 1d6e9e38b0 pt-BR translation some missing phrases (#2661) 2019-10-22 10:04:49 -04:00
Wyatt Johnson 626d46d813 [CORL-726] Repeat Comment Fix (#2655)
* fix: strip tags when comparing for repeat

- share the stripped tag version of the comment
- share the logger to improve pipeline clarity

* fix: renamed variables based on review
2019-10-21 18:34:41 +00:00
Wyatt Johnson 24915be712 fix: repaired translation key name (#2657) 2019-10-21 17:15:52 +00:00
Tessa Thornton 044cbd39f1 Revert "ensure tab text doesn't get cut off if tab titles are long (#2619)" (#2654)
This reverts commit 8409dbb4ea.
2019-10-18 20:58:14 +00:00
Wyatt Johnson cd6a1db85b chore: version bump (#2653) 2019-10-18 19:08:57 +00:00
Wyatt Johnson 8bee5ae1f3 [CORL-721] SSO Logout Fix (#2652)
* fix: hide logout button when jti is undefined

* fix: improved local state init
2019-10-18 18:48:46 +00:00
Cristian Dean 153c910de7 Fix pt-BR moderate-comment-deleted-body translation (#2647) 2019-10-18 12:44:41 -04:00
Wyatt Johnson 990a70d6f9 [CORL-680] Persist Fragment Access Tokens in sessionStorage (#2643)
* feat: persist access tokens passed via fragment to sessionStorage

* fix: review comments
2019-10-18 16:08:43 +00:00
Allan Assis 9aa39bf842 Fix pt-BR translations (#2645) 2019-10-17 15:22:05 -04:00
Wyatt Johnson d169d3dd51 fix: allow unknown fields (#2640) 2019-10-16 18:52:33 -04:00
Wyatt Johnson b38a4f117b [CORL-667] Constraint Directive (#2641)
* feat: added constraints

* fix: fixes bug with index creation

- fixes #2612

* fix: added default resolver

* fix: added defaultTo to handle defaulting
2019-10-16 18:44:44 -04:00
Tessa Thornton ea71230b90 [CORL_653] throw error if inviting existing users (#2637)
* throw error if inviting existing users

* check for existing users before sending out any invites

* fix lints
2019-10-16 16:28:49 -04:00
Vinh 2dac5f29c1 Typescript-eslint bug with new or renamed files in IDE (#2642)
* fix: workaround for eslint typescript bug when creating or renaming files.

* fix: downgrade

* fix: do not lint during build

* fix: no need to permanently show eslint status in vscode.
2019-10-17 02:48:48 +07:00
Tessa Thornton 9df473178b [CORL-8] prevent repeat comments (#2632)
* prevent users from reposting the same comment

* fix tests

* fix lints

* update comments for users service

* make updates based on review
2019-10-16 15:51:34 +00:00
Tessa Thornton 1f7fdb0d00 change keyboard shortcuts for approve/reject (#2635) 2019-10-15 23:04:31 +00:00
Vinh 3bfcc509d2 [CORL-678] Transition to eslint (#2634)
* chore: setup eslint

* chore: tslint checks with types & check for import order

* chore: complete eslint transition

* fix: tests

* fix: linting after rebase, faster lint for lint-staged

* chore: remove line

* fix: lint rules

* feat: add a11y linter and fix errors

* fix: tests
2019-10-15 22:56:38 +00:00
Cristian Dean b0e0ba6633 Remove canChangeStatus condition (#2631) 2019-10-15 22:28:16 +00:00
Cristian Dean 0fbfdac846 Add CHANGE_STORY_STATUS permission to Moderators (#2630) 2019-10-15 22:16:08 +00:00
Wyatt Johnson 8d08382aea feat: skip username validation for SSO users (#2627) 2019-10-15 21:56:47 +00:00
Tessa Thornton 8409dbb4ea ensure tab text doesn't get cut off if tab titles are long (#2619) 2019-10-15 20:49:37 +00:00
Sami Safatli bae88f476e Fix Typo (#2629) 2019-10-09 11:07:28 -04:00
Wyatt Johnson 918ba10867 [CORL-681] Configurable Staff Badges (#2620)
* feat: added configurable staff badge

* fix: removed ambigous styles
2019-10-04 20:22:31 +00:00
Wyatt Johnson e7ab0b27f9 fix: fixed looping effect (#2626) 2019-10-04 19:53:14 +00:00
Wyatt Johnson 191687335b [CORL-682] OIDC Email and setEmail mutation (#2623)
* fix: make email optional for oidc, fix schema perms

- fixes #2622

* fix: adjust defaults for heroku
2019-10-04 17:00:25 +00:00
Tessa Thornton 33487be0ce [CORL-469] keyboard shortcuts (#2588)
* make comments selectable in queue

* manage focus

* switching focus works

* keyboard navigation sort of works

* scroll into view

* prev, next, approve, reject

* shortcuts for queue switching

* ban modal?

* focus search bar with ctrl f

* keyboards shortcuts modal

* zen mode

* single comment view

* ban users from moderate cards

* clean up ts and lints

* update snaps

* add strings

* update smaps

* remove single comment view shortcut

* add single comment view to moderation queue

* clean up types

* fix deps for useEffect/useCallback hooks

* feat: added toggable help
2019-10-03 23:22:24 +00:00
Sami Safatli e56793f2fb Updated email templates (#2621) 2019-10-03 20:28:28 +00:00
Wyatt Johnson 6fe4646755 [CORL-668] User Comment Rate Limiting (#2616)
* feat: added user comment rate limiting

* fix: change 30 second to 3 second

* fix: moved file

* fix: services cleanup

* fix: changes based on review
2019-10-03 19:33:52 +00:00
Tessa Thornton dd45f46b19 update email templates (#2615) 2019-10-03 17:03:03 +00:00
Tessa Thornton 4a1492e88d [CORL-90] moderator notes (#2601)
* add moderator notes and mutations

* add create moderator note mutation

* add components for moderator notes

* style moderator notes

* update styles

* prevent warning about optimistic update unused fields

* fix incorrect comment

* fix optimistic response shape

* fix create note payload type

* add migration for moderator notes

* updates from feedback

* add border width for firefox
2019-10-02 18:41:53 +00:00
Wyatt Johnson 3a4eae87ad [CORL-661] Encode Story ID (#2614)
* fix: encode story ID's for URL

* fix: use shared method
2019-10-02 16:21:49 +00:00
Tessa Thornton fa3d442b36 [CORL-624] add note about word list case-sensitivity (#2607)
* add note about word list case-sensitivity:

* fix: removed unused keys

* fix: removed unused components

* fix: linting and key fix
2019-10-01 22:24:56 +00:00
Tessa Thornton 6edb411175 [CORL-650] remove duplicate text in invite form (#2608)
* remove duplicate text in invite form

* fix: reused message key

- message change consituted a grammar change
  that wasn't reflected in other translated languages
2019-10-01 20:54:50 +00:00
Tessa Thornton 6dbc724f31 [CORL-619] update search results copy (#2606)
* update search results copy

* fix: removed unused messages
2019-10-01 20:43:50 +00:00
Cristian Dean d0df6bc849 Add missing translations pt-BR (#2604) 2019-10-01 19:37:25 +00:00
Vinh 808b355a27 [CORL-181] Comment Count Injection (#2581)
* feat: inject comment counts

* fix: tests

* feat: integrate stream embed with coral counts

* chore: refactor constants

* test: test for count bundle

* test: test live comment count integration with stream embed

* feat: use defer

* fix: snapshot

* feat: auto add count.js in when calling Coral.createStreamEmbed

* fix: tests

* fix: rm duplicate test

* chore: remove unuse file
2019-10-01 19:22:15 +00:00
Wyatt Johnson 53fa5f43e5 [CORL-325] Comment Search API (#2578)
* feat: added comment search support

* fix: updated readme

* Update README.md
2019-10-01 18:57:50 +00:00
Wyatt Johnson c045f52daa [CORL-649] Migrations (#2597)
* feat: added migration framework

* chore: added premod user status migration

* feat: enhanced error handling of migrations

* fix: added missing argument from abstract method

* fix: another templating blunder

* fix: removed debug code

* feat: enhanced migration tracking

* fix: remove skipping migrations

* feat: moved indexing to migration system

* fix: linting
2019-10-01 16:00:27 +00:00
Cristian Dean b3b26bd9f3 Remove X-Powered-By header (#2605) 2019-10-01 15:35:32 +00:00
Wyatt Johnson 0af2fec31b fix: include tenantID in query operation to improve index use (#2596) 2019-09-30 16:25:55 +00:00
Wyatt Johnson 139c1eb01f fix: fixed readme to reflect correct docker image tag (#2593) 2019-09-30 16:15:31 +00:00
Tessa Thornton 941a385d53 [CORL-51] show comment revisions (#2590)
* add comment revisions

* reverse history order

* add strings

* update fixtures

* fix fixtures
2019-09-30 09:18:34 -04:00
Tessa Thornton e61e62a238 [CORL-174] premod user (#2572)
* add types for setting user premod

* add types, fields, and mutations for user premod

* enforce premoderation on users marked premod

* add user premod to dropdown

* update specs

* fix tests

* update premod workflow to match v4

* allow user filtering by premod status

* add premod user history to account actions drawer

* update snaps

* fix permissions on new premod fields

* update strings

* fix tests

* clean up formatting and copy

* fix: nullable fixes pre-migration
2019-09-25 21:31:12 +00:00
Tessa Thornton a5c3e94751 [CORL-544] use react helmet to add favicon declarations to head (#2595)
* use react helmet to add favicon declarations to head

* rename adminHead to head

* fix: case
2019-09-25 20:22:32 +00:00
Wyatt Johnson 5c1eb57d3e chore: cleaned up package.json (#2591) 2019-09-24 19:54:05 +00:00
Wyatt Johnson 7b5b260a18 chore: version bump (#2589) 2019-09-24 18:08:05 +00:00
Kim Gardner 1c24060f94 Add no comments copy in danish (#2586) 2019-09-24 17:57:31 +00:00
Vinh 4e4986fc03 fix: some form fields are not nullable (#2577) 2019-09-23 21:36:24 +00:00
Cristian Dean 37e39ff8c5 Avoid compute metrics with 404 status code (#2587) 2019-09-23 21:26:47 +00:00
Wyatt Johnson fe2d78f1f7 [CORl-640] User Registration Race (#2583)
*  fix: fixes user registration endpoints

- fixes #2579

* feat: cleanup from review

- Added seperate create function
- Moved some validation around

* fix: linting
2019-09-23 21:11:16 +00:00
Kim Gardner 43844bca18 Danish translation fixes (#2582)
* Update da files for email and errors

* Update da translations

* Add errors translations
2019-09-23 15:03:44 -04:00
Wyatt Johnson 6fb772944c feat: expanded allowed characters in usernames to support accents (#2585) 2019-09-23 14:55:38 -04:00
Tessa Thornton b3064c89de [CORL-623] add live updates for comments moving from premod to approved (#2571)
* add live updates for comments moving from premod to approved

* rename disposables

* check if comment is system withheld as well as premod to publish event
2019-09-20 20:11:05 +00:00
Nick Funk e4bfbbc1f8 Update COMMENT_DETECTED_SPAM marker text to Spam Detected (#2576)
Since this is used by Akismet and other spam detection tools, this
wording matches what Wordpress users will expect when seeing comments
detected as spam.

CORL-395
2019-09-20 19:48:00 +00:00
Wyatt Johnson 25c7f89b31 [CORL-620] Persisted Query Improvements (#2567)
* fix: resolves #2566

* fix: improved retry logic
2019-09-19 18:49:57 +00:00
Wyatt Johnson 921461008e [CORL-621] Auth Fixes (#2569)
* fix: resolve error with redirects

- fixes #2529

* fix: apply validations to username for oidc

* fix: converted components to function components

* fix: snapshots
2019-09-18 14:01:06 -04:00
Wyatt Johnson 64f102e6d4 [CORL-540] Logging improvements (#2565)
* fix: enhanced errors around story creation

* feat: enhanced child loggers

* feat: logging enhancements
2019-09-18 13:07:42 -04:00
Nick Funk 741739bc16 Un-feature a comment when it is rejected (#2555)
Also decrements the featured count for the story if rejected using the
in-stream moderation.

CORL-554
2019-09-18 12:17:31 -04:00
Sami Safatli 2cc32d4332 Translation pt-BR (#2542) 2019-09-18 11:47:34 -04:00
Tessa Thornton 867f0993cc [CORL 581] toggleable timestamp (#2561)
* toggle between relative and absolute timestamps

* make one common timestamp component

* remove unused timestamp component from /admin
2019-09-17 17:11:52 -04:00
Nick Funk 24a27ca958 Show the viewer's username in "moderated by" on moderation cards (#2573)
If a mod is looking at a card they themselves moderated, we want their
username to show up under "moderated by". This helps them recognize cards
they themselves moderated.

CORL-615
2019-09-17 10:59:03 -06:00
Nick Funk d03b180a30 Set createdAt in optimistic response during SuspendUserMutation steps (#2575)
CORL-616
2019-09-17 09:51:43 -06:00
Tessa Thornton 72ef65577b [CORL-605] remove user agent margin on safari search bar (#2559)
* remove user agent padding on safari search bar

* remove margin on other text fields too

* add margin reset to TextField
2019-09-13 20:29:01 +00:00
Tessa Thornton 8ef366f132 move toxic comment detection ahead of recent history in moderation pipeline (#2562) 2019-09-13 20:21:23 +00:00
Tessa Thornton 3114727410 [CORL-606] fix incorrect translation string (#2560)
* fix incorrect translation string

* update snap
2019-09-12 16:59:22 +00:00
Tessa Thornton a89dc4675a [CORL-516] forgot password flow in admin (#2558)
* add forgot password page and link to admin

* add translations

* update snaps

* fix: install patch

* fix: adapted localizations

* fix: added email type to email field

* fix: updated snapshots
2019-09-12 16:30:19 +00:00
Vinh 2559d1c0e8 fix: turn off bidi characters in favor of <bidi> tags (#2557) 2019-09-12 16:10:17 +00:00
Tessa Thornton ea3c1e3b58 fix safari user drawer display bugs (#2554) 2019-09-12 16:01:58 +00:00
Wyatt Johnson 457644c45b fix: fixed heroku install button (#2551) 2019-09-10 19:40:18 +00:00
Nick Funk 1b9a72c2f0 Show text field selection border on Firefox (#2550)
CORL-515
2019-09-10 19:28:03 +00:00
dependabot[bot] 47834a82bb chore(deps): bump lodash.merge from 4.6.1 to 4.6.2 (#2549)
Bumps [lodash.merge](https://github.com/lodash/lodash) from 4.6.1 to 4.6.2.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2019-09-10 16:14:30 +00:00
dependabot[bot] a8976a2141 chore(deps): bump lodash.template from 4.4.0 to 4.5.0 (#2548)
Bumps [lodash.template](https://github.com/lodash/lodash) from 4.4.0 to 4.5.0.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.4.0...4.5.0)

Signed-off-by: dependabot[bot] <support@github.com>
2019-09-10 15:57:40 +00:00
dependabot[bot] 718bd2758f chore(deps-dev): bump lodash-es from 4.17.11 to 4.17.14 (#2547)
Bumps [lodash-es](https://github.com/lodash/lodash) from 4.17.11 to 4.17.14.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.11...4.17.14)

Signed-off-by: dependabot[bot] <support@github.com>
2019-09-10 15:46:26 +00:00
Kim Gardner 721b2e7d67 [CORL-527] English copy tweaks (#2546)
* English copy tweaks

* fix: unit tests

* Use reactions to be more general

* fix: snapshots
2019-09-10 20:55:34 +07:00
Nick Funk f9e1bf144e [CORL-523] Document GraphQL mutations for managing accounts (#2544)
* Create preliminary endpoint for updating user names via API

CORL-523

* Create preliminary endpoint for updating user emails via API

CORL-523

* Create preliminary endpoint for deleting users via the API

CORL-523

* Hook up the delete, update email, and update user name endpoints

CORL-523

* Update readme to include preliminary GDPR endpoint instructions

CORL-523

* Uncomment limiters on updateEmail endpoint

My mistake leaving these commented while testing, should not have been committed.

CORL-523

* Run DocToc on the README to generate table of contents

CORL-523

* feat: enhanced documentation on account management edges

* fix: prevent double delete of user account

* fix: swapped order of params

* Update README.md

* Update README.md

* fix: remove unknown field title

* fix: remove unknown field title on story
2019-09-10 20:16:40 +07:00
Tessa Thornton e86e9d2c68 [CORL-586] update logos to match new branding (#2540)
* update logos to match new branding

* update styles of logo

* move version number to bottom of admin

* update specs
2019-09-10 09:02:35 -04:00
Kim Gardner d031df38f6 Add new da translations (#2543) 2019-09-10 10:27:53 +00:00
Nick Funk 8d368b03ec Add call out placeholder for comment stream empty state (#2545)
CORL-567
2019-09-10 10:18:36 +00:00
Tessa Thornton 3e64aa1193 [CORL-591] add reactions to my comments history (#2541)
* add reactions to my comments history

* remove unused field

* fix lanaguage for reactions
2019-09-10 09:52:05 +00:00
Tessa Thornton f84c3e4a60 [CORL-595] move notification settings update button (#2538)
* move notification settings update button

* move settings save button

* fix: version bump in package lock file
2019-09-10 08:13:14 +00:00
Tessa Thornton 36225e443b [CORL-556] Allow session length configuration (#2530)
* make session duration configurable by tenant

* fix expiration date calculation

* add form field for session length

* use constant for default session

* update spec

* update fixtures

* add missing translation

* update snap

* fix: use shared variable instead of hardcoded value for tests

* fix: adjusted missing expires in parameter
2019-09-10 08:06:07 +00:00
Tessa Thornton ed8be75774 fix styling on radio inputs for long labels (#2539) 2019-09-09 12:06:25 -04:00
Tessa Thornton f8cf4e94d3 change button style for change username box (#2537)
* change button style for change username box

* fix lints
2019-09-09 11:53:28 -04:00
Tessa Thornton 1a69f48087 show cancel button for ignore commenters config (#2536) 2019-09-09 11:39:55 -04:00
Nick Funk e0ab799c84 Add hover state to navigation links in Admin > Config (#2533) 2019-09-09 09:03:30 -06:00
Nick Funk aa8e1e2d7a [CORL-560] Update Admin > Config area to match new designs (#2514)
* Add solid background to Moderate > Config area headers

CORL-560

* Update Config > General section text and font weight

CORL-560

* Update ordering of Config > Moderation page items

CORL-560

* Update text and font weights in Configure > Moderation page

CORL-560

* Update text and font weight on Configure > Organization page

CORL-560

* Update text and font weight on Configure > Banned and Suspect Words page

CORL-560

* Update text and font weight on Configure > Authentication page

CORL-560

* Update text and font weights on Configure > Email page

CORL-560

* Update text and font weights on Configure > Advanced page

CORL-560

* Repair tests around the styling changes made to the moderation config area

CORL-560

* Fix capitalization on the Config > General page

CORL-560

* Make email checkbox light themed so its text shows up on new headers

CORL-560

* Update Header width to account for padding

Prevents it from sticking out over the edge of the page layout

CORL-560

* Align "Sort by" text to the drop down in Admin > Config > General > Reactions

CORL-560

* Update Admin > Config > General snapshot to adjust for vertical aligned text

Vertical flex aligned the "Sort by" text to a combo box in a previous changed

CORL-560

* Rename "Perspective Toxic Comment Filter" header to "Toxic Comment Filter"

CORL-560

* Use overlay scrolling if available to prevent scroll bar width popping

CORL-560

* Remove overflow-y: overlay

CORL-560

* Rename Akismet Spam Detection to Spam Detection

CORL-560

* Make all non-complex text fields full width in Admin > Config

CORL-560

* Remove Authentication Integrations header

Area underneath was empty

CORL-560

* Re-arrange SSO Key Field layout

CORL-560

* Create new typography types for bodyShort and fieldDescription

CORL-560

* Update Admin > Config to use bodyShort, fieldDescription typography

CORL-560

* Add a slight padding on InputLabel

CORL-560

* Add padding on either side of Admin > Config section content

CORL-560
2019-09-06 12:29:27 -06:00
Tessa Thornton d2af78d3d7 [CORL 568] more user drawer links (#2532)
* add drawer links for in reply to and moderated by

* fix ts

* update spec
2019-09-06 14:01:19 -04:00
Tessa Thornton 9bd38db61a allow organization url config (#2531) 2019-09-06 13:52:48 -04:00
Tessa Thornton ac203607ae [CORL-561, CORL-562] Comment stream and account profile styling cleanup (#2517)
* update styles for comment stream

* more comment styling fixes

* reconfigure settings tab and account settings in stream

* add new button variant

* style settings tab

* style change username/email/password

* clean up settings tab

* integrate account deletion to account settings

* update comment history styles

* more history comment styles

* update snaps

* move userbox

* fix tests

* fix delete account tests

* update message box styling

* update snap

* update snaps

* fix lints

* update snaps again

* userbox styles

* history comment styling

* update snaps

* fix ts

* Integrate notifications settings

* clean up account setting styles

* update snaps and tests

* fix lints

* fix: PENDING_DELETION permissions

* chore: sync stable classnames, rename settings to account

* simplify css

* update snaps
2019-09-06 13:41:55 -04:00
Nick Funk e49905c5ad [CORL-571] Filter empty strings from banned and suspect word lists (#2521)
* Filter empty strings from banned and suspect word lists

CORL-571

* fix: changed form
2019-09-05 21:36:09 +00:00
Kim Gardner 15d41f1ae8 Typo fixes and bump Coral version to 5.0.0 (#2528)
* Typo fixes and bump package version

* fix: tests
2019-09-05 17:15:40 -04:00
Wyatt Johnson 60a1f43d51 fix: wrap queries in quotes (#2527) 2019-09-05 18:06:12 +00:00
Wyatt Johnson 3c9ebcf1a1 feat: added moderation email notifications (#2525) 2019-09-05 17:33:49 +00:00
Wyatt Johnson e35978096f feat: added featured comment notifications (#2524) 2019-09-05 17:10:03 +00:00
Wyatt Johnson 04c56b3fb5 [CORL-406] Tenant Locale Selection (#2450)
* feat: added preload config

* feat: support changing locale

* fix: name case

* fix: removed unused code

* feat: added translations for default reactions

* fix: do not translate icon

* fix: shorter i18n keys
2019-09-05 17:02:06 +00:00
Tessa Thornton 5bf4f22931 [CORL-392] add settings config for story configuration on advanced panel (#2499)
* add settings config for story configuration on advanced panel

* include external link in proxy description

* update copy

* update copy

* fix lints

* update snap
2019-09-05 11:35:16 -04:00
Tessa Thornton 1550377f39 update durationfield component to not use lifecycle methods (#2502)
* update durationfield component to not use lifecycle methods

* fix: default param cleanup
2019-09-05 09:51:51 -04:00
Wyatt Johnson efea0e8e1c [CORL-498, CORL-495, CORL-539, CORL-496, CORL-494] Email Notifications Support & Framework (#2498)
* chore: renamed old templates

* feat: initial notifications support

* feat: email enhancements

* fix: linting

* feat: initial digesting beheviour

* feat: added notification configuration

* feat: added unsubscribe routes

* fix: fixed failing snapshots/tests bc random ids

* feat: adjusted the save beheviour, added tests

* feat: added tests

* feat: added staff replies

* feat: renamed E-Mail to Email

* feat: enhanced cron processing

* fix: linting + updating tests

* feat: enhanced cron context

* fix: added staff replies back in
2019-09-05 07:02:26 +00:00
Wyatt Johnson 0fad1070a6 fix: addressed issue with count doubling (#2518) 2019-09-04 17:33:42 -04:00
Wyatt Johnson ac1ed45e4b [CORL-588] Download and token fixes (#2519)
* fix: fixed up download handler to improve error handling

* fix: fixed invalid expiry date being set

* fix: nick found bug
2019-09-04 17:23:59 -04:00
Nick Funk a696ef9be5 [CORL-572] Filter rejected comments from reply counts on user comment history (#2516)
* Filter rejected comments from reply counts on user comment history

CORL-572

* fix: use cache to improve performance

* fix: added TODO
2019-09-04 12:43:00 -04:00
Vinh aa32fcaea6 [CORL-550] More stable css classes (#2515)
* feat: more stable css classes

* chore: renamee ValidationMessageHelper to FormValidationMessage

* chore: Rename FormValidationMessage to FieldValidationMessage

* feat: more css classes, icons use <i> tag

* feat: stable classnames for gdpr features

* feat: more css stable classnames
2019-09-04 12:34:42 -04:00
Tessa Thornton fcf3640adc [CORL-439] Allow admins to configure GDPR features (#2497)
* allow tenant admins to modify commenter account features

* style account features configuration

* prevent changing username or downloading comments based on tenant settings

* update fixtures and snaps

* add i18n strings

* update snap
2019-09-03 16:56:36 -06:00
Nick Funk 46f0d08cf7 [CORL-236] Allow user to delete their account (#2495)
* Create preliminary account deletion program flow

Does not validate password as of yet.
Does not submit a mutation to begin deletion scheduling.

CORL-236

* Create preliminary account deletion confirmation form and schema mutation

Hooks up validation and error handling to confirm the user's
password during account deletion.

Scheduling the account deletion is not implemented yet.

CORL-236

* Update requestAccountDeletion to set the user's scheduled deletion date

CORL-236

* Create preliminary cron job for scheduled account deletions

CORL-236

* Show tombstoned comments when the user has deleted their account and comments

CORL-236

* Pull in client locales for date formatting on account deletion steps

CORL-236

* Update more areas due to comment revision being nullable

CORL-236

* Allow users to cancel their account deletion request

CORL-236

* Show account deletion callout at the top of the profile stream tab

CORL-236

* Further comments and edge case handling for deleted comments site wide

CORL-236

* Show account deletion callout in the comments stream tab

CORL-236

* Send out emails during relevant account deletion events

- Confirm deletion requested
- Cancellation of deletion request
- Account deletion completed

CORL-236

* Unset emails when deleting user accounts instead of setting to null

Fixes email index collision error from setting multiple emails to null.

CORL-236

* Show organization email in account deletion steps

CORL-236

* Mark comments as deleted so that we can properly tombstone them in the stream

CORL-236

* Fix improper templating for account deletion completed emails

Sub organization name in where previously we were using the account username.

CORL-236

* Disable user drawer and display deleted flag on users in community

CORL-236

* Check for author before passing inReplyTo in ModerateCardContainer

Sometimes null when user is deleted.

CORL-236

* Tombstone moderate cards when the underlying comment is deleted

CORL-236

* Re-add deleted field to CommentContainer fragment

CORL-236

* Disable commenting or replying when user is scheduled for deletion

CORL-236

* Add snapshot tests around deletion states for comment cards and forms

CORL-236

* Add tests around the account deletion steps

CORL-236

* Set account deletion to run every 30 minutes

CORL-236

* Rename DELETION authorization to PENDING_DELETION

CORL-236

* Change log message for when account deletion tasks finds no more users to delete

CORL-236

* Update snapshots to handle proper deleted flag for scheduled deleting users

CORL-236

* Fix import ordering on various files to match coding standards

CORL-236

* Specify tenantID when querying for user during deletion

CORL-236

* Add back missing clientMutationId on cancelAccountDeletion mutation

CORL-236

* Use $unset to clear the scheduledDeletionDate

CORL-236

* Simplify create of scheduledAccountDeletion CRON function

CORL-236

* Rename and organize scheduledTask creation logic

- Rename startCronJobs to startScheduledTasks
- Return an object of named ScheduledTask items instead of an array of tasks

CORL-236

* Add comment about returning updated user when clearing scheduledDeletionDate

CORL-236

* Remove unnecessary array around tenant.locale in dateTime formatter

CORL-236

* Remove unnecessary non-null enforcement on tenant.id

CORL-236

* Remove string templated React element content

CORL-236

* Move english translated validation error to correct language file

CORL-236

* Simplify rendering of DeletionRequestCallout when deletionDate isn't available

CORL-236

* Use ternary expression instead of multiple {bool && (...)} checks

CORL-236

* Use classes object with line child style to style the step bar

CORL-236

* Update StepBar so that children of Step nodes can be undefined

Allows us to not have to insert empty placeholder strings/elements when
we want to have an empty step node.

CORL-236

* Remove use of luxon from account deletion request callouts for now

We need to decide if the extra 15KB from luxon is worth it in our bundles
before making this change.

CORL-236

* Remove unnecessary embed/deleteAccount endpoint

CORL-236

* Move DeleteAccountModal contents into a separate component

Cleans up the state management when user closes the modal and we
need the modal steps to restart.

CORL-236

* Remove localization around delete account button icon

CORL-236

* Use inline `collection(mongo)` in clearing and setting deletion date

CORL-236

* Simplify form state validation on account deletion confirm page

CORL-236

* Add todo to iterate over tenants in account deletion

CORL-236

* Remove punctuation and add context to logger messages during account deletion

CORL-236

* Break out mongo collection initialization into a shared object

Also adds a try catch error handling around the scheduled deletion
operations. Prevents errors from taking the server down.

CORL-236

* Add TODO to extract out a common formatter for date times

CORL-236

* Update translations for account deletion details

CORL-236

* Update translations for account deletion completion page

CORL-236

* During scheduled deletion, when locking deletion date, return modified user

CORL-236

* Remove un-necessary fragment container around deletion steps component

CORL-236

* Move preventSubmit outside of render function on ConfirmPage

CORL-236

* Convert collections into object of Mongo db collection initializers

Allows us to only initialize the collections we need with our
Mongo instance.

CORL-236

* Remove un-necessary lambda wrapping around collections initializer functions

CORL-236
2019-09-03 16:38:43 -06:00
Nick Funk fc039f8186 Filter null comments coming into moderation queues to prevent white screening (#2509)
Wyatt and I are not sure why comments are coming back null, but this should
keep the moderation area from blanking out when it errors handling nulled out
comments crashing the queue.

FIXME tag added with Wyatt/Nick as this is only a patching fix and does
not resolve the underlying issue.
2019-08-28 14:21:35 -04:00
Nick Funk 7e7ce9fbd0 Ensure user drawer status is padded properly when a user selects themselves (#2506)
CORL-531
2019-08-26 11:51:54 -04:00
Tessa Thornton 0e37a474fa downloads by user id (#2500) 2019-08-23 16:05:53 -04:00
Tessa Thornton ffecd5e981 change routing for auth and auth/callback (#2493) 2019-08-23 15:00:36 -04:00
Tessa Thornton 07df7ac182 show email verification resend (#2492)
* always show users with unverified email address the prompt and resend option

* udpate snaps

* update snap
2019-08-23 14:16:08 -04:00
Wyatt Johnson 2edabf44c7 feat: added closedAt to createStory mutation (#2496) 2019-08-23 14:01:43 -04:00
Wyatt Johnson 1c75b7659d fix: added more strict handling of input for URL config (#2478) 2019-08-23 13:51:13 -04:00
Nick Funk 6d803b0179 [CORL-382] Allow moderators to ban a commenter from the comment stream (#2385)
* Add a ban user action to the stream moderation drop down

CORL-382

* Show banned stated for user in stream moderation drop down

If the user is actively banned, the ban user option will be
disabled and show a status of "banned".

CORL-382

* Create utility for generating random stories, comments, and users

CORL-382

* Add ban and suspension values to baseUser in fixtures

CORL-382

* Updated banned.spec.tsx to use new test utilities for generating fixture data

CORL-382

* Prevent users from being able to ban themselves in the moderation dropdown

CORL-382

* Kill optimistic response errors for comment mutations

Set the author.status.current to an empty array so it stops
complaining about it being unused.

CORL-382

* Rename util in tests to helpers/fixture.ts

CORL-382

* Remove unused import from CreateCommentReplyMutation.ts

CORL-382

* Put back the optimistic ban responses into comment mutations

The warnings spewing out during tests are false, for further
detail please see: https://github.com/facebook/relay/pull/2760

CORL-382

* Denormalize generated stories and comments

CORL-382

* Clean up import ordering in ModerationActionsContainer.tsx

CORL-382

* Inject appropriate scoped items into callbacks for moderation dropdown

CORL-382

* Set optimistic response author status from known viewer status

CORL-382

* fix: Make in stream banning work

* feat: Send translated email + reject comment + fix tests

* test: add feature test

* feat: add copy mentioning comment rejection

* chore: improve loading state

* chore: add tiny comment
2019-08-22 22:47:30 +07:00
Vinh f3e615a610 [CORL-503] More stable CSS classNames (#2477)
* feat: add more css classnames

* fix: snapshots
2019-08-20 19:22:58 +00:00
Vinh b9f9e9b2d4 [CORL-491] Support event alias loginPrompt (#2474)
* feat: support event alias loginPrompt

* lint: import ordering
2019-08-21 00:58:44 +07:00
Tessa ThorntonandWyatt Johnson 7809cd3d68 [CORL-476] Add badges to user from SSO token (#2470)
* fix: bug in lookup not checking object properly before accessing

* fix: Recursive types not handling optional arrays

* add user badges component

* create user badges from sso token

* update badges type

* revert src/core/client/embed/index.html

* remove duplicated line

* add user badges component

* create user badges from sso token

* revert src/core/client/embed/index.html

* remove duplicated line

* fix types

* fix tests and snaps

* add user badges to user drawer

* update snaps

* update readme

* [CORL-476] add user role from SSO token (#2475)

* add role from token

* use joi to validate role values

Co-Authored-By: Wyatt Johnson <wyattjoh@gmail.com>

* simplify sso.role validation

* add test for invalid role in sso token
2019-08-20 13:15:59 -04:00
Tessa Thornton add5224338 [CORL-504] Configure reaction language (#2490)
* add reaction config to settings

* fix reaction preview styles

* update snaps and fixtures

* add i18n strings

* update spec

* ensure dynamic value shows in reaction config preview
2019-08-20 13:03:25 -04:00
Kim Gardner 8dae0feb52 Updates version number (#2488)
* Updates version number

* fix: updated version in package-lock.json
2019-08-20 12:47:30 -04:00
Tessa Thornton ee93267d97 [CORL-220] Change email address (#2461)
* change email form

* add update email mutation

* validate new email addresses

* add email verification callout

* add translation strings

* fix submit button logic

* rename model methods

* style email verifcation box

* resend email verificatoin button

* show success message on email resend

* update user profile email

* fix duplicate import

* add change email spec

* fix profile spacing

* update snapshots

* update snaps

* add preventSubmit function to email change component

* update business logic for profile updating

* update logic for when users can update email or username

* check for less specific duplicate email error

* prevent sso-only users from editing email

* only allow email and username edit if enabeld in local profile

* use generic server error for cannot update profile

* remove merge conflict

* fix tests

* extract logic to get auth integrations

* fix merge error
2019-08-20 12:28:14 -04:00
Tessa Thornton 0f02b05689 update readme with info about watchman (#2486)
* update readme with info about watchman

* run doctoc

* fix: reordered sections

* run doctoc
2019-08-20 12:14:49 -04:00
Tessa Thornton 3baf4e96f5 prevent improper sort menu styling on ios (#2468) 2019-08-20 11:37:09 -04:00
Wyatt Johnson 8d91bd7dd1 fix: corrected invalid copy (#2479) 2019-08-19 12:57:03 -04:00
Vinh d400c1518c fix: temporary use min-height for single comment view to not cutoff report dialog (#2476) 2019-08-19 12:34:39 -04:00
Wyatt Johnson d9ad1b2ee4 [CORL-529] Proxy (#2473)
* fix: implemented trust configuration

* fix: removed debugging code

* fix: increase hash size (#2484)
2019-08-16 21:03:20 +00:00
Wyatt Johnson 035ee73edd [CORL-543] Persisted Query Fix (#2482)
* fix: addressed query loading bug

* fix: refine logging
2019-08-16 16:09:59 +00:00
Vinh 43b6a2cdcd [CORL-149] Persisted Queries (#2445)
* feat: enable persisted queries on the client

* fix: use `id` inside websocket message

* feat: initial server support for PQ

* feat: deeper server support

* feat: abstracted persisted query replacing logic
2019-08-15 21:03:32 +00:00
Vinh 635e740fc0 [next] Fix lookup and recursive types (#2472)
* fix: bug in lookup not checking object properly before accessing

* fix: Recursive types not handling optional arrays
2019-08-15 01:07:49 +07:00
Tessa Thornton c37ee12e57 Bugfix/userdrawer changestatus (#2469)
* fix date formatting breaking user details popvoer

* update mutation return fields for user status changes
2019-08-14 08:32:03 -04:00
Wyatt Johnson e76fddd13c [next] Added trust proxy configuration (#2471)
* feat: added trust proxy configuration

* chore: version bump
2019-08-13 15:41:50 -04:00
Tessa Thornton abb793b16a fix date formatting breaking user details popvoer (#2465) 2019-08-12 19:39:55 -04:00
Wyatt Johnson aa873559d0 fix: fixed forgot password button (#2467) 2019-08-12 19:32:50 -04:00
Wyatt Johnson 0296041ce8 fix: build sourcemaps in production (#2466) 2019-08-12 19:12:34 -04:00
Wyatt Johnson 1a62203d45 fix: adjusted build to also compile locales (#2464) 2019-08-12 17:47:30 -04:00
Nick Funk 0eddf9619f Center sign in popovers over the browser window so they are easily visible (#2457)
* Center sign in popovers in the browser window so they are always visible

* Set popover window feature by default using interfaced parameters

* Update snapshots to account for feature parameters on popup windows

* Lowercase the bar in menubar for Popup so it matches window api

* fix: externalized feature reconciliation
2019-08-12 17:03:09 -04:00
Kim Gardner fda9f40616 Bump version to beta 6 (#2462) 2019-08-12 16:52:31 -04:00
Wyatt Johnson 8d78677154 [CORL-219] Common Tweaks (#2460)
* fix: i18n fixes

* fix: figma tweaks

* fix: tweaked date manipulation logic

* fix: linting

* fix: linting

* fix: tests
2019-08-12 16:44:35 -04:00
Wyatt Johnson 7900b02c40 fix: moved url-regex to deps not devDeps (#2463) 2019-08-12 16:36:46 -04:00
Wyatt Johnson 956014b53f [next] i18n cleanup (#2456)
* fix: removed english translations from es files

* feat: improved github messaging
2019-08-12 14:55:29 -04:00
Wyatt Johnson 86bba73919 [CORL-513] Permitted Domain Fixes (#2455)
* fix: adjusted allowed domains configuration

* fix: removed unused logic around allowed domains prefixing

* fix: removed unused localizations

* fix: fixed signup flow
2019-08-12 18:02:15 +00:00
Tessa Thornton c245e9ba74 [CORL-219] Change username (#2452)
* add types for username updates

* add update username methods

* add types and resolvers for username history

* connect frontend to username history and change history mutation

* style update username box

* show username changes in user history drawer

* new users have 1 username entry

* add translations for username change

* allow new users to change username once

* add tests for change username ui

* send email to users after username change

* add types to tests

* treat status.username.history as mandatory

* remove hardcoded 14 day username update frequency

* use framework translation directives

* add account history action component

* clean up strings

* fix imports in change username container

* fix templates

* rename update username methods

* fix nunjucks formatting

* add warning if user missing email

* add fixme

* fix spacing
2019-08-12 10:45:49 -04:00
Nick Funk e3f24811fc [CORL-235] Allow a user to download their comment history (#2449)
* Create preliminary UI for requesting a user's comment history

CORL-235

* Create preliminary download handler

CORL-235

* Create preliminary CSV exporter for downloading user's comments

CORL-235

* Create preliminary download comments flow with email and landing page

User can download their comments after requesting via an email.
Does not include limiters preventing users from spamming the download flow.

CORL-235

* Style the download comments landing page to match the spec

CORL-235

* Update the profile download comments to respect 14 day download limit

CORL-235

* Hook up request limiter on the comment download end points.

CORL-235

* Create sorry dialogue for when the download comments link is invalid/expired

CORL-235

* Create loading spinner for comment download landing page

CORL-235

* Create an optimistic updater for requesting a comment download

CORL-235

* Add localization for download comments landing page's sorry text

CORL-235

* Adjust downloaded comment format and contents

- Format the date to match tenant local
- Format dates to be using numeric values with date and time
- Add comment URL to exported CSV record

CORL-235

* Rename `createCSV` to `createExportedContent` in the download handler

CORL-235

* Make comment download callout full width and left justified

CORL-235

* Update stream snapshots to account for the download comments changes

CORL-235

* Update snapshots to correspond to download comments changes

CORL-235

* Add date filter to the comment download

Will filter by date of the issued token to only include comments before
that time.

CORL-235

* Clean up the layout of the list items in the download comments landing page

CORL-235

* fix: localization fixes

* fix: formatting

* fix: download file enhancements

* fix: addressed download request loophole

* Update snapshots to address button changes on download comments landing page

CORL-235

* Simplify download comment landing page components

CORL-235

* Remove max-width calc's on account main layout and set them in child views

Sets the max-width settings on the download route since it's custom
set on all the othere routes (email and password).

CORL-235

* Bump npm version in package.json

CORL-235
2019-08-09 21:17:24 +00:00
Wyatt Johnson 11fc3697ed fix: addressed chrome browser issues (#2453) 2019-08-09 20:24:54 +00:00
Kim Gardner 22188e7c31 [CORL-478] Porting over Danish translations to v5 (#2448)
* WIP add missing Spanish translations

* WIP porting over Danish

* Update

* WIP

* Complete stream translations

* Admin translations WIP

* Admin WIP

* Fix lint issue

* Typo

* Fix

* Fix lint errors

* More Admin translations

* Fix es translation

* Add Account translations

* Add translations for Framework

* Admin WIP continued

* Finished Admin translations

* Remove unncessary translations for now

* Missed merge conflict

* Missing translations

* Translation cleanup
2019-08-08 15:22:55 -04:00
Wyatt Johnson 4c65d43954 [CORL-404] Recent Comment History (#2354)
* feat: initial support for auto pre-moderation

* chore: refactor collection access

* fix: linting

* fix: rebasing issue

* fix: exported helpers

* feat: added extensions, lintd

* fix: rebase fix

* feat: renamed automaticPreModeration to recentCommentHistory

* feat: initial implementation of admin config

* feat: support recent history markers

* feat: rename visible to published

* feat: reworked history drawer

* chore: extracted tooltip

* feat: implemented user drawer

* fix: fixed translation key

* fix: resolved issue with NaN
2019-08-08 18:18:18 +00:00
Tessa Thornton ff964b54a3 [CORL-97] Add user status details popover to user drawer (#2431)
* show tooltip for user status details

* localize strings

* fix merge conflict

* fix date formatting in user details popover

* update snapshot

* fix rebase
2019-08-07 16:22:47 -04:00
Vinh 3f307002e2 [CORL-391] Fix jankiness when opening reply box of last comment in Stream (#2447)
* feat: add useResizeObserver hook

* fix: stream jumps when clicking on the last reply button when scrolled to the bottom

* chore: use resize observer in auth popup and simplify resize handling

* fix: snapshots and remove resize observer from stream

* fix: focus unmounted rte
2019-08-07 15:26:30 -04:00
Vinh b41ddbc22f [next] Add new typography design tokens (#2451)
* feat: add new typography design tokens

* fix: revert renaming fontFamilySansSerif
2019-08-07 14:09:07 -04:00
Kim Gardner c979dfd7d5 [CORL-487] Update Spanish translations for v5 (#2444)
* WIP add missing Spanish translations

* Add more stream translations

* More stream translations

* Add rest of translations for stream

* fix: accessing undefined message
2019-08-07 02:58:59 +07:00
Bogdan Deren 1f07c23dea [next] Dutch locale for stream (#2439)
* stream dutch translation

* added nl-NL locale

* missed line

* Dutch translation for framework (date/time)
2019-08-05 16:39:06 -04:00
Tessa Thornton 3694794b01 [CORL-458] open user drawer when flagger username clicked (#2436)
* open user drawer when flagger useranme clicked

* fix comment author click

* update style of flagger button

* update snapshots

* chore: use round-corners design token
2019-08-05 18:02:13 +07:00
Tessa Thornton 5df2de6afc [CORL-156] Manage user suspension status (#2419)
* wire up suspension modal

* show user suspended callout on stream

* prevent comment actions for suspended users

* set default to 3 hour suspension

* add message field to suspension

* allow custom message for suspension

* show suspend success modal

* fix type errors

* remove unused code

* update styles

* fix fixture for streams

* add suspension ui tests

* fix types

* remove warnings?

* remove snapshot

* fix merge conflicts

* allow custom email message when banning users

* fix typo

* correct message type

* use final-form in suspend modal

* refactor suspend modal to use final-form

* refactor ban modal to use final-form

* refactor userStatusChangeContainer to use useCallback

* feat: improve translated form

* fix: addressed issue caused by i18n refactor

* update getMessage to accept arguments, remove format method

* translate suspend info

* change hour format

* make message a mandatory input for suspend and ban user

* fix types in user table

* Revert "fix types in user table"

This reverts commit d396e90b88bb1bd354c5cdbdd72b6d8f1ab72929.

* fix types for user table

* fix: small review tweaks
2019-08-02 21:16:21 +00:00
Wyatt Johnson 4e548e8fbf fix: addresses issue with creating stories (#2442) 2019-08-02 17:02:47 -04:00
Tessa Thornton fdc6afec42 prevent zooming on filter select (#2434) 2019-08-02 16:55:13 -04:00
Wyatt Johnson 836a2267bf [CORL-445] Change Password (#2426)
* fix: reversed `new-password` autocomplete option

* feat: initial implementation

* fix: localization and testing

* fix: updated snapshot
2019-08-01 22:08:14 +00:00
Wyatt Johnson 290ceee8e9 chore: form standardization (#2425) 2019-08-01 21:59:05 +00:00
Wyatt Johnson 1c5c82286e [CORL-474] Harmonize SSO User ID's (#2440)
* feat: harmonized user id's

* fix: doctoc
2019-08-01 17:30:33 -04:00
Tessa Thornton 3d9c94d36a [CORL-456] add empty state for comment history (#2432)
* add empty state for comment history

* localize strings

* fix string casing
2019-08-01 17:18:12 -04:00
Wyatt Johnson 3a8e4e6429 [next] User Creation Improvements (#2438)
* feat: added user insert improvements

* fix: added redirect filter support

* fix: addressed some bugs with sign in
2019-07-31 13:01:10 -04:00
Tessa Thornton 7a8a9f425a fix dismiss button being cut off in pre-mod notice (#2433) 2019-07-31 11:12:25 -04:00
Cristian Dean b108faed5e Fix logout action when no local authentication provider (#2435) 2019-07-30 23:39:30 +00:00
Wyatt Johnson 7b7cec5258 feat: switch to comments tab if on featured (#2437) 2019-07-30 18:48:52 -04:00
Wyatt Johnson 8e130aa5d4 fix: fixed bug with token generation and verification (#2427) 2019-07-29 23:01:57 +00:00
Nick Funk c069bbe923 [CORL-234] Show user's account history in the user drawer (#2415)
* Create unit tests around the user drawer

CORL-443

* Update comment fixture generation to include reason metadata, action counts

CORL-443

* Add a user status drop down to the user history drawer

CORL-227

* Refactor UserHistoryDrawer tabs and queries for consistency

CORL-234

* Create and empty account history tab in the user drawer

CORL-234

* Create preliminary account history tab queries

Massages the suspension and ban history together into a usable format.
Sorts the ban and suspension history by date, ascending.
Ready to process the history data into components to make up the future
table records.

CORL-234

* Style the user drawer account history records into table format

CORL-234

* Fix typo in admin localization around banned text

CORL-234

* Update suspension record to support templated insertion of timespan

CORL-234

* Create localized parsing of suspension times for account history

CORL-234

* Handle errors when loading the user drawer account history

CORL-234

* Adjust margins of tab content relative to the tab controls

CORL-234

* Extract suspension record time calculations out of component

CORL-234

* Handle when a user has no account history

CORL-234

* Adjust line height to prevent cutting off usernames in community table

CORL-234

* Override hover behaviour for user drawer account history rows

CORL-234

* Adjust default column widths in user drawer - account history

CORL-234

* Use react node type instead of any for component substitution

CORL-234

* Rename TabType to UserTabs

CORL-234

* Use nullable type instead of strict typing using OR's

CORL-234

* Set UserStatusChangeContainer fullWidth default to false

CORL-234

* Combine timed suspension localization into one Localized element

CORL-234

* fix: useMemo on history bar

* Rename UserHistoryDrawerAccountHistory to UserDrawerAccountHistory

Cleans up the agglutination's on these file names.

Probably means we should rename UserHistoryDrawer... to
UserDrawer... everywhere. Will get to that later.

CORL-234

* Reorder imports to match alphabetization preferences

CORL-234

* feat: simplication to action table

* feat: visual tweaks

* chore: added notes for deprecating
2019-07-29 22:54:53 +00:00
Nick Funk 06a3e4e0a7 User drawer details tweaks (#2423)
* Add alternative text titles to the user drawer icons

* Tweak padding on user status popover

* Add default alt text for user drawer icons
2019-07-29 22:45:12 +00:00
Wyatt Johnson 0c5cdc9f3c [next] Version Bump + Docs Update (#2428)
* chore: version bump, updated email docs

* chore: updated package-lock.json
2019-07-29 18:02:47 +00:00
Cristian Dean 1dba761b40 [Next] Add auto redirect when there is a single enabled auth integration (#2422)
* Add auto redirect when there is a single enabled auth integration

* fix: small syntax cleanup
2019-07-29 16:35:19 +00:00
Wyatt Johnson 14e225e26f [CORL-435] Stable CSS Classes (#2414)
* feat: fixed import issue with react

* feat: initial impl

* fix: review
2019-07-26 22:43:42 +00:00
Wyatt Johnson 4f640826de fix: ensure that number is whole (#2416) 2019-07-25 00:38:46 +00:00
Wyatt Johnson b1732f8a00 feat: initial implementation (#2409) 2019-07-23 17:20:40 +00:00
Cristian Dean fc464bd7c9 Fix pt-BR translations (#2410) 2019-07-23 00:27:49 +01:00
Nick Funk eabf1b28a2 [CORL-227] Add user status actions to the user history drawer (#2407)
* Create unit tests around the user drawer

CORL-443

* Update comment fixture generation to include reason metadata, action counts

CORL-443

* Move testRenderer construction outside of act() operations

CORL-443

* Add a user status drop down to the user history drawer

CORL-227

* Update snapshots to handle status change dropdown full width styling

In other areas we want to override the full width: 100% styling of the
status change drop down. By default, we turn it on. This added a class
to the element that needs to be updated in the snapshots.

CORL-227

* Ignore the user history drawer tests for the time being

Getting an Invariant Violation: Unable to find node on an unmounted component.
when the UserStatusChangeContainer is present on the UserHistoryDrawerQuery.

CORL-227

* Align the user drawer status label and button text

CORL-227

* Update community tests to handle removal of dot icon from user status

CORL-227
2019-07-22 10:57:33 -06:00
Wyatt Johnson 0c97ba0f4a [CORL-450] Embed Code copy (#2411)
* fix: adjusted copy in embed code

* fix: adjusted translation message API

* fix: fixed tests
2019-07-19 15:55:29 +00:00
Nick Funk d73bdc7eec [CORL-443] Add user drawer to the community section of the moderation area (#2401)
* Move the user history drawer into shared location that all admin routes access

CORL-443

* Move the moderate card to the shared components for admin

CORL-443

* Add a user drawer to the community area of the admin section

CORL-443

* Touch up missing tabs in UserRow.css

CORL-443

* Create unit tests around the user drawer

CORL-443

* Move toxicity label to new shared component location to fix rebase

CORL-443

* Update comment fixture generation to include reason metadata, action counts

CORL-443

* Rename userDrawerID to userDrawerUserID

CORL-443

* Clean up imports on user drawer unit tests

CORL-443

* Add coral-test to the jest config paths

CORL-443

* Add todo around creating predictable date times for test fixtures

CORL-443

* Move testRenderer construction outside of act() operations

CORL-443
2019-07-16 14:53:55 -06:00
Wyatt Johnson bdb57aef7f feat: default to the featured tab when available (#2402) 2019-07-16 16:32:33 +00:00
Wyatt Johnson d312d380ae [CORL-436] Embed Code (#2398)
* feat: initial implementation

* fix: moved embed configuration to advanced

* feat: added copy button to embed code

* fix: removing unused line
2019-07-12 22:35:09 +00:00
Wyatt Johnson bc0b0d0339 [next] Email/Password Improvements (#2400)
* feat: improved some password/email fields

- All secret fields are now "hidden" by default
- All email fields now have autocomplete/spellcheck disabled with
  correct typings

* feat: add additional email validation
2019-07-12 21:07:14 +00:00
Wyatt Johnson f95b705585 feat: added toxic labels (#2396) 2019-07-12 20:41:34 +00:00
Wyatt Johnson b5b9cb7e2f feat: support static uri's with paths (#2399) 2019-07-12 19:11:11 +00:00
Nick Funk 0b20542f05 [CORL-233] Create a user drawer with comment streams (#2395)
* Create preliminary user history drawer with user fragment

CORL-233

* Set font size for the user details to match spec at 16 px

CORL-233

* Update username font style/size, add horizontal rule for user history drawer

CORL-233

* Set user detail min width to align the copy buttons in user history drawer

CORL-233

* Set min width of the user history drawer to match spec

CORL-233

* Remove min width for detail area and card to allow responsive sizing

Allows the copy buttons to sit just to the right of the user
details text.

CORL-233

* Update snapshots to handle clickable username in moderation comment cards

CORL-233

* Create initial pagination of all comments in user history drawer

Shows all comments with a rubbish "load more" button as we haven't
decided if we want to do a load more button or infinite scrolling
solution.

Flex layout helps it create a scroll bar when needed to traverse through
the comment stream.

CORL-233

* Create preliminary user history drawer with user fragment

CORL-233

* Set font size for the user details to match spec at 16 px

CORL-233

* Update username font style/size, add horizontal rule for user history drawer

CORL-233

* Set user detail min width to align the copy buttons in user history drawer

CORL-233

* Set min width of the user history drawer to match spec

CORL-233

* Remove min width for detail area and card to allow responsive sizing

Allows the copy buttons to sit just to the right of the user
details text.

CORL-233

* Update snapshots to handle clickable username in moderation comment cards

CORL-233

* Let the user history drawer fill full height of page

CORL-233

* Add a close button to the user history drawer

CORL-233

* Preliminarily get all and rejected comments showing in the user drawer

CORL-233

* Create preliminary user history drawer with user fragment

CORL-233

* Set font size for the user details to match spec at 16 px

CORL-233

* Update username font style/size, add horizontal rule for user history drawer

CORL-233

* Set user detail min width to align the copy buttons in user history drawer

CORL-233

* Set min width of the user history drawer to match spec

CORL-233

* Remove min width for detail area and card to allow responsive sizing

Allows the copy buttons to sit just to the right of the user
details text.

CORL-233

* Update snapshots to handle clickable username in moderation comment cards

CORL-233

* Let the user history drawer fill full height of page

CORL-233

* Add a close button to the user history drawer

CORL-233

* Set the user history drawer to fill full height of window

CORL-233

* Convert ModerateCardContainer to a FunctionComponent

CORL-233

* Show spinner while all comments are loading in user history drawer

CORL-233

* Style tabs within the user history drawer to be secondary

CORL-233

* Center load more button in the comments for the user history drawer

CORL-233

* Fix scroll bars on nested flexboxes in the user history drawer

CORL-233

* Tweak styling for the user history drawer comments section

Add margins where necessary.
Style the Load More button to match the comment feed elsewhere.

CORL-233

* Fix background of copy buttons to match the user history drawer

CORL-233

* Preliminarily get rejected comments showing in the user history tabs

Still need to consolidate any duplication between the all and rejected
comments containers/queries, but this works correctly as is.

CORL-233

* Show hover and active background color on moderate card usernames

CORL-233

* Hide load more buttons on comment streams when no comments available

CORL-233

* Handle when no comments are available in user history drawer streams

CORL-233

* Actually show all comments in the user history all comments tab

Previously was filtering out rejected and other pre-mod
status-ed comments.

CORL-233

* Style the user history drawer cards to match the spec

CORL-233

* Add localizations around user history drawer tabs

CORL-233

* Allow a mini mode for moderate cards that are shown in the user drawer

CORL-233

* Style user drawer comment tabs to match the spec

CORL-233

* Update snapshots to account for mini moderate cards

Updates the fact that a separator on regular sized cards
now has an additional class name to turn on the divider's
visible border.

CORL-233

* Add user drawer to the queue instead of childed to the moderate card

Prevents the user drawer from prematurely closing when we approve
all items in the moderate queue.

CORL-233

* Update snapshots to account for user drawer on queues

CORL-233

* Set mini and username defaults more cleanly on ModerateCardContainer

CORL-233

* Rename usernameClicked to onUsernameClicked

CORL-233

* Use callback on loadMore calls within user drawer queries

CORL-233

* Localize the no comment text for the user drawer comment streams

CORL-233

* Localize the user not found callout in the user drawer comment streams

CORL-233

* Remove superfluous curly braces

CORL-233

* Extract user history drawer internals into a distinct query component

CORL-233

* Move comments conditional below useCallback initializations

CORL-233

* feat: fixed consistency issues

* Use a concrete tab type on user drawer comment stream tabs

CORL-233

* Use <hr/> instead of <HorizontalRule>

CORL-233

* Clarify logic for showing horizontal rule after comments in user drawer

CORL-233

* Remover bottom border on user drawer tab bar

CORL-233
2019-07-10 10:59:28 -06:00
Wyatt Johnson 9e55ea66fa [next] Websocket Keep Alive (#2394)
* feat: added keepalive config

* fix: format

* fix: adjusted client timeout default

* Update server.ts
2019-07-09 16:27:58 +00:00
Wyatt Johnson 9a191b44ba [CORL-276] Sitewide Pre-Moderation (#2392)
* feat: initial implementation

* chore: docs update

* fix: lint

* fix: naming
2019-07-05 23:18:58 +00:00
Wyatt Johnson e7745a85aa [CORL-416] Disable Live Updates (#2391)
* feat: initial implementation

* fix: docs
2019-07-05 23:10:19 +00:00
Wyatt Johnson da1fa9c9fc [CORL-437] SSO Token Documentation + Updates (#2390)
* feat: updated README, added more SSO functionality

* fix: lint

* fix: lint

* fix: lint

* fix: typos
2019-07-05 21:49:41 +00:00
Wyatt Johnson 0754ceb803 fix: allowed banned/suspended users to view their ignored users (#2387) 2019-07-04 22:11:51 +00:00
Wyatt Johnson 6fe7d56836 [next] Bump to 5.0.0-beta.4 (#2386)
* chore: version bump

* chore: bump resource class
2019-07-02 22:51:43 +00:00
Wyatt Johnson 633998307d Revert "[CORL-382] Allow moderators to ban a commenter from the comment stream (#2378)" (#2384)
This reverts commit e77103d872.
2019-07-02 21:59:18 +00:00
Vinh 414a4c2a42 [CORL-421, CORL-415] Live Comments on Stream (#2379)
* feat: support comment replies

* feat: comment created

* feat: live update top level comments

* feat: live updates on the stream embed

* fix: tests

* chore: refactor FadeInTransition

* fix: add missing translation and a live update bug

* fix: graqphql

* feat: improve loading experiene :-)

* fix: live comment bugs

* chore: adapt translation

* feat: stop live updates when story is closed or commenting is disabled

* test: add tests for stream live updates

* fix: remove forgotten piece of code

* fix: tests
2019-06-28 23:20:10 +00:00
Nick Funk e77103d872 [CORL-382] Allow moderators to ban a commenter from the comment stream (#2378)
* Add a ban user action to the stream moderation drop down

CORL-382

* Show banned stated for user in stream moderation drop down

If the user is actively banned, the ban user option will be
disabled and show a status of "banned".

CORL-382

* Create utility for generating random stories, comments, and users

CORL-382

* Add ban and suspension values to baseUser in fixtures

CORL-382

* Updated banned.spec.tsx to use new test utilities for generating fixture data

CORL-382

* Prevent users from being able to ban themselves in the moderation dropdown

CORL-382

* Kill optimistic response errors for comment mutations

Set the author.status.current to an empty array so it stops
complaining about it being unused.

CORL-382

* Rename util in tests to helpers/fixture.ts

CORL-382

* Remove unused import from CreateCommentReplyMutation.ts

CORL-382

* Put back the optimistic ban responses into comment mutations

The warnings spewing out during tests are false, for further
detail please see: https://github.com/facebook/relay/pull/2760

CORL-382

* Denormalize generated stories and comments

CORL-382

* Clean up import ordering in ModerationActionsContainer.tsx

CORL-382

* Inject appropriate scoped items into callbacks for moderation dropdown

CORL-382

* Set optimistic response author status from known viewer status

CORL-382
2019-06-28 23:10:09 +00:00
Wyatt JohnsonandVinh 51b142035e [CORL-183] Invite Users (#2349)
* feat: initial UI impl

* feat: attach react devtools hook in development

* feat: working mutations

* feat: polished the invite modal with mutation

Co-authored-by: Vinh <vinh@wikiwi.io>

* feat: added check

* feat: improve the invite server impl

* feat: admin invite interface improvements

* fix: update tests

* feat: moved invite UI to admin

* fix: include email enabled as condition for invite

* feat: added admin tests

* feat: added tests for invite complete flow

* fix: review
2019-06-28 22:51:42 +00:00
Wyatt Johnson 76033118e5 [CORl-278] Perspective Model (#2337)
* feat: support switching the perspective model

* feat: support changing the model on admin

* fix: linting

* fix: removed defaulted value
2019-06-28 22:10:32 +00:00
Wyatt Johnson e72b15c505 [next] Cookie Support (#2339)
* feat: added cookie support to coral

* feat: adapt client to use cookies

* fix: safari input styles

* fix: lint

* fix: linting

* fix: support clearing cookies properly, oauth

* feat: support cookies for websocket upgrade requests

* fix: lint

* fix: tests
2019-06-25 21:48:58 +02:00
Wyatt Johnson 3576b6a943 chore: bumped version (#2375) 2019-06-21 23:04:52 +00:00
Vinh 413f3e2f1e [CORL-166] Live Updates on Mod Queues (#2368)
* feat: client implementation of subscriptions and modqueue live counts

* fix: unit tests

* feat: live status update in moderation

* feat: live update of new comments in moderation

* chore: View New instead of View More

* feat: fade in transition for new comments

* chore: turn websocket proxy back on

* feat: initial server impl

* fix: make it work :-)

* fix: add box shadow

* chore: make test subscriptions only support 1 top level field following the spec

* fix: linting

* feat: support clientID

* fix: linting

* feat: support commentStatusUpdated subscription

* fix: disabled styles for approve and reject button

* feat: show moderated by system and update flags

* feat: support metrics recording on websocket connections

* fix: handle when same comment enters but leaves again
2019-06-21 17:01:07 +00:00
Nick Funk 0e247ba383 [CORL-163] Add feature button to moderation cards (#2367)
* Preliminarily add a feature button to the moderate card

Pulls the tags for a comment and checks if any or of type "Featured".
Naively compares the tag code with the string "Featured".
Shows a featured button, but is missing localization for the text.

CORL-163

* Use GQLTag definitions from schema to compute whether comment is featured

CORL-163

* Add functionality to feature/unfeature comments in the moderation area

Hooks up the feature button to two mutations for featuring and
unfeaturing comments on moderation comment cards.

CORL-163

* Add localizations for the feature button on the moderation card

CORL-163

* Fix moderate card snapshot banned and suspect word lists

CORL-163

* Fix issue with feature buttons never showing "Featured" text

Localization on parent of the button was causing an override
in the naming of the feature button text. Removed parent
localization and let the child show the localized text values.

CORL-163

* Update moderate card snapshots with feature button

CORL-163

* Update moderate fixtures and snapshots to handle feature button

Add tags section to fixture data.
Add feature button to moderate snapshots.

CORL-163

* Set a default for feature button's localized text

CORL-163

* Add test to verify feature button is available on moderation cards

CORL-163

* Add tests to verify featured comments show featured button state in moderation

Verify that when a featured comment shows in single moderation view,
that the comment shows appropriate featured button state.

CORL-163

* Update feature button styles to use design tokens

CORL-163

* Replace true | false prop definition with boolean type

CORL-163

* Remove un-necessary beforeEach history location in feature.spec.tsx

CORL-163

* Organize styling for feature button

Move float:right with the rest of the layout options.

CORL-163

* Update the feature comment mutation moderate side to update queues

CORL-163

* Update queue counts when a comment is approved via featuring

CORL-163
2019-06-20 23:11:02 +02:00
Vinh 4ab8b09be1 [next] byText search should look into React Fragments (#2372)
* fix: byText search should look into React fragments

* chore: better comments

* chore: typo
2019-06-19 21:19:03 +02:00
Cristian Dean 05f9daec2e Update mongodb and mongodb-core libs (#2366) 2019-06-19 16:06:38 +00:00
Nick Funk 972f4b702d [CORL-409] Add tests around ignore button visibility on user popover cards (#2364)
* Add tests around ignore button visibility on user popover cards

Staff member -> ignore should not be present
Regular commenter -> ignore button should be present

CORL-409

* chore: simplify fixture

* chore: normalize naming in test and use type instead of any
2019-06-14 21:14:41 +02:00
Vinh 3cd7bf655e chore: remove obsolete test code (#2363) 2019-06-14 17:17:30 +00:00
Wyatt Johnson 9d1f03115f [CORL-381] Featured Comments (#2335)
* feat: initial serverside featuring support

* Update schema.graphql

* feat: add feature comment to moderation dropdown

* feat: feature comments on the stream embed

* fix: tests

* fix: optimize loading and fix tests

* feat: hide featured tab when empty

* feat: introduced flattening

* fix: snapshots

* fix: spacing

* feat: added a dark variant to popover

* feat: add featured comments tooltip

* fix: better tests

* feat: added tag counts

* chore: changed string to enum

* fix: removed unused translation

* fix: changed schema for String -> TAG

* feat: split comments -> comments, featuredComments

* fix: adapt client to new endpoints

* feat: use featured comment counts

* test: featured count handling

* fix: snapshots and optimistically approve comment during feature

* fix: remove unnecessary assertion

* feat: approve featured comments

* fix: make optimistic update less reliant on existing data
2019-06-14 16:27:25 +00:00
Wyatt Johnson f8cf34e34d feat: added email confirmation UI (#2358) 2019-06-13 23:37:51 +02:00
Diego Chavão 5311681333 [next] Fix skew tolerance to token expiration. (#2356) 2019-06-13 20:30:09 +02:00
Nick Funk 662f5ce314 [CORL-409] Prevent users from ignoring staff members (#2355)
* Throw error if user tries to ignore a staff member

Throws a UserCannotBeIgnoredError if a user tries to ignore
a user who is a staff member. A staff member in this case is
considered anyone who has a role of staff, moderator, or admin.

CORL-409

* Prevent users from ignoring staff in the user info popover

Creates the staff roles in a constant next to the user model.
Uses this to add a computed property to the user resolver.

CORL-409

* Remove unnecessary async declaration from userIsStaff helper function

CORL-409

* Specify ignoreable on users in client test fixtures

Allows the tests to pass for the required computed property
of ignoreable that is computed by whether a user is a staff
member or not.

CORL-409

* Update more fixtures with ignoreable property on mocked users/commenters

CORL-409

* Consolidate ignore-able calculation into re-usable helper methods

Re-use the logic for whether a role is a staff member to
clearly define when a user is ignore-able or not across the
business logic.

CORL-409

* Set the ignoreable optimisticResponse on comment mutations

We have set the ignoreable value in the graphQL schema, so now
the optimisticResponses are looking for a default value to use
until the data result arrives. Put to false since the ignoreable
value is set on our author, we likely don't want to ignore ourselves.

CORL-409
2019-06-13 17:24:50 +00:00
Vinh 3947b143cb [CORL-423] Rearrange client files and folder (#2352)
* chore: reorganize account

* chore: reorganize install

* chore: reorganize auth files

* chore: reorganize admin files

* fix: graphql naming

* chore: adapt account routing
2019-06-11 15:33:46 +00:00
Kim Gardner 05c8f06ed8 Merge pull request #2353 from coralproject/next-patch-readme
README.md
2019-06-10 23:27:49 +01:00
Kim Gardner f320e5521b Fix lint 2019-06-10 23:09:20 +01:00
Jessie Rushing 25a7945408 README.md 2019-06-10 14:17:07 -07:00
Wyatt Johnson d30ced8142 fix: sort by replies (#2351) 2019-06-08 02:10:26 +02:00
Vinh 812649153e chore+fix: upgrade jest + fix flaky renderStream test! (#2350) 2019-06-07 23:44:08 +00:00
Vinh 5b64d3d14b [next] Fix warnings and errors that appear during test (#2348)
* fix: handle react act errors

* fix: fixed deprecated reporter option
2019-06-07 22:01:57 +00:00
Vinh d4b99a2a57 [CORL-420] Upgrade Relay (#2346)
* chore: upgrade Relay

* fix: fix errors

* fix: snapshot

* fix: relay prefix

* fix: fragment spec error
2019-06-07 21:42:26 +00:00
Wyatt Johnson ed4e5fa2a8 [CORL-399] Hide Non-Visible Comments (#2344)
* feat: added support for hiding non-visible comments

* fix: updated copy

* feat: refined style
2019-06-06 22:24:02 +00:00
Kiwi 2cff4572cf [CORL-394] Pre-Mod Link feature wouldn't capture suspect and other flags (#2342)
* fix: move detect premod links further down the moderation phases

* chore: version bump
2019-06-06 19:09:23 +00:00
Kiwi 8cfced464b chore: rename accept comment to approve comment (#2341) 2019-06-06 18:58:11 +00:00
Cristian Dean 39d7540987 [next] Add pt-BR translation (#2338)
* Add pt-BR translation

* Add pt-BR translation on server
2019-06-05 18:04:07 +02:00
Kiwi 593d94ddbb [CORL-218] In Stream Approve and Reject (#2340)
* feat: instream moderation

* fix: tests

* test: add tests for in stream moderation

* fix: lint

* fix: snapshots
2019-06-04 19:27:15 +02:00
Wyatt Johnson 1cf2f61bfb [CORL-388] Health Check (#2336)
* feat: added prelim health-check endpoint

* Update health.ts

* Update health.ts

* fix: lint
2019-05-31 23:20:00 +00:00
Wyatt Johnson 132ffdb2dc [next] Ancestors (#2333)
* feat: simplified ancestor management

* fix: removed old comment
2019-05-31 23:02:43 +00:00
Wyatt Johnson 6bd3e093b3 [CORL-157] Ignore Users (#2328)
* feat: added server impl of ignore user

* feat: added development error wrapping

* feat: Add arror to popover

* fix: dependencies

* fix:  server locals watch paths

* feat: AuthorPopover & Member Since

* chore: refactor stream file organization

* feat: ignore user

* fix: file case

* feat: add support for useLocal hook!

* test: test ignored users
2019-05-31 22:41:20 +00:00
Wyatt Johnson 1302ea1918 [CORL-147] Lazy Story Creation + Proxy (#2302)
* feat: added toggle for lazy stories and proxy

* feat: improve story loading logic
2019-05-31 22:28:40 +00:00
Wyatt Johnson 4ce7512c4b fix: replace npm-run-all with patched version (#2332) 2019-05-29 16:53:22 +00:00
Cristian Dean 7beab6419e [next] Add redis_options config (#2323)
* [next] Add redis_options config

* fix: updated docs, camelcased arg, used native merge

* fix: improved CI issues
2019-05-27 19:07:56 +00:00
Kiwi e7151806ea fix: nunjuck formatting error (#2322) 2019-05-23 01:57:09 +02:00
Kiwi dce60c5eab [CORL-349] Show banned user info (#2319)
* feat: stream banned state ui

* fix: button color

* fix: banned permissions

* test: add tests and hide edit

* fix: show reaction button and adapt snapshots

* fix: snapshot
2019-05-23 01:48:10 +02:00
Kiwi 8618ef0b96 [CORL-380] Duplicate Email Error (#2320)
* fix: throw duplicate email error during signup

* fix: Coral.Coral.* -> Coral.*

* fix: tests
2019-05-22 22:07:07 +02:00
Kiwi 6da97c57d7 [CORL-314] Rename to Coral (#2318)
* chore: rename talk to coral

* fix: lint and unit tests

* fix: snapshot
2019-05-22 21:32:24 +02:00
Kiwi 3ea696fcc8 [CORL-339] Allow comment draft when not logged in (#2315)
* feat: allow user to draft a comment before logging in

* test: snapshot

* fix: lint

* chore: upgrade rte

* fix: snapshots
2019-05-20 18:14:09 +02:00
Kiwi ab466510f3 fix: use custom webpackHotDevClient with debounced reload and filter unwanted warnings (#2314) 2019-05-15 20:33:39 +02:00
Wyatt Johnson 3ba10fd15f [CORL-224] Flag Details (#2293)
* feat: flag details

* fix: review

* feat: implement flag details in admin

* test: test flag details
2019-05-15 18:50:47 +02:00
Kiwi 898bc0de06 feat: allow explicitly disabling client routes (#2311) 2019-05-15 01:27:34 +02:00
Kiwi 1794086683 [CORL-360] Embrace CSS Variables (#2312)
* fix: docz bug

* feat: implement css variables

* fix: adapt docz

* fix: webpack config

* fix: add ui l10n bundle to tests
2019-05-15 01:21:07 +02:00
Wyatt Johnson d4b8e5ef70 [CORL-281] Metrics (#2298)
* feat: iunitial metrics implementation

* fix: graphql endpoint was throwing errors.

* feat: add metrics env variables to readme
2019-05-10 00:26:24 +02:00
Wyatt Johnson df57b4eb17 [next] Email (#2261)
* feat: suspending, banning, now propogation

* feat: added email rendering + localization support

* fix: fix related to lib

* refactor: moved juicer to queue task

* refactor: cleanup of job processor

* refactor: improved error messaging around failed email

* feat: initial forgot passwor impl

* fix: fixed rebase errors

* feat: send back Content-Language header with requests

* feat: added ban email

* feat: implemented forgotten password API

* fix: linting

* feat: support more emails

* fix: promise patches

* feat: initial confirm email API

* feat: added rate limiting

* feat: added URL support

* feat: added email docs

* fix: updated docs

* chore: documentation review

* fix: fixed build bug

* feat: implement forgot password in auth popup

* test: add tests + fixes

* chore: rename StatelessComponent to FunctionComponent

* fix: types and test fixes

* chore: upgrade deps

* fix: THANK YOU TESTS FOR SAVING MY A**

* chore: reorder imports

* chore: remove obsolete !

* feat: implement accounts bundle

* refactor: review suggestion

* fix: rebase upgrade error

* fix: rebase bug

* feat: reset password link support

* test: add tests for account password reset page

* fix: remove redirect uri

* fix: revert local state changes
2019-05-09 22:54:56 +02:00
Kiwi 945bd7f2b0 [CORL-367] Speedup scripts & better server development and debugging (#2306)
* feat: speedup scripts & make server development faster

* feat: fix server debugging + allow attaching to a running node process

* feat: make npm run during watch less chatty
2019-05-09 22:08:12 +02:00
Wyatt Johnson 9972253056 feat: removed pulling displayName from facebook/google (#2301) 2019-05-07 22:34:31 +00:00
Wyatt Johnson 2b878b224b fix: double logout will not result in failures (#2300) 2019-05-07 22:23:57 +00:00
Kiwi ea6f42c256 [CORL-305] Speed up build process and upgrade dependencies (#2304)
* feat: speed up build process, upgrade majority of dependencies

* feat: allow specifying max cores

* chore: add some comments
2019-05-07 20:22:19 +02:00
Kiwi 98795d161d [CORL-263, CORL-225] Story Titles, Moderate Story, Mini Unit (#2294)
* feat: show story title + moderate this story link

* feat: introduce mini unit

* test: add tests for moderate story link

* fix: snapshot

* fix: lint / test

* fix: unwanted margin
2019-05-03 22:01:34 +02:00
3253 changed files with 160505 additions and 76310 deletions
+5 -2
View File
@@ -1,12 +1,15 @@
# job_environment will setup the environment for any job being executed.
job_environment: &job_environment
NODE_ENV: test
WEBPACK_MAX_CORES: 4
NODE_OPTIONS: --max-old-space-size=8192
# job_defaults applies all the defaults for each job.
job_defaults: &job_defaults
working_directory: ~/coralproject/talk
resource_class: large
docker:
- image: circleci/node:10
- image: circleci/node:12
environment:
<<: *job_environment
@@ -86,7 +89,7 @@ jobs:
name: Perform testing
# We're running these tests in band to avoid errors where the circleci
# test runner runs out of RAM trying to run them all in parallel.
command: npm run test -- --ci --runInBand --testResultsProcessor="jest-junit"
command: npm run test -- --ci --runInBand --reporters=default --reporters=jest-junit
- store_test_results:
path: reports/junit
- store_artifacts:
+3
View File
@@ -0,0 +1,3 @@
*.d.ts
*.graphql.ts
**/__generated__/**
+210
View File
@@ -0,0 +1,210 @@
const typescriptEslintRecommended = require('@typescript-eslint/eslint-plugin/dist/configs/eslint-recommended').default.overrides[0];
const typescriptRecommended = require('@typescript-eslint/eslint-plugin/dist/configs/recommended.json');
const typescriptRecommendedTypeChecking = require('@typescript-eslint/eslint-plugin/dist/configs/recommended-requiring-type-checking.json');
const typescriptEslintPrettier = require('eslint-config-prettier/@typescript-eslint');
const react = require('eslint-plugin-react').configs.recommended;
const jsxA11y = require('eslint-plugin-jsx-a11y').configs.recommended;
const reactPrettier = require('eslint-config-prettier/react');
const typescriptOverrides = {
files: ["*.ts", "*.tsx"],
parser: "@typescript-eslint/parser",
parserOptions: {
sourceType: "module",
ecmaFeatures: {
jsx: true,
},
},
plugins: [
"@typescript-eslint",
"@typescript-eslint/tslint",
"react",
"jsx-a11y",
],
settings: {
react: {
version: "detect",
}
},
rules: Object.assign(
typescriptEslintRecommended.rules,
typescriptRecommended.rules,
typescriptEslintPrettier.rules,
react.rules,
jsxA11y.rules,
reactPrettier.rules,
{
"@typescript-eslint/adjacent-overload-signatures": "error",
// TODO: (cvle) change `readonly` param to `array-simple` when upgraded typescript.
"@typescript-eslint/array-type": ["error", { "default": "array-simple", "readonly": "generic"}],
"@typescript-eslint/ban-types": "error",
"@typescript-eslint/camelcase": "off",
"@typescript-eslint/consistent-type-assertions": "error",
"@typescript-eslint/consistent-type-definitions": "error",
"@typescript-eslint/class-name-casing": "error",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/explicit-member-accessibility": [
"error",
{
"overrides": {
"constructors": "off",
},
},
],
"@typescript-eslint/indent": "off",
"@typescript-eslint/interface-name-prefix": "error",
"@typescript-eslint/member-delimiter-style": "off",
"@typescript-eslint/no-empty-function": "error",
"@typescript-eslint/no-empty-interface": "error",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-misused-new": "error",
"@typescript-eslint/no-namespace": "error",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-parameter-properties": "off",
"@typescript-eslint/no-unused-vars": ["error", {"args": "none", "ignoreRestSiblings": true}],
"@typescript-eslint/no-use-before-define": "off", // TODO: (cvle) Should be on?
"@typescript-eslint/no-use-before-declare": "off",
"@typescript-eslint/no-var-requires": "error",
"@typescript-eslint/prefer-for-of": "error",
"@typescript-eslint/prefer-function-type": "error",
"@typescript-eslint/prefer-namespace-keyword": "error",
"@typescript-eslint/triple-slash-reference": "error",
"@typescript-eslint/type-annotation-spacing": "off",
"@typescript-eslint/unified-signatures": "error",
// (cvle) disabled, because the way we use labels in our code cause to many
// false positives.
"jsx-a11y/label-has-associated-control": "off",
"react/display-name": "error",
"react/prop-types": "off",
"react/no-unescaped-entities": "off",
}
),
};
let typescriptTypeCheckingOverrides = {
files: ["*.ts", "*.tsx"],
parserOptions: {
project: ["tsconfig.json", "./src/tsconfig.json", "./src/core/client/tsconfig.json"],
// TODO: (cvle) this is a workaround, see: https://github.com/typescript-eslint/typescript-eslint/issues/1091.
createDefaultProgram: true,
},
rules: Object.assign(
typescriptRecommendedTypeChecking.rules,
{
"@typescript-eslint/tslint/config": ["error", {
"rules": {
"ordered-imports": {
"options": {
"import-sources-order": "case-insensitive",
"module-source-path": "full",
"named-imports-order": "case-insensitive",
},
},
},
}],
// 28.11.19: (cvle) Disabled because behavior of regexp.exec seems different than str.match?
"@typescript-eslint/prefer-regexp-exec": "off",
"@typescript-eslint/require-await": "off",
"@typescript-eslint/no-misused-promises": "off",
"@typescript-eslint/unbound-method": "off", // 10.10.19: (cvle) seems to give false positive.
}
),
};
const jestOverrides = {
env: {
jest: true,
},
files: ["test/**/*.ts", "test/**/*.tsx"],
globals: {
"expectAndFail": "readonly",
"fail": "readonly",
},
};
// Setup the overrides.
const overrides = [jestOverrides, typescriptOverrides];
// Skip type information to make it faster!
if (process.env.FAST_LINT !== "true") {
overrides.push(typescriptTypeCheckingOverrides);
}
module.exports = {
overrides,
env: {
browser: true,
es6: true,
node: true,
},
extends: [
"eslint:recommended",
"plugin:jsdoc/recommended",
"plugin:prettier/recommended",
],
parserOptions: {
"ecmaVersion": 2018,
},
rules: {
"arrow-body-style": "off",
"arrow-parens": [
"off",
"as-needed",
],
"camelcase": "off",
"complexity": "off",
"constructor-super": "error",
"spaced-comment": ["error", "always"],
"curly": "error",
"dot-notation": "error",
"eol-last": "off",
"eqeqeq": "error",
"guard-for-in": "error",
"jsdoc/require-jsdoc": "off",
"jsdoc/require-returns": "off",
"jsdoc/require-param": "off",
"jsdoc/require-param-type": "off",
"jsdoc/require-returns-type": "off",
"linebreak-style": "off",
"max-classes-per-file": [
"error",
1,
],
"member-ordering": "off",
"new-parens": "off",
"newline-per-chained-call": "off",
"no-bitwise": "error",
"no-caller": "error",
"no-cond-assign": "error",
"no-console": "error",
"no-debugger": "error",
"no-empty": "error",
"no-eval": "error",
"no-extra-semi": "off",
"no-fallthrough": "error",
"no-invalid-this": "off",
"no-irregular-whitespace": "off",
"no-multiple-empty-lines": "off",
"no-new-wrappers": "error",
"no-prototype-builtins": "off",
"no-shadow": "error",
"no-throw-literal": "error",
"no-undef": "off",
"no-undef-init": "error",
"no-unsafe-finally": "error",
"no-unused-expressions": "error",
"no-unused-labels": "error",
"no-unused-vars": ["error", {"args": "none", "ignoreRestSiblings": true}],
"no-var": "error",
"object-shorthand": "error",
"one-var": "off",
"prefer-arrow-callback": "off",
"prefer-const": "error",
"quote-props": "off",
"radix": "error",
"require-atomic-updates": "off",
"space-before-function-paren": "off",
"sort-imports": "off",
"use-isnan": "error",
"valid-typeof": "off",
},
};
+32
View File
@@ -0,0 +1,32 @@
---
name: Bug report
about: Create a bug report to help us improve Coral
labels: "bug"
---
<!--
Please note that but submitting a bug report, you agree to our Code of Conduct: http://code-of-conduct.voxmedia.com/
When submitting a bug report, provide as much detail as possible, including a screenshot or copy-paste of any related error messages, logs, or other output that might be related. Places to look for information include your browser console, server console, and network logs. The more information you can give the better.
Please help us by doing the following steps before logging an issue:
* Search for other similar issues: https://github.com/coralproject/talk/search
* Read our docs: https://docs.coralproject.net
Please fill in the *entire* template below.
-->
**Expected behavior:**
**Actual behavior:**
**Related Issues:** <!-- Did you find other bugs that looked similar? -->
**Versions:**
- NodeJS:
- NPM:
- MongoDB:
- Redis:
- Browser:
- OS:
+29
View File
@@ -0,0 +1,29 @@
---
name: Feature request
about: Suggest a feature idea for Coral
labels: "feature idea"
---
<!--
Please note that but submitting a feature request, you agree to our Code of Conduct: http://code-of-conduct.voxmedia.com/
Please help us by doing the following steps before logging an issue:
* Search to see if there are similar requests and ideas that are already logged: https://github.com/coralproject/talk/search
-->
## Search Terms
<!-- List of keywords you searched for before creating this issue. Write them down here so that others can find this suggestion more easily -->
## Suggestion
<!-- A summary of what you'd like to see added or changed -->
## Use Cases
<!--
What do you want to use this for?
What shortcomings exist with current approaches?
-->
+14
View File
@@ -0,0 +1,14 @@
<!--
Thank you for submitting a pull request! Please note that by contributing to Coral, you agree to our Code of Conduct: http://code-of-conduct.voxmedia.com/
Before submitting your PR, please verify that:
* [ ] Your code is up-to-date with the base branch
* [ ] You've successfully run `npm run test` locally
Refer to CONTRIBUTING.MD for more details.
https://github.com/coralproject/talk/blob/master/CONTRIBUTING.md
-->
## What does this PR do?
## How do I test this PR?
+1
View File
@@ -21,3 +21,4 @@ dist
*.css.d.ts
__generated__
README.md.orig
persisted-queries.json
+1 -1
View File
@@ -1,7 +1,7 @@
{
"projects": {
"tenant": {
"schemaPath": "src/core/server/graph/tenant/schema/schema.graphql"
"schemaPath": "src/core/server/graph/schema/schema.graphql"
}
}
}
+1 -2
View File
@@ -1,2 +1 @@
10
12
+9
View File
@@ -0,0 +1,9 @@
{
"recommendations": [
"dbaeumer.vscode-eslint",
"kumar-harsh.graphql-for-vscode",
"editorconfig.editorconfig",
"ms-azuretools.vscode-cosmosdb",
"mike-co.import-sorter"
]
}
+11 -1
View File
@@ -7,7 +7,7 @@
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"name": "Launch Development Server",
"cwd": "${workspaceFolder}",
"args": [
"-r",
@@ -18,10 +18,20 @@
],
"env": {
"TS_NODE_PROJECT": "${workspaceFolder}/src/tsconfig.json",
"TS_NODE_FILES": "true",
"TS_NODE_TRANSPILE_ONLY": "true",
// Set to 1 here beacuse clustering gets all weird with logging + output.
"CONCURRENCY": "1"
},
"outputCapture": "std"
},
{
"type": "node",
"request": "attach",
"name": "Attach to Node Process",
"processId": "${command:PickProcess}",
"restart": true,
"protocol": "inspector"
}
]
}
+47 -5
View File
@@ -1,5 +1,4 @@
{
"editor.formatOnSave": true,
"files.associations": {
"*.css": "postcss"
},
@@ -11,9 +10,52 @@
"**/.DS_Store": true,
".vs": true
},
"tslint.exclude": "**/node_modules/**",
"tslint.autoFixOnSave": true,
"tslint.jsEnable": true,
"tslint.enable": false,
"eslint.validate": [
{ "language": "javascript", "autoFix": true },
{ "language": "typescript", "autoFix": true },
{ "language": "typescriptreact", "autoFix": true }
],
"typescript.tsdk": "node_modules/typescript/lib",
"postcss.validate": false
"postcss.validate": false,
"javascript.preferences.importModuleSpecifier": "non-relative",
"importSorter.importStringConfiguration.quoteMark": "double",
"importSorter.importStringConfiguration.maximumNumberOfImportExpressionsPerLine.count": 80,
"importSorter.sortConfiguration.customOrderingRules.rules": [
{
"type": "importMember",
"regex": "^$",
"orderLevel": 70,
"disableSort": true
},
{
"regex": "__generated__",
"orderLevel": 40
},
{
"regex": "^coral-",
"orderLevel": 30
},
{
"regex": "\\.css$",
"orderLevel": 60,
"disableSort": true
},
{
"regex": "^[.]",
"orderLevel": 50
},
],
"importSorter.importStringConfiguration.maximumNumberOfImportExpressionsPerLine.type": "newLineEachExpressionAfterCountLimitExceptIfOnlyOne",
"importSorter.importStringConfiguration.trailingComma": "multiLine",
"importSorter.importStringConfiguration.tabSize": 2,
"eslint.enable": true,
"importSorter.generalConfiguration.exclude": [
"d\\.ts$",
"__generated__"
],
"debug.node.autoAttach": "on",
"search.exclude": {
"package-lock.json": true
},
}
+7
View File
@@ -0,0 +1,7 @@
# Vox Media Code of Conduct
## Introduction
This code of conduct governs the environment of the Vox Product team. We created it not because we anticipate bad behavior, but because we believe that articulating our values and obligations to one another reinforces the already exceptional level of respect among the team and because having a code provides us with clear avenues to correct our culture should it ever stray from that course. We make this code public in the hopes of contributing to the ongoing conversation about inclusion in the tech, design, and media communities and encourage other teams to fork it and make it their own. To our team, we commit to enforce and evolve this code as our team grows.
Read the rest here: http://code-of-conduct.voxmedia.com/
+71
View File
@@ -0,0 +1,71 @@
# Contributor's Guide
Welcome! We are very excited that you are interested in contributing to Coral.
This document is a companion to help you approach contributing. If it does not
do so, please [let us know how we can improve it](https://github.com/coralproject/talk/issues)!
By contributing to this project you agree to the
[Code of Conduct](CODE_OF_CONDUCT.md).
## What should I Contribute?
There are at least three ways to contribute to Coral:
- Writing Code
- Providing Translations
## Writing Code
Conversation surrounding contributions begins in
[issues](https://github.com/coralproject/talk/issues).
### When should I create an issue?
File an issue as soon as you have an idea of something you'd like to contribute.
We would love to hear what you're thinking and help refine the idea to make it
into the Coral ecosystem.
Please file issues if you would like to contribute to Coral.
### What should I include?
Coral has adopted an iterative, agile development philosophy. All contributions
that make it into the Coral repository should start with a user story in this
form:
> As a [type of Coral user] I'd like to [do something] so that I can [get some result/value].
This exercise does two things:
- allows us to ground our technical choices in a clear, simple product need.
- expresses that product need in a way that doesn't imply a specific technical
solution allowing for debate as to the best way to solve the problem.
Please feel free to provide as much detail as possible when filing the issue but
please do keep the initial issue specific to one need and try to avoid including
technical or design solutions.
If you have a specific technical or design solution in mind, please submit it as
the first comment on the thread.
## Localization
We use the [fluent](http://projectfluent.org/) library and store our
translations in [FTL](http://projectfluent.org/fluent/guide/) files in
`src/locales/` and `src/core/server/locales/`.
Strings are added or removed from localization bundles in the translation files
as needed. Strings **MUST NOT** be _changed_ after they've been committed and
pushed to master. Changing a string requires creating a new ID with a new name
(preferably descriptive instead of incremented) and deletion of the obsolete ID.
It's often useful to add a comment above the string with info about how and
where the string is used.
Once a language has enough coverage, it should be added to
`src/core/common/helpers/i18n/locales.ts`.
The [Perspective API](https://github.com/conversationai/perspectiveapi/blob/master/2-api/methods.md#analyzecomment-request)
also supports comments in specific languages. When the language is supported in
Coral and supported by the Perspective API, the language should be added to the
language map in `src/core/server/services/comments/pipeline/phases/toxic.ts`.
+3 -8
View File
@@ -1,4 +1,4 @@
FROM node:10-alpine
FROM node:12-alpine
# Install build dependancies.
RUN apk --no-cache add git python
@@ -7,9 +7,6 @@ RUN apk --no-cache add git python
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# Setup the environment for production.
ENV NODE_ENV production
# Bundle application source.
COPY . /usr/src/app
@@ -19,15 +16,13 @@ RUN mkdir -p dist/core/common/__generated__ && \
echo "{\"revision\": \"${REVISION_HASH}\"}" > dist/core/common/__generated__/revision.json
# Install build static assets and clear caches.
RUN NODE_ENV=development npm install && \
npm run generate && \
RUN npm ci && \
npm run build && \
npm prune --production
# Setup the environment
ENV PATH /usr/src/app/bin:$PATH
ENV NODE_ENV production
ENV PORT 5000
EXPOSE 5000
ENV NODE_ENV production
CMD ["npm", "run", "start"]
+17 -220
View File
@@ -1,244 +1,41 @@
# talk ![CircleCI](https://img.shields.io/circleci/project/github/coralproject/talk/next.svg)
# Coral ![CircleCI](https://img.shields.io/circleci/project/github/coralproject/talk.svg)
Online comments are broken. Our open-source commenting platform, Talk, rethinks
Online comments are broken. Our open-source commenting platform, Coral, rethinks
how moderation, comment display, and conversation function, creating the
opportunity for safer, smarter discussions around your work.
[Read more about Talk here](https://coralproject.net/talk).
[Read more about Coral here](https://coralproject.net/talk).
Built with <3 by The Coral Project, a part of [Vox Media](https://product.voxmedia.com/).
Preview Talk easily by running Talk via a Heroku App:
Preview Coral easily by running Coral via a Heroku App:
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/coralproject/talk/tree/next)
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/coralproject/talk)
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
## Table of Contents
- [Requirements](#requirements)
- [Running](#running)
- [Docker](#docker)
- [Source](#source)
- [Development](#development)
- [Design Language System (UI Components)](#design-language-system-ui-components)
- [Configuration](#configuration)
- [Documentation](#documentation)
- [Pre-Launch Guide](#pre-launch-guide)
- [More Resources](#more-resources)
- [License](#license)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## Requirements
## Documentation
- MongoDB >=3.6
- Redis >=3.2
- NodeJS >=10
- NPM >=6.7
You can get started with Coral using our [Documentation](https://docs.coralproject.net/talk/).
## Running
## Pre-Launch Guide
You can install Talk using Docker or via Source. We recommend Docker, as it
provides the easiest deployment solution going forward, as all the dependencies
are baked and shipped with the provided
[coralproject/talk:next](https://hub.docker.com/r/coralproject/talk) image.
When v5 releases to master, you'll be able to select it using
`coralproject/talk:5`.
Youve installed Talk on your server, and youre preparing to launch it on your site. The real community work starts now, before you go live. You have a unique opportunity pre-launch to set your community up for success. Read our [Talk Community Guide](https://coralproject.net/blog/youve-installed-talk-now-what/).
### Docker
## More Resources
The easiest way to get started with Talk is through our published Docker image
and provided example `docker-compose.yml` file. The following assumes that you
have Docker and Docker Compose installed on your local machine:
- Install Docker: https://docs.docker.com/install/
- Install Docker Compose: https://docs.docker.com/compose/install/ (this is typically included in the Docker Desktop editions already)
```bash
# Create directories to persist the data in MongoDB and Redis.
mkdir -p data/{mongo,redis}
# Create the `docker-compose.yml` file to get started.
cat > docker-compose.yml <<EOF
version: "2"
services:
talk:
image: coralproject/talk:next
restart: always
ports:
- "127.0.0.1:3000:5000"
depends_on:
- mongo
- redis
environment:
- MONGODB_URI=mongodb://mongo:27017/talk
- REDIS_URI=redis://redis:6379
- SIGNING_SECRET=<replace me with something secret>
mongo:
image: mongo:3.6
volumes:
- ./data/mongo:/data/db
redis:
image: redis:3.2
volumes:
- ./data/redis:/data
EOF
# Start up Talk using Docker.
docker-compose up -d
```
Then head on over to http://localhost:3000 to install Talk!
### Source
Talk requires NodeJS >=10, we recommend using `nvm` to help manage node
versions: https://github.com/creationix/nvm.
```bash
# Clone and cd into the Talk directory.
git clone https://github.com/coralproject/talk.git
cd talk
# Install dependencies.
npm install
# Build the application dependencies.
# This might take a while.
npm run build
```
This should output all the compiled application code to `./dist`.
Running Talk with default settings assumes that you have:
- MongoDB >=3.6 running on `127.0.0.1:27017`
- Redis >=3.2 running on `127.0.0.1:6379`
If you don't already have these databases running, you can execute the following
assuming you have Docker installed on your local machine:
```bash
docker run -d -p 27017:27017 --restart always --name mongo mongo:3.6
docker run -d -p 6379:6379 --restart always --name redis redis:3.2
```
Then start Talk with:
```bash
# Start the server in production mode.
npm run start
```
Then head on over to http://localhost:3000 to install Talk!
### Development
Running Talk for development is very similar to installing Talk via Source as
described above.
Talk requires NodeJS >=10, we recommend using `nvm` to help manage node
versions: https://github.com/creationix/nvm.
```bash
# Clone and cd into the Talk directory.
git clone https://github.com/coralproject/talk.git
cd talk
# Install dependencies.
npm install
```
Running Talk with default settings assumes that you have:
- MongoDB >=3.6 running on `127.0.0.1:27017`
- Redis >=3.2 running on `127.0.0.1:6379`
If you don't already have these databases running, you can execute the following
assuming you have Docker installed on your local machine:
```bash
docker run -d -p 27017:27017 --restart always --name mongo mongo:3.6
docker run -d -p 6379:6379 --restart always --name redis redis:3.2
```
Then start Talk with:
```bash
# Run the server in development mode in order to facilitate auto-restarting and
# rebuilding when file changes are detected. This might take a while to fully run.
npm run watch
```
When the client code has been built, navigate to http://localhost:8080/install.html
to start the installation wizard. **Note: Ensure `localhost:8080` is used in the permitted domains list.**
To see the comment stream goto http://localhost:8080/.
To run linting and tests use the following commands:
```bash
# Run the linters.
npm run lint
# Run our unit and integration tests.
npm run test
```
#### Design Language System (UI Components)
We use [docz](https://docz.site) to document and develop our Design Language System. To start docz run:
```bash
# Make sure CSS types are generated.
# This is not required when `npm run watch` is already running.
npm run generate:css-types
# Run docz in development.
npm run docz -- dev
```
After compilation has finished you can access docz at http://localhost:3030/.
## Configuration
The following environment variables can be set to configure the Talk Server. You
can expose them in your shell via `export NODE_ENV=development` or by placing
the variables in a `.env` file in the root of the project in a simple
`NODE_ENV=development` format delimited by newlines.
- `NODE_ENV` - Can be one of `production` or `development`. All production
deployments should use `production`. Defaults to `production` when ran with
`npm run start` and `development` when run with `npm run start:development`.
- `PORT` - The port to listen for HTTP and Websocket requests. (Default `3000`)
- `MONGODB_URI` - The MongoDB database URI to connect to.
(Default `mongodb://127.0.0.1:27017/talk`)
- `REDIS_URI` - The Redis database URI to connect to.
(Default `redis://127.0.0.1:6379`)
- `SIGNING_SECRET` - The shared secret to use to sign JSON Web Tokens (JWT) with
the selected signing algorithm. 🚨 **Don't forget to set this variable!** 🚨
(Default: `keyboard cat`)
- `SIGNING_ALGORITHM` - The signing algorithm to use for signing JWT's.
(Default `HS256`).
- `LOGGING_LEVEL` - The logging level that can be set to one of `fatal`,
`error`, `warn`, `info`, `debug`, or `trace`. (Default `info`)
- `STATIC_URI` - The URI that static assets can be accessed from. This URI can
be to a proxy that uses this Talk server on `PORT` as the upstream. Disabled
by default.
- `DISABLE_TENANT_CACHING` - When `true`, all tenants will be loaded from the
database when needed rather than keeping a in-memory copy in sync via
published events on Redis. (Default `false`)
- `DISABLE_MONGODB_AUTOINDEXING` - When `true`, Talk will not perform indexing
operations when it starts up. This can be desired when you've already
installed Talk on the target MongoDB, but want to improve start performance.
(Default `false`)
- `LOCALE` - Specify the default locale to use for all requests without a locale
specified. (Default `en-US`)
- `ENABLE_GRAPHIQL` - When `true`, it will enable the `/graphiql` even in
production, use with care. (Default `false`)
- `CONCURRENCY` - The number of worker nodes to spawn to handle web traffic,
this should be tied to the number of CPU's available. (Default
`os.cpus().length`)
- `DEV_PORT` - The port where the Webpack Development server is running on.
(Default `8080`)
- [Our Blog](https://coralproject.net/blog)
- [Community Guides for Journalism](https://guides.coralproject.net/)
- [More About Us](https://coralproject.net/)
## License
Talk is released under the [Apache License, v2.0](/LICENSE).
Coral is released under the [Apache License, v2.0](/LICENSE).
+7 -5
View File
@@ -1,5 +1,5 @@
{
"name": "Talk",
"name": "Coral",
"description": "A better commenting experience from Vox Media.",
"env": {
"REWRITE_ENV": {
@@ -7,8 +7,7 @@
"value": "REDIS_URI:REDIS_URL,MONGODB_URI:MONGO_URI"
},
"SIGNING_SECRET": {
"description":
"The shared secret to use to sign JSON Web Tokens (JWT) with the selected signing algorithm.",
"description": "The shared secret to use to sign JSON Web Tokens (JWT) with the selected signing algorithm.",
"generator": "secret"
},
"CONCURRENCY": {
@@ -16,13 +15,16 @@
"value": "1"
},
"LOCALE": {
"description":
"Specify the default locale to use for all requests without a locale specified",
"description": "Specify the default locale to use for all requests without a locale specified",
"value": "en-US"
},
"ENABLE_GRAPHIQL": {
"description": "When true, this will enable the GraphiQL routes",
"value": "false"
},
"TRUST_PROXY": {
"description": "When set to 1, it instructs Coral to trust up to one proxy, this is needed for authentication support",
"value": "1"
}
},
"addons": [
+2 -4
View File
@@ -9,6 +9,7 @@ const lodashOptimizations =
process.env.WEBPACK === "true" ? ["use-lodash-es", "lodash"] : [];
module.exports = {
babelrcRoots: ["./src/core/client/*"],
env: {
production: {
plugins: [...lodashOptimizations],
@@ -18,10 +19,7 @@ module.exports = {
},
test: {
presets: [
[
"@babel/env",
{ targets: "last 2 versions, ie 11", modules: "commonjs" },
],
["@babel/env", { targets: { node: "current" } }],
"@babel/react",
],
plugins: ["dynamic-import-node"],
+14 -9
View File
@@ -10,8 +10,7 @@ module.exports = {
"<rootDir>/src/core/build/polyfills.js",
"<rootDir>/src/core/client/test/setup.ts",
],
setupTestFrameworkScriptFile:
"<rootDir>/src/core/client/test/setupTestFramework.ts",
setupFilesAfterEnv: ["<rootDir>/src/core/client/test/setupTestFramework.ts"],
testMatch: ["**/*.spec.{js,jsx,mjs,ts,tsx}"],
testEnvironment: "node",
testURL: "http://localhost",
@@ -27,19 +26,25 @@ module.exports = {
"[/\\\\]node_modules[/\\\\](?!(fluent|react-relay-network-modern)[/\\\\]).+\\.(js|jsx|mjs|ts|tsx)$",
],
moduleNameMapper: {
"^talk-admin/(.*)$": "<rootDir>/src/core/client/admin/$1",
"^talk-auth/(.*)$": "<rootDir>/src/core/client/auth/$1",
"^talk-ui/(.*)$": "<rootDir>/src/core/client/ui/$1",
"^talk-stream/(.*)$": "<rootDir>/src/core/client/stream/$1",
"^talk-framework/(.*)$": "<rootDir>/src/core/client/framework/$1",
"^talk-common/(.*)$": "<rootDir>/src/core/common/$1",
"^coral-account/(.*)$": "<rootDir>/src/core/client/account/$1",
"^coral-admin/(.*)$": "<rootDir>/src/core/client/admin/$1",
"^coral-auth/(.*)$": "<rootDir>/src/core/client/auth/$1",
"^coral-count/(.*)$": "<rootDir>/src/core/client/count/$1",
"^coral-ui/(.*)$": "<rootDir>/src/core/client/ui/$1",
"^coral-stream/(.*)$": "<rootDir>/src/core/client/stream/$1",
"^coral-framework/(.*)$": "<rootDir>/src/core/client/framework/$1",
"^coral-common/(.*)$": "<rootDir>/src/core/common/$1",
"^coral-test/(.*)$": "<rootDir>/src/core/client/test/$1",
},
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node", "ftl"],
snapshotSerializers: ["enzyme-to-json/serializer"],
globals: {
"ts-jest": {
useBabelrc: true,
tsConfigFile: path.resolve(__dirname, "tsconfig.jest.json"),
tsConfigFile: path.resolve(
__dirname,
"../../src/core/client/tsconfig.json"
),
},
},
};
+6 -2
View File
@@ -1,9 +1,12 @@
const path = require("path");
module.exports = {
displayName: "server",
rootDir: "../../",
roots: ["<rootDir>/src"],
collectCoverageFrom: ["**/*.{js,jsx,mjs,ts,tsx}"],
coveragePathIgnorePatterns: ["/node_modules/"],
setupFilesAfterEnv: ["<rootDir>/src/core/server/test/setupTestFramework.ts"],
testMatch: ["**/*.spec.{js,jsx,mjs,ts,tsx}"],
testPathIgnorePatterns: ["/node_modules/", "/client/"],
testEnvironment: "node",
@@ -12,13 +15,14 @@ module.exports = {
"^.+\\.tsx?$": "<rootDir>/node_modules/ts-jest",
},
moduleNameMapper: {
"^talk-server/(.*)$": "<rootDir>/src/core/server/$1",
"^talk-common/(.*)$": "<rootDir>/src/core/common/$1",
"^coral-server/(.*)$": "<rootDir>/src/core/server/$1",
"^coral-common/(.*)$": "<rootDir>/src/core/common/$1",
},
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
globals: {
"ts-jest": {
useBabelrc: true,
tsConfigFile: path.resolve(__dirname, "../../src/tsconfig.json"),
},
},
};
-7
View File
@@ -1,7 +0,0 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"target": "es2015",
"module": "es2015"
}
}
+55 -11
View File
@@ -27,7 +27,24 @@ const config: Config = {
"**/test/**/*",
"core/**/*.spec.*",
],
executor: new CommandExecutor("npm run generate:relay-stream", {
executor: new CommandExecutor("npm run --silent generate:relay:stream", {
runOnInit: true,
}),
},
generateRelayAccount: {
paths: [
"core/client/account/**/*.ts",
"core/client/account/**/*.tsx",
"core/client/account/**/*.graphql",
"core/server/**/*.graphql",
],
ignore: [
"core/**/*.d.ts",
"core/**/*.graphql.ts",
"**/test/**/*",
"core/**/*.spec.*",
],
executor: new CommandExecutor("npm run generate:relay:account", {
runOnInit: true,
}),
},
@@ -44,7 +61,7 @@ const config: Config = {
"**/test/**/*",
"core/**/*.spec.*",
],
executor: new CommandExecutor("npm run generate:relay-admin", {
executor: new CommandExecutor("npm run --silent generate:relay:admin", {
runOnInit: true,
}),
},
@@ -61,7 +78,7 @@ const config: Config = {
"**/test/**/*",
"core/**/*.spec.*",
],
executor: new CommandExecutor("npm run generate:relay-auth", {
executor: new CommandExecutor("npm run --silent generate:relay:auth", {
runOnInit: true,
}),
},
@@ -78,40 +95,67 @@ const config: Config = {
"**/test/**/*",
"core/**/*.spec.*",
],
executor: new CommandExecutor("npm run generate:relay-install", {
executor: new CommandExecutor("npm run --silent generate:relay:install", {
runOnInit: true,
}),
},
generateCSSTypes: {
paths: ["**/*.css"],
executor: new CommandExecutor("npm run generate:css-types", {
executor: new CommandExecutor("npm run --silent generate:css-types", {
runOnInit: true,
}),
},
runServer: {
paths: ["core/**/*.ts", "core/locales/**/*.ftl"],
paths: ["core/server/locales/**/*.ftl"],
ignore: ["core/client/**/*"],
executor: new LongRunningExecutor("npm run start:development"),
executor: new LongRunningExecutor("npm run --silent start:development"),
},
runServerWithoutClientRoutes: {
paths: ["core/server/locales/**/*.ftl"],
ignore: ["core/client/**/*"],
executor: new LongRunningExecutor(
"DISABLE_CLIENT_ROUTES=true npm run --silent start:development"
),
},
runServerLint: {
paths: ["core/**/*.ts"],
ignore: ["core/client/**/*"],
executor: new LongRunningExecutor("npm run --silent lint:server"),
},
runServerSyntaxCheck: {
paths: ["core/**/*.ts"],
ignore: ["core/client/**/*"],
executor: new LongRunningExecutor("npm run --silent tscheck:server"),
},
runWebpackDevServer: {
paths: [],
executor: new LongRunningExecutor("npm run start:webpackDevServer"),
executor: new LongRunningExecutor(
"npm run --silent start:webpackDevServer"
),
},
runDocz: {
paths: [],
executor: new LongRunningExecutor("npm run docz -- dev"),
executor: new LongRunningExecutor("npm run --silent docz -- dev"),
},
},
defaultSet: "client",
sets: {
server: ["generateSchemaTypes", "runServer"],
client: [
server: [
"generateSchemaTypes",
"runServer",
"runServerLint",
"runServerSyntaxCheck",
],
client: [
"runServerWithoutClientRoutes",
"runServerLint",
"runServerSyntaxCheck",
"runWebpackDevServer",
"generateCSSTypes",
"generateRelayStream",
"generateRelayAuth",
"generateRelayInstall",
"generateRelayAccount",
"generateRelayAdmin",
"generateSchemaTypes",
],
+20 -1
View File
@@ -1,4 +1,5 @@
import errorOverlayMiddleware from "react-dev-utils/errorOverlayMiddleware";
import evalSourceMapMiddleware from "react-dev-utils/evalSourceMapMiddleware";
import ignoredFiles from "react-dev-utils/ignoredFiles";
import noopServiceWorkerMiddleware from "react-dev-utils/noopServiceWorkerMiddleware";
import { Configuration } from "webpack-dev-server";
@@ -16,6 +17,12 @@ export default function({
publicPath,
}: WebpackDevServerConfig): Configuration {
return {
stats: {
// https://github.com/TypeStrong/ts-loader#transpileonly-boolean-defaultfalse
// Using transpilation only without typechecks gives warnings when we reexport types.
// We can ignore them here.
warningsFilter: /export .* was not found in/,
},
// Enable gzip compression of generated files.
compress: true,
// Silence WebpackDevServer's own logs since they're generally not useful.
@@ -61,19 +68,31 @@ export default function({
historyApiFallback: {
disableDotRule: true,
rewrites: [
{ from: /^\/account/, to: "/account.html" },
{ from: /^\/admin/, to: "/admin.html" },
{ from: /^\/embed\/stream/, to: "/stream.html" },
{ from: /^\/embed\/auth$/, to: "/auth.html" },
{ from: /^\/embed\/auth\/callback/, to: "/auth-callback.html" },
{ from: /^\/install/, to: "/install.html" },
],
},
public: allowedHost,
index: "embed.html",
proxy: {
// Proxy websocket connections.
"/api/graphql/live": {
target: `ws://localhost:${serverPort}`,
ws: true,
},
// Proxy to the graphql server.
"/api": {
target: `http://localhost:${serverPort}`,
},
},
before(app: any) {
before(app, server) {
// This lets us fetch source contents from webpack for the error overlay
app.use(evalSourceMapMiddleware(server));
// This lets us open files from the runtime error overlay.
app.use(errorOverlayMiddleware());
// This service worker file is effectively a 'no-op' that will reset any
+64 -12
View File
@@ -1,40 +1,92 @@
// Apply all the configuration provided in the .env file.
require("dotenv").config();
const path = require("path");
const fs = require("fs");
const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin");
const extensions = [".ts", ".tsx", ".js"];
// TODO: There is some weird issue with including paths.ts here
const postCSSConfigPath = "./src/core/build/postcss.config";
const appDirectory = fs.realpathSync(process.cwd());
export default {
title: "Talk 5.0",
const styleLoader = {
loader: require.resolve("style-loader"),
options: {
sourceMap: true,
hmr: true,
},
};
module.exports = {
title: "Coral 5.0",
source: "./src",
typescript: true,
host: process.env.HOST || "0.0.0.0",
port: parseInt(process.env.DOCZ_PORT, 10) || 3030,
codeSandbox: false, // Too large to create code sandboxes..
modifyBundlerConfig: config => {
config.entry.app.push(
`${appDirectory}/src/core/client/ui/theme/variables.css.ts`
);
config.module.rules.push({
test: /\.css$/,
test: /\.css\.ts$/,
use: [
require.resolve("style-loader"),
styleLoader,
{
loader: require.resolve("css-loader"),
options: {
modules: true,
importLoaders: 1,
localIdentName:
process.env.NODE_ENV === "production"
? "[hash:base64]"
: "[name]-[local]-[hash:base64:5]",
importLoaders: 2,
localIdentName: "[name]-[local]-[hash:base64:5]",
sourceMap: true,
},
},
{
loader: require.resolve("postcss-loader"),
options: {
config: {
// TODO: There is some weird issue with including paths.ts here
path: "./src/core/build/postcss.config",
path: postCSSConfigPath,
},
parser: "postcss-js",
},
},
{
loader: require.resolve("babel-loader"),
options: {
configFile: false,
babelrc: false,
presets: [
"@babel/typescript",
[
"@babel/env",
{ targets: { node: "12.0.0" }, modules: "commonjs" },
],
],
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
// directory for faster rebuilds.
cacheDirectory: true,
},
},
],
});
config.module.rules.push({
test: /\.css$/,
use: [
styleLoader,
{
loader: require.resolve("css-loader"),
options: {
modules: true,
importLoaders: 1,
localIdentName: "[name]-[local]-[hash:base64:5]",
sourceMap: true,
},
},
{
loader: require.resolve("postcss-loader"),
options: {
config: {
path: postCSSConfigPath,
},
},
},
+586
View File
@@ -0,0 +1,586 @@
## Viewer Events
_Viewer Events_ are emitted when the viewer performs certain actions.
They can be subscribed to using the `events` parameter in
`Coral.createStreamEmbed`.
```html
<script>
const CoralStreamEmbed = Coral.createStreamEmbed({
events: function(events) {
events.onAny(function(eventName, data) {
console.log(eventName, data);
});
},
});
</script>
```
Example events:
- `setMainTab {tab: "PROFILE"}`
- `showFeaturedCommentTooltip`
- `viewConversation {from: "FEATURED_COMMENTS", commentID: "c45fb5f5-03f9-49a3-a755-488c698ca0df"}`
### Viewer Network Events
_Viewer Network Events_ are events that involves a network request and thus can succeed or fail. Succeeding events will have a `.success` appended to the event name while failing events have an `.error` appended to the event name.
Moreover _Viewer Network Events_ contains the `rtt` field which indicates the time it needed from initiating the request until the _UI_ has been updated with the response data.
Example events:
```
createComment.success
{
body: "Hello world!",
storyID: "238b95ec-2b80-43f4-ab68-a6ea1f4e2584",
rtt: 307,
success: {
id: "6fecfb11-4d0f-4edc-89b7-878a9928addd"
status: "APPROVED"`
}
}
```
```
createComment.error
{
body: "Hi!",
storyID: "238b95ec-2b80-43f4-ab68-a6ea1f4e2584",
rtt: 229,
error: {
code: "COMMENT_BODY_TOO_SHORT"
message: "Comment body must have at least 10 characters."
}
}
```
## Event List
<!-- START docs:events -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN npm run docs:events -->
### Index
- <a href="#approveComment">approveComment</a>
- <a href="#banUser">banUser</a>
- <a href="#cancelAccountDeletion">cancelAccountDeletion</a>
- <a href="#changeEmail">changeEmail</a>
- <a href="#changePassword">changePassword</a>
- <a href="#changeUsername">changeUsername</a>
- <a href="#closeStory">closeStory</a>
- <a href="#copyPermalink">copyPermalink</a>
- <a href="#createComment">createComment</a>
- <a href="#createCommentFocus">createCommentFocus</a>
- <a href="#createCommentReaction">createCommentReaction</a>
- <a href="#createCommentReply">createCommentReply</a>
- <a href="#editComment">editComment</a>
- <a href="#featureComment">featureComment</a>
- <a href="#gotoModeration">gotoModeration</a>
- <a href="#ignoreUser">ignoreUser</a>
- <a href="#loadMoreAllComments">loadMoreAllComments</a>
- <a href="#loadMoreFeaturedComments">loadMoreFeaturedComments</a>
- <a href="#loadMoreHistoryComments">loadMoreHistoryComments</a>
- <a href="#loginPrompt">loginPrompt</a>
- <a href="#openSortMenu">openSortMenu</a>
- <a href="#openStory">openStory</a>
- <a href="#rejectComment">rejectComment</a>
- <a href="#removeCommentReaction">removeCommentReaction</a>
- <a href="#removeUserIgnore">removeUserIgnore</a>
- <a href="#replyCommentFocus">replyCommentFocus</a>
- <a href="#reportComment">reportComment</a>
- <a href="#requestAccountDeletion">requestAccountDeletion</a>
- <a href="#requestDownloadCommentHistory">requestDownloadCommentHistory</a>
- <a href="#resendEmailVerification">resendEmailVerification</a>
- <a href="#setCommentsOrderBy">setCommentsOrderBy</a>
- <a href="#setCommentsTab">setCommentsTab</a>
- <a href="#setMainTab">setMainTab</a>
- <a href="#setProfileTab">setProfileTab</a>
- <a href="#showAbsoluteTimestamp">showAbsoluteTimestamp</a>
- <a href="#showAllReplies">showAllReplies</a>
- <a href="#showAuthPopup">showAuthPopup</a>
- <a href="#showEditEmailDialog">showEditEmailDialog</a>
- <a href="#showEditForm">showEditForm</a>
- <a href="#showEditPasswordDialog">showEditPasswordDialog</a>
- <a href="#showEditUsernameDialog">showEditUsernameDialog</a>
- <a href="#showFeaturedCommentTooltip">showFeaturedCommentTooltip</a>
- <a href="#showIgnoreUserdDialog">showIgnoreUserdDialog</a>
- <a href="#showModerationPopover">showModerationPopover</a>
- <a href="#showMoreOfConversation">showMoreOfConversation</a>
- <a href="#showMoreReplies">showMoreReplies</a>
- <a href="#showReplyForm">showReplyForm</a>
- <a href="#showReportPopover">showReportPopover</a>
- <a href="#showSharePopover">showSharePopover</a>
- <a href="#showUserPopover">showUserPopover</a>
- <a href="#signOut">signOut</a>
- <a href="#unfeatureComment">unfeatureComment</a>
- <a href="#updateNotificationSettings">updateNotificationSettings</a>
- <a href="#updateStorySettings">updateStorySettings</a>
- <a href="#viewConversation">viewConversation</a>
- <a href="#viewFullDiscussion">viewFullDiscussion</a>
- <a href="#viewNewComments">viewNewComments</a>
### Events
- <a id="approveComment">**approveComment.success**, **approveComment.error**</a>: This event is emitted when the viewer approves a comment.
```ts
{
commentID: string;
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="banUser">**banUser.success**, **banUser.error**</a>: This event is emitted when the viewer bans a user.
```ts
{
userID: string;
commentID: string;
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="cancelAccountDeletion">**cancelAccountDeletion.success**, **cancelAccountDeletion.error**</a>: This event is emitted when the viewer cancels the account deletion.
```ts
{
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="changeEmail">**changeEmail.success**, **changeEmail.error**</a>: This event is emitted when the viewer changes its email.
```ts
{
oldEmail: string;
newEmail: string;
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="changePassword">**changePassword.success**, **changePassword.error**</a>: This event is emitted when the viewer changes its password.
```ts
{
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="changeUsername">**changeUsername.success**, **changeUsername.error**</a>: This event is emitted when the viewer changes its username.
```ts
{
oldUsername: string;
newUsername: string;
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="closeStory">**closeStory.success**, **closeStory.error**</a>: This event is emitted when the viewer closes the story.
```ts
{
storyID: string;
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="copyPermalink">**copyPermalink**</a>: This event is emitted when the viewer copies the permalink with the button.
```ts
{
commentID: string;
}
```
- <a id="createComment">**createComment.success**, **createComment.error**</a>: This event is emitted when a top level comment is created.
```ts
{
storyID: string;
body: string;
success: {
id: string;
status: COMMENT_STATUS;
};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="createCommentFocus">**createCommentFocus**</a>: This event is emitted when the viewer focus on the RTE to create a comment.
- <a id="createCommentReaction">**createCommentReaction.success**, **createCommentReaction.error**</a>: This event is emitted when the viewer reacts to a comment.
```ts
{
commentID: string;
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="createCommentReply">**createCommentReply.success**, **createCommentReply.error**</a>: This event is emitted when a comment reply is created.
```ts
{
body: string;
parentID: string;
success: {
id: string;
status: COMMENT_STATUS;
};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="editComment">**editComment.success**, **editComment.error**</a>: This event is emitted when the viewer edits a comment.
```ts
{
body: string;
commentID: string;
success: {
status: COMMENT_STATUS;
};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="featureComment">**featureComment.success**, **featureComment.error**</a>: This event is emitted when the viewer features a comment.
```ts
{
commentID: string;
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="gotoModeration">**gotoModeration**</a>: This event is emitted when the viewer goes to moderation.
```ts
{
commentID: string;
}
```
- <a id="ignoreUser">**ignoreUser.success**, **ignoreUser.error**</a>: This event is emitted when the viewer ignores a user.
```ts
{
userID: string;
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="loadMoreAllComments">**loadMoreAllComments.success**, **loadMoreAllComments.error**</a>: This event is emitted when the viewer loads more top level comments into the comment stream.
```ts
{
storyID: string;
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="loadMoreFeaturedComments">**loadMoreFeaturedComments.success**, **loadMoreFeaturedComments.error**</a>: This event is emitted when the viewer loads more featured comments.
```ts
{
storyID: string;
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="loadMoreHistoryComments">**loadMoreHistoryComments.success**, **loadMoreHistoryComments.error**</a>: This event is emitted when the viewer loads more top level comments into the history comment stream.
```ts
{
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="loginPrompt">**loginPrompt**</a>: This event is emitted when the viewer does an action that will prompt a login dialog.
- <a id="openSortMenu">**openSortMenu**</a>: This event is emitted when the viewer clicks on the sort menu.
- <a id="openStory">**openStory.success**, **openStory.error**</a>: This event is emitted when the viewer opens the story.
```ts
{
storyID: string;
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="rejectComment">**rejectComment.success**, **rejectComment.error**</a>: This event is emitted when the viewer rejects a comment.
```ts
{
commentID: string;
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="removeCommentReaction">**removeCommentReaction.success**, **removeCommentReaction.error**</a>: This event is emitted when the viewer removes its reaction from a comment.
```ts
{
commentID: string;
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="removeUserIgnore">**removeUserIgnore.success**, **removeUserIgnore.error**</a>: This event is emitted when the viewer remove a user from its ignored users list.
```ts
{
userID: string;
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="replyCommentFocus">**replyCommentFocus**</a>: This event is emitted when the viewer focus on the RTE to reply to a comment.
- <a id="reportComment">**reportComment.success**, **reportComment.error**</a>: This event is emitted when the viewer reports a comment.
```ts
{
reason: string;
commentID: string;
additionalDetails?: string | undefined;
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="requestAccountDeletion">**requestAccountDeletion.success**, **requestAccountDeletion.error**</a>: This event is emitted when the viewer requests to delete its account.
```ts
{
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="requestDownloadCommentHistory">**requestDownloadCommentHistory.success**, **requestDownloadCommentHistory.error**</a>: This event is emitted when the viewer requests to download its comment history.
```ts
{
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="resendEmailVerification">**resendEmailVerification.success**, **resendEmailVerification.error**</a>: This event is emitted when the viewer request another email verification email.
```ts
{
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="setCommentsOrderBy">**setCommentsOrderBy**</a>: This event is emitted when the viewer changes the sort order of the comments.
```ts
{
orderBy: string;
}
```
- <a id="setCommentsTab">**setCommentsTab**</a>: This event is emitted when the viewer changes the tab of the comments tab bar.
```ts
{
tab: string;
}
```
- <a id="setMainTab">**setMainTab**</a>: This event is emitted when the viewer changes the tab of the main tab bar.
```ts
{
tab: string;
}
```
- <a id="setProfileTab">**setProfileTab**</a>: This event is emitted when the viewer changes the tab of the profile tab bar.
```ts
{
tab: string;
}
```
- <a id="showAbsoluteTimestamp">**showAbsoluteTimestamp**</a>: This event is emitted when the viewer clicks on the relative timestamp to show the absolute time.
- <a id="showAllReplies">**showAllReplies.success**, **showAllReplies.error**</a>: This event is emitted when the viewer reveals all replies of a comment.
```ts
{
commentID: string;
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="showAuthPopup">**showAuthPopup**</a>: This event is emitted when the viewer requests the auth popup.
```ts
{
view: string;
}
```
- <a id="showEditEmailDialog">**showEditEmailDialog**</a>: This event is emitted when the viewer opens the edit email dialog.
- <a id="showEditForm">**showEditForm**</a>: This event is emitted when the viewer opens the edit form.
```ts
{
commentID: string;
}
```
- <a id="showEditPasswordDialog">**showEditPasswordDialog**</a>: This event is emitted when the viewer opens the edit password dialog.
- <a id="showEditUsernameDialog">**showEditUsernameDialog**</a>: This event is emitted when the viewer opens the edit username dialog.
- <a id="showFeaturedCommentTooltip">**showFeaturedCommentTooltip**</a>: This event is emitted when the viewer clicks to show the featured comment tooltip.
- <a id="showIgnoreUserdDialog">**showIgnoreUserdDialog**</a>: This event is emitted when the viewer opens the ignore user dialog.
- <a id="showModerationPopover">**showModerationPopover**</a>: This event is emitted when the viewer opens the moderation popover.
```ts
{
commentID: string;
}
```
- <a id="showMoreOfConversation">**showMoreOfConversation.success**, **showMoreOfConversation.error**</a>: This event is emitted when the viewer reveals more of the parent conversation thread.
```ts
{
commentID: string | null;
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="showMoreReplies">**showMoreReplies**</a>: This event is emitted when the viewer reveals new live replies.
```ts
{
commentID: string;
count: number;
}
```
- <a id="showReplyForm">**showReplyForm**</a>: This event is emitted when the viewer opens the reply form.
```ts
{
commentID: string;
}
```
- <a id="showReportPopover">**showReportPopover**</a>: This event is emitted when the viewer opens the report popover.
```ts
{
commentID: string;
}
```
- <a id="showSharePopover">**showSharePopover**</a>: This event is emitted when the viewer opens the share popover.
```ts
{
commentID: string;
}
```
- <a id="showUserPopover">**showUserPopover**</a>: This event is emitted when the viewer clicks on a username which shows the user popover.
```ts
{
userID: string;
}
```
- <a id="signOut">**signOut.success**, **signOut.error**</a>: This event is emitted when the viewer signs out.
```ts
{
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="unfeatureComment">**unfeatureComment.success**, **unfeatureComment.error**</a>: This event is emitted when the viewer unfeatures a comment.
```ts
{
commentID: string;
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="updateNotificationSettings">**updateNotificationSettings.success**, **updateNotificationSettings.error**</a>: This event is emitted when the viewer updates its notification settings.
```ts
{
onReply?: boolean | null | undefined;
onFeatured?: boolean | null | undefined;
onStaffReplies?: boolean | null | undefined;
onModeration?: boolean | null | undefined;
digestFrequency?: "NONE" | "DAILY" | "HOURLY" | null | undefined;
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="updateStorySettings">**updateStorySettings.success**, **updateStorySettings.error**</a>: This event is emitted when the viewer updates the story settings.
```ts
{
storyID: string;
live?: {
enabled?: boolean | null | undefined;
} | null | undefined;
moderation?: "POST" | "PRE" | null | undefined;
premodLinksEnable?: boolean | null | undefined;
messageBox?: {
enabled?: boolean | null | undefined;
icon?: string | null | undefined;
content?: string | null | undefined;
} | null | undefined;
success: {};
error: {
message: string;
code?: string | undefined;
};
}
```
- <a id="viewConversation">**viewConversation**</a>: This event is emitted when the viewer changes to the single conversation view.
```ts
{
from: "FEATURED_COMMENTS" | "COMMENT_STREAM" | "COMMENT_HISTORY";
commentID: string;
}
```
- <a id="viewFullDiscussion">**viewFullDiscussion**</a>: This event is emitted when the viewer exits the single conversation.
```ts
{
commentID: string | null;
}
```
- <a id="viewNewComments">**viewNewComments**</a>: This event is emitted when the viewer reveals new live comments.
```ts
{
storyID: string;
count: number;
}
```
<!-- END docs:events -->
+10 -1
View File
@@ -67,9 +67,18 @@ gulp.task("server:static", () =>
.pipe(gulp.dest(resolveDistFolder() + "/core/server"))
);
gulp.task("locales", () =>
gulp
.src(["./src/locales/**/*"])
.pipe(gulp.dest(resolveDistFolder() + "/locales"))
);
gulp.task(
"server",
gulp.series("server:schema", gulp.parallel("server:scripts", "server:static"))
gulp.series(
"server:schema",
gulp.parallel("server:scripts", "server:static", "locales")
)
);
gulp.task("default", gulp.series("clean", "server"));
+15228 -7106
View File
File diff suppressed because it is too large Load Diff
+229 -156
View File
@@ -1,6 +1,6 @@
{
"name": "@coralproject/talk",
"version": "5.0.0-beta.1",
"version": "5.4.2",
"author": "The Coral Project",
"homepage": "https://coralproject.net/",
"sideEffects": [
@@ -11,322 +11,395 @@
"url": "git://github.com/coralproject/talk.git"
},
"engines": {
"node": ">=10.0.0",
"npm": ">=6.7.0"
"node": "^12",
"npm": "^6"
},
"bugs": "https://github.com/coralproject/talk/issues",
"contributors": "https://github.com/coralproject/talk/graphs/contributors",
"contributors": [
"https://github.com/coralproject/talk/graphs/contributors"
],
"description": "A better commenting experience from Mozilla, The Washington Post, and The New York Times.",
"scripts": {
"build": "npm-run-all generate --parallel build:*",
"build:client": "ts-node ./scripts/build.ts",
"build": "NODE_ENV=production npm-run-all generate-persist --parallel build:client build:server",
"build:development": "NODE_ENV=development npm-run-all generate --parallel build:client build:server",
"build:client": "ts-node --transpile-only ./scripts/build.ts",
"build:server": "gulp server",
"migration:create": "ts-node --transpile-only ./scripts/migration/create.ts",
"doctoc": "doctoc --title='## Table of Contents' --github README.md",
"generate": "npm-run-all --parallel generate:*",
"docs:events": "ts-node ./scripts/generateEventDocs.ts ./src/core/client/stream/events.ts ./events.md",
"generate": "npm-run-all generate:css-types generate:schema generate:relay",
"generate-persist": "npm-run-all generate:css-types generate:schema generate:relay-persist",
"generate:css-types": "tcm src/core/client/",
"generate:relay-stream": "ts-node ./scripts/compileRelay --src ./src/core/client/stream --schema tenant",
"generate:relay-auth": "ts-node ./scripts/compileRelay --src ./src/core/client/auth --schema tenant",
"generate:relay-install": "ts-node ./scripts/compileRelay --src ./src/core/client/install --schema tenant",
"generate:relay-admin": "ts-node ./scripts/compileRelay --src ./src/core/client/admin --schema tenant",
"generate:relay": "npm-run-all --parallel generate:relay:*",
"generate:relay-persist": "npm-run-all --parallel 'generate:relay:* -- --persist'",
"generate:relay:stream": "ts-node --transpile-only ./scripts/compileRelay --src ./src/core/client/stream --schema tenant",
"generate:relay:account": "ts-node --transpile-only ./scripts/compileRelay --src ./src/core/client/account --schema tenant",
"generate:relay:auth": "ts-node --transpile-only ./scripts/compileRelay --src ./src/core/client/auth --schema tenant",
"generate:relay:install": "ts-node --transpile-only ./scripts/compileRelay --src ./src/core/client/install --schema tenant",
"generate:relay:admin": "ts-node --transpile-only ./scripts/compileRelay --src ./src/core/client/admin --schema tenant",
"generate:schema": "node ./scripts/generateSchemaTypes.js",
"docz": "docz",
"start": "NODE_ENV=production node dist/index.js",
"start:development": "NODE_ENV=development CONCURRENCY=${CONCURRENCY:-2} TS_NODE_PROJECT=./src/tsconfig.json node -r ts-node/register -r tsconfig-paths/register ./src/index.ts",
"start:webpackDevServer": "ts-node ./scripts/start.ts",
"start:development": "NODE_ENV=development CONCURRENCY=${CONCURRENCY:-1} TS_NODE_PROJECT=./src/tsconfig.json ts-node-dev --inspect --transpile-only --no-notify -r tsconfig-paths/register ./src/index.ts",
"start:webpackDevServer": "ts-node --transpile-only ./scripts/start.ts",
"lint": "npm-run-all --parallel lint:* tscheck:*",
"lint:server": "tslint --project ./src/tsconfig.json",
"lint:client": "tslint --project ./src/core/client/tsconfig.json",
"lint:scripts": "tslint --project ./tsconfig.json",
"lint:graphql": "graphql-schema-linter src/core/server/graph/tenant/schema/schema.graphql",
"lint:server": "eslint 'src/**/*.{js,ts,tsx}' --ignore-pattern 'src/core/client/**'",
"lint:client": "eslint 'src/core/client/**/*.{js,ts,tsx}'",
"lint:scripts": "eslint 'scripts/**/*.{js,ts,tsx}'",
"lint:graphql": "graphql-schema-linter src/core/server/graph/schema/schema.graphql",
"lint-fix": "npm run lint:server -- --fix && npm run lint:client -- --fix && npm run lint:scripts -- --fix",
"test": "node scripts/test.js --env=jsdom",
"test": "node --trace-warnings scripts/test.js --env=jsdom",
"tscheck": "npm-run-all --parallel tscheck:*",
"tscheck:server": "tsc --project ./src/tsconfig.json --noEmit",
"tscheck:client": "tsc --project ./src/core/client/tsconfig.json --noEmit",
"tscheck:scripts": "tsc --project ./tsconfig.json --noEmit",
"watch": "NODE_ENV=development ts-node ./scripts/watcher/bin/watcher.ts --config ./config/watcher.ts"
"watch": "NODE_ENV=development ts-node --transpile-only ./scripts/watcher/bin/watcher.ts --config ./config/watcher.ts"
},
"license": "Apache-2.0",
"dependencies": {
"@coralproject/bunyan-prettystream": "^0.1.4",
"@fluent/bundle": "^0.14.0",
"@fluent/dom": "^0.6.0",
"@metascraper/helpers": "^5.7.21",
"abort-controller": "^3.0.0",
"akismet-api": "^4.2.0",
"apollo-server-express": "^2.1.0",
"apollo-server-express": "^2.8.1",
"archiver": "^3.0.3",
"basic-auth": "^2.0.1",
"bcryptjs": "^2.4.3",
"bull": "^3.4.4",
"bull": "^3.8.1",
"bunyan": "^1.8.12",
"cheerio": "^1.0.0-rc.2",
"consolidate": "0.14.0",
"content-security-policy-builder": "^2.0.0",
"convict": "^4.3.1",
"cookie": "^0.4.0",
"cookie-parser": "^1.4.4",
"cors": "^2.8.4",
"cron": "^1.7.1",
"csv-stringify": "^5.3.0",
"dataloader": "^1.4.0",
"dompurify": "^1.0.8",
"dompurify": "^2.0.7",
"dotenv": "^6.0.0",
"dotenv-expand": "^4.2.0",
"env-rewrite": "^1.0.2",
"express": "^4.16.3",
"express-enforces-ssl": "^1.1.0",
"express-static-gzip": "^0.3.2",
"farce": "^0.2.6",
"fluent": "^0.10.0",
"found": "^0.3.21",
"found-relay": "^0.3.1",
"fs-extra": "^6.0.1",
"graphql": "^0.13.2",
"graphql-config": "^2.0.1",
"graphql-extensions": "^0.2.1",
"graphql-fields": "^1.1.0",
"graphql-import": "^0.7.1",
"graphql-playground-html": "^1.6.0",
"graphql-redis-subscriptions": "^2.1.0",
"graphql-subscriptions": "^1.1.0",
"graphql-tools": "^3.0.5",
"helmet": "^3.21.2",
"html-minifier": "^3.5.21",
"html-to-text": "^4.0.0",
"ioredis": "^3.2.2",
"ioredis": "^4.9.0",
"joi": "^13.4.0",
"jsdom": "^11.12.0",
"jsonwebtoken": "^8.3.0",
"jwks-rsa": "^1.3.0",
"linkify-it": "^2.1.0",
"linkifyjs": "^2.1.7",
"lodash": "^4.17.10",
"luxon": "^1.3.1",
"metascraper-author": "^3.11.8",
"metascraper-date": "^3.11.4",
"metascraper-description": "^3.11.8",
"metascraper-image": "^3.11.8",
"metascraper-title": "^3.11.8",
"mongodb": "^3.2.1",
"mongodb-core": "^3.2.3",
"jsdom": "^15.0.0",
"jsonwebtoken": "^8.5.1",
"juice": "^5.2.0",
"jwks-rsa": "^1.6.0",
"keymaster": "^1.6.2",
"linkifyjs": "^2.1.8",
"lodash": "^4.17.15",
"lru-cache": "^5.1.1",
"luxon": "^1.12.0",
"metascraper-author": "^5.8.0",
"metascraper-description": "^5.8.0",
"metascraper-image": "^5.8.0",
"metascraper-title": "^5.8.0",
"mongodb": "^3.2.7",
"mongodb-core": "^3.2.7",
"ms": "^2.1.1",
"node-fetch": "^2.2.0",
"node-fetch": "^2.6.0",
"nodemailer": "^4.6.7",
"nunjucks": "^3.1.3",
"passport": "^0.4.0",
"passport-facebook": "^2.1.1",
"passport-google-oauth2": "^0.1.6",
"on-finished": "^2.3.0",
"passport": "^0.4.1",
"passport-facebook": "^3.0.0",
"passport-google-oauth2": "^0.2.0",
"passport-local": "^1.0.0",
"passport-oauth2": "^1.4.0",
"passport-strategy": "^1.0.0",
"performance-now": "^2.1.0",
"permit": "^0.2.4",
"prom-client": "^11.3.0",
"proxy-agent": "^3.1.1",
"querystringify": "^2.1.0",
"react-relay-network-modern": "^2.4.0",
"source-map-support": "^0.5.10",
"react-helmet": "^5.2.1",
"source-map-support": "^0.5.12",
"stack-utils": "^1.0.2",
"striptags": "^3.1.1",
"throng": "^4.0.0",
"tlds": "^1.203.1",
"uuid": "^3.3.2",
"tsscmp": "^1.0.6",
"url-regex": "^5.0.0",
"uuid": "^3.3.3",
"verror": "^1.10.0"
},
"devDependencies": {
"@babel/core": "^7.2.0",
"@babel/core": "^7.4.5",
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/plugin-transform-modules-commonjs": "^7.2.0",
"@babel/polyfill": "^7.0.0",
"@babel/preset-env": "^7.2.0",
"@babel/plugin-transform-modules-commonjs": "^7.4.4",
"@babel/polyfill": "^7.4.4",
"@babel/preset-env": "^7.4.5",
"@babel/preset-react": "^7.0.0",
"@coralproject/rte": "^0.10.13",
"@babel/preset-typescript": "^7.3.3",
"@coralproject/npm-run-all": "^4.1.5",
"@coralproject/rte": "^0.11.0",
"@fluent/react": "^0.10.0",
"@intervolga/optimize-cssnano-plugin": "^1.0.6",
"@types/agent-base": "^4.2.0",
"@types/archiver": "^3.0.0",
"@types/basic-auth": "^1.1.2",
"@types/bcryptjs": "^2.4.1",
"@types/bull": "^3.3.16",
"@types/bull": "^3.5.12",
"@types/bunyan": "^1.8.4",
"@types/case-sensitive-paths-webpack-plugin": "^2.1.2",
"@types/cheerio": "^0.22.8",
"@types/chokidar": "^1.7.5",
"@types/classnames": "^2.2.4",
"@types/classnames": "^2.2.7",
"@types/commander": "^2.12.2",
"@types/compression-webpack-plugin": "^0.4.2",
"@types/common-tags": "^1.8.0",
"@types/compression-webpack-plugin": "^2.0.0",
"@types/consolidate": "0.0.34",
"@types/convict": "^4.2.0",
"@types/cookie": "^0.3.3",
"@types/cookie-parser": "^1.4.1",
"@types/cors": "^2.8.4",
"@types/cron": "^1.7.1",
"@types/cross-spawn": "^6.0.0",
"@types/dompurify": "0.0.33",
"@types/dotenv": "^4.0.3",
"@types/enzyme": "^3.1.15",
"@types/enzyme-adapter-react-16": "^1.0.3",
"@types/eventemitter2": "^4.1.0",
"@types/express": "^4.16.0",
"@types/express-enforces-ssl": "^1.1.1",
"@types/flat": "0.0.28",
"@types/fs-extra": "^5.0.4",
"@types/graphql": "^0.13.3",
"@types/helmet": "0.0.45",
"@types/html-minifier": "^3.5.2",
"@types/html-to-text": "^1.4.31",
"@types/html-webpack-plugin": "^3.2.0",
"@types/ioredis": "^3.2.12",
"@types/jest": "^23.1.5",
"@types/ioredis": "^4.0.10",
"@types/jest": "^24.0.23",
"@types/jest-axe": "^3.2.1",
"@types/joi": "^13.0.8",
"@types/jsdom": "^11.12.0",
"@types/jsonwebtoken": "^7.2.7",
"@types/linkify-it": "^2.0.4",
"@types/linkifyjs": "^2.1.0",
"@types/jsdom": "^12.2.3",
"@types/jsonwebtoken": "^8.3.5",
"@types/linkifyjs": "^2.1.1",
"@types/lodash": "^4.14.118",
"@types/lru-cache": "^5.1.0",
"@types/luxon": "^1.12.0",
"@types/marked": "^0.6.0",
"@types/mini-css-extract-plugin": "^0.2.0",
"@types/mongodb": "^3.1.22",
"@types/ms": "^0.7.30",
"@types/node": "^10.5.2",
"@types/node-fetch": "^2.1.2",
"@types/node-fetch": "^2.5.3",
"@types/nodemailer": "^4.6.2",
"@types/nunjucks": "^3.0.0",
"@types/passport": "^0.4.6",
"@types/nunjucks": "^3.1.1",
"@types/object-diff": "0.0.0",
"@types/on-finished": "^2.3.1",
"@types/passport": "^1.0.2",
"@types/passport-facebook": "^2.1.8",
"@types/passport-local": "^1.0.33",
"@types/passport-oauth2": "^1.4.5",
"@types/passport-strategy": "^0.2.33",
"@types/permit": "^0.2.1",
"@types/prettier": "^1.19.0",
"@types/prop-types": "^15.5.8",
"@types/react": "^16.8.10",
"@types/react-copy-to-clipboard": "^4.2.5",
"@types/react-dom": "^16.8.3",
"@types/react-relay": "^1.3.9",
"@types/react": "^16.8.15",
"@types/react-axe": "^3.1.0",
"@types/react-copy-to-clipboard": "^4.2.6",
"@types/react-dom": "^16.8.4",
"@types/react-helmet": "^5.0.10",
"@types/react-relay": "^1.3.14",
"@types/react-responsive": "^3.0.1",
"@types/react-test-renderer": "^16.8.1",
"@types/react-transition-group": "^2.0.14",
"@types/recompose": "^0.26.5",
"@types/relay-runtime": "^1.3.6",
"@types/relay-runtime": "^1.3.12",
"@types/sane": "^2.0.0",
"@types/shallow-equals": "^1.0.0",
"@types/simplemde": "^1.11.7",
"@types/sinon": "^5.0.1",
"@types/source-map-support": "^0.4.1",
"@types/sinon": "^7.0.11",
"@types/source-map-support": "^0.5.0",
"@types/stack-trace": "0.0.29",
"@types/stack-utils": "^1.0.1",
"@types/throng": "^4.0.2",
"@types/tlds": "^1.199.0",
"@types/uuid": "^3.4.3",
"@types/uuid": "^3.4.4",
"@types/verror": "^1.10.3",
"@types/vinyl": "^2.0.2",
"@types/webpack": "^4.4.7",
"@types/webpack": "^4.4.31",
"@types/webpack-assets-manifest": "^3.0.0",
"@types/webpack-bundle-analyzer": "^2.13.0",
"@types/webpack-dev-server": "^2.9.5",
"@types/webpack-bundle-analyzer": "^2.13.1",
"@types/webpack-dev-server": "^3.1.5",
"@types/ws": "^5.1.2",
"autoprefixer": "^8.6.5",
"@typescript-eslint/eslint-plugin": "2.3.3",
"@typescript-eslint/eslint-plugin-tslint": "2.3.3",
"@typescript-eslint/parser": "2.3.3",
"acorn": "^6.1.1",
"ansi-styles": "^3.2.0",
"autoprefixer": "^9.5.1",
"babel-core": "^7.0.0-bridge.0",
"babel-jest": "^23.6.0",
"babel-loader": "^8.0.4",
"babel-jest": "^24.8.0",
"babel-loader": "^8.0.5",
"babel-plugin-dynamic-import-node": "^2.2.0",
"babel-plugin-lodash": "^3.3.4",
"babel-plugin-module-resolver": "^3.1.1",
"babel-plugin-relay": "^1.7.0",
"babel-plugin-module-resolver": "^3.2.0",
"babel-plugin-relay": "^4.0.0",
"babel-plugin-use-lodash-es": "^0.2.0",
"babel-preset-react-optimize": "^1.0.1",
"bowser": "^1.9.4",
"case-sensitive-paths-webpack-plugin": "^2.1.2",
"chalk": "^2.4.1",
"chokidar": "^2.0.4",
"classnames": "^2.2.5",
"commander": "^2.16.0",
"case-sensitive-paths-webpack-plugin": "^2.2.0",
"chalk": "^2.4.2",
"chokidar": "^3.0.0",
"classnames": "^2.2.6",
"commander": "^2.20.0",
"comment-json": "^1.1.3",
"compression-webpack-plugin": "^1.1.11",
"copy-webpack-plugin": "^4.5.1",
"common-tags": "^1.8.0",
"compression-webpack-plugin": "^2.0.0",
"copy-webpack-plugin": "^5.0.3",
"cross-spawn": "^6.0.5",
"css-loader": "^1.0.1",
"del": "^3.0.0",
"del": "^4.1.1",
"doctoc": "^1.4.0",
"docz": "^0.13.7",
"docz-theme-default": "^0.13.7",
"enzyme": "^3.8.0",
"enzyme-adapter-react-16": "^1.7.1",
"enzyme": "^3.9.0",
"enzyme-adapter-react-16": "^1.12.1",
"enzyme-to-json": "^3.3.5",
"eslint": "^6.5.1",
"eslint-config-prettier": "^6.3.0",
"eslint-plugin-jsdoc": "^15.9.7",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-prettier": "^3.1.1",
"eslint-plugin-react": "^7.15.1",
"eventemitter2": "^5.0.1",
"final-form": "^4.8.1",
"farce": "^0.2.6",
"final-form": "4.18.6",
"final-form-arrays": "1.1.2",
"flat": "^4.1.0",
"fluent-intl-polyfill": "^0.1.0",
"fluent-langneg": "^0.1.0",
"fluent-react": "^0.8.3",
"fork-ts-checker-webpack-plugin": "^1.5.0",
"found": "^0.4.0-alpha.17",
"found-relay": "^0.4.0-alpha.8",
"graphql-schema-linter": "^0.2.0",
"graphql-schema-typescript": "^1.2.9",
"gulp": "^4.0.0",
"gulp": "^4.0.2",
"gulp-babel": "^8.0.0",
"gulp-cli": "^2.0.1",
"gulp-sourcemaps": "^2.6.4",
"gulp-typescript": "^5.0.0-alpha.3",
"gulp-cli": "^2.2.0",
"gulp-sourcemaps": "^2.6.5",
"gulp-typescript": "^5.0.1",
"html-webpack-plugin": "^4.0.0-beta.5",
"husky": "^1.1.0",
"intersection-observer": "^0.5.1",
"jest": "^23.4.1",
"jest-junit": "^5.1.0",
"jest-localstorage-mock": "^2.2.0",
"jest-mock-console": "^0.4.0",
"lint-staged": "^7.3.0",
"loader-utils": "^1.1.0",
"lodash-es": "^4.17.11",
"marked": "^0.6.0",
"husky": "^2.2.0",
"intersection-observer": "^0.6.0",
"jest": "^24.8.0",
"jest-axe": "^3.2.0",
"jest-junit": "^6.4.0",
"jest-localstorage-mock": "^2.4.0",
"jest-mock-console": "^1.0.0",
"lint-staged": "^8.1.6",
"loader-utils": "^1.2.3",
"lodash-es": "^4.17.14",
"marked": "^0.7.0",
"material-design-icons": "^3.0.1",
"mini-css-extract-plugin": "^0.5.0",
"npm-run-all": "^4.1.3",
"mini-css-extract-plugin": "^0.6.0",
"object-diff": "0.0.4",
"postcss-advanced-variables": "^3.0.0",
"postcss-css-variables": "^0.11.0",
"postcss-flexbugs-fixes": "^4.1.0",
"postcss-font-magician": "^2.2.1",
"postcss-import": "^11.1.0",
"postcss-js": "^2.0.1",
"postcss-loader": "^3.0.0",
"postcss-mixins": "^6.2.1",
"postcss-nested": "^4.1.1",
"postcss-prepend-imports": "^1.0.1",
"postcss-preset-env": "^6.5.0",
"prettier": "^1.16.4",
"prettier": "^1.18.2",
"prop-types": "^15.6.2",
"pstree.remy": "^1.1.0",
"pstree.remy": "^1.1.6",
"pym.js": "^1.3.2",
"raw-loader": "^0.5.1",
"react": "^16.8.4",
"react": "^16.9.0-alpha.0",
"react-axe": "^3.3.0",
"react-copy-to-clipboard": "^5.0.1",
"react-dev-utils": "6.0.0-next.3e165448",
"react-dom": "^16.8.4",
"react-final-form": "^3.6.4",
"react-dev-utils": "^9.0.0",
"react-dom": "^16.9.0-alpha.0",
"react-error-overlay": "^5.1.6",
"react-final-form": "6.3.0",
"react-final-form-arrays": "3.1.0",
"react-popper": "^1.3.2",
"react-relay": "^1.7.0-rc.1",
"react-responsive": "^5.0.0",
"react-test-renderer": "^16.8.4",
"react-relay": "^4.0.0",
"react-relay-network-modern": "^4.0.4",
"react-responsive": "^7.0.0",
"react-test-renderer": "^16.9.0-alpha.0",
"react-timeago": "^4.1.9",
"react-transition-group": "^2.5.0",
"react-transition-group": "^2.9.0",
"react-with-state-props": "^2.0.4",
"recompose": "^0.27.1",
"relay-compiler": "^1.7.0-rc.1",
"relay-compiler-language-typescript": "^1.1.0",
"relay-local-schema": "^0.7.0",
"relay-runtime": "^1.7.0-rc.1",
"sane": "^4.0.2",
"recompose": "0.27.1",
"relay-compiler": "^4.0.0",
"relay-compiler-language-typescript": "^4.1.0",
"relay-local-schema": "^0.8.0",
"relay-runtime": "^4.0.0",
"resize-observer-polyfill": "^1.5.1",
"sane": "^4.1.0",
"shallow-equals": "^1.0.0",
"simplemde": "^1.11.2",
"simulant": "^0.2.2",
"sinon": "^6.1.5",
"sinon": "^7.3.2",
"sockjs-client": "^1.3.0",
"stack-trace": "0.0.10",
"strip-ansi": "^5.2.0",
"style-loader": "^0.23.1",
"terser-webpack-plugin": "^1.1.0",
"timekeeper": "^2.1.2",
"ts-jest": "^23.0.0",
"ts-loader": "^4.4.2",
"ts-node": "^6.2.0",
"tsconfig-paths": "^3.4.2",
"tsconfig-paths-webpack-plugin": "^3.1.4",
"tslint": "^5.15.0",
"tslint-config-prettier": "^1.18.0",
"tslint-loader": "^3.6.0",
"tslint-plugin-prettier": "^2.0.1",
"tslint-react": "^4.0.0",
"typed-css-modules": "^0.3.4",
"typeface-manuale": "0.0.54",
"typeface-source-sans-pro": "0.0.54",
"subscriptions-transport-ws": "^0.9.16",
"terser-webpack-plugin": "^1.2.3",
"thread-loader": "^2.1.2",
"timekeeper": "^2.2.0",
"ts-jest": "<23.10.0",
"ts-loader": "^6.0.0",
"ts-node": "^8.1.0",
"ts-node-dev": "^1.0.0-pre.37",
"tsconfig-paths": "^3.8.0",
"tsconfig-paths-webpack-plugin": "^3.2.0",
"tslint": "^5.20.0",
"typed-css-modules": "^0.4.2",
"typeface-manuale": "^0.0.71",
"typeface-nunito": "0.0.72",
"typeface-open-sans": "0.0.75",
"typeface-source-sans-pro": "^0.0.54",
"typescript": "3.3.4000",
"typescript-snapshots-plugin": "^1.2.0",
"wait-for-expect": "^1.1.0",
"webpack": "^4.27.1",
"typescript-snapshots-plugin": "^1.6.0",
"wait-for-expect": "^1.1.1",
"webpack": "^4.30.0",
"webpack-assets-manifest": "^3.1.1",
"webpack-bundle-analyzer": "^3.0.3",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.14",
"webpack-hot-client": "^4.1.1",
"whatwg-fetch": "^2.0.4"
"webpack-bundle-analyzer": "^3.3.2",
"webpack-cli": "^3.3.2",
"webpack-dev-server": "3.2.1",
"whatwg-fetch": "^3.0.0"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
"pre-commit": "FAST_LINT=true lint-staged"
}
},
"lint-staged": {
"*.{j,t}s{,x}": [
"tslint"
"eslint"
],
"src/core/server/graph/tenant/schema/schema.graphql": [
"src/core/server/graph/schema/schema.graphql": [
"graphql-schema-linter"
],
"{src/core/client/stream/events.ts,scripts/generateEventDocs.ts,events.md}": [
"npm run docs:events -- --verify"
]
},
"bundlesize": [
+291
View File
@@ -0,0 +1,291 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* This file is taken from create-react-app.
* We disabled tty support, removed unessential parts to us and
* added support for `warningsFilter` and Multi Webpack Config.
*
* TODO: (cvle) The `forkTsCheckerWebpackPlugin` hooks are not working atm.
*/
"use strict";
/* eslint-disable no-console */
const address = require("address");
const url = require("url");
const chalk = require("chalk");
const clearConsole = require("react-dev-utils/clearConsole");
const formatWebpackMessages = require("react-dev-utils/formatWebpackMessages");
const typescriptFormatter = require("react-dev-utils/typescriptFormatter");
const forkTsCheckerWebpackPlugin = require("react-dev-utils/ForkTsCheckerWebpackPlugin");
// (cvle): Changed to false as we are sharing the tty with other processes.
// const isInteractive = process.stdout.isTTY && false;
const isInteractive = false;
function prepareUrls(protocol, host, port) {
const formatUrl = hostname =>
url.format({
protocol,
hostname,
port,
pathname: "/",
});
const prettyPrintUrl = hostname =>
url.format({
protocol,
hostname,
port: chalk.bold(port),
pathname: "/",
});
const isUnspecifiedHost = host === "0.0.0.0" || host === "::";
let prettyHost;
let lanUrlForConfig;
let lanUrlForTerminal;
if (isUnspecifiedHost) {
prettyHost = "localhost";
try {
// This can only return an IPv4 address
lanUrlForConfig = address.ip();
if (lanUrlForConfig) {
// Check if the address is a private ip
// https://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces
if (
/^10[.]|^172[.](1[6-9]|2[0-9]|3[0-1])[.]|^192[.]168[.]/.test(
lanUrlForConfig
)
) {
// Address is private, format it for later use
lanUrlForTerminal = prettyPrintUrl(lanUrlForConfig);
} else {
// Address is not private, so we will discard it
lanUrlForConfig = undefined;
}
}
} catch (_e) {
// ignored
}
} else {
prettyHost = host;
}
const localUrlForTerminal = prettyPrintUrl(prettyHost);
const localUrlForBrowser = formatUrl(prettyHost);
return {
lanUrlForConfig,
lanUrlForTerminal,
localUrlForTerminal,
localUrlForBrowser,
};
}
function printInstructions(appName, urls, useYarn) {
console.log();
console.log(`You can now view ${chalk.bold(appName)} in the browser.`);
console.log();
if (urls.lanUrlForTerminal) {
console.log(
` ${chalk.bold("Local:")} ${urls.localUrlForTerminal}`
);
console.log(
` ${chalk.bold("On Your Network:")} ${urls.lanUrlForTerminal}`
);
} else {
console.log(` ${urls.localUrlForTerminal}`);
}
console.log();
console.log("Note that the development build is not optimized.");
console.log(
`To create a production build, use ` +
`${chalk.cyan(`${useYarn ? "yarn" : "npm run"} build`)}.`
);
console.log();
}
function createCompiler({
appName,
config,
devSocket,
urls,
useYarn,
useTypeScript,
webpack,
}) {
// "Compiler" is a low-level interface to Webpack.
// It lets us listen to some events and provide our own custom messages.
let compiler;
try {
compiler = webpack(config);
} catch (err) {
console.log(chalk.red("Failed to compile."));
console.log();
console.log(err.message || err);
console.log();
process.exit(1);
}
// "invalid" event fires when you have changed a file, and Webpack is
// recompiling a bundle. WebpackDevServer takes care to pause serving the
// bundle, so if you refresh, it'll wait instead of serving the old one.
// "invalid" is short for "bundle invalidated", it doesn't imply any errors.
compiler.hooks.invalid.tap("invalid", () => {
if (isInteractive) {
clearConsole();
}
console.log("Compiling...");
});
let isFirstCompile = true;
const tsMessagesPromises = [];
if (useTypeScript) {
compiler.compilers.forEach(singleCompiler => {
let tsMessagesPromise;
let tsMessagesResolver;
singleCompiler.hooks.beforeCompile.tap("beforeCompile", () => {
tsMessagesPromise = new Promise(resolve => {
tsMessagesResolver = msgs => resolve(msgs);
});
tsMessagesPromises.push(tsMessagesPromise);
});
const tsCheckerHooks = forkTsCheckerWebpackPlugin.getCompilerHooks(
singleCompiler
);
// now access hooks just like before
// TODO: (cvle) this doesn't work currently.
tsCheckerHooks.waiting.tap("yourListenerName", () => {
console.log("waiting for typecheck results");
});
// TODO: (cvle) this doesn't work currently.
tsCheckerHooks.receive.tap(
"afterTypeScriptCheck",
(diagnostics, lints) => {
console.log("RECEIVED");
const allMsgs = [...diagnostics, ...lints];
const format = message =>
`${message.file}\n${typescriptFormatter(message, true)}`;
tsMessagesResolver({
errors: allMsgs.filter(msg => msg.severity === "error").map(format),
warnings: allMsgs
.filter(msg => msg.severity === "warning")
.map(format),
});
}
);
});
}
// "done" event fires when Webpack has finished recompiling the bundle.
// Whether or not you have warnings or errors, you will get this event.
compiler.hooks.done.tap("done", async stats => {
if (isInteractive) {
clearConsole();
}
// We have switched off the default Webpack output in WebpackDevServer
// options so we are going to "massage" the warnings and errors and present
// them in a readable focused way.
// We only construct the warnings and errors for speed:
// https://github.com/facebook/create-react-app/issues/4492#issuecomment-421959548
const statOptions = {
all: false,
warnings: true,
errors: true,
...config.stats,
};
if (Array.isArray(config)) {
statOptions.children = config.map(c => c.stats || {});
}
const statsData = stats.toJson(statOptions);
if (useTypeScript && statsData.errors.length === 0) {
const delayedMsg = setTimeout(() => {
console.log(
chalk.yellow(
"Files successfully emitted, waiting for typecheck results..."
)
);
}, 100);
// Get back all tscheck results.
const results = await Promise.all(tsMessagesPromises);
clearTimeout(delayedMsg);
results.forEach(msgs => {
statsData.errors.push(...msgs.errors);
statsData.warnings.push(...msgs.warnings);
// Push errors and warnings into compilation result
// to show them after page refresh triggered by user.
stats.compilation.errors.push(...msgs.errors);
stats.compilation.warnings.push(...msgs.warnings);
if (msgs.errors.length > 0) {
devSocket.errors(msgs.errors);
} else if (msgs.warnings.length > 0) {
devSocket.warnings(msgs.warnings);
}
});
if (isInteractive) {
clearConsole();
}
}
const messages = formatWebpackMessages(statsData);
const isSuccessful = !messages.errors.length && !messages.warnings.length;
if (isSuccessful) {
console.log(chalk.green("Compiled successfully!"));
}
if (isSuccessful && (isInteractive || isFirstCompile)) {
printInstructions(appName, urls, useYarn);
}
isFirstCompile = false;
// If errors exist, only show errors.
if (messages.errors.length) {
// Only keep the first error. Others are often indicative
// of the same problem, but confuse the reader with noise.
if (messages.errors.length > 1) {
messages.errors.length = 1;
}
console.log(chalk.red("Failed to compile.\n"));
console.log(messages.errors.join("\n\n"));
return;
}
// Show warnings if no errors were found.
if (messages.warnings.length) {
console.log(chalk.yellow("Compiled with warnings.\n"));
console.log(messages.warnings.join("\n\n"));
// Teach some ESLint tricks.
console.log(
"\nSearch for the " +
chalk.underline(chalk.yellow("keywords")) +
" to learn more about each warning."
);
console.log(
"To ignore, add " +
chalk.cyan("// eslint-disable-next-line") +
" to the line before.\n"
);
}
});
return compiler;
}
module.exports = {
createCompiler,
prepareUrls,
};
+16 -8
View File
@@ -17,14 +17,16 @@ import paths from "../config/paths";
import config from "../src/core/build/config";
import createWebpackConfig from "../src/core/build/createWebpackConfig";
// tslint:disable: no-console
// Enforce environment to be production.
config.validate().set("env", "production");
/* eslint-disable no-console */
process.env.WEBPACK = "true";
process.env.BABEL_ENV = "production";
process.env.NODE_ENV = "production";
process.env.NODE_ENV = process.env.NODE_ENV || "production";
process.env.BABEL_ENV = process.env.NODE_ENV;
// Enforce environment to be NODE_ENV.
config.validate().set("env", process.env.NODE_ENV);
const isProduction = process.env.NODE_ENV === "production";
// Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will
@@ -102,7 +104,11 @@ measureFileSizesBeforeBuild(paths.appDistStatic)
// Create the production build and print the deployment instructions.
function build(previousFileSizes: any) {
console.log("Creating an optimized production build...");
if (isProduction) {
console.log("Creating an optimized production build...");
} else {
console.log("Creating development build...");
}
const webpackConfig = createWebpackConfig(config);
const compiler = webpack(webpackConfig);
return new Promise((resolve, reject) => {
@@ -110,7 +116,9 @@ function build(previousFileSizes: any) {
if (err) {
return reject(err);
}
const messages = formatWebpackMessages(stats.toJson({}));
const messages = formatWebpackMessages(
stats.toJson({ children: webpackConfig.map(c => c.stats || {}) as any })
);
if (messages.errors.length) {
// Only keep the first error. Others are often indicative
// of the same problem, but confuse the reader with noise.
+31 -6
View File
@@ -2,7 +2,7 @@
import program from "commander";
import spawn from "cross-spawn";
import fs from "fs";
import fs from "fs-extra";
import path from "path";
const config = JSON.parse(
@@ -14,33 +14,34 @@ program
.usage("--src ./src/core/client/stream --schema tenant")
.option("--src <folder>", "Find gql recursively in this folder")
.option("--schema <schema>", "Identifier of schema")
.option("--persist", "Use persisted queries")
.description("Compile relay gql data")
.parse(process.argv);
if (!program.schema) {
// tslint:disable-next-line:no-console
// eslint-disable-next-line no-console
console.error("Schema identifier not provided");
process.exit(1);
}
if (!program.src) {
// tslint:disable-next-line:no-console
// eslint-disable-next-line no-console
console.error("Src not provided");
process.exit(1);
}
if (!config.projects) {
// tslint:disable-next-line:no-console
// eslint-disable-next-line no-console
console.error("Missing projects key in .graphqconfig");
process.exit(1);
}
if (!config.projects[program.schema]) {
// tslint:disable-next-line:no-console
// eslint-disable-next-line no-console
console.error(`Project ${program.schema} not found in .graphqconfig`);
process.exit(1);
}
if (!config.projects[program.schema].schemaPath) {
// tslint:disable-next-line:no-console
// eslint-disable-next-line no-console
console.error(
`SchemaPath for project ${program.schema} not found in .graphqconfig`
);
@@ -59,4 +60,28 @@ const args = [
config.projects[program.schema].schemaPath,
];
// Set the persisted query path.
const persist = program.persist
? `${program.src}/persisted-queries.json`
: null;
if (persist) {
args.push("--persist-output", persist);
}
spawn.sync("relay-compiler", args, { stdio: "inherit" });
if (persist) {
if (fs.existsSync(persist)) {
// Create the new filename.
const name = path.basename(program.src);
const generated = "./src/core/server/graph/persisted/__generated__";
// Create the generated directory if it doesn't exist.
fs.ensureDirSync(generated);
// Copy the file over to the destination directory.
fs.copySync(persist, `${generated}/${name}.json`, {
overwrite: true,
});
}
}
+1 -9
View File
@@ -12,7 +12,7 @@ deploy_tag() {
# v5.0.0-beta.1 will be tagged with 5.0.0-beta.1
# v5.0.0 will be tagged with 5, 5.0, 5.0.0
#
if [ -n "$(echo $CIRCLE_TAG | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$')" ]
if [[ -n "$(echo $CIRCLE_TAG | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$')" ]]
then
major=$(echo ${CIRCLE_TAG/#v} | cut -d. -f1)
minor=$(echo ${CIRCLE_TAG/#v} | cut -d. -f2)
@@ -40,14 +40,6 @@ deploy_tag() {
echo "==> pushing $version"
docker push coralproject/talk:$version
done
# Deploy latest if we're at master, or deploy this branch if we aren't.
if [ "$CIRCLE_BRANCH" = "master" ]
then
deploy_latest
else
deploy_branch
fi
}
deploy_latest() {
+277
View File
@@ -0,0 +1,277 @@
/* eslint-disable no-bitwise */
import { codeBlock, stripIndent } from "common-tags";
import * as fs from "fs";
import * as path from "path";
import ts from "typescript";
interface DocEntry {
name: string;
docs?: string;
type: "ViewerNetworkEvent" | "ViewerEvent";
text?: string;
}
/**
* We use this regexp to find a previous block that we
* are going to update in the readme file.
*/
const BLOCK_REGEXP = /<!-- START docs:events -->(.|\n)*<!-- END docs:events -->/gm;
/** Build flags that affects AST generation */
const buildFlags =
// Do not truncate output.
ts.NodeBuilderFlags.NoTruncation |
// Use multiline object literals format.
ts.NodeBuilderFlags.MultilineObjectLiterals;
/** Generate documentation for all classes in a set of .ts files */
function gatherEntries(
fileNames: string[],
options: ts.CompilerOptions
): DocEntry[] {
// Build a program using the set of root file names in fileNames
const program = ts.createProgram(fileNames, options);
const printer = ts.createPrinter({
noEmitHelpers: true,
omitTrailingSemicolon: true,
removeComments: false,
});
// Get the checker, we will use it to find more about classes
const checker = program.getTypeChecker();
const data: DocEntry[] = [];
/** Hold a pointer to the sourcefile we are currently processing. */
let currentSourceFile: ts.SourceFile;
// Visit every sourceFile in the program
for (const sourceFile of program.getSourceFiles()) {
if (!sourceFile.isDeclarationFile) {
currentSourceFile = sourceFile;
// Walk the tree to search for classes
ts.forEachChild(sourceFile, visit);
}
}
const sorted = data.sort((a, b) => {
if (a.name > b.name) {
return 1;
}
if (b.name > a.name) {
return -1;
}
return 0;
});
return sorted;
/** visit nodes finding exported events */
function visit(node: ts.Node) {
// Only consider exported nodes
if (!isNodeExported(node)) {
return;
}
if (ts.isVariableStatement(node)) {
if (
!node.getFullText().includes("createViewerNetworkEvent") &&
!node.getFullText().includes("createViewerEvent")
) {
return;
}
const firstChild = node.declarationList.declarations[0];
if (ts.isVariableDeclaration(firstChild)) {
const symbol = checker.getSymbolAtLocation(firstChild.name);
if (symbol) {
serializeEventSymbol(symbol);
}
}
}
}
function serializeEventSymbol(symbol: ts.Symbol) {
const type = checker.getTypeOfSymbolAtLocation(
symbol,
symbol.valueDeclaration
);
const typeNode = checker.typeToTypeNode(type, undefined, buildFlags)!;
const typeName = symbol.getName();
const entry: DocEntry = {
name: typeName,
docs: ts.displayPartsToString(symbol.getDocumentationComment(checker)),
type: type.getSymbol()!.getName() as DocEntry["type"],
};
typeNode.forEachChild(ch => {
if (ts.isTypeLiteralNode(ch)) {
const text = printer.printNode(
ts.EmitHint.Unspecified,
ch,
currentSourceFile
);
if (text !== "{}") {
entry.text = text;
}
/*
Go through each parameter.
ch.members.forEach(m => {
if (ts.isPropertySignature(m)) {
if (ts.isIdentifier(m.name)) {
data.parameters[m.name.text] = printer.printNode(
ts.EmitHint.Unspecified,
m.type!,
currentSourceFile
);
}
}
});
*/
}
});
data.push(entry);
}
/** True if this is visible outside this file, false otherwise */
function isNodeExported(node: ts.Node): boolean {
return (
// eslint-disable-next-line no-bitwise, @typescript-eslint/no-unnecessary-type-assertion
(ts.getCombinedModifierFlags(node as ts.Declaration) &
ts.ModifierFlags.Export) !==
0 ||
(!!node.parent && node.parent.kind === ts.SyntaxKind.SourceFile)
);
}
}
function prefixLines(text: string, prefix: string) {
return text.split("\n").join(`\n${prefix}`);
}
function getEventName(typeName: string) {
return (
typeName[0].toLocaleLowerCase() +
typeName.slice(1, typeName.length - "Event".length)
);
}
/**
* Removes "%future added value" from text. This is a placeholder type
* added by Relay to help with future proofness.
*/
function removeFutureAddedValue(text: string) {
return text
.replace(': "%future added value" | ', ": ")
.replace(' | "%future added value"', "");
}
/**
* Append or update previous documention in markdownFile.
*
* @param markdownFile The markdown file we want to inject the docs too.
* @param entries data as returned by gatherEntries.
*/
function emitDocs(markdownFile: string, entries: DocEntry[], verify = false) {
const previousContent = fs.existsSync(markdownFile)
? fs.readFileSync(markdownFile).toString()
: "";
const summary = stripIndent`
- ${entries
.map(
e => `<a href="#${getEventName(e.name)}">${getEventName(e.name)}</a>`
)
.join("\n - ")}
`;
const list = entries
.map(
e =>
codeBlock`
- ${
e.type === "ViewerEvent"
? `<a id="${getEventName(e.name)}">**${getEventName(e.name)}**</a>`
: `<a id="${getEventName(e.name)}">**${getEventName(
e.name
)}.success**, **${getEventName(e.name)}.error**</a>`
}: ${e.docs ? e.docs.replace("\n", " ") : ""}
${
e.text
? codeBlock`
\`\`\`ts
${removeFutureAddedValue(e.text)}
\`\`\`
`
: ""
}
`
)
.join("\n");
const output = stripIndent`
<!-- START docs:events -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN npm run docs:events -->
### Index
${prefixLines(summary, " ")}
### Events
${prefixLines(list, " ")}
<!-- END docs:events -->
`;
let newContent;
// Find previous block.
if (BLOCK_REGEXP.test(previousContent)) {
newContent = previousContent.replace(BLOCK_REGEXP, output);
} else {
newContent = previousContent + "\n" + output;
}
if (previousContent === newContent) {
// eslint-disable-next-line no-console
console.log(`${markdownFile} is up to date`);
return;
}
if (verify) {
// eslint-disable-next-line no-console
console.error(
`${markdownFile} is outdated, please run \`npm run docs:events\``
);
process.exit(1);
return;
}
fs.writeFileSync(markdownFile, newContent);
// eslint-disable-next-line no-console
console.log(`Successfully injected documentation into ${markdownFile}`);
}
function main() {
if (process.argv.length < 4) {
throw new Error("Must provide path to events and a markdown file.");
}
const eventFile = process.argv[2];
const markdownFile = process.argv[3];
// Find tsconfig file.
const configFile = ts.findConfigFile(eventFile, fs.existsSync);
if (!configFile) {
throw new Error("tsconfig file not found");
}
const configText = fs.readFileSync(configFile).toString();
const result = ts.parseConfigFileTextToJson(configFile, configText);
if (result.error) {
throw result.error;
}
// Parse the JSON raw data into actual consumable compiler options.
const config = ts.parseJsonConfigFileContent(
result.config,
ts.sys,
path.dirname(configFile)
);
const entries = gatherEntries([eventFile], config.options);
emitDocs(markdownFile, entries, process.argv[4] === "--verify");
}
main();
+6 -7
View File
@@ -1,4 +1,3 @@
const { Linter, Configuration } = require("tslint");
const { generateTSTypesAsString } = require("graphql-schema-typescript");
const { getGraphQLConfig } = require("graphql-config");
const path = require("path");
@@ -13,13 +12,13 @@ async function main() {
name: "tenant",
fileName: path.join(
__dirname,
"../src/core/server/graph/tenant/schema/__generated__/types.ts"
"../src/core/server/graph/schema/__generated__/types.ts"
),
config: {
contextType: "TenantContext",
contextType: "GraphContext",
importStatements: [
'import TenantContext from "talk-server/graph/tenant/context";',
'import { Cursor } from "talk-server/models/helpers/connection";',
'import GraphContext from "coral-server/graph/context";',
'import { Cursor } from "coral-server/models/helpers";',
],
customScalarType: { Cursor: "Cursor", Time: "Date" },
},
@@ -69,12 +68,12 @@ if (require.main === module) {
main()
.then(files => {
for (const { fileName } of files) {
// tslint:disable-next-line:no-console
// eslint-disable-next-line no-console
console.log(`Generated ${fileName}`);
}
})
.catch(err => {
// tslint:disable-next-line:no-console
// eslint-disable-next-line no-console
console.error(err);
});
}
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env ts-node
/**
* This script can be invoked via:
*
* npm run migration:create <migration name>
*
* To create new database migrations.
*/
/* eslint-disable no-console */
import fs from "fs-extra";
import lodash from "lodash";
import path from "path";
const templateFilePath = path.resolve(
path.join(
__dirname,
"../../src/core/server/services/migrate/migration_sample.ts"
)
);
const argv = process.argv.slice(2);
if (argv.length !== 1) {
console.error("usage: npm run migration:create <migration name>");
process.exit(1);
}
// Get the name of the new migration.
const name = lodash.snakeCase(argv[0]);
// Get the version of the new migration.
const version = Date.now();
// Get the filePath of the new migration.
const filePath = path.resolve(
path.join(
__dirname,
`../../src/core/server/services/migrate/migrations/${version}_${name}.ts`
)
);
if (fs.existsSync(filePath)) {
console.error(`migration already exists at: ${filePath}`);
process.exit(1);
}
// Write the template out to the file.
fs.copyFileSync(templateFilePath, filePath);
console.log(`created new migration at: ${filePath}`);
+26 -7
View File
@@ -6,18 +6,15 @@ import dotenv from "dotenv";
dotenv.config();
import chalk from "chalk";
import {
createCompiler,
prepareUrls,
} from "react-dev-utils/WebpackDevServerUtils";
import webpack from "webpack";
import WebpackDevServer from "webpack-dev-server";
import { createCompiler, prepareUrls } from "./WebpackDevServerUtils";
import createDevServerConfig from "../config/webpackDevServer.config";
import config from "../src/core/build/config";
import createWebpackConfig from "../src/core/build/createWebpackConfig";
// tslint:disable: no-console
/* eslint-disable no-console */
// Enforce environment to be development.
config.validate().set("env", "development");
@@ -52,9 +49,31 @@ if (process.env.HOST) {
}
const urls = prepareUrls("http", HOST, PORT);
const webpackConfig = createWebpackConfig(config);
const webpackConfig = createWebpackConfig(config, { watch: true });
const devSocket = {
warnings: (warnings: any) =>
(devServer as any).sockWrite(
(devServer as any).sockets,
"warnings",
warnings
),
errors: (errors: any) =>
(devServer as any).sockWrite((devServer as any).sockets, "errors", errors),
};
// Create a webpack compiler that is configured with custom messages.
const compiler = createCompiler(webpack, webpackConfig, "Talk", urls);
const compiler = createCompiler({
webpack,
config: webpackConfig,
appName: "Coral",
urls,
// useTypeScript: true, /* This is currently not working, as the ForkTsCheckerWebpackPlugin hooks are not triggering for some reason */
useTypeScript: false, // This would
useYarn: false,
devSocket,
});
// Serve webpack assets generated by the compiler over a web sever.
const serverConfig = createDevServerConfig({
allowedHost: urls.lanUrlForConfig,
+1 -1
View File
@@ -24,7 +24,7 @@ process.on("unhandledRejection", err => {
const paths = require("../config/paths.ts").default;
const jest = require("jest");
let argv = process.argv.slice(2);
const argv = process.argv.slice(2);
argv.push("--config", paths.appJestConfig);
// Watch unless on CI or in coverage mode
+4 -3
View File
@@ -23,9 +23,10 @@ export default class ChokidarWatcher implements Watcher {
let firstError: Error | null = null;
// If this is set, a pending promise is waiting for the next result.
let pending:
| ({ resolve: (result: string) => void; reject: (error: Error) => void })
| null = null;
let pending: {
resolve: (result: string) => void;
reject: (error: Error) => void;
} | null = null;
// Only start client if we have something to watch.
if (paths.length) {
+3 -3
View File
@@ -21,8 +21,8 @@ export default class CommandExecutor implements Executor {
private args?: ReadonlyArray<string>;
private spawnMultiple: boolean;
private runOnInit: boolean;
private isRunning: boolean = false;
private shouldRespawn: boolean = false;
private isRunning = false;
private shouldRespawn = false;
private spawnProcessDebounced?: (() => void) & Cancelable;
constructor(cmd: string, opts: CommandExecutorOptions = {}) {
@@ -67,7 +67,7 @@ export default class CommandExecutor implements Executor {
child.on("close", (code: number) => {
this.isRunning = false;
if (code !== 0 && code !== null) {
// tslint:disable-next-line: no-console
// eslint-disable-next-line no-console
console.log(chalk.red(`Command exited with ${code}`));
}
if (this.shouldRespawn) {
+11 -9
View File
@@ -17,8 +17,8 @@ export default class LongRunningExecutor implements Executor {
private cmd: string;
private args?: ReadonlyArray<string>;
private process: ChildProcess | null = null;
private isRunning: boolean = false;
private shouldRestart: boolean = false;
private isRunning = false;
private shouldRestart = false;
private restartDebounced: (() => void) & Cancelable;
constructor(cmd: string, opts: LongRunningExecutorOptions = {}) {
@@ -37,11 +37,11 @@ export default class LongRunningExecutor implements Executor {
shell: !this.args,
});
this.process!.on("exit", (code: number) => {
this.process.on("exit", (code: number) => {
this.isRunning = false;
if (code !== 0 && code !== null) {
// tslint:disable-next-line: no-console
// eslint-disable-next-line no-console
console.log(chalk.red(`Command exited with ${code}`));
return;
}
@@ -81,11 +81,13 @@ export default class LongRunningExecutor implements Executor {
if (err) {
reject(err);
}
spawn.sync("kill", [
`-${signal}`,
this.process!.pid.toString(),
...kids.map(p => p.PID.toString()),
]);
if (kids) {
spawn.sync("kill", [
`-${signal}`,
this.process!.pid.toString(),
...kids,
]);
}
resolve();
});
});
+3 -3
View File
@@ -18,7 +18,7 @@ function canUseWatchman(): boolean {
try {
execSync("watchman --version", { stdio: ["ignore"] });
return true;
// tslint:disable-next-line:no-empty
// eslint-disable-next-line no-empty
} catch (e) {}
return false;
}
@@ -34,7 +34,7 @@ export default class SaneWatcher implements Watcher {
// Autodetect watchman.
if (this.watchman === undefined && canUseWatchman()) {
this.watchman = true;
// tslint:disable-next-line:no-console
// eslint-disable-next-line no-console
console.log(chalk.grey(`Watchman detected`));
}
}
@@ -48,7 +48,7 @@ export default class SaneWatcher implements Watcher {
const queue: string[] = [];
// If this is set, a pending promise is waiting for the next result.
let pending: ({ resolve: (result: string) => void }) | null = null;
let pending: { resolve: (result: string) => void } | null = null;
// Only start client if we have something to watch.
if (paths.length) {
+7 -9
View File
@@ -14,19 +14,13 @@ async function run(
throw new Error("Config file not specified");
}
// tslint:disable-next-line:no-var-requires
// eslint-disable-next-line @typescript-eslint/no-var-requires
let config: any = require(path.resolve(configFile));
if (config.__esModule) {
config = config.default;
}
try {
await watch(config, { only });
} catch (err) {
// tslint:disable-next-line:no-console
console.error(err);
process.exit(1);
}
await watch(config, { only });
}
const cmd = program
@@ -36,4 +30,8 @@ const cmd = program
.description("Run watchers defined in <configFile>")
.parse(process.argv);
run(cmd.args, cmd.opts());
run(cmd.args, cmd.opts()).catch(err => {
// eslint-disable-next-line no-console
console.error(err);
process.exit(1);
});
+1 -1
View File
@@ -1,6 +1,6 @@
declare module "pstree.remy" {
export default function psTree(
pid: number,
callback: (err: Error, kids: Array<{ PID: number }>) => void
callback: (err: Error, kids: string[]) => void
): void;
}
+8 -4
View File
@@ -21,7 +21,7 @@ async function beginWatch(
await executor.onInit();
}
for await (const filePath of watcher.watch(rootDir, paths, { ignore })) {
// tslint:disable-next-line:no-console
// eslint-disable-next-line no-console
console.log(chalk.cyanBright(`Execute "${key}"`));
executor.execute(filePath);
}
@@ -72,7 +72,7 @@ function filterOnly(
}
return pickBy(watchers, (value, key) => {
if (resolved.indexOf(key) === -1) {
// tslint:disable-next-line:no-console
// eslint-disable-next-line no-console
console.log(chalk.grey(`Disabled watcher "${key}"`));
return false;
}
@@ -98,9 +98,13 @@ export default async function watch(config: Config, options: Options = {}) {
}
for (const key of Object.keys(watchersConfigs)) {
// tslint:disable-next-line:no-console
// eslint-disable-next-line no-console
console.log(chalk.cyanBright(`Start watcher "${key}"`));
const watcherConfig = watchersConfigs[key];
beginWatch(watcher, key, watcherConfig, rootDir);
beginWatch(watcher, key, watcherConfig, rootDir).catch(err => {
// eslint-disable-next-line no-console
console.error(err);
process.exit(1);
});
}
}
+287
View File
@@ -0,0 +1,287 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
"use strict";
/* eslint-disable */
// This alternative WebpackDevServer combines the functionality of:
// https://github.com/webpack/webpack-dev-server/blob/webpack-1/client/index.js
// https://github.com/webpack/webpack/blob/webpack-1/hot/dev-server.js
// It only supports their simplest configuration (hot updates on same server).
// It makes some opinionated choices on top, like adding a syntax error overlay
// that looks similar to our console output. The error overlay is inspired by:
// https://github.com/glenjamin/webpack-hot-middleware
var SockJS = require("sockjs-client");
var stripAnsi = require("strip-ansi");
var url = require("url");
var launchEditorEndpoint = require("react-dev-utils/launchEditorEndpoint");
var formatWebpackMessages = require("react-dev-utils/formatWebpackMessages");
var ErrorOverlay = require("react-error-overlay");
var { debounce } = require("lodash");
ErrorOverlay.setEditorHandler(function editorHandler(errorLocation) {
// Keep this sync with errorOverlayMiddleware.js
fetch(
launchEditorEndpoint +
"?fileName=" +
window.encodeURIComponent(errorLocation.fileName) +
"&lineNumber=" +
window.encodeURIComponent(errorLocation.lineNumber || 1) +
"&colNumber=" +
window.encodeURIComponent(errorLocation.colNumber || 1)
);
});
// We need to keep track of if there has been a runtime error.
// Essentially, we cannot guarantee application state was not corrupted by the
// runtime error. To prevent confusing behavior, we forcibly reload the entire
// application. This is handled below when we are notified of a compile (code
// change).
// See https://github.com/facebook/create-react-app/issues/3096
var hadRuntimeError = false;
ErrorOverlay.startReportingRuntimeErrors({
onError: function() {
hadRuntimeError = true;
},
filename: "/static/js/bundle.js",
});
if (module.hot && typeof module.hot.dispose === "function") {
module.hot.dispose(function() {
// TODO: why do we need this?
ErrorOverlay.stopReportingRuntimeErrors();
});
}
// Connect to WebpackDevServer via a socket.
var connection = new SockJS(
url.format({
protocol: window.location.protocol,
hostname: window.location.hostname,
port: window.location.port,
// Hardcoded in WebpackDevServer
pathname: "/sockjs-node",
})
);
// Unlike WebpackDevServer client, we won't try to reconnect
// to avoid spamming the console. Disconnect usually happens
// when developer stops the server.
connection.onclose = function() {
if (typeof console !== "undefined" && typeof console.info === "function") {
console.info(
"The development server has disconnected.\nRefresh the page if necessary."
);
}
};
// Remember some state related to hot module replacement.
var isFirstCompilation = true;
var mostRecentCompilationHash = null;
var hasCompileErrors = false;
function clearOutdatedErrors() {
// Clean up outdated compile errors, if any.
if (typeof console !== "undefined" && typeof console.clear === "function") {
if (hasCompileErrors) {
console.clear();
}
}
}
// Successful compilation.
function handleSuccess() {
clearOutdatedErrors();
var isHotUpdate = !isFirstCompilation;
isFirstCompilation = false;
hasCompileErrors = false;
// Attempt to apply hot updates or reload.
if (isHotUpdate) {
tryApplyUpdates(function onHotUpdateSuccess() {
// Only dismiss it when we're sure it's a hot update.
// Otherwise it would flicker right before the reload.
tryDismissErrorOverlay();
});
}
}
// Compilation with warnings (e.g. ESLint).
function handleWarnings(warnings) {
clearOutdatedErrors();
var isHotUpdate = !isFirstCompilation;
isFirstCompilation = false;
hasCompileErrors = false;
// TODO: remove this workaround when we can upgrade to WebpackDevServer >= v3.3.0,
// which includes proper `warningsFilter` support.
warnings = warnings.filter(w => !/export .* was not found in/.test(w));
function printWarnings() {
// Print warnings to the console.
var formatted = formatWebpackMessages({
warnings: warnings,
errors: [],
});
if (typeof console !== "undefined" && typeof console.warn === "function") {
for (var i = 0; i < formatted.warnings.length; i++) {
if (i === 5) {
console.warn(
"There were more warnings in other files.\n" +
"You can find a complete log in the terminal."
);
break;
}
console.warn(stripAnsi(formatted.warnings[i]));
}
}
}
printWarnings();
// Attempt to apply hot updates or reload.
if (isHotUpdate) {
tryApplyUpdates(function onSuccessfulHotUpdate() {
// Only dismiss it when we're sure it's a hot update.
// Otherwise it would flicker right before the reload.
tryDismissErrorOverlay();
});
}
}
// Compilation with errors (e.g. syntax error or missing modules).
function handleErrors(errors) {
clearOutdatedErrors();
isFirstCompilation = false;
hasCompileErrors = true;
// "Massage" webpack messages.
var formatted = formatWebpackMessages({
errors: errors,
warnings: [],
});
// Only show the first error.
ErrorOverlay.reportBuildError(formatted.errors[0]);
// Also log them to the console.
if (typeof console !== "undefined" && typeof console.error === "function") {
for (var i = 0; i < formatted.errors.length; i++) {
console.error(stripAnsi(formatted.errors[i]));
}
}
// Do not attempt to reload now.
// We will reload on next success instead.
}
function tryDismissErrorOverlay() {
if (!hasCompileErrors) {
ErrorOverlay.dismissBuildError();
}
}
// There is a newer version of the code available.
function handleAvailableHash(hash) {
// Update last known compilation hash.
mostRecentCompilationHash = hash;
}
const debouncedReload = debounce(() => {
window.location.reload();
}, 1000);
// Handle messages from the server.
connection.onmessage = function(e) {
var message = JSON.parse(e.data);
switch (message.type) {
case "hash":
handleAvailableHash(message.data);
break;
case "still-ok":
case "ok":
handleSuccess();
break;
case "content-changed":
// Triggered when a file from `contentBase` changed.
debouncedReload();
break;
case "warnings":
handleWarnings(message.data);
break;
case "errors":
handleErrors(message.data);
break;
default:
// Do nothing.
}
};
// Is there a newer version of this code available?
function isUpdateAvailable() {
/* globals __webpack_hash__ */
// __webpack_hash__ is the hash of the current compilation.
// It's a global variable injected by Webpack.
return mostRecentCompilationHash !== __webpack_hash__;
}
// Webpack disallows updates in other states.
function canApplyUpdates() {
return module.hot.status() === "idle";
}
// Attempt to update code on the fly, fall back to a hard reload.
function tryApplyUpdates(onHotUpdateSuccess) {
if (!module.hot) {
// HotModuleReplacementPlugin is not in Webpack configuration.
debouncedReload();
return;
}
if (!isUpdateAvailable() || !canApplyUpdates()) {
return;
}
function handleApplyUpdates(err, updatedModules) {
if (err || !updatedModules || hadRuntimeError) {
debouncedReload();
return;
}
if (typeof onHotUpdateSuccess === "function") {
// Maybe we want to do something.
onHotUpdateSuccess();
}
if (isUpdateAvailable()) {
// While we were updating, there was a new update! Do it again.
tryApplyUpdates();
}
}
// https://webpack.github.io/docs/hot-module-replacement.html#check
var result = module.hot.check(/* autoApply */ true, handleApplyUpdates);
// // Webpack 2 returns a Promise instead of invoking a callback
if (result && result.then) {
result.then(
function(updatedModules) {
handleApplyUpdates(null, updatedModules);
},
function(err) {
handleApplyUpdates(err, null);
}
);
}
}
+15
View File
@@ -1,5 +1,7 @@
import convict from "convict";
import { LOCALES } from "../common/helpers/i18n/locales";
const config = convict({
env: {
doc: "The application environment.",
@@ -21,6 +23,12 @@ const config = convict({
env: "DEV_PORT",
arg: "dev-port",
},
defaultLocale: {
doc: "Specify the default locale to use",
format: LOCALES,
default: "en-US",
env: "LOCALE",
},
generateReport: {
doc: "Generate a report using webpack-bundle-analyzer",
format: Boolean,
@@ -56,6 +64,13 @@ const config = convict({
env: "WEBPACK_TREESHAKE",
arg: "enableTreeShake",
},
maxCores: {
doc: "Set maximum of available cores",
format: "nat",
default: require("os").cpus().length,
env: "WEBPACK_MAX_CORES",
arg: "maxCores",
},
});
export type Config = typeof config;
+254 -92
View File
@@ -1,10 +1,13 @@
import OptimizeCssnanoPlugin from "@intervolga/optimize-cssnano-plugin";
import bunyan from "bunyan";
import CaseSensitivePathsPlugin from "case-sensitive-paths-webpack-plugin";
import CompressionPlugin from "compression-webpack-plugin";
import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
import HtmlWebpackPlugin from "html-webpack-plugin";
import { identity } from "lodash";
import MiniCssExtractPlugin from "mini-css-extract-plugin";
import path from "path";
import typescriptFormatter from "react-dev-utils/typescriptFormatter";
import WatchMissingNodeModulesPlugin from "react-dev-utils/WatchMissingNodeModulesPlugin";
import TerserPlugin from "terser-webpack-plugin";
import TsconfigPathsPlugin from "tsconfig-paths-webpack-plugin";
@@ -12,7 +15,7 @@ import webpack, { Configuration, Plugin } from "webpack";
import WebpackAssetsManifest from "webpack-assets-manifest";
import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";
// TODO: import form talk-common/version, for some reason this fails currently.
// TODO: import form coral-common/version, for some reason this fails currently.
// Try again when we have a chance to upgrade typescript.
import { version } from "../common/version";
import { Config } from "./config";
@@ -28,16 +31,26 @@ import paths from "./paths";
const filterPlugins = (plugins: Array<Plugin | null>): Plugin[] =>
plugins.filter(identity) as Plugin[];
// Create the build logger.
const logger = bunyan.createLogger({
name: "coral",
level: "debug",
});
interface CreateWebpackOptions {
appendPlugins?: any[];
watch?: boolean;
}
const publicPath = "/";
export default function createWebpackConfig(
config: Config,
{ appendPlugins = [] }: CreateWebpackOptions = {}
{ appendPlugins = [], watch = false }: CreateWebpackOptions = {}
): Configuration[] {
logger.debug({ config: config.toString() }, "loaded configuration");
const maxCores = config.get("maxCores");
const env = createClientEnv(config);
const disableSourcemaps = config.get("disableSourcemaps");
const generateReport = config.get("generateReport");
@@ -59,57 +72,58 @@ export default function createWebpackConfig(
};
/**
* ifProduction will only include the nodes if we're in production mode.
* ifWatch will only include the nodes if we're in watch mode.
*/
const ifProduction = isProduction
? (...nodes: any[]) => nodes
: (...nodes: any[]) => [];
const ifWatch = watch ? (...nodes: any[]) => nodes : () => [];
/**
* ifNotProduction will only include the nodes if we're not in production
* mode.
* ifBuild will only include the nodes if we're in build mode.
*/
const ifNotProduction = !isProduction
? (...nodes: any[]) => nodes
: (...nodes: any[]) => [];
const ifBuild = !watch ? (...nodes: any[]) => nodes : () => [];
const styleLoader = {
loader: require.resolve("style-loader"),
options: {
hmr: !isProduction,
sourceMap: !disableSourcemaps,
hmr: watch,
},
};
const localesOptions = {
pathToLocales: paths.appLocales,
// Default locale if non could be negotiated.
defaultLocale: "en-US",
// Default locale if none was specified.
defaultLocale: config.get("defaultLocale"),
// Fallback locale if a translation was not found.
// If not set, will use the text that is already
// in the code base.
fallbackLocale: "en-US",
fallbackLocale: config.get("defaultLocale"),
// Common fluent files are always included in the locale bundles.
commonFiles: ["framework.ftl", "common.ftl"],
commonFiles: ["framework.ftl", "common.ftl", "ui.ftl"],
// Locales that come with the main bundle. Others are loaded on demand.
bundled: ["en-US"],
bundled: [config.get("defaultLocale")],
// All available locales can be loadable on demand.
// To restrict available locales set:
// availableLocales: ["en-US"],
// availableLocales: [config.get("defaultLocale")],
};
const additionalPlugins = isProduction
? [
new MiniCssExtractPlugin({
filename: "assets/css/[name].[hash].css",
chunkFilename: "assets/css/[id].[hash].css",
}),
const additionalPlugins = [
...ifBuild(
new MiniCssExtractPlugin({
filename: isProduction
? "assets/css/[name].[hash].css"
: "assets/css/[name].css",
chunkFilename: isProduction
? "assets/css/[id].[hash].css"
: "assets/css/[id].css",
}),
isProduction &&
new OptimizeCssnanoPlugin({
sourceMap: true,
sourceMap: !disableSourcemaps,
cssnanoOptions: {
preset: [
"default",
@@ -121,26 +135,41 @@ export default function createWebpackConfig(
],
},
}),
// Pre-compress all the assets as they will be served as is.
new CompressionPlugin({}),
]
: [
// Add module names to factory functions so they appear in browser profiler.
new webpack.NamedModulesPlugin(),
// This is necessary to emit hot updates (currently CSS only):
new webpack.HotModuleReplacementPlugin(),
// Watcher doesn't work well if you mistype casing in a path so we use
// a plugin that prints an error when you attempt to do this.
// See https://github.com/facebookincubator/create-react-app/issues/240
new CaseSensitivePathsPlugin(),
// If you require a missing module and then `npm install` it, you still have
// to restart the development server for Webpack to discover it. This plugin
// makes the discovery automatic so you don't have to restart.
// See https://github.com/facebookincubator/create-react-app/issues/186
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
];
// Pre-compress all the assets as they will be served as is.
new CompressionPlugin({})
),
...ifWatch(
// Add module names to factory functions so they appear in browser profiler.
new webpack.NamedModulesPlugin(),
// Watcher doesn't work well if you mistype casing in a path so we use
// a plugin that prints an error when you attempt to do this.
// See https://github.com/facebookincubator/create-react-app/issues/240
new CaseSensitivePathsPlugin(),
// If you require a missing module and then `npm install` it, you still have
// to restart the development server for Webpack to discover it. This plugin
// makes the discovery automatic so you don't have to restart.
// See https://github.com/facebookincubator/create-react-app/issues/186
new WatchMissingNodeModulesPlugin(paths.appNodeModules)
),
];
const devtool = disableSourcemaps
? false
: isProduction
? // We generate sourcemaps in production. This is slow but gives good results.
// You can exclude the *.map files from the build during deployment.
"source-map"
: // You may want 'eval' instead if you prefer to see the compiled output in DevTools.
// See the discussion in https://github.com/facebookincubator/create-react-app/issues/343.
"cheap-module-source-map";
const baseConfig: Configuration = {
stats: {
// https://github.com/TypeStrong/ts-loader#transpileonly-boolean-defaultfalse
// Using transpilation only without typechecks gives warnings when we reexport types.
// We can ignore them here.
warningsFilter: /export .* was not found in/,
},
// Set webpack mode.
mode: isProduction ? "production" : "development",
optimization: {
@@ -182,14 +211,7 @@ export default function createWebpackConfig(
}),
],
},
devtool:
!disableSourcemaps && isProduction
? // We generate sourcemaps in production. This is slow but gives good results.
// You can exclude the *.map files from the build during deployment.
"source-map"
: // You may want 'eval' instead if you prefer to see the compiled output in DevTools.
// See the discussion in https://github.com/facebookincubator/create-react-app/issues/343.
"cheap-module-source-map",
devtool,
// These are the "entry points" to our application.
// This means they will be the "root" imports that are included in JS bundle.
// The first two entry points enable "hot" CSS and auto-refreshes for JS.
@@ -201,10 +223,10 @@ export default function createWebpackConfig(
// Generated JS file names (with nested folders).
// There will be one main bundle, and one file per asynchronous chunk.
filename: isProduction
? "assets/js/[name].[chunkhash:8].js"
? "assets/js/[name].[chunkhash].js"
: "assets/js/[name].js",
chunkFilename: isProduction
? "assets/js/[name].[chunkhash:8].chunk.js"
? "assets/js/[name].[chunkhash].chunk.js"
: "assets/js/[name].chunk.js",
// We inferred the "public path" (such as / or /my-project) from homepage.
publicPath,
@@ -229,26 +251,13 @@ export default function createWebpackConfig(
modules: ["node_modules", paths.appLoaders],
},
module: {
strictExportPresence: true,
// https://github.com/TypeStrong/ts-loader#transpileonly-boolean-defaultfalse
// Using transpilation only without typechecks gives warnings when we reexport types
// thus we can't turn on `strictExportPresence` which would turn warnings into errors.
strictExportPresence: false,
rules: [
// Disable require.ensure as it's not a standard language feature.
{ parser: { requireEnsure: false } },
// First, run the linter.
// It's important to do this before Babel processes the JS.
{
test: /\.(js|ts|tsx)$/,
enforce: "pre",
use: [
{
options: {
tsConfigFile: paths.appTsconfig,
},
loader: require.resolve("tslint-loader"),
},
],
include: paths.appSrc,
},
{
// "oneOf" will traverse all following loaders until one will
// match the requirements. When no loader matches it will fall
@@ -286,6 +295,22 @@ export default function createWebpackConfig(
},
],
},
{
test: paths.appAccountLocalesTemplate,
use: [
// This is the locales loader that loads available locales
// from a particular target.
{
loader: "locales-loader",
options: {
...localesOptions,
// Target specifies the prefix for fluent files to be loaded.
// ${target}-xyz.ftl and ${†arget}.ftl are loaded into the locales.
target: "account",
},
},
],
},
{
test: paths.appAdminLocalesTemplate,
use: [
@@ -331,14 +356,66 @@ export default function createWebpackConfig(
loader: require.resolve("url-loader"),
options: {
limit: 10000,
name: "assets/media/[name].[hash:8].[ext]",
name: isProduction
? "assets/media/[name].[hash].[ext]"
: "assets/media/[name].[ext]",
},
},
{
test: /\.css\.ts$/,
use: [
!watch ? MiniCssExtractPlugin.loader : styleLoader,
{
loader: require.resolve("css-loader"),
options: {
modules: true,
importLoaders: 2,
localIdentName: "[name]-[local]-[contenthash]",
sourceMap: !disableSourcemaps,
},
},
{
loader: require.resolve("postcss-loader"),
options: {
config: {
path: paths.appPostCssConfig,
},
parser: "postcss-js",
},
},
{
loader: require.resolve("babel-loader"),
options: {
configFile: false,
babelrc: false,
presets: [
"@babel/typescript",
[
"@babel/env",
{ targets: { node: "12.0.0" }, modules: "commonjs" },
],
],
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
// directory for faster rebuilds.
cacheDirectory: true,
},
},
],
},
// Process JS with Babel.
{
test: /\.(ts|tsx)$/,
include: paths.appSrc,
use: [
{
loader: "thread-loader",
options: {
// there should be 1 cpu for the fork-ts-checker-webpack-plugin
workers: maxCores - 1,
poolTimeout: watch ? Infinity : 500, // set this to Infinity in watch mode - see https://github.com/webpack-contrib/thread-loader
},
},
{
loader: require.resolve("babel-loader"),
options: {
@@ -349,7 +426,7 @@ export default function createWebpackConfig(
},
},
{
loader: "ts-loader",
loader: require.resolve("ts-loader"),
options: {
configFile: paths.appTsconfig,
compilerOptions: {
@@ -357,12 +434,14 @@ export default function createWebpackConfig(
module: "esnext",
jsx: "preserve",
noEmit: false,
sourceMap: !disableSourcemaps,
},
transpileOnly: true,
// Overwrites the behavior of `include` and `exclude` to only
// include files that are actually being imported and which
// are necessary to compile the bundle.
onlyCompileBundledFiles: true,
happyPackMode: true, // IMPORTANT! use happyPackMode mode to speed-up compilation and reduce errors reported to webpack
},
},
],
@@ -381,7 +460,7 @@ export default function createWebpackConfig(
presets: [
[
"@babel/env",
{ targets: "last 2 versions, ie 11", modules: false },
{ targets: "last 2 versions", modules: false },
],
],
cacheDirectory: true,
@@ -392,19 +471,19 @@ export default function createWebpackConfig(
// "postcss" loader applies autoprefixer to our CSS.
// "css" loader resolves paths in CSS and adds assets as dependencies.
// "style" loader turns CSS into JS modules that inject <style> tags.
// In production, we use a plugin to extract that CSS to a file, and
// in development "style" loader enables hot editing of CSS.
// When building we use a plugin to extract that CSS to a file, and
// in watch mode "style" loader enables hot editing of CSS.
{
test: /\.css$/,
use: [
isProduction ? MiniCssExtractPlugin.loader : styleLoader,
!watch ? MiniCssExtractPlugin.loader : styleLoader,
{
loader: require.resolve("css-loader"),
options: {
modules: true,
importLoaders: 1,
localIdentName: "[name]-[local]-[hash:base64:5]",
sourceMap: isProduction && !disableSourcemaps,
sourceMap: !disableSourcemaps,
},
},
{
@@ -413,13 +492,14 @@ export default function createWebpackConfig(
config: {
path: paths.appPostCssConfig,
},
sourceMap: !disableSourcemaps,
},
},
],
},
// "file" loader makes sure those assets get served by WebpackDevServer.
// When you `import` an asset, you get its (virtual) filename.
// In production, they would get copied to the `build` folder.
// When building, they would get copied to the `build` folder.
// This loader doesn't use a "test" so it will catch all modules
// that fall through the other loaders.
{
@@ -430,7 +510,14 @@ export default function createWebpackConfig(
exclude: [/\.(js|ts|tsx)$/, /\.html$/, /\.json$/],
loader: require.resolve("file-loader"),
options: {
name: "assets/media/[name].[hash:8].[ext]",
// Because the resources loaded via CSS can sometimes be loaded
// directly from a CSS file, this will ensure that they are
// relative to those referencing files.
publicPath: (loaderPublicPath: string) =>
"../../" + loaderPublicPath,
name: isProduction
? "assets/media/[name].[hash:8].[ext]"
: "assets/media/[name].[ext]",
},
},
],
@@ -440,6 +527,25 @@ export default function createWebpackConfig(
],
},
plugins: [
// TODO: (cvle) this should work in build too but for some reasons it terminates the build afterwards.
// Preventing from running post build steps.
...ifWatch(
// We run eslint in a separate process to have a quicker build.
new ForkTsCheckerWebpackPlugin({
eslint: true,
typescript: require.resolve("typescript"),
async: true,
// TODO: (cvle) For some reason if incremental build is turned on it does not find lint errors during initial build.
useTypescriptIncrementalApi: false,
checkSyntacticErrors: true,
tsconfig: paths.appTsconfig,
watch: paths.appSrc,
// TODO: (cvle) ForkTsCheckerWebpackPlugin are currently not working, so we resort to default reporting.
silent: false,
// The formatter is normally invoked directly in WebpackDevServerUtils during development
formatter: typescriptFormatter,
})
),
// Makes some environment variables available to the JS code, for example:
// if (process.env.NODE_ENV === 'development') { ... }. See `./env.js`.
new webpack.DefinePlugin(envStringified),
@@ -470,7 +576,7 @@ export default function createWebpackConfig(
},
};
const devServerEntries = isProduction
const devServerEntries = !watch
? []
: [
// Include an alternative client for WebpackDevServer. A client's job is to
@@ -483,7 +589,7 @@ export default function createWebpackConfig(
// the line below with these two lines if you prefer the stock client:
// require.resolve('webpack-dev-server/client') + '?/',
// require.resolve('webpack/hot/dev-server'),
require.resolve("react-dev-utils/webpackHotDevClient"),
paths.appWebpackHotDevClient,
];
return [
@@ -494,41 +600,52 @@ export default function createWebpackConfig(
stream: [
// We ship polyfills by default
paths.appPolyfill,
...ifProduction(paths.appPublicPath),
...ifBuild(paths.appPublicPath),
...devServerEntries,
paths.appStreamIndex,
],
auth: [
// We ship polyfills by default
paths.appPolyfill,
...ifProduction(paths.appPublicPath),
...ifBuild(paths.appPublicPath),
...devServerEntries,
paths.appAuthIndex,
// Remove deactivated entries.
],
authCallback: [
...ifProduction(paths.appPublicPath),
...ifBuild(paths.appPublicPath),
...devServerEntries,
paths.appAuthCallbackIndex,
],
install: [
// We ship polyfills by default
paths.appPolyfill,
...ifProduction(paths.appPublicPath),
...ifBuild(paths.appPublicPath),
...devServerEntries,
paths.appInstallIndex,
],
account: [
// We ship polyfills by default
paths.appPolyfill,
...ifBuild(paths.appPublicPath),
...devServerEntries,
paths.appAccountIndex,
],
admin: [
// We ship polyfills by default
paths.appPolyfill,
...ifProduction(paths.appPublicPath),
...ifBuild(paths.appPublicPath),
...devServerEntries,
paths.appAdminIndex,
],
},
plugins: filterPlugins([
...baseConfig.plugins!,
...ifNotProduction(
...ifWatch(
// This is necessary to emit hot updates
new webpack.HotModuleReplacementPlugin({
multiStep: true,
}),
// Generates an `stream.html` file with the <script> injected.
new HtmlWebpackPlugin({
filename: "stream.html",
@@ -557,6 +674,13 @@ export default function createWebpackConfig(
chunks: ["install"],
inject: "body",
}),
// Generates an `account.html` file with the <script> injected.
new HtmlWebpackPlugin({
filename: "account.html",
template: paths.appAccountHTML,
chunks: ["account"],
inject: "body",
}),
// Generates an `admin.html` file with the <script> injected.
new HtmlWebpackPlugin({
filename: "admin.html",
@@ -565,7 +689,7 @@ export default function createWebpackConfig(
inject: "body",
})
),
...ifProduction(
...ifBuild(
new WebpackAssetsManifest({
output: "asset-manifest.json",
entrypoints: true,
@@ -590,7 +714,6 @@ export default function createWebpackConfig(
entry: [
/* Use minimal amount of polyfills (for IE) */
"intersection-observer", // also for Safari
...devServerEntries,
paths.appEmbedIndex,
],
output: {
@@ -602,7 +725,7 @@ export default function createWebpackConfig(
},
plugins: filterPlugins([
...baseConfig.plugins!,
...ifNotProduction(
...ifWatch(
// Generates an `embed.html` file with the <script> injected.
new HtmlWebpackPlugin({
filename: "embed.html",
@@ -620,7 +743,7 @@ export default function createWebpackConfig(
inject: "head",
})
),
...ifProduction(
...ifBuild(
new WebpackAssetsManifest({
output: "embed-asset-manifest.json",
entrypoints: true,
@@ -629,5 +752,44 @@ export default function createWebpackConfig(
),
]),
},
/* Webpack config for count */
{
...baseConfig,
optimization: {
...baseConfig.optimization,
// Ensure that we never split the count into chunks.
splitChunks: {
chunks: "async",
},
// We can turn on sideEffects here as we don't use
// css here and don't run into: https://github.com/webpack/webpack/issues/7094
sideEffects: true,
},
entry: [paths.appCountIndex],
output: {
...baseConfig.output,
// don't hash the count, cache-busting must be completed by the requester
// as this lives in a static template on the embed site.
filename: "assets/js/count.js",
},
plugins: filterPlugins([
...baseConfig.plugins!,
...ifWatch(
// Generates an `embed.html` file with the <script> injected.
new HtmlWebpackPlugin({
filename: "count.html",
template: paths.appCountHTML,
inject: "body",
})
),
...ifBuild(
new WebpackAssetsManifest({
output: "count-asset-manifest.json",
entrypoints: true,
integrity: true,
})
),
]),
},
];
}
-1
View File
@@ -59,7 +59,6 @@ function generateTarget(target, context) {
defaultLocale,
fallbackLocale,
pathToLocales,
resourcePath,
locales,
bundled,
} = context;
+9
View File
@@ -34,10 +34,17 @@ export default {
appAuthCallbackHTML: resolveSrc("core/client/auth-callback/index.html"),
appAuthCallbackIndex: resolveSrc("core/client/auth-callback/index.ts"),
appCountHTML: resolveSrc("core/client/count/index.html"),
appCountIndex: resolveSrc("core/client/count/index.ts"),
appInstallHTML: resolveSrc("core/client/install/index.html"),
appInstallLocalesTemplate: resolveSrc("core/client/install/locales.ts"),
appInstallIndex: resolveSrc("core/client/install/index.tsx"),
appAccountHTML: resolveSrc("core/client/account/index.html"),
appAccountLocalesTemplate: resolveSrc("core/client/account/locales.ts"),
appAccountIndex: resolveSrc("core/client/account/index.tsx"),
appAdminHTML: resolveSrc("core/client/admin/index.html"),
appAdminLocalesTemplate: resolveSrc("core/client/admin/locales.ts"),
appAdminIndex: resolveSrc("core/client/admin/index.tsx"),
@@ -51,4 +58,6 @@ export default {
appPublic: resolveApp("public"),
appPackageJson: resolveApp("package.json"),
appNodeModules: resolveApp("node_modules"),
appWebpackHotDevClient: resolveApp("scripts/webpackHotDevClient.js"),
};
+1 -12
View File
@@ -9,7 +9,6 @@ const paths = require("./paths").default;
const autoprefixer = require("autoprefixer");
const postcssFontMagician = require("postcss-font-magician");
const postcssFlexbugsFixes = require("postcss-flexbugs-fixes");
const postcssVariables = require("postcss-css-variables");
const postcssPresetEnv = require("postcss-preset-env");
const postcssNested = require("postcss-nested");
const postcssImport = require("postcss-import");
@@ -24,12 +23,6 @@ const flatKebabVariables = mapKeys(
(_, k) => kebabCase(k)
);
// These are the default css standard variables.
const cssVariables = pickBy(
flatKebabVariables,
(v, k) => !k.startsWith("breakpoints-")
);
// These are sass style variables used in media queries.
const mediaQueryVariables = mapValues(
pickBy(flatKebabVariables, (v, k) => k.startsWith("breakpoints-")),
@@ -49,7 +42,7 @@ module.exports = {
// This allows us to define dynamic css variables.
postcssPrependImports({
path: "",
files: [paths.appThemeVariablesCSS, paths.appThemeMixinsCSS],
files: [paths.appThemeMixinsCSS],
}),
// Needed by above plugin.
postcssImport(),
@@ -59,10 +52,6 @@ module.exports = {
postcssNested(),
// Sass style variables to be used in media queries.
postcssAdvancedVariables({ variables: mediaQueryVariables }),
// CSS standard variables for everything else.
postcssVariables({
variables: cssVariables,
}),
// Provides a modern CSS environment.
postcssPresetEnv(),
// Does all the font handling logic.
+1 -1
View File
@@ -1,2 +1,2 @@
__webpack_public_path__ =
JSON.parse(document.getElementById("config").innerText) || "/";
JSON.parse(document.getElementById("config").innerText).staticURI || "/";
+4 -4
View File
@@ -1,19 +1,19 @@
const lodashOptimizations = ["use-lodash-es", "lodash"];
module.exports = {
presets: [
["@babel/env", { targets: "last 2 versions, ie 11", modules: false }],
"@babel/react",
],
presets: ["@babel/react"],
plugins: ["@babel/syntax-dynamic-import"],
env: {
production: {
presets: [["@babel/env", { targets: "last 2 versions", modules: false }]],
plugins: [...lodashOptimizations],
},
development: {
presets: [["@babel/env", { targets: "last 2 versions", modules: false }]],
plugins: [...lodashOptimizations],
},
test: {
presets: [["@babel/env", { targets: { node: "current" } }]],
plugins: ["@babel/transform-modules-commonjs"],
},
},
+10
View File
@@ -0,0 +1,10 @@
const path = require("path");
module.exports = {
extends: "../.babelrc.js",
plugins: [
[
"babel-plugin-relay",
{ artifactDirectory: path.resolve(__dirname, "./__generated__") },
],
],
};
+42
View File
@@ -0,0 +1,42 @@
import { BrowserProtocol, queryMiddleware } from "farce";
import { createFarceRouter, ElementsRenderer } from "found";
import { Resolver } from "found-relay";
import React, { FunctionComponent } from "react";
import { CoralContextConsumer } from "coral-framework/lib/bootstrap/CoralContext";
import TransitionControl from "coral-framework/testHelpers/TransitionControl";
import routeConfig from "../routeConfig";
import NotFound from "../routes/NotFound";
import MainLayout from "./MainLayout";
import "./App.css";
const Router = createFarceRouter({
historyProtocol: new BrowserProtocol(),
historyMiddlewares: [queryMiddleware],
routeConfig,
renderReady: function FarceRouterReady({ elements }) {
return (
<>
<ElementsRenderer elements={elements} />
{process.env.NODE_ENV === "test" && <TransitionControl />}
</>
);
},
renderError: function FarceRouterError({ error }) {
return <div>{error.status === 404 ? <NotFound /> : "Error"}</div>;
},
});
const EntryContainer: FunctionComponent = () => (
<CoralContextConsumer>
{({ relayEnvironment }) => (
<MainLayout>
<Router resolver={new Resolver(relayEnvironment)} />
</MainLayout>
)}
</CoralContextConsumer>
);
export default EntryContainer;
@@ -0,0 +1,10 @@
.bar {
height: calc(6 * var(--mini-unit));
background-color: var(--palette-text-primary);
}
.centered {
margin: 10% auto;
padding: 0 calc(0.5 * var(--mini-unit));
box-sizing: border-box;
}
@@ -0,0 +1,12 @@
import React from "react";
import styles from "./MainLayout.css";
const MainLayout: React.FunctionComponent = ({ children }) => (
<div data-testid="main-layout">
<div className={styles.bar} />
<div className={styles.centered}>{children}</div>
</div>
);
export default MainLayout;
+1
View File
@@ -0,0 +1 @@
export { default, default as App } from "./App";
@@ -0,0 +1,13 @@
import React, { FunctionComponent } from "react";
import { Delay, Flex, Spinner } from "coral-ui/components";
const Loading: FunctionComponent = () => (
<Flex justifyContent="center">
<Delay>
<Spinner />
</Delay>
</Flex>
);
export default Loading;
+13
View File
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<title>Coral - Account</title>
<meta charset="utf-8" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width" />
</head>
<body>
<div id="app"></div>
</body>
</html>
+31
View File
@@ -0,0 +1,31 @@
import React, { FunctionComponent } from "react";
import ReactDOM from "react-dom";
import potentiallyInjectAxe from "coral-framework/helpers/potentiallyInjectAxe";
import { createManaged } from "coral-framework/lib/bootstrap";
import App from "./App";
import { initLocalState } from "./local";
import localesData from "./locales";
// Import css variables.
import "coral-ui/theme/variables.css";
async function main() {
// Potentially inject react-axe for runtime a11y checks.
await potentiallyInjectAxe();
const ManagedCoralContextProvider = await createManaged({
initLocalState,
localesData,
});
const Index: FunctionComponent = () => (
<ManagedCoralContextProvider>
<App />
</ManagedCoralContextProvider>
);
ReactDOM.render(<Index />, document.getElementById("app"));
}
main();
+1
View File
@@ -0,0 +1 @@
export { default as initLocalState } from "./initLocalState";
@@ -0,0 +1,14 @@
import { Environment } from "relay-runtime";
import { CoralContext } from "coral-framework/lib/bootstrap";
import { initLocalBaseState } from "coral-framework/lib/relay";
/**
* Initializes the local state, before we start the App.
*/
export default async function initLocalState(
environment: Environment,
context: CoralContext
) {
await initLocalBaseState(environment, context);
}
@@ -0,0 +1,9 @@
type Local {
accessToken: String
accessTokenExp: Int
accessTokenJTI: String
}
extend type Query {
local: Local!
}
+10
View File
@@ -0,0 +1,10 @@
/**
* The actual content of this file is being generated by our `locales-loader`.
* Please check `./src/loaders` and the webpack config for more information.
*
* This file only represents the types that gets exported.
*/
import { LocalesData } from "coral-framework/lib/i18n";
export default {} as LocalesData;
+22
View File
@@ -0,0 +1,22 @@
import { makeRouteConfig, Route } from "found";
import React from "react";
import DownloadRoute from "./routes/download/Download";
import ConfirmRoute from "./routes/email/Confirm";
import UnsubscribeRoute from "./routes/notifications/Unsubscribe";
import ResetRoute from "./routes/password/Reset";
export default makeRouteConfig(
<Route path="account">
<Route path="password">
<Route path="reset" {...ResetRoute.routeConfig} />
</Route>
<Route path="email">
<Route path="confirm" {...ConfirmRoute.routeConfig} />
</Route>
<Route path="download" {...DownloadRoute.routeConfig} />
<Route path="notifications">
<Route path="unsubscribe" {...UnsubscribeRoute.routeConfig} />
</Route>
</Route>
);
@@ -0,0 +1,13 @@
import React, { FunctionComponent } from "react";
import { HorizontalGutter, Typography } from "coral-ui/components";
const NotFound: FunctionComponent = () => (
<HorizontalGutter container="main">
<Typography variant="heading3" container="h1">
Not Found
</Typography>
</HorizontalGutter>
);
export default NotFound;
@@ -0,0 +1,48 @@
.title {
font-family: var(--font-family-serif);
font-weight: 600;
font-style: normal;
font-size: calc(48rem / var(--rem-base));
line-height: calc(42em / 48);
text-align: center;
color: var(--palette-common-black);
}
.content {
padding: var(--spacing-2) var(--spacing-3) var(--spacing-3) var(--spacing-3);
}
.section {
margin-bottom: var(--spacing-4);
}
.sectionText {
font-family: var(--font-family-sans-serif);
font-weight: normal;
font-style: normal;
font-size: calc(24rem / var(--rem-base));
line-height: calc(34em / 24);
letter-spacing: 0.3px;
color: var(--palette-text-primary);
}
.list {
list-style: none;
margin-left: calc(-1 * var(--spacing-5));
}
.list li {
margin-bottom: calc(16em / 24);
}
.listContent {
display: inline-flex;
}
.bullet {
display: inline-block;
min-width: 24px;
margin-right: var(--spacing-2);
}
@@ -0,0 +1,101 @@
import { Localized } from "@fluent/react/compat";
import cn from "classnames";
import React, { FunctionComponent } from "react";
import { Flex, HorizontalGutter, Icon, Typography } from "coral-ui/components";
import styles from "./DownloadDescription.css";
const DownloadDescription: FunctionComponent = () => {
return (
<HorizontalGutter size="double">
<Localized id="download-landingPage-title">
<h1 className={styles.title}>Download Your Comment History</h1>
</Localized>
<div className={styles.content}>
<div className={styles.section}>
<Localized id="download-landingPage-description">
<Typography variant="bodyCopy" className={styles.sectionText}>
Your comment history will be downloaded into a .zip file. After
your comment history is unzipped you will have a comma separated
value (or .csv) file that you can easily import into your favorite
spreadsheet application.
</Typography>
</Localized>
</div>
<div className={styles.section}>
<Localized id="download-landingPage-contentsDescription">
<Typography variant="bodyCopy" className={styles.sectionText}>
For each of your comments the following information is included:
</Typography>
</Localized>
<ul className={styles.list}>
<li>
<Flex alignItems="center">
<Icon size="lg" className={styles.bullet}>
check
</Icon>
<Localized id="download-landingPage-contentsDate">
<Typography
variant="bodyCopy"
className={cn(styles.sectionText, styles.listContent)}
>
When you wrote the comment
</Typography>
</Localized>
</Flex>
</li>
<li>
<Flex alignItems="center">
<Icon size="lg" className={styles.bullet}>
check
</Icon>
<Localized id="download-landingPage-contentsUrl">
<Typography
variant="bodyCopy"
className={cn(styles.sectionText, styles.listContent)}
>
The permalink URL for the comment
</Typography>
</Localized>
</Flex>
</li>
<li>
<Flex alignItems="center">
<Icon size="lg" className={styles.bullet}>
check
</Icon>
<Localized id="download-landingPage-contentsText">
<Typography
variant="bodyCopy"
className={cn(styles.sectionText, styles.listContent)}
>
The comment text
</Typography>
</Localized>
</Flex>
</li>
<li>
<Flex alignItems="center">
<Icon size="lg" className={styles.bullet}>
check
</Icon>
<Localized id="download-landingPage-contentsStoryUrl">
<Typography
variant="bodyCopy"
className={cn(styles.sectionText, styles.listContent)}
>
The URL on the article or story where the comment appears
</Typography>
</Localized>
</Flex>
</li>
</ul>
</div>
</div>
</HorizontalGutter>
);
};
export default DownloadDescription;
@@ -0,0 +1,8 @@
.form {
text-align: center;
}
.downloadButton {
font-size: calc(18rem / var(--rem-base));
line-height: calc(23em / 18);
}
@@ -0,0 +1,44 @@
import { Localized } from "@fluent/react/compat";
import React, { FunctionComponent, useCallback, useState } from "react";
import { Button, HorizontalGutter } from "coral-ui/components";
import styles from "./DownloadForm.css";
interface Props {
token: string;
}
const DownloadForm: FunctionComponent<Props> = ({ token }) => {
const [submitted, setSubmitted] = useState(false);
const onSubmit = useCallback(() => {
setSubmitted(true);
return true;
}, [setSubmitted]);
return (
<HorizontalGutter size="double">
<form
className={styles.form}
method="post"
action="/api/account/download"
onSubmit={onSubmit}
>
<input name="token" type="hidden" value={token} />
<Localized id="download-landingPage-downloadComments">
<Button
type="submit"
variant="filled"
color="primary"
disabled={submitted}
className={styles.downloadButton}
>
Download My Comment History
</Button>
</Localized>
</form>
</HorizontalGutter>
);
};
export default DownloadForm;
@@ -0,0 +1,16 @@
.container {
width: 100%;
text-align: center;
}
.root {
display: inline-block;
max-width: calc(70 * var(--mini-unit));
@media (min-width: $breakpoints-xs) {
max-width: calc(90 * var(--mini-unit));
}
text-align: left;
}
@@ -0,0 +1,75 @@
import React, { FunctionComponent } from "react";
import { Environment } from "relay-runtime";
import Loading from "coral-account/components/Loading";
import { useToken } from "coral-framework/hooks";
import { createFetch } from "coral-framework/lib/relay";
import { withRouteConfig } from "coral-framework/lib/router";
import { parseHashQuery } from "coral-framework/utils";
import { HorizontalGutter } from "coral-ui/components";
import DownloadDescription from "./DownloadDescription";
import DownloadForm from "./DownloadForm";
import Sorry from "./Sorry";
import styles from "./DownloadRoute.css";
const fetcher = createFetch(
"downloadToken",
async (environment: Environment, variables: { token: string }, { rest }) =>
await rest.fetch<void>("/account/download", {
method: "GET",
token: variables.token,
})
);
interface Props {
token: string | undefined;
}
const DownloadRoute: FunctionComponent<Props> = ({ token }) => {
const [state, error] = useToken(fetcher, token);
if (state === "UNCHECKED") {
return (
<main className={styles.container}>
<div className={styles.root}>
<HorizontalGutter size="double">
<Loading />
</HorizontalGutter>
</div>
</main>
);
}
if (state !== "VALID" || error) {
return (
<main className={styles.container}>
<div className={styles.root}>
<HorizontalGutter size="double">
<DownloadDescription />
<Sorry />
</HorizontalGutter>
</div>
</main>
);
}
return (
<main className={styles.container}>
<div className={styles.root}>
<DownloadDescription />
<DownloadForm token={token!} />
</div>
</main>
);
};
const enhanced = withRouteConfig<Props>({
render: function DownloadRouteRender({ match, Component }) {
return (
<Component token={parseHashQuery(match.location.hash).downloadToken} />
);
},
})(DownloadRoute);
export default enhanced;
@@ -0,0 +1,7 @@
.icon {
margin-right: var(--spacing-2);
}
.callout {
justify-content: left;
}
@@ -0,0 +1,23 @@
import { Localized } from "@fluent/react/compat";
import React, { FunctionComponent } from "react";
import { CallOut, Flex, Icon } from "coral-ui/components";
import styles from "./Sorry.css";
const Sorry: FunctionComponent = () => {
return (
<CallOut color="error" fullWidth className={styles.callout}>
<Flex>
<Icon size="md" className={styles.icon}>
error
</Icon>
<Localized id="download-landingPage-sorry">
Your download link is invalid.
</Localized>
</Flex>
</CallOut>
);
};
export default Sorry;
@@ -0,0 +1 @@
export { default, default as DownloadRoute } from "./DownloadRoute";
@@ -0,0 +1,67 @@
import { Localized } from "@fluent/react/compat";
import { FORM_ERROR } from "final-form";
import React, { useCallback } from "react";
import { Form } from "react-final-form";
import { InvalidRequestError } from "coral-framework/lib/errors";
import { useMutation } from "coral-framework/lib/relay";
import { Button, HorizontalGutter, Typography } from "coral-ui/components";
import ConfirmMutation from "./ConfirmMutation";
interface Props {
token: string;
disabled?: boolean;
onSuccess: () => void;
}
const ConfirmForm: React.FunctionComponent<Props> = ({ onSuccess, token }) => {
const confirm = useMutation(ConfirmMutation);
const onSubmit = useCallback(async () => {
try {
await confirm({ token });
onSuccess();
} catch (error) {
if (error instanceof InvalidRequestError) {
return error.invalidArgs;
}
return { [FORM_ERROR]: error.message };
}
return;
}, [token]);
return (
<Form onSubmit={onSubmit}>
{({ handleSubmit, submitting }) => (
<form autoComplete="off" onSubmit={handleSubmit}>
<HorizontalGutter size="double">
<HorizontalGutter>
<Localized id="confirmEmail-emailConfirmation">
<Typography variant="heading1">Email Confirmation</Typography>
</Localized>
<Localized id="confirmEmail-pleaseClickToConfirm">
<Typography variant="bodyCopy">
Click below to confirm your email address.
</Typography>
</Localized>
</HorizontalGutter>
<HorizontalGutter>
<Localized id="confirmEmail-confirmEmail">
<Button
type="submit"
variant="filled"
color="primary"
disabled={submitting}
fullWidth
>
Confirm email
</Button>
</Localized>
</HorizontalGutter>
</HorizontalGutter>
</form>
)}
</Form>
);
};
export default ConfirmForm;
@@ -0,0 +1,14 @@
import { Environment } from "relay-runtime";
import { createMutation } from "coral-framework/lib/relay";
const ConfirmMutation = createMutation(
"confirm",
async (environment: Environment, variables: { token: string }, { rest }) =>
await rest.fetch<void>("/account/confirm", {
method: "PUT",
token: variables.token,
})
);
export default ConfirmMutation;
@@ -0,0 +1,16 @@
.container {
width: 100%;
text-align: center;
}
.root {
display: inline-block;
max-width: calc(70 * var(--mini-unit));
@media (min-width: $breakpoints-xs) {
max-width: calc(42 * var(--mini-unit));
}
text-align: left;
}
@@ -0,0 +1,79 @@
import React, { useCallback, useState } from "react";
import { Environment } from "relay-runtime";
import Loading from "coral-account/components/Loading";
import { useToken } from "coral-framework/hooks";
import { createFetch } from "coral-framework/lib/relay";
import { withRouteConfig } from "coral-framework/lib/router";
import { parseHashQuery } from "coral-framework/utils";
import ConfirmForm from "./ConfirmForm";
import Sorry from "./Sorry";
import Success from "./Success";
import styles from "./ConfirmRoute.css";
const fetcher = createFetch(
"confirmToken",
async (environment: Environment, variables: { token: string }, { rest }) =>
await rest.fetch<void>("/account/confirm", {
method: "GET",
token: variables.token,
})
);
interface Props {
token: string | undefined;
}
const ConfirmRoute: React.FunctionComponent<Props> = ({ token }) => {
const [finished, setFinished] = useState(false);
const onSuccess = useCallback(() => {
setFinished(true);
}, []);
const [state, error] = useToken(fetcher, token);
if (state === "UNCHECKED") {
return (
<main className={styles.container}>
<div className={styles.root}>
<Loading />
</div>
</main>
);
}
if (state !== "VALID" || error) {
return (
<main className={styles.container}>
<div className={styles.root}>
<Sorry reason={error} />
</div>
</main>
);
}
return !finished ? (
<main className={styles.container}>
<div className={styles.root}>
<ConfirmForm token={token!} onSuccess={onSuccess} />
</div>
</main>
) : (
<main className={styles.container}>
<div className={styles.root}>
<Success />
</div>
</main>
);
};
const enhanced = withRouteConfig<Props>({
render: function ConfirmRouteRender({ match, Component }) {
return (
<Component token={parseHashQuery(match.location.hash).confirmToken} />
);
},
})(ConfirmRoute);
export default enhanced;
@@ -0,0 +1,32 @@
import { Localized } from "@fluent/react/compat";
import React from "react";
import { CallOut, HorizontalGutter, Typography } from "coral-ui/components";
interface Props {
reason: React.ReactNode;
}
const Sorry: React.FunctionComponent<Props> = ({ reason }) => {
return (
<HorizontalGutter size="double">
<Localized id="confirmEmail-oopsSorry">
<Typography variant="heading1">Oops Sorry!</Typography>
</Localized>
<CallOut color="error" fullWidth>
{reason ? (
reason
) : (
<Localized id="account-tokenNotFound">
<span data-testid="invalid-link">
The specified link is invalid, check to see if it was copied
correctly.
</span>
</Localized>
)}
</CallOut>
</HorizontalGutter>
);
};
export default Sorry;
@@ -0,0 +1,21 @@
import { Localized } from "@fluent/react/compat";
import React from "react";
import { HorizontalGutter, Typography } from "coral-ui/components";
const Success: React.FunctionComponent = () => {
return (
<HorizontalGutter size="double">
<Localized id="confirmEmail-successfullyConfirmed">
<Typography variant="heading1">Email successfully confirmed</Typography>
</Localized>
<Localized id="confirmEmail-youMayClose">
<Typography variant="bodyCopy">
You may now close this window.
</Typography>
</Localized>
</HorizontalGutter>
);
};
export default Success;
@@ -0,0 +1 @@
export { default, default as ConfirmRoute } from "./ConfirmRoute";
@@ -0,0 +1,32 @@
import { Localized } from "@fluent/react/compat";
import React from "react";
import { CallOut, HorizontalGutter, Typography } from "coral-ui/components";
interface Props {
reason: React.ReactNode;
}
const Sorry: React.FunctionComponent<Props> = ({ reason }) => {
return (
<HorizontalGutter size="double">
<Localized id="unsubscribe-oopsSorry">
<Typography variant="heading1">Oops Sorry!</Typography>
</Localized>
<CallOut color="error" fullWidth>
{reason ? (
reason
) : (
<Localized id="account-tokenNotFound">
<span data-testid="invalid-link">
The specified link is invalid, check to see if it was copied
correctly.
</span>
</Localized>
)}
</CallOut>
</HorizontalGutter>
);
};
export default Sorry;
@@ -0,0 +1,18 @@
import { Localized } from "@fluent/react/compat";
import React from "react";
import { HorizontalGutter, Typography } from "coral-ui/components";
const Success: React.FunctionComponent = () => {
return (
<HorizontalGutter data-testid="success" size="double">
<Localized id="unsubscribe-successfullyUnsubscribed">
<Typography variant="heading1">
You are now unsubscribed from all notifications
</Typography>
</Localized>
</HorizontalGutter>
);
};
export default Success;
@@ -0,0 +1,76 @@
import { Localized } from "@fluent/react/compat";
import { FORM_ERROR } from "final-form";
import React, { useCallback } from "react";
import { Form } from "react-final-form";
import { InvalidRequestError } from "coral-framework/lib/errors";
import { useMutation } from "coral-framework/lib/relay";
import {
Button,
CallOut,
HorizontalGutter,
Typography,
} from "coral-ui/components";
import UnsubscribeNotificationsMutation from "./UnsubscribeNotificationsMutation";
interface Props {
token: string;
disabled?: boolean;
onSuccess: () => void;
}
const UnsubscribeForm: React.FunctionComponent<Props> = ({
onSuccess,
token,
}) => {
const unsubscribe = useMutation(UnsubscribeNotificationsMutation);
const onSubmit = useCallback(async () => {
try {
await unsubscribe({ token });
onSuccess();
} catch (error) {
if (error instanceof InvalidRequestError) {
return error.invalidArgs;
}
return { [FORM_ERROR]: error.message };
}
return;
}, [token]);
return (
<div data-testid="unsubscribe-form">
<Form onSubmit={onSubmit}>
{({ handleSubmit, submitting, submitError }) => (
<form onSubmit={handleSubmit}>
<HorizontalGutter>
<Localized id="unsubscribe-clickToConfirm">
<Typography variant="heading1">
Click below to confirm that you want to unsubscribe from all
notifications.
</Typography>
</Localized>
{submitError && (
<CallOut color="error" fullWidth>
{submitError}
</CallOut>
)}
<Localized id="unsubscribe-confirm">
<Button
type="submit"
variant="filled"
color="primary"
disabled={submitting}
fullWidth
>
Confirm
</Button>
</Localized>
</HorizontalGutter>
</form>
)}
</Form>
</div>
);
};
export default UnsubscribeForm;
@@ -0,0 +1,14 @@
import { Environment } from "relay-runtime";
import { createMutation } from "coral-framework/lib/relay";
const UnsubscribeNotificationsMutation = createMutation(
"unsubscribeNotifications",
async (environment: Environment, variables: { token: string }, { rest }) =>
await rest.fetch<void>("/account/notifications/unsubscribe", {
method: "DELETE",
token: variables.token,
})
);
export default UnsubscribeNotificationsMutation;
@@ -0,0 +1,16 @@
.container {
width: 100%;
text-align: center;
}
.root {
display: inline-block;
max-width: calc(70 * var(--mini-unit));
@media (min-width: $breakpoints-xs) {
max-width: calc(42 * var(--mini-unit));
}
text-align: left;
}
@@ -0,0 +1,79 @@
import React, { useCallback, useState } from "react";
import { Environment } from "relay-runtime";
import Loading from "coral-account/components/Loading";
import { useToken } from "coral-framework/hooks";
import { createFetch } from "coral-framework/lib/relay";
import { withRouteConfig } from "coral-framework/lib/router";
import { parseHashQuery } from "coral-framework/utils";
import Sorry from "./Sorry";
import Success from "./Success";
import UnsubscribeForm from "./UnsubscribeForm";
import styles from "./UnsubscribeRoute.css";
const fetcher = createFetch(
"unsubscribeToken",
async (environment: Environment, variables: { token: string }, { rest }) =>
await rest.fetch<void>("/account/notifications/unsubscribe", {
method: "GET",
token: variables.token,
})
);
interface Props {
token: string | undefined;
}
const UnsubscribeRoute: React.FunctionComponent<Props> = ({ token }) => {
const [finished, setFinished] = useState(false);
const onSuccess = useCallback(() => {
setFinished(true);
}, []);
const [state, error] = useToken(fetcher, token);
if (state === "UNCHECKED") {
return (
<main className={styles.container}>
<div className={styles.root}>
<Loading />
</div>
</main>
);
}
if (state !== "VALID" || error) {
return (
<main className={styles.container}>
<div className={styles.root}>
<Sorry reason={error} />
</div>
</main>
);
}
return !finished ? (
<main className={styles.container}>
<div className={styles.root}>
<UnsubscribeForm token={token!} onSuccess={onSuccess} />
</div>
</main>
) : (
<main className={styles.container}>
<div className={styles.root}>
<Success />
</div>
</main>
);
};
const enhanced = withRouteConfig<Props>({
render: function UnsubscribeRouteRender({ match, Component }) {
return (
<Component token={parseHashQuery(match.location.hash).unsubscribeToken} />
);
},
})(UnsubscribeRoute);
export default enhanced;
@@ -0,0 +1 @@
export { default, default as UnsubscribeRoute } from "./UnsubscribeRoute";
@@ -0,0 +1,139 @@
import { Localized } from "@fluent/react/compat";
import { FORM_ERROR } from "final-form";
import React, { useCallback } from "react";
import { Field, Form } from "react-final-form";
import { InvalidRequestError } from "coral-framework/lib/errors";
import { colorFromMeta, ValidationMessage } from "coral-framework/lib/form";
import { useMutation } from "coral-framework/lib/relay";
import {
composeValidators,
required,
validatePassword,
} from "coral-framework/lib/validation";
import {
Button,
CallOut,
FormField,
HorizontalGutter,
InputDescription,
InputLabel,
PasswordField,
Typography,
} from "coral-ui/components";
import ResetPasswordMutation from "./ResetPasswordMutation";
interface Props {
token: string;
disabled?: boolean;
onSuccess: () => void;
}
interface FormProps {
password: string;
}
const ResetPasswordForm: React.FunctionComponent<Props> = ({
onSuccess,
token,
disabled,
}) => {
const resetPassword = useMutation(ResetPasswordMutation);
const onSubmit = useCallback(
async ({ password }: FormProps) => {
try {
await resetPassword({ token, password });
onSuccess();
} catch (error) {
if (error instanceof InvalidRequestError) {
return error.invalidArgs;
}
return { [FORM_ERROR]: error.message };
}
return;
},
[token]
);
return (
<div>
<Form onSubmit={onSubmit}>
{({ handleSubmit, submitting, submitError }) => (
<form autoComplete="off" onSubmit={handleSubmit}>
<HorizontalGutter size="double">
<HorizontalGutter>
<Localized id="resetPassword-resetYourPassword">
<Typography variant="heading1">
Reset your password
</Typography>
</Localized>
<Localized id="resetPassword-pleaseEnterNewPassword">
<Typography variant="bodyCopy">
Please enter a new password to use to sign in to your
account. Make sure it is unique and be sure to keep it
secure.
</Typography>
</Localized>
</HorizontalGutter>
<HorizontalGutter>
<Field
name="password"
validate={composeValidators(required, validatePassword)}
>
{({ input, meta }) => (
<FormField>
<Localized id="resetPassword-passwordLabel">
<InputLabel htmlFor={input.name}>Password</InputLabel>
</Localized>
<Localized
id="resetPassword-passwordDescription"
$minLength={8}
>
<InputDescription>
{"Must be at least {$minLength} characters"}
</InputDescription>
</Localized>
<Localized
id="resetPassword-passwordTextField"
attrs={{ placeholder: true }}
>
<PasswordField
id={input.name}
placeholder="Password"
color={colorFromMeta(meta)}
autoComplete="new-password"
disabled={disabled || meta.submitting}
fullWidth
{...input}
/>
</Localized>
<ValidationMessage meta={meta} fullWidth />
</FormField>
)}
</Field>
{submitError && (
<CallOut color="error" fullWidth>
{submitError}
</CallOut>
)}
<Localized id="resetPassword-resetPassword">
<Button
type="submit"
variant="filled"
color="primary"
disabled={submitting}
fullWidth
>
Reset Password
</Button>
</Localized>
</HorizontalGutter>
</HorizontalGutter>
</form>
)}
</Form>
</div>
);
};
export default ResetPasswordForm;
@@ -0,0 +1,21 @@
import { Environment } from "relay-runtime";
import { createMutation } from "coral-framework/lib/relay";
const ResetPasswordMutation = createMutation(
"resetPassword",
async (
environment: Environment,
variables: { token: string; password: string },
{ rest }
) =>
await rest.fetch<void>("/auth/local/forgot", {
method: "PUT",
token: variables.token,
body: {
password: variables.password,
},
})
);
export default ResetPasswordMutation;
@@ -0,0 +1,16 @@
.container {
width: 100%;
text-align: center;
}
.root {
display: inline-block;
max-width: calc(70 * var(--mini-unit));
@media (min-width: $breakpoints-xs) {
max-width: calc(42 * var(--mini-unit));
}
text-align: left;
}
@@ -0,0 +1,77 @@
import React, { useCallback, useState } from "react";
import { Environment } from "relay-runtime";
import Loading from "coral-account/components/Loading";
import { useToken } from "coral-framework/hooks";
import { createFetch } from "coral-framework/lib/relay";
import { withRouteConfig } from "coral-framework/lib/router";
import { parseHashQuery } from "coral-framework/utils";
import ResetPasswordForm from "./ResetPasswordForm";
import Sorry from "./Sorry";
import Success from "./Success";
import styles from "./ResetRoute.css";
const fetcher = createFetch(
"resetToken",
async (environment: Environment, variables: { token: string }, { rest }) =>
await rest.fetch<void>("/auth/local/forgot", {
method: "GET",
token: variables.token,
})
);
interface Props {
token: string | undefined;
}
const ResetRoute: React.FunctionComponent<Props> = ({ token }) => {
const [finished, setFinished] = useState(false);
const onSuccess = useCallback(() => {
setFinished(true);
}, []);
const [state, error] = useToken(fetcher, token);
if (state === "UNCHECKED") {
return (
<main className={styles.container}>
<div className={styles.root}>
<Loading />
</div>
</main>
);
}
if (state !== "VALID" || error) {
return (
<main className={styles.container}>
<div className={styles.root}>
<Sorry reason={error} />
</div>
</main>
);
}
return !finished ? (
<main className={styles.container}>
<div className={styles.root}>
<ResetPasswordForm token={token!} onSuccess={onSuccess} />
</div>
</main>
) : (
<main className={styles.container}>
<div className={styles.root}>
<Success />
</div>
</main>
);
};
const enhanced = withRouteConfig<Props>({
render: function ResetRouteRender({ match, Component }) {
return <Component token={parseHashQuery(match.location.hash).resetToken} />;
},
})(ResetRoute);
export default enhanced;
@@ -0,0 +1,32 @@
import { Localized } from "@fluent/react/compat";
import React from "react";
import { CallOut, HorizontalGutter, Typography } from "coral-ui/components";
interface Props {
reason: React.ReactNode;
}
const Sorry: React.FunctionComponent<Props> = ({ reason }) => {
return (
<HorizontalGutter size="double">
<Localized id="resetPassword-oopsSorry">
<Typography variant="heading1">Oops Sorry!</Typography>
</Localized>
<CallOut color="error" fullWidth>
{reason ? (
reason
) : (
<Localized id="account-tokenNotFound">
<span data-testid="invalid-link">
The specified link is invalid, check to see if it was copied
correctly.
</span>
</Localized>
)}
</CallOut>
</HorizontalGutter>
);
};
export default Sorry;
@@ -0,0 +1,22 @@
import { Localized } from "@fluent/react/compat";
import React from "react";
import { HorizontalGutter, Typography } from "coral-ui/components";
const Success: React.FunctionComponent = () => {
return (
<HorizontalGutter size="double">
<Localized id="resetPassword-successfullyReset">
<Typography variant="heading1">Password successfully reset</Typography>
</Localized>
<Localized id="resetPassword-youMayClose">
<Typography variant="bodyCopy">
You may now close this window and sign in to your account with your
new password.
</Typography>
</Localized>
</HorizontalGutter>
);
};
export default Success;
@@ -0,0 +1 @@
export { default, default as ResetRoute } from "./ResetRoute";
@@ -0,0 +1,106 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders form 1`] = `
<div
data-testid="main-layout"
>
<div
className="MainLayout-bar"
/>
<div
className="MainLayout-centered"
>
<main
className="ConfirmRoute-container"
>
<div
className="ConfirmRoute-root"
>
<form
autoComplete="off"
onSubmit={[Function]}
>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-double"
>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-full"
>
<h1
className="Box-root Typography-root Typography-heading1 Typography-colorTextPrimary"
>
Email Confirmation
</h1>
<p
className="Box-root Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Click below to confirm your email address.
</p>
</div>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-full"
>
<button
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled Button-fullWidth"
data-color="primary"
data-variant="filled"
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Confirm email
</button>
</div>
</div>
</form>
</div>
</main>
</div>
</div>
`;
exports[`renders missing confirm token 1`] = `
<div
data-testid="main-layout"
>
<div
className="MainLayout-bar"
/>
<div
className="MainLayout-centered"
>
<main
className="ConfirmRoute-container"
>
<div
className="ConfirmRoute-root"
>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-double"
>
<h1
className="Box-root Typography-root Typography-heading1 Typography-colorTextPrimary"
>
Oops Sorry!
</h1>
<div
className="CallOut-root CallOut-colorError CallOut-fullWidth"
>
<div>
<span
data-testid="invalid-link"
>
The specified link is invalid, check to see if it was copied correctly.
</span>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
`;
@@ -0,0 +1,163 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders form 1`] = `
<div
data-testid="main-layout"
>
<div
className="MainLayout-bar"
/>
<div
className="MainLayout-centered"
>
<main
className="ResetRoute-container"
>
<div
className="ResetRoute-root"
>
<div>
<form
autoComplete="off"
onSubmit={[Function]}
>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-double"
>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-full"
>
<h1
className="Box-root Typography-root Typography-heading1 Typography-colorTextPrimary"
>
Reset your password
</h1>
<p
className="Box-root Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Please enter a new password to use to sign in to your account.
Make sure it is unique and be sure to keep it secure.
</p>
</div>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-full"
>
<div
className="Box-root HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<label
className="Box-root Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
htmlFor="password"
>
Password
</label>
<p
className="Box-root Typography-root Typography-fieldDescription Typography-colorTextSecondary"
>
Must be at least 8 characters
</p>
<div
className="PasswordField-fullWidth PasswordField-root"
>
<div
className="PasswordField-wrapper"
>
<input
autoCapitalize="off"
autoComplete="new-password"
autoCorrect="off"
className="PasswordField-colorRegular PasswordField-fullWidth PasswordField-input"
disabled={false}
id="password"
name="password"
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
placeholder="Password"
spellCheck={false}
type="password"
value=""
/>
<div
className="PasswordField-icon"
onClick={[Function]}
onKeyUp={[Function]}
role="button"
tabIndex={0}
title="Hide password"
>
<i
aria-hidden="true"
className="Icon-root Icon-sm"
>
visibility
</i>
</div>
</div>
</div>
</div>
<button
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled Button-fullWidth"
data-color="primary"
data-variant="filled"
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Reset Password
</button>
</div>
</div>
</form>
</div>
</div>
</main>
</div>
</div>
`;
exports[`renders missing reset token 1`] = `
<div
data-testid="main-layout"
>
<div
className="MainLayout-bar"
/>
<div
className="MainLayout-centered"
>
<main
className="ResetRoute-container"
>
<div
className="ResetRoute-root"
>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-double"
>
<h1
className="Box-root Typography-root Typography-heading1 Typography-colorTextPrimary"
>
Oops Sorry!
</h1>
<div
className="CallOut-root CallOut-colorError CallOut-fullWidth"
>
<div>
<span
data-testid="invalid-link"
>
The specified link is invalid, check to see if it was copied correctly.
</span>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
`;
@@ -0,0 +1,96 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders form 1`] = `
<div
data-testid="main-layout"
>
<div
className="MainLayout-bar"
/>
<div
className="MainLayout-centered"
>
<main
className="UnsubscribeRoute-container"
>
<div
className="UnsubscribeRoute-root"
>
<div
data-testid="unsubscribe-form"
>
<form
onSubmit={[Function]}
>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-full"
>
<h1
className="Box-root Typography-root Typography-heading1 Typography-colorTextPrimary"
>
Click below to confirm that you want to unsubscribe from all notifications.
</h1>
<button
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled Button-fullWidth"
data-color="primary"
data-variant="filled"
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Confirm
</button>
</div>
</form>
</div>
</div>
</main>
</div>
</div>
`;
exports[`renders missing confirm token 1`] = `
<div
data-testid="main-layout"
>
<div
className="MainLayout-bar"
/>
<div
className="MainLayout-centered"
>
<main
className="UnsubscribeRoute-container"
>
<div
className="UnsubscribeRoute-root"
>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-double"
>
<h1
className="Box-root Typography-root Typography-heading1 Typography-colorTextPrimary"
>
Oops Sorry!
</h1>
<div
className="CallOut-root CallOut-colorError CallOut-fullWidth"
>
<div>
<span
data-testid="invalid-link"
>
The specified link is invalid, check to see if it was copied correctly.
</span>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
`;

Some files were not shown because too many files have changed in this diff Show More