Compare commits

...
288 Commits
Author SHA1 Message Date
Wyatt Johnson 94e1c75c0b chore: bump version 5.5.1 (#2832) 2020-02-10 17:11:14 +00:00
Hector NievaandWyatt Johnson 3014017976 Fix regexp mathing of banned word list by exchanging .test() with .match() (#2828)
* [Fix] Use .match instead of .test to avoid false positives when matching regex returned groups

* Add test to assure memoized regexp for banned words will match properly

* fix: fixes bug with wordlist matching

Co-authored-by: Wyatt Johnson <accounts+github@wyattjoh.ca>
2020-02-08 00:28:08 +00:00
Sami Safatli 1388e0bc5e Update translation pt-BR (#2826) 2020-02-06 13:38:55 -05:00
Wyatt Johnson 86191e388a feat: added support for customUserAgent (#2825) 2020-02-05 16:35:57 +00:00
Wyatt JohnsonandKim Gardner 2bf3e34218 chore: bump 5.5.0 (#2822)
Co-authored-by: Kim Gardner <kgardnr@gmail.com>
2020-02-04 15:10:10 -05:00
Vinh 75818cf808 Use absolute graphql url to fix ie11 bug (#2823)
* fix: use absolute graphql to fix ie11 bug

* fix: improved url composition
2020-02-04 19:16:36 +00:00
Tessa Thornton 503eb0b06a fix styling of icons and text in stream announcement (#2821) 2020-02-04 12:53:27 -05:00
Wyatt Johnson 9cc3a57295 fix: fixed subheader text color (#2820) 2020-02-04 17:04:43 +00:00
Wyatt Johnson e42cb59661 feat: added config variable for perspective timeout (#2817)
Added a configuration timeout variable for changing the perspective
timeout number.
2020-02-04 00:21:14 +00:00
Tessa ThorntonandKim Gardner a1a8652f7e [CORL 547] org-wide announcements (#2813)
* CRUD announcements

* only show announcement if not disabled

* make announcements dismissable

* add announcement mutations

* update announcement form logic

* style announcements on stream

* update snap

* localize strings

* close form if announcement is removed

* move announcement config below sitewide commenting config

* move date calculation inside useMemo

* move announcementconfig code to announcementconfigcontainer

* use coralContext for localStorage

* fix type of announcement createdAt

* move announcement form to modal

* remove payload pruning from configure route

* simplify announcement display logic

* make validation message full width

Co-authored-by: Kim Gardner <kgardnr@gmail.com>
2020-02-03 13:12:25 -05:00
Wyatt JohnsonandKim Gardner a7b2af85fc fix: fixes bug related to migration error (#2816)
When a user did not have any comments when the migration 1575649180000
hits, they are not migrated to get a `commentCounts` property. This
migration finds those users without `commentCounts` and sets them to the
empty comment counts.

Co-authored-by: Kim Gardner <kgardnr@gmail.com>
2020-01-30 18:53:34 -05:00
VinhandKim Gardner 516611e289 fix: set access token only once (#2815)
Co-authored-by: Kim Gardner <kgardnr@gmail.com>
2020-01-30 18:37:26 -05:00
Tessa Thornton 190010c898 change marker variant for suspect word badge (#2814) 2020-01-30 18:23:42 -05:00
Vinh 2b1c9aea78 [CORL-832] IE11 Compatibility for the Embed Stream (#2801)
* IE11 support

* fix: types and tests

* chore+fix: optimise polyfills and bugfixes for ie11

* chore+fix: fix docz, radically cleanup babel configs

* fix: ie11 proxy bug

* feat: normalize rte html

* fix: css issues in ie11

* chore: improve bundle size

* chore: upgrade coral rte

* feat: make counts work in ie11

* fix: remove native password reveal in ie11

* fix: markdowneditor icons in ie11

* fix: ie11 brittleness in auth popup

* fix: callout width in ie11

* test: fix snapshots
2020-01-29 17:42:32 -05:00
Felix DandKim Gardner 9c4a9753bb New language swedish (#2798)
* Adding Swedish translation

* updated snap file for Swedish translation

* Translated sv/auth.ftl and partially translated install.ftl and errors.ftl to swedish

* Translated server/error completly and removed the swedish translation file install

* Fixed merge conflict with Finnish language that was added before this PR was completed

* Fixed wrong semi colon and white space

Co-authored-by: Kim Gardner <kgardnr@gmail.com>
2020-01-28 15:51:22 -05:00
Dan Zajdband e43a3ddf75 Spanish translation fixes (#2812) 2020-01-28 10:58:20 -05:00
jpkilpi 6364eced63 Finnish translation (#2811)
* Initial Finnish translation

* Added Finnish translation to test configuration
2020-01-23 12:45:23 -05:00
DzarrandKim Gardner 62ce3a2c49 Translations of server locales in french (#2807)
Co-authored-by: Kim Gardner <kgardnr@gmail.com>
2020-01-21 17:22:25 -05:00
Nick Funk 48d815aebb Refactor v2 css variable names into post css variables (#2791)
We don't want to clog up the variables list with every
design token from AirTable. Instead, more efficient to
use Post CSS var's that compile down into the CSS bundle
more efficiently.

CORL-748
2020-01-20 21:09:10 +00:00
Nick Funk 38ccbd0ef8 Show new message when no comments and story closed (#2792)
Show "There are no comments on this story." instead
of "There are no comments yet. Why don't you write one?"

CORL-787
2020-01-20 20:56:54 +00:00
Nick Funk cfebe7733e Fix radio button positioning on Firefox (#2805)
Remove the top: 6px since it's unnecessary
to vertically align the dot in the radio
button.

CORL-873
2020-01-20 18:27:44 +00:00
Nick FunkandWyatt Johnson e7b83c6b39 [CORL-875] Semi-bold selected text on radio buttons (#2804)
* Semi-bold selected text on radio buttons

Makes it very obvious which item is selected.

CORL-875

* fix: updated snapshots

Co-authored-by: Wyatt Johnson <accounts+github@wyattjoh.ca>
2020-01-20 18:13:11 +00:00
Nick Funk 5c46884a97 Change load amounts for comments (#2800)
Load 20 comments initially and on load more.
Load 3 replies initially and on load more.

CORL-864
2020-01-20 17:54:34 +00:00
Nick Funk 4bd929f3f0 [CORL-748] Update admin login to match new branding styles (#2788)
* Update admin login to match new branding styles

CORL-748

* Update admin login to match new branding styles

CORL-748
2020-01-20 17:43:40 +00:00
Nick FunkandWyatt Johnson 785735d36a [CORL-874] Align the sidebar and config content in Admin>Configure (#2803)
* Align the sidebar and config content in Admin>Configure

CORL-874

* fix: updated snapshots

Co-authored-by: Wyatt Johnson <accounts+github@wyattjoh.ca>
2020-01-20 17:27:37 +00:00
Wyatt Johnson 20da16331e fix: renamed migration file to ms format (#2802) 2020-01-17 18:59:50 +00:00
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
2249 changed files with 93136 additions and 36385 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ job_defaults: &job_defaults
working_directory: ~/coralproject/talk
resource_class: large
docker:
- image: circleci/node:10
- image: circleci/node:12
environment:
<<: *job_environment
+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"
]
}
+44 -6
View File
@@ -1,5 +1,4 @@
{
"editor.formatOnSave": true,
"files.associations": {
"*.css": "postcss"
},
@@ -11,10 +10,49 @@
"**/.DS_Store": true,
".vs": true
},
"tslint.exclude": "**/node_modules/**",
"tslint.autoFixOnSave": true,
"tslint.jsEnable": true,
"tslint.nodePath": "node_modules/.bin/tslint",
"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__"],
"search.exclude": {
"package-lock.json": true
},
"debug.node.autoAttach": "off"
}
+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"]
+13 -366
View File
@@ -1,4 +1,4 @@
# Coral ![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, Coral, rethinks
how moderation, comment display, and conversation function, creating the
@@ -9,385 +9,32 @@ Built with <3 by The Coral Project, a part of [Vox Media](https://product.voxmed
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)
- [Embed On Your Site](#embed-on-your-site)
- [Single Sign On](#single-sign-on)
- [Email](#email)
- [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 Coral 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 Coral 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/coral
- 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 Coral using Docker.
docker-compose up -d
```
Then head on over to http://localhost:3000 to install Coral!
### Source
Coral requires NodeJS >=10, we recommend using `nvm` to help manage node
versions: https://github.com/creationix/nvm.
```bash
# Clone and cd into the Coral 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 Coral 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 Coral with:
```bash
# Start the server in production mode.
npm run start
```
Then head on over to http://localhost:3000 to install Coral!
### Development
Running Coral for development is very similar to installing Coral via Source as
described above.
Coral requires NodeJS >=10, we recommend using `nvm` to help manage node
versions: https://github.com/creationix/nvm.
```bash
# Clone and cd into the Coral directory.
git clone https://github.com/coralproject/talk.git
cd talk
# Install dependencies.
npm install
```
Running Coral 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 Coral 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
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
```
#### Embed On Your Site
With Coral setup and running locally you can test embeding the comment stream with this sample embed script:
```
<div id="coral_thread"></div>
<script type="text/javascript">
(function() {
var talk = document.createElement('script'); talk.type = 'text/javascript'; talk.async = true;
var url = '{{ CORAL_DOMAIN_NAME }}';
talk.src = '//' + url + '/assets/js/embed.js';
talk.onload = function() {
Coral.createStreamEmbed({
id: "coral_thread",
autoRender: true,
rootURL: '//' + url,
});
};
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(talk);
})();
</script>
```
> **NOTE:** Replace the value of `{{ CORAL_DOMAIN_NAME }}` with the location of your running instance of Coral.
#### Single Sign On
In order to allow seamless connection to an existing authentication system,
Coral utilizes the industry standard [JWT Token](https://jwt.io/) to connect. To
learn more about how to create a JWT token, see [this introduction](https://jwt.io/introduction/).
1. Visit: `https://{{ CORAL_DOMAIN_NAME }}/admin/configure/auth`
2. Scroll to the `Login with Single Sign On` section
3. Enable the Single Sign On Authentication Integration
4. Enable `Allow Registration`
5. Copy the string in the `Key` box
6. Click Save
> **NOTE:** Replace the value of `{{ CORAL_DOMAIN_NAME }}` with the location of your running instance of Coral.
You will then have to generate a JWT with the following claims:
- `jti` (_optional_) - A unique ID for this particular JWT token. We recommend
using a [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier)
for this value. Without this parameter, the logout functionality inside the
embed stream will not work and you will need to call logout on the embed
itself.
- `exp` (_optional_) - When the given SSO token should expire. This is
specified as a unix time stamp in seconds. Once the token has expired, a new
token should be generated and passed into Coral. Without this parameter, the
logout functionality inside the embed stream will not work and you will need
to call logout on the embed itself.
- `iat` (_optional_) - When the given SSO token was issued. This is required to
utilize the automatic user detail update system. If this time is newer than
the time we received the last update, the contents of the token will be used
to update the user.
- `user.id` (**required**) - the ID of the user from your authentication system.
This is required to connect the user in your system to allow a seamless
connection to Coral.
- `user.email` (**required**) - the email address of the user from your
authentication system. This is required to facilitate notification email's
about status changes on a user account such as bans or suspensions.
- `user.username` (**required**) - the username that should be used when being
presented inside Coral to moderators and other users.
An example of the claims for this token would be:
```json
{
"jti": "151c19fc-ad15-4f80-a49c-09f137789fbb",
"exp": 1572172094,
"iat": 1562172094,
"user": {
"id": "628bdc61-6616-4add-bfec-dd79156715d4",
"email": "bob@example.com",
"username": "bob"
}
}
```
With the claims provided, you can sign them with the `Key` obtained from the
Coral administration panel in the previous steps with a `HS256` algorithm. This
token can be provided in the above mentioned embed code by adding it to the
`createStreamEmbed` function:
```js
Coral.createStreamEmbed({
// Don't forget to include the parameters from the
// "Embed On Your Site" section.
accessToken: "{{ SSO_TOKEN }}",
});
```
Or by calling the `login/logout` method on the embed object:
```js
var embed = Coral.createStreamEmbed({
// Don't forget to include the parameters from the
// "Embed On Your Site" section.
});
// Login the current embed with the generated SSO token.
embed.login("{{ SSO_TOKEN }}");
// Logout the user.
embed.logout();
```
#### Email
To test out the email sending functionality, you can run [inbucket](https://www.inbucket.org/)
which provides a test SMTP server that can visualize emails in the browser:
```bash
docker run -d --name inbucket --restart always -p 2500:2500 -p 9000:9000 inbucket/inbucket
```
You can then configure the email server on Coral by updating the Tenant with:
```js
{
// ...
"email": {
"enabled": true,
"smtpURI": "smtp://localhost:2500",
"fromAddress": "community@test.com"
},
// ...
}
```
Restarting Coral will be needed. Navigate to http://localhost:9000, click the
"Monitor" tab. New emails received on this screen.
#### 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 Coral 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/coral`)
- `REDIS_URI` - The Redis database URI to connect to.
(Default `redis://127.0.0.1:6379`)
- `REDIS_OPTIONS` - A JSON string with optional configuration options to be used
when connecting to Redis as specified in the [ioredis](https://github.com/luin/ioredis/blob/1dac50a63753c2afc969315cfe38faf0edc50bc5/API.md#new_Redis_new) documentation.
(Default: `{}`)
- `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 Coral 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`, Coral will not perform indexing
operations when it starts up. This can be desired when you've already
installed Coral 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`)
- `METRICS_USERNAME` - The username for _Basic Authentication_ at the `/metrics` and `/cluster_metrics`
endpoint.
- `METRICS_PASSWORD` - The password for _Basic Authentication_ at the `/metrics` and `/cluster_metrics`
endpoint.
- `CLUSTER_METRICS_PORT` - If `CONCURRENCY` is more than `1`, the metrics are provided at this port under `/cluster_metrics`. (Default `3001`)
- `DISABLE_LIVE_UPDATES` - When `true`, disables subscriptions for the comment
stream for all stories across all tenants (Default `false`)
- `WEBSOCKET_KEEP_ALIVE_TIMEOUT` - A duration in a parsable format (e.g. `30 seconds`
, `1 minute`) that should be used to send keep alive messages through the
websocket to keep the socket alive (Default `30 seconds`)
- [Our Blog](https://coralproject.net/blog)
- [Community Guides for Journalism](https://guides.coralproject.net/)
- [More About Us](https://coralproject.net/)
## License
+4
View File
@@ -21,6 +21,10 @@
"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": [
+10 -18
View File
@@ -1,28 +1,20 @@
/**
* This is a project wide babel configuration.
* https://babeljs.io/docs/en/config-files#project-wide-configuration
*
* We use this file to apply babel configuration to packages in `node_modules`
*/
const lodashOptimizations =
// Note: If Webpack env is set, we are building for the client.
const plugins =
process.env.WEBPACK === "true" ? ["use-lodash-es", "lodash"] : [];
const environment =
process.env.WEBPACK === "true"
? { targets: "IE 11", modules: false }
: { targets: { node: "current" }, modules: "commonjs" };
module.exports = {
babelrcRoots: ["./src/core/client/*"],
env: {
production: {
plugins: [...lodashOptimizations],
},
development: {
plugins: [...lodashOptimizations],
},
test: {
presets: [
["@babel/env", { targets: { node: "current" } }],
"@babel/react",
],
plugins: ["dynamic-import-node"],
},
},
plugins,
presets: [["@babel/env", environment]],
};
+3 -1
View File
@@ -7,7 +7,7 @@ module.exports = {
collectCoverageFrom: ["**/*.{js,jsx,mjs,ts,tsx}"],
coveragePathIgnorePatterns: ["/node_modules/"],
setupFiles: [
"<rootDir>/src/core/build/polyfills.js",
"<rootDir>/src/core/client/test/polyfills.ts",
"<rootDir>/src/core/client/test/setup.ts",
],
setupFilesAfterEnv: ["<rootDir>/src/core/client/test/setupTestFramework.ts"],
@@ -29,10 +29,12 @@ module.exports = {
"^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"],
+1
View File
@@ -6,6 +6,7 @@ module.exports = {
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",
+5 -5
View File
@@ -27,7 +27,7 @@ const config: Config = {
"**/test/**/*",
"core/**/*.spec.*",
],
executor: new CommandExecutor("npm run --silent generate:relay-stream", {
executor: new CommandExecutor("npm run --silent generate:relay:stream", {
runOnInit: true,
}),
},
@@ -44,7 +44,7 @@ const config: Config = {
"**/test/**/*",
"core/**/*.spec.*",
],
executor: new CommandExecutor("npm run generate:relay-account", {
executor: new CommandExecutor("npm run generate:relay:account", {
runOnInit: true,
}),
},
@@ -61,7 +61,7 @@ const config: Config = {
"**/test/**/*",
"core/**/*.spec.*",
],
executor: new CommandExecutor("npm run --silent generate:relay-admin", {
executor: new CommandExecutor("npm run --silent generate:relay:admin", {
runOnInit: true,
}),
},
@@ -78,7 +78,7 @@ const config: Config = {
"**/test/**/*",
"core/**/*.spec.*",
],
executor: new CommandExecutor("npm run --silent generate:relay-auth", {
executor: new CommandExecutor("npm run --silent generate:relay:auth", {
runOnInit: true,
}),
},
@@ -95,7 +95,7 @@ const config: Config = {
"**/test/**/*",
"core/**/*.spec.*",
],
executor: new CommandExecutor("npm run --silent generate:relay-install", {
executor: new CommandExecutor("npm run --silent generate:relay:install", {
runOnInit: true,
}),
},
+1 -1
View File
@@ -71,7 +71,7 @@ export default function({
{ 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$/, to: "/auth.html" },
{ from: /^\/embed\/auth\/callback/, to: "/auth-callback.html" },
{ from: /^\/install/, to: "/install.html" },
],
+2 -4
View File
@@ -1,13 +1,11 @@
// 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 isProduction = process.NODE_ENV === "production";
const appDirectory = fs.realpathSync(process.cwd());
const styleLoader = {
@@ -18,7 +16,7 @@ const styleLoader = {
},
};
export default {
module.exports = {
title: "Coral 5.0",
source: "./src",
typescript: true,
@@ -60,7 +58,7 @@ export default {
"@babel/typescript",
[
"@babel/env",
{ targets: { node: "10.0.0" }, modules: "commonjs" },
{ targets: { node: "current" }, modules: "commonjs" },
],
],
// This is a feature of `babel-loader` for webpack (not Babel itself).
+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"));
+5584 -2842
View File
File diff suppressed because it is too large Load Diff
+130 -85
View File
@@ -1,6 +1,6 @@
{
"name": "@coralproject/talk",
"version": "5.0.0-beta.4",
"version": "5.5.1",
"author": "The Coral Project",
"homepage": "https://coralproject.net/",
"sideEffects": [
@@ -11,8 +11,8 @@
"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": [
@@ -20,30 +20,35 @@
],
"description": "A better commenting experience from Mozilla, The Washington Post, and The New York Times.",
"scripts": {
"build": "NODE_ENV=production WEBPACK_DISABLE_SOURCEMAPS=1 npm-run-all generate --parallel lint:client build:client build:server",
"build:development": "NODE_ENV=development npm-run-all generate --parallel lint:client build:client build:server",
"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 --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: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:-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",
@@ -53,8 +58,13 @@
"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.8.1",
@@ -66,84 +76,84 @@
"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",
"fluent-dom": "^0.4.1",
"found": "^0.4.0-alpha.17",
"found-relay": "^0.4.0-alpha.8",
"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": "^4.9.0",
"joi": "^13.4.0",
"jsdom": "^15.0.0",
"jsonwebtoken": "^8.3.0",
"jsonwebtoken": "^8.5.1",
"juice": "^5.2.0",
"jwks-rsa": "^1.3.0",
"linkify-it": "^2.1.0",
"jwks-rsa": "^1.6.0",
"keymaster": "^1.6.2",
"linkifyjs": "^2.1.8",
"lodash": "^4.17.10",
"lodash": "^4.17.15",
"lru-cache": "^5.1.1",
"luxon": "^1.12.0",
"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",
"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",
"on-finished": "^2.3.0",
"passport": "^0.4.0",
"passport-facebook": "^2.1.1",
"passport-google-oauth2": "^0.1.6",
"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.0",
"querystringify": "^2.1.0",
"react-relay-network-modern": "^3.0.4",
"proxy-agent": "^3.1.1",
"querystringify": "^2.1.1",
"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",
"tsscmp": "^1.0.6",
"uuid": "^3.3.2",
"url-regex": "^5.0.0",
"uuid": "^3.3.3",
"verror": "^1.10.0"
},
"devDependencies": {
"@babel/core": "^7.4.5",
"@babel/plugin-syntax-dynamic-import": "^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",
"@babel/preset-typescript": "^7.3.3",
"@babel/core": "^7.8.3",
"@babel/preset-env": "^7.8.3",
"@babel/preset-react": "^7.8.3",
"@babel/preset-typescript": "^7.8.3",
"@babel/runtime-corejs3": "^7.8.3",
"@coralproject/npm-run-all": "^4.1.5",
"@coralproject/rte": "^0.10.15",
"@coralproject/rte": "^0.11.1",
"@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.5.12",
@@ -152,66 +162,76 @@
"@types/cheerio": "^0.22.8",
"@types/classnames": "^2.2.7",
"@types/commander": "^2.12.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": "^4.0.10",
"@types/jest": "^24.0.13",
"@types/jest": "^24.0.23",
"@types/jest-axe": "^3.2.1",
"@types/joi": "^13.0.8",
"@types/jsdom": "^12.2.3",
"@types/jsonwebtoken": "^7.2.7",
"@types/linkify-it": "^2.1.0",
"@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.3.3",
"@types/node-fetch": "^2.5.3",
"@types/nodemailer": "^4.6.2",
"@types/nunjucks": "^3.1.1",
"@types/object-diff": "0.0.0",
"@types/on-finished": "^2.3.1",
"@types/passport": "^0.4.6",
"@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.15",
"@types/react-axe": "^3.1.0",
"@types/react-copy-to-clipboard": "^4.2.6",
"@types/react-dom": "^16.8.4",
"@types/react-relay": "^1.3.14",
"@types/react-helmet": "^5.0.10",
"@types/react-relay": "^5.0.1",
"@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.12",
"@types/relay-runtime": "^5.0.5",
"@types/sane": "^2.0.0",
"@types/shallow-equals": "^1.0.0",
"@types/simplemde": "^1.11.7",
"@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.4",
"@types/verror": "^1.10.3",
"@types/vinyl": "^2.0.2",
@@ -220,18 +240,19 @@
"@types/webpack-bundle-analyzer": "^2.13.1",
"@types/webpack-dev-server": "^3.1.5",
"@types/ws": "^5.1.2",
"@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": "^24.8.0",
"babel-loader": "^8.0.5",
"babel-plugin-dynamic-import-node": "^2.2.0",
"babel-jest": "^24.9.0",
"babel-loader": "^8.0.6",
"babel-plugin-lodash": "^3.3.4",
"babel-plugin-module-resolver": "^3.2.0",
"babel-plugin-relay": "^4.0.0",
"babel-plugin-relay": "^5.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.2.0",
"chalk": "^2.4.2",
@@ -239,10 +260,13 @@
"classnames": "^2.2.6",
"commander": "^2.20.0",
"comment-json": "^1.1.3",
"common-tags": "^1.8.0",
"compression-webpack-plugin": "^2.0.0",
"copy-webpack-plugin": "^5.0.3",
"core-js": "^3.6.4",
"cross-spawn": "^6.0.5",
"css-loader": "^1.0.1",
"css-vars-ponyfill": "^2.1.2",
"del": "^4.1.1",
"doctoc": "^1.4.0",
"docz": "^0.13.7",
@@ -250,13 +274,21 @@
"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.11.0",
"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.1",
"fluent-react": "^0.8.4",
"fork-ts-checker-webpack-plugin": "^1.3.0",
"fork-ts-checker-webpack-plugin": "^1.5.0",
"found": "^0.4.9",
"found-relay": "^0.4.3",
"graphql-schema-linter": "^0.2.0",
"graphql-schema-typescript": "^1.2.9",
"gulp": "^4.0.2",
@@ -264,20 +296,23 @@
"gulp-cli": "^2.2.0",
"gulp-sourcemaps": "^2.6.5",
"gulp-typescript": "^5.0.1",
"html-webpack-plugin": "^4.0.0-beta.5",
"html-webpack-plugin": "^4.0.0-beta.11",
"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.11",
"marked": "^0.6.0",
"lodash-es": "^4.17.14",
"marked": "^0.7.0",
"material-design-icons": "^3.0.1",
"mini-css-extract-plugin": "^0.6.0",
"object-diff": "0.0.4",
"postcss-advanced-variables": "^3.0.0",
"postcss-calc-function": "^1.1.0",
"postcss-css-variables": "^0.11.0",
"postcss-flexbugs-fixes": "^4.1.0",
"postcss-font-magician": "^2.2.1",
@@ -288,35 +323,43 @@
"postcss-nested": "^4.1.1",
"postcss-prepend-imports": "^1.0.1",
"postcss-preset-env": "^6.5.0",
"prettier": "^1.17.0",
"prettier": "^1.18.2",
"prop-types": "^15.6.2",
"proxy-polyfill": "^0.3.0",
"pstree.remy": "^1.1.6",
"pym.js": "^1.3.2",
"raw-loader": "^0.5.1",
"react": "^16.9.0-alpha.0",
"react": "^16.9.0",
"react-axe": "^3.3.0",
"react-copy-to-clipboard": "^5.0.1",
"react-dev-utils": "^9.0.0",
"react-dom": "^16.9.0-alpha.0",
"react-dom": "^16.9.0",
"react-error-overlay": "^5.1.6",
"react-final-form": "4.0.2",
"react-final-form": "6.3.0",
"react-final-form-arrays": "3.1.0",
"react-popper": "^1.3.2",
"react-relay": "^4.0.0",
"react-relay": "^5.0.0",
"react-relay-network-modern": "^4.0.4",
"react-responsive": "^7.0.0",
"react-test-renderer": "^16.9.0-alpha.0",
"react-test-renderer": "^16.9.0",
"react-timeago": "^4.1.9",
"react-transition-group": "^2.9.0",
"react-with-state-props": "^2.0.4",
"recompose": "0.27.1",
"relay-compiler": "^4.0.0",
"regenerator-runtime": "^0.13.3",
"relay-compiler": "^5.0.0",
"relay-compiler-language-typescript": "^4.1.0",
"relay-local-schema": "^0.8.0",
"relay-runtime": "^4.0.0",
"relay-runtime": "^5.0.0",
"resize-observer-polyfill": "^1.5.1",
"sane": "^4.1.0",
"scheduler": "^0.15.0",
"shallow-equals": "^1.0.0",
"simplemde": "^1.11.2",
"simulant": "^0.2.2",
"sinon": "^7.3.2",
"sockjs-client": "^1.3.0",
"stack-trace": "0.0.10",
"strip-ansi": "^5.2.0",
"style-loader": "^0.23.1",
"subscriptions-transport-ws": "^0.9.16",
@@ -329,17 +372,16 @@
"ts-node-dev": "^1.0.0-pre.37",
"tsconfig-paths": "^3.8.0",
"tsconfig-paths-webpack-plugin": "^3.2.0",
"tslint": "^5.16.0",
"tslint-config-prettier": "^1.18.0",
"tslint-plugin-prettier": "^2.0.1",
"tslint-react": "^4.0.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.6.0",
"wait-for-expect": "^1.1.1",
"webpack": "^4.30.0",
"webpack": "^4.41.5",
"webpack-assets-manifest": "^3.1.1",
"webpack-bundle-analyzer": "^3.3.2",
"webpack-cli": "^3.3.2",
@@ -348,15 +390,18 @@
},
"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": [
+2 -3
View File
@@ -12,7 +12,7 @@
*/
"use strict";
// tslint:disable:no-console
/* eslint-disable no-console */
const address = require("address");
const url = require("url");
@@ -21,7 +21,6 @@ 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");
const Stats = require("webpack/lib/Stats");
// (cvle): Changed to false as we are sharing the tty with other processes.
// const isInteractive = process.stdout.isTTY && false;
@@ -142,7 +141,7 @@ function createCompiler({
});
let isFirstCompile = true;
let tsMessagesPromises = [];
const tsMessagesPromises = [];
if (useTypeScript) {
compiler.compilers.forEach(singleCompiler => {
+1 -1
View File
@@ -17,7 +17,7 @@ import paths from "../config/paths";
import config from "../src/core/build/config";
import createWebpackConfig from "../src/core/build/createWebpackConfig";
// tslint:disable: no-console
/* eslint-disable no-console */
process.env.WEBPACK = "true";
process.env.NODE_ENV = process.env.NODE_ENV || "production";
+34 -8
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`
);
@@ -51,14 +52,39 @@ const args = [
"--language",
"typescript",
"--no-watchman",
"--customScalars.Time=String",
"--customScalars.Cursor=unknown",
"--customScalars.Locale=string",
"--src",
program.src,
"--artifactDirectory",
`${program.src}/__generated__`,
"--schema",
config.projects[program.schema].schemaPath,
// "--persist-output",
// `${program.src}/persisted-queries.json`,
];
// 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,
});
}
}
+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 "coral-server/graph/tenant/context";',
'import { Cursor } from "coral-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}`);
+1 -1
View File
@@ -14,7 +14,7 @@ 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");
+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) {
+4 -4
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;
}
+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) {
+2 -2
View File
@@ -14,7 +14,7 @@ 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;
@@ -31,7 +31,7 @@ const cmd = program
.parse(process.argv);
run(cmd.args, cmd.opts()).catch(err => {
// tslint:disable-next-line:no-console
// eslint-disable-next-line no-console
console.error(err);
process.exit(1);
});
+4 -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,11 +98,11 @@ 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).catch(err => {
// tslint:disable-next-line:no-console
// eslint-disable-next-line no-console
console.error(err);
process.exit(1);
});
+5 -4
View File
@@ -7,7 +7,7 @@
"use strict";
// tslint:disable:no-console
/* eslint-disable */
// This alternative WebpackDevServer combines the functionality of:
// https://github.com/webpack/webpack-dev-server/blob/webpack-1/client/index.js
@@ -24,7 +24,8 @@ 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");
var lodash = require("lodash");
var debounce = lodash.debounce;
ErrorOverlay.setEditorHandler(function editorHandler(errorLocation) {
// Keep this sync with errorOverlayMiddleware.js
@@ -124,7 +125,7 @@ function handleWarnings(warnings) {
// 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));
warnings = warnings.filter(function(w){ return !/export .* was not found in/.test(w)});
function printWarnings() {
// Print warnings to the console.
@@ -198,7 +199,7 @@ function handleAvailableHash(hash) {
mostRecentCompilationHash = hash;
}
const debouncedReload = debounce(() => {
const debouncedReload = debounce(function() {
window.location.reload();
}, 1000);
+8
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,
+79 -34
View File
@@ -1,4 +1,5 @@
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";
@@ -17,8 +18,7 @@ import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";
// 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";
import { createClientEnv } from "./config";
import { Config, createClientEnv } from "./config";
import paths from "./paths";
/**
@@ -30,6 +30,12 @@ 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;
@@ -41,6 +47,8 @@ export default function createWebpackConfig(
config: Config,
{ 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");
@@ -83,23 +91,23 @@ export default function createWebpackConfig(
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", "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 = [
@@ -144,6 +152,16 @@ export default function createWebpackConfig(
),
];
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
@@ -192,15 +210,7 @@ export default function createWebpackConfig(
}),
],
},
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",
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.
@@ -212,10 +222,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,
@@ -346,7 +356,7 @@ export default function createWebpackConfig(
options: {
limit: 10000,
name: isProduction
? "assets/media/[name].[hash:8].[ext]"
? "assets/media/[name].[hash].[ext]"
: "assets/media/[name].[ext]",
},
},
@@ -359,7 +369,7 @@ export default function createWebpackConfig(
options: {
modules: true,
importLoaders: 2,
localIdentName: "[name]-[local]-[hash:base64:5]",
localIdentName: "[name]-[local]-[contenthash]",
sourceMap: !disableSourcemaps,
},
},
@@ -381,7 +391,7 @@ export default function createWebpackConfig(
"@babel/typescript",
[
"@babel/env",
{ targets: { node: "10.0.0" }, modules: "commonjs" },
{ targets: { node: "current" }, modules: "commonjs" },
],
],
// This is a feature of `babel-loader` for webpack (not Babel itself).
@@ -439,19 +449,11 @@ export default function createWebpackConfig(
{
test: /\.js$/,
include: /node_modules\//,
exclude: /node_modules\/(@babel|babel|core-js|regenerator-runtime)/,
use: [
{
loader: require.resolve("babel-loader"),
options: {
// This will ensure that all packages in node_modules that
// import lodash do so in a way that supports tree shaking.
plugins: ["lodash"],
presets: [
[
"@babel/env",
{ targets: "last 2 versions", modules: false },
],
],
cacheDirectory: true,
},
},
@@ -499,6 +501,11 @@ export default function createWebpackConfig(
exclude: [/\.(js|ts|tsx)$/, /\.html$/, /\.json$/],
loader: require.resolve("file-loader"),
options: {
// 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]",
@@ -514,9 +521,9 @@ export default function createWebpackConfig(
// 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 tslint in a separate process to have a quicker build.
// We run eslint in a separate process to have a quicker build.
new ForkTsCheckerWebpackPlugin({
tslint: true,
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.
@@ -696,8 +703,7 @@ export default function createWebpackConfig(
sideEffects: true,
},
entry: [
/* Use minimal amount of polyfills (for IE) */
"intersection-observer", // also for Safari
// Polyfills are in the index.ts file.
paths.appEmbedIndex,
],
output: {
@@ -736,5 +742,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;
+4 -1
View File
@@ -16,7 +16,7 @@ export default {
appLoaders: resolveSrc("core/build/loaders"),
appSrc: resolveSrc("."),
appTsconfig: resolveSrc("core/client/tsconfig.json"),
appPolyfill: resolveSrc("core/build/polyfills.js"),
appPolyfill: resolveSrc("core/build/polyfills.ts"),
appPublicPath: resolveSrc("core/build/publicPath.js"),
appLocales: resolveSrc("locales"),
appThemeVariables: resolveSrc("core/client/ui/theme/variables.ts"),
@@ -34,6 +34,9 @@ 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"),
-2
View File
@@ -1,2 +0,0 @@
require("@babel/polyfill");
require("intersection-observer");
+6
View File
@@ -0,0 +1,6 @@
import polyfillNodeListForEach from "../client/framework/helpers/polyfillNodeListForEach";
import "core-js/stable";
import "regenerator-runtime/runtime";
polyfillNodeListForEach();
+3 -6
View File
@@ -7,6 +7,7 @@ const pickBy = require("lodash/pickBy");
const flat = require("flat");
const paths = require("./paths").default;
const autoprefixer = require("autoprefixer");
const postcssCalcFunction = require("postcss-calc-function").default;
const postcssFontMagician = require("postcss-font-magician");
const postcssFlexbugsFixes = require("postcss-flexbugs-fixes");
const postcssPresetEnv = require("postcss-preset-env");
@@ -23,12 +24,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-")),
@@ -58,6 +53,8 @@ module.exports = {
postcssNested(),
// Sass style variables to be used in media queries.
postcssAdvancedVariables({ variables: mediaQueryVariables }),
// Reduce some calc()
postcssCalcFunction(),
// 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 || "/";
-17
View File
@@ -1,20 +1,3 @@
const lodashOptimizations = ["use-lodash-es", "lodash"];
module.exports = {
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"],
},
},
};
+3
View File
@@ -2,4 +2,7 @@
body {
margin: 0;
}
input::-ms-clear, input::-ms-reveal {
display: none;
}
}
+12 -11
View File
@@ -1,10 +1,10 @@
import TransitionControl from "coral-framework/testHelpers/TransitionControl";
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";
@@ -16,16 +16,17 @@ const Router = createFarceRouter({
historyProtocol: new BrowserProtocol(),
historyMiddlewares: [queryMiddleware],
routeConfig,
renderReady: ({ elements }) => (
<>
<ElementsRenderer elements={elements} />
{// this enables router transition control when writing tests.
process.env.NODE_ENV === "test" && <TransitionControl />}
</>
),
renderError: ({ error }) => (
<div>{error.status === 404 ? <NotFound /> : "Error"}</div>
),
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 = () => (
+1 -6
View File
@@ -4,12 +4,7 @@
}
.centered {
max-width: calc(70 * var(--mini-unit));
margin: 10% auto;
padding: 0 calc(0.5 * var(--mini-unit));
box-sizing: border-box;
@media (min-width: $breakpoints-xs) {
max-width: calc(42 * var(--mini-unit));
}
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
<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, user-scalable=no" />
<meta name="viewport" content="width=device-width" />
</head>
<body>
+7 -2
View File
@@ -1,7 +1,10 @@
import { createManaged } from "coral-framework/lib/bootstrap";
import React, { FunctionComponent } from "react";
import ReactDOM from "react-dom";
import injectConditionalPolyfills from "coral-framework/helpers/injectConditionalPolyfills";
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";
@@ -10,10 +13,12 @@ import localesData from "./locales";
import "coral-ui/theme/variables.css";
async function main() {
await injectConditionalPolyfills();
// Potentially inject react-axe for runtime a11y checks.
await potentiallyInjectAxe();
const ManagedCoralContextProvider = await createManaged({
initLocalState,
localesData,
userLocales: navigator.languages,
});
const Index: FunctionComponent = () => (
+1
View File
@@ -6,4 +6,5 @@
*/
import { LocalesData } from "coral-framework/lib/i18n";
export default {} as LocalesData;
+6
View File
@@ -1,7 +1,9 @@
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(
@@ -12,5 +14,9 @@ export default makeRouteConfig(
<Route path="email">
<Route path="confirm" {...ConfirmRoute.routeConfig} />
</Route>
<Route path="download" {...DownloadRoute.routeConfig} />
<Route path="notifications">
<Route path="unsubscribe" {...UnsubscribeRoute.routeConfig} />
</Route>
</Route>
);
+6 -3
View File
@@ -1,9 +1,12 @@
import { HorizontalGutter, Typography } from "coral-ui/components";
import React, { FunctionComponent } from "react";
import { HorizontalGutter, Typography } from "coral-ui/components";
const NotFound: FunctionComponent = () => (
<HorizontalGutter>
<Typography variant="heading3">Not Found</Typography>
<HorizontalGutter container="main">
<Typography variant="heading3" container="h1">
Not Found
</Typography>
</HorizontalGutter>
);
@@ -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";
@@ -1,5 +1,5 @@
import { Localized } from "@fluent/react/compat";
import { FORM_ERROR } from "final-form";
import { Localized } from "fluent-react/compat";
import React, { useCallback } from "react";
import { Form } from "react-final-form";
@@ -30,39 +30,37 @@ const ConfirmForm: React.FunctionComponent<Props> = ({ onSuccess, token }) => {
return;
}, [token]);
return (
<div>
<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>
<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>
</form>
)}
</Form>
</div>
<HorizontalGutter>
<Localized id="confirmEmail-confirmEmail">
<Button
type="submit"
variant="filled"
color="primary"
disabled={submitting}
fullWidth
>
Confirm email
</Button>
</Localized>
</HorizontalGutter>
</HorizontalGutter>
</form>
)}
</Form>
);
};
@@ -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;
}
@@ -11,6 +11,8 @@ 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 }) =>
@@ -32,24 +34,46 @@ const ConfirmRoute: React.FunctionComponent<Props> = ({ token }) => {
const [state, error] = useToken(fetcher, token);
if (state === "UNCHECKED") {
return <Loading />;
return (
<main className={styles.container}>
<div className={styles.root}>
<Loading />
</div>
</main>
);
}
if (state !== "VALID" || error) {
return <Sorry reason={error} />;
return (
<main className={styles.container}>
<div className={styles.root}>
<Sorry reason={error} />
</div>
</main>
);
}
return !finished ? (
<ConfirmForm token={token!} onSuccess={onSuccess} />
<main className={styles.container}>
<div className={styles.root}>
<ConfirmForm token={token!} onSuccess={onSuccess} />
</div>
</main>
) : (
<Success />
<main className={styles.container}>
<div className={styles.root}>
<Success />
</div>
</main>
);
};
const enhanced = withRouteConfig<Props>({
render: ({ match, Component }) => (
<Component token={parseHashQuery(match.location.hash).confirmToken} />
),
render: function ConfirmRouteRender({ match, Component }) {
return (
<Component token={parseHashQuery(match.location.hash).confirmToken} />
);
},
})(ConfirmRoute);
export default enhanced;
@@ -1,4 +1,4 @@
import { Localized } from "fluent-react/compat";
import { Localized } from "@fluent/react/compat";
import React from "react";
import { CallOut, HorizontalGutter, Typography } from "coral-ui/components";
@@ -1,4 +1,4 @@
import { Localized } from "fluent-react/compat";
import { Localized } from "@fluent/react/compat";
import React from "react";
import { HorizontalGutter, Typography } from "coral-ui/components";
@@ -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";
@@ -1,9 +1,10 @@
import { Localized } from "@fluent/react/compat";
import { FORM_ERROR } from "final-form";
import { Localized } from "fluent-react/compat";
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,
@@ -19,7 +20,6 @@ import {
InputLabel,
PasswordField,
Typography,
ValidationMessage,
} from "coral-ui/components";
import ResetPasswordMutation from "./ResetPasswordMutation";
@@ -99,32 +99,23 @@ const ResetPasswordForm: React.FunctionComponent<Props> = ({
>
<PasswordField
id={input.name}
name={input.name}
onChange={input.onChange}
value={input.value}
placeholder="Password"
color={
meta.touched && (meta.error || meta.submitError)
? "error"
: "regular"
}
disabled={disabled}
color={colorFromMeta(meta)}
autoComplete="new-password"
disabled={disabled || meta.submitting}
fullWidth
{...input}
/>
</Localized>
{submitError && (
<CallOut color="error" fullWidth>
{submitError}
</CallOut>
)}
{meta.touched && (meta.error || meta.submitError) && (
<ValidationMessage fullWidth>
{meta.error || meta.submitError}
</ValidationMessage>
)}
<ValidationMessage meta={meta} fullWidth />
</FormField>
)}
</Field>
{submitError && (
<CallOut color="error" fullWidth>
{submitError}
</CallOut>
)}
<Localized id="resetPassword-resetPassword">
<Button
type="submit"
@@ -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;
}
@@ -11,6 +11,8 @@ 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 }) =>
@@ -32,24 +34,44 @@ const ResetRoute: React.FunctionComponent<Props> = ({ token }) => {
const [state, error] = useToken(fetcher, token);
if (state === "UNCHECKED") {
return <Loading />;
return (
<main className={styles.container}>
<div className={styles.root}>
<Loading />
</div>
</main>
);
}
if (state !== "VALID" || error) {
return <Sorry reason={error} />;
return (
<main className={styles.container}>
<div className={styles.root}>
<Sorry reason={error} />
</div>
</main>
);
}
return !finished ? (
<ResetPasswordForm token={token!} onSuccess={onSuccess} />
<main className={styles.container}>
<div className={styles.root}>
<ResetPasswordForm token={token!} onSuccess={onSuccess} />
</div>
</main>
) : (
<Success />
<main className={styles.container}>
<div className={styles.root}>
<Success />
</div>
</main>
);
};
const enhanced = withRouteConfig<Props>({
render: ({ match, Component }) => (
<Component token={parseHashQuery(match.location.hash).resetToken} />
),
render: function ResetRouteRender({ match, Component }) {
return <Component token={parseHashQuery(match.location.hash).resetToken} />;
},
})(ResetRoute);
export default enhanced;
@@ -1,4 +1,4 @@
import { Localized } from "fluent-react/compat";
import { Localized } from "@fluent/react/compat";
import React from "react";
import { CallOut, HorizontalGutter, Typography } from "coral-ui/components";
@@ -1,4 +1,4 @@
import { Localized } from "fluent-react/compat";
import { Localized } from "@fluent/react/compat";
import React from "react";
import { HorizontalGutter, Typography } from "coral-ui/components";
@@ -10,47 +10,55 @@ exports[`renders form 1`] = `
<div
className="MainLayout-centered"
>
<div>
<form
autoComplete="off"
onSubmit={[Function]}
<main
className="ConfirmRoute-container"
>
<div
className="ConfirmRoute-root"
>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-double"
<form
autoComplete="off"
onSubmit={[Function]}
>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-full"
className="Box-root HorizontalGutter-root HorizontalGutter-double"
>
<h1
className="Box-root Typography-root Typography-heading1 Typography-colorTextPrimary"
<div
className="Box-root HorizontalGutter-root HorizontalGutter-full"
>
Email Confirmation
</h1>
<p
className="Box-root Typography-root Typography-bodyCopy Typography-colorTextPrimary"
<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"
>
Click below to confirm your email address.
</p>
<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>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-full"
>
<button
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled Button-fullWidth"
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Confirm email
</button>
</div>
</div>
</form>
</div>
</form>
</div>
</main>
</div>
</div>
`;
@@ -65,26 +73,36 @@ exports[`renders missing confirm token 1`] = `
<div
className="MainLayout-centered"
>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-double"
<main
className="ConfirmRoute-container"
>
<h1
className="Box-root Typography-root Typography-heading1 Typography-colorTextPrimary"
>
Oops Sorry!
</h1>
<div
className="CallOut-root CallOut-colorError CallOut-fullWidth"
className="ConfirmRoute-root"
>
<div>
<span
data-testid="invalid-link"
<div
className="Box-root HorizontalGutter-root HorizontalGutter-double"
>
<h1
className="Box-root Typography-root Typography-heading1 Typography-colorTextPrimary"
>
The specified link is invalid, check to see if it was copied correctly.
</span>
Oops Sorry!
</h1>
<div
className="CallOut-root CallOut-colorError CallOut-fullWidth"
>
<div
className="CallOut-inner"
>
<span
data-testid="invalid-link"
>
The specified link is invalid, check to see if it was copied correctly.
</span>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
`;
@@ -10,94 +10,112 @@ exports[`renders form 1`] = `
<div
className="MainLayout-centered"
>
<div>
<form
autoComplete="off"
onSubmit={[Function]}
<main
className="ResetRoute-container"
>
<div
className="ResetRoute-root"
>
<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>
<form
autoComplete="off"
onSubmit={[Function]}
>
<div
className="Box-root HorizontalGutter-root FormField-root HorizontalGutter-half"
className="Box-root HorizontalGutter-root HorizontalGutter-double"
>
<label
className="Box-root Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
htmlFor="password"
>
Password
</label>
<p
className="Box-root Typography-root Typography-detail Typography-colorTextSecondary"
>
Must be at least 8 characters
</p>
<div
className="PasswordField-fullWidth PasswordField-root"
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="PasswordField-wrapper"
className="Box-root HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<input
className="PasswordField-colorRegular PasswordField-fullWidth PasswordField-input"
id="password"
name="password"
onChange={[Function]}
placeholder="Password"
type="password"
value=""
/>
<div
className="PasswordField-icon"
onClick={[Function]}
role="button"
tabIndex={0}
title="Hide password"
<label
className="Box-root Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
htmlFor="password"
>
<span
aria-hidden="true"
className="Icon-root Icon-sm"
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"
>
visibility
</span>
<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>
<button
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled Button-fullWidth"
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Reset Password
</button>
</div>
</form>
</div>
</form>
</div>
</div>
</main>
</div>
</div>
`;
@@ -112,26 +130,36 @@ exports[`renders missing reset token 1`] = `
<div
className="MainLayout-centered"
>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-double"
<main
className="ResetRoute-container"
>
<h1
className="Box-root Typography-root Typography-heading1 Typography-colorTextPrimary"
>
Oops Sorry!
</h1>
<div
className="CallOut-root CallOut-colorError CallOut-fullWidth"
className="ResetRoute-root"
>
<div>
<span
data-testid="invalid-link"
<div
className="Box-root HorizontalGutter-root HorizontalGutter-double"
>
<h1
className="Box-root Typography-root Typography-heading1 Typography-colorTextPrimary"
>
The specified link is invalid, check to see if it was copied correctly.
</span>
Oops Sorry!
</h1>
<div
className="CallOut-root CallOut-colorError CallOut-fullWidth"
>
<div
className="CallOut-inner"
>
<span
data-testid="invalid-link"
>
The specified link is invalid, check to see if it was copied correctly.
</span>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
`;
@@ -0,0 +1,98 @@
// 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
className="CallOut-inner"
>
<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>
`;
@@ -1,5 +1,7 @@
import sinon from "sinon";
import { ERROR_CODES } from "coral-common/errors";
import { InvalidRequestError } from "coral-framework/lib/errors";
import { GQLResolver } from "coral-framework/schema";
import {
act,
@@ -10,8 +12,6 @@ import {
within,
} from "coral-framework/testHelpers";
import { ERROR_CODES } from "coral-common/errors";
import { InvalidRequestError } from "coral-framework/lib/errors";
import create from "./create";
const token = createAccessToken();
@@ -31,7 +31,9 @@ it("renders missing confirm token", async () => {
replaceHistoryLocation("http://localhost/account/email/confirm");
const { root } = await createTestRenderer();
await waitForElement(() => within(root).getByTestID("invalid-link"));
expect(within(root).toJSON()).toMatchSnapshot();
expect(await within(root).axe()).toHaveNoViolations();
});
it("renders form", async () => {
@@ -57,6 +59,7 @@ it("renders form", async () => {
);
});
expect(within(root).toJSON()).toMatchSnapshot();
expect(await within(root).axe()).toHaveNoViolations();
restMock.verify();
});
@@ -98,6 +101,7 @@ it("renders error from server", async () => {
);
});
restMock.verify();
expect(await within(root).axe()).toHaveNoViolations();
}
});
@@ -1,5 +1,7 @@
import sinon from "sinon";
import { ERROR_CODES } from "coral-common/errors";
import { InvalidRequestError } from "coral-framework/lib/errors";
import { GQLResolver } from "coral-framework/schema";
import {
act,
@@ -10,8 +12,6 @@ import {
within,
} from "coral-framework/testHelpers";
import { ERROR_CODES } from "coral-common/errors";
import { InvalidRequestError } from "coral-framework/lib/errors";
import create from "./create";
const token = createAccessToken();
@@ -32,6 +32,7 @@ it("renders missing reset token", async () => {
const { root } = await createTestRenderer();
await waitForElement(() => within(root).getByTestID("invalid-link"));
expect(within(root).toJSON()).toMatchSnapshot();
expect(await within(root).axe()).toHaveNoViolations();
});
it("renders form", async () => {
@@ -57,6 +58,7 @@ it("renders form", async () => {
);
});
expect(within(root).toJSON()).toMatchSnapshot();
expect(await within(root).axe()).toHaveNoViolations();
restMock.verify();
});
@@ -98,6 +100,7 @@ it("renders error from server", async () => {
);
});
restMock.verify();
expect(await within(root).axe()).toHaveNoViolations();
}
});
@@ -0,0 +1,137 @@
import sinon from "sinon";
import { ERROR_CODES } from "coral-common/errors";
import { InvalidRequestError } from "coral-framework/lib/errors";
import { GQLResolver } from "coral-framework/schema";
import {
act,
createAccessToken,
CreateTestRendererParams,
replaceHistoryLocation,
waitForElement,
within,
} from "coral-framework/testHelpers";
import create from "./create";
const token = createAccessToken();
async function createTestRenderer(
params: CreateTestRendererParams<GQLResolver> = {}
) {
const { testRenderer, context } = create();
return {
context,
testRenderer,
root: testRenderer.root,
};
}
it("renders missing confirm token", async () => {
replaceHistoryLocation("http://localhost/account/notifications/unsubscribe");
const { root } = await createTestRenderer();
await waitForElement(() => within(root).getByTestID("invalid-link"));
expect(within(root).toJSON()).toMatchSnapshot();
expect(await within(root).axe()).toHaveNoViolations();
});
it("renders form", async () => {
replaceHistoryLocation(
`http://localhost/account/notifications/unsubscribe#unsubscribeToken=${token}`
);
const { root, context } = await createTestRenderer();
const restMock = sinon.mock(context.rest);
restMock
.expects("fetch")
.withArgs("/account/notifications/unsubscribe", {
method: "GET",
token,
})
.once();
await act(async () => {
await waitForElement(() => within(root).getByTestID("unsubscribe-form"));
});
expect(within(root).toJSON()).toMatchSnapshot();
expect(await within(root).axe()).toHaveNoViolations();
restMock.verify();
});
it("renders error from server", async () => {
replaceHistoryLocation(
`http://localhost/account/notifications/unsubscribe#unsubscribeToken=${token}`
);
const codes = [
ERROR_CODES.RATE_LIMIT_EXCEEDED,
ERROR_CODES.INTEGRATION_DISABLED,
ERROR_CODES.USER_NOT_FOUND,
ERROR_CODES.TOKEN_INVALID,
];
for (const code of codes) {
const { root, context } = await createTestRenderer();
const restMock = sinon.mock(context.rest);
restMock
.expects("fetch")
.withArgs("/account/notifications/unsubscribe", {
method: "GET",
token,
})
.once()
.throwsException(
new InvalidRequestError({
code,
})
);
await act(async () => {
await waitForElement(() =>
within(root).getByText(code, {
exact: false,
})
);
});
restMock.verify();
expect(await within(root).axe()).toHaveNoViolations();
}
});
it("submits form", async () => {
replaceHistoryLocation(
`http://localhost/account/notifications/unsubscribe#unsubscribeToken=${token}`
);
const { root, context } = await createTestRenderer();
const restMock = sinon.mock(context.rest);
restMock
.expects("fetch")
.withArgs("/account/notifications/unsubscribe", {
method: "GET",
token,
})
.once();
restMock
.expects("fetch")
.withArgs("/account/notifications/unsubscribe", {
method: "DELETE",
token,
})
.once();
await act(async () => {
await waitForElement(() => within(root).getByTestID("unsubscribe-form"));
});
const form = within(root).getByType("form");
// Submit valid form.
await act(async () => {
form.props.onSubmit();
await waitForElement(() => within(root).getByTestID("success"));
});
restMock.verify();
});
+12 -11
View File
@@ -1,10 +1,10 @@
import TransitionControl from "coral-framework/testHelpers/TransitionControl";
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";
@@ -13,16 +13,17 @@ const Router = createFarceRouter({
historyProtocol: new BrowserProtocol(),
historyMiddlewares: [queryMiddleware],
routeConfig,
renderReady: ({ elements }) => (
<>
<ElementsRenderer elements={elements} />
{// this enables router transition control when writing tests.
process.env.NODE_ENV === "test" && <TransitionControl />}
</>
),
renderError: ({ error }) => (
<div>{error.status === 404 ? <NotFound /> : "Error"}</div>
),
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 App: FunctionComponent = () => (
@@ -1,4 +1,4 @@
import { Localized } from "fluent-react/compat";
import { Localized } from "@fluent/react/compat";
import React, { FunctionComponent } from "react";
import ApprovedIcon from "./ApprovedIcon";
@@ -8,8 +8,7 @@ import Footer from "./Footer";
import GoToCommentLink from "./GoToCommentLink";
import Info from "./Info";
import Timestamp from "./Timestamp";
import { Typography } from "coral-ui/components";
import Username from "./Username";
interface Props {
href: string;
@@ -18,10 +17,6 @@ interface Props {
onGotoComment?: React.EventHandler<React.MouseEvent>;
}
const Username: FunctionComponent<{ username: string }> = ({ username }) => (
<strong>{username}</strong>
);
const ApprovedComment: FunctionComponent<Props> = props => (
<DecisionItem icon={<ApprovedIcon />}>
<Localized
@@ -31,9 +26,7 @@ const ApprovedComment: FunctionComponent<Props> = props => (
<Info>{"Approved comment by <Username></Username>"}</Info>
</Localized>
<Footer>
<Typography variant="timestamp">
<Timestamp>{props.date}</Timestamp>
</Typography>
<Timestamp>{props.date}</Timestamp>
<DotDivider />
<GoToCommentLink href={props.href} onClick={props.onGotoComment} />
</Footer>
@@ -1,3 +1,3 @@
.root {
color: var(--palette-success-main);
color: var(--v2-palette-success-main);
}
@@ -1,11 +1,13 @@
import React, { FunctionComponent } from "react";
import { Icon } from "coral-ui/components";
import { Icon } from "coral-ui/components/v2";
import styles from "./ApprovedIcon.css";
const ApprovedIcon: FunctionComponent = () => (
<Icon className={styles.root}>check</Icon>
<Icon size="md" className={styles.root}>
check_circled
</Icon>
);
export default ApprovedIcon;
@@ -3,6 +3,6 @@
}
.historyIcon {
color: var(--palette-text-secondary);
margin-right: calc(1.5 * var(--mini-unit));
color: var(--v2-palette-text-secondary);
margin-right: 10px;
}
@@ -1,7 +1,12 @@
import { Localized } from "fluent-react/compat";
import { Localized } from "@fluent/react/compat";
import React, { FunctionComponent } from "react";
import { BaseButton, ClickOutside, Icon, Popover } from "coral-ui/components";
import {
BaseButton,
ClickOutside,
Icon,
Popover,
} from "coral-ui/components/v2";
import DecisionHistoryQuery from "./DecisionHistoryQuery";
@@ -1,9 +1,10 @@
import React from "react";
import { graphql, RelayPaginationProp } from "react-relay";
import { withPaginationContainer } from "coral-framework/lib/relay";
import { DecisionHistoryContainer_viewer as ViewerData } from "coral-admin/__generated__/DecisionHistoryContainer_viewer.graphql";
import { DecisionHistoryContainerPaginationQueryVariables } from "coral-admin/__generated__/DecisionHistoryContainerPaginationQuery.graphql";
import { withPaginationContainer } from "coral-framework/lib/relay";
import DecisionHistory from "./DecisionHistory";
@@ -45,7 +46,7 @@ export class DecisionHistoryContainer extends React.Component<
error => {
this.setState({ disableLoadMore: false });
if (error) {
// tslint:disable-next-line:no-console
// eslint-disable-next-line no-console
console.error(error);
}
}
@@ -1,9 +1,10 @@
import React from "react";
import { graphql } from "react-relay";
import { DecisionHistoryItemContainer_action as ActionData } from "coral-admin/__generated__/DecisionHistoryItemContainer_action.graphql";
import { withFragmentContainer } from "coral-framework/lib/relay";
import { DecisionHistoryItemContainer_action as ActionData } from "coral-admin/__generated__/DecisionHistoryItemContainer_action.graphql";
import ApprovedComment from "./ApprovedComment";
import RejectedComment from "./RejectedComment";
@@ -16,9 +17,7 @@ class DecisionHistoryItemContainer extends React.Component<
DecisionHistoryItemContainerProps
> {
public render() {
const href = `/admin/moderate/comment/${
this.props.action.revision.comment.id
}`;
const href = `/admin/moderate/comment/${this.props.action.revision.comment.id}`;
const username =
(this.props.action.revision.comment.author &&
this.props.action.revision.comment.author.username) ||
@@ -1,6 +1,6 @@
import React, { FunctionComponent } from "react";
import { Delay, Flex, Spinner } from "coral-ui/components";
import { Delay, Flex, Spinner } from "coral-ui/components/v2";
import Main from "./Main";
import Title from "./Title";
@@ -1,6 +1,7 @@
import { graphql, QueryRenderer } from "coral-framework/lib/relay";
import React, { Component } from "react";
import { graphql, QueryRenderer } from "coral-framework/lib/relay";
import { DecisionHistoryQuery as QueryTypes } from "coral-admin/__generated__/DecisionHistoryQuery.graphql";
import DecisionHistoryContainer from "./DecisionHistoryContainer";

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