Showing preview only (2,410K chars total). Download the full file or copy to clipboard to get everything.
Repository: conversationai/conversationai-moderator
Branch: main
Commit: 0373a11e3376
Files: 535
Total size: 15.7 MB
Directory structure:
gitextract_v8p2gl5q/
├── .circleci/
│ └── config.yml
├── .dockerignore
├── .editorconfig
├── .github/
│ ├── ISSUE_TEMPLATE.md
│ └── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── QUICKSTART.md
├── README.md
├── bin/
│ ├── build
│ ├── initdb
│ ├── install
│ ├── install-circleci
│ ├── link-packages
│ ├── lint
│ ├── lint-fix
│ ├── osmod
│ ├── run
│ ├── storybook
│ ├── sync-db
│ ├── test
│ └── watch
├── deployments/
│ ├── gcloud/
│ │ ├── Dockerfile
│ │ ├── README.md
│ │ ├── deploy-sql.sh
│ │ ├── deploy.sh
│ │ ├── kubernetes-deployment.yaml
│ │ └── kubernetes-networking.yaml
│ ├── local/
│ │ ├── Dockerfile
│ │ ├── README.md
│ │ └── docker-compose.yml
│ └── standalone/
│ ├── .dockerignore
│ ├── Dockerfile
│ └── initialise_db.sh
├── design-files/
│ ├── Moderator-StickerSheet-20161117-DAS/
│ │ ├── document.json
│ │ ├── meta.json
│ │ ├── pages/
│ │ │ ├── 1B67083D-3430-4865-A36A-6C687A1EEB45.json
│ │ │ └── 226D6615-C16F-4B84-92E0-291B2F1B15C4.json
│ │ └── user.json
│ └── Moderator-StickerSheet-20161117-DAS.sketch
├── docs/
│ ├── auth.md
│ ├── comment_flow.md
│ ├── modeling.md
│ ├── osmod_assistant_protocol.md
│ ├── osmod_services_api.md
│ ├── osmod_task_api.md
│ ├── sql_queries.sql
│ ├── worker.md
│ └── youtube_integration.md
├── lerna.json
├── package.json
├── packages/
│ ├── README.md
│ ├── backend-api/
│ │ ├── .sequelizerc
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── bin/
│ │ │ ├── check_migrations.sh
│ │ │ ├── make_migration.sh
│ │ │ ├── osmod-test.js
│ │ │ ├── osmod.js
│ │ │ ├── run_sequelize_sync.js
│ │ │ └── run_task
│ │ ├── data/
│ │ │ ├── alice.txt
│ │ │ ├── brexit.csv
│ │ │ ├── climate.csv
│ │ │ ├── election.csv
│ │ │ └── wikipedia.csv
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── actions/
│ │ │ │ ├── assignment_updaters.ts
│ │ │ │ └── object_updaters.ts
│ │ │ ├── api/
│ │ │ │ ├── assistant/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── schema.ts
│ │ │ │ ├── constants.ts
│ │ │ │ ├── router.ts
│ │ │ │ ├── services/
│ │ │ │ │ ├── assignments.ts
│ │ │ │ │ ├── authorCounts.ts
│ │ │ │ │ ├── commentActions.ts
│ │ │ │ │ ├── commentSources.ts
│ │ │ │ │ ├── editComment.ts
│ │ │ │ │ ├── histogramScores/
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ └── util.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── moderatedCounts.ts
│ │ │ │ │ ├── search.ts
│ │ │ │ │ ├── serializer.ts
│ │ │ │ │ ├── simple.ts
│ │ │ │ │ ├── textSizes.ts
│ │ │ │ │ └── updateNotifications.ts
│ │ │ │ └── util/
│ │ │ │ ├── permissions.ts
│ │ │ │ ├── server.ts
│ │ │ │ ├── sortCommentIds.ts
│ │ │ │ └── validation.ts
│ │ │ ├── auth/
│ │ │ │ ├── config.ts
│ │ │ │ ├── providers/
│ │ │ │ │ ├── google.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── jwt.ts
│ │ │ │ ├── router.ts
│ │ │ │ ├── tokens.ts
│ │ │ │ ├── users.ts
│ │ │ │ ├── utils.ts
│ │ │ │ └── youtube.ts
│ │ │ ├── commands/
│ │ │ │ ├── articles/
│ │ │ │ │ └── delete.ts
│ │ │ │ ├── comments/
│ │ │ │ │ ├── calculate_text_size.ts
│ │ │ │ │ ├── data_helpers.ts
│ │ │ │ │ ├── delete.ts
│ │ │ │ │ ├── flag.ts
│ │ │ │ │ ├── generate.ts
│ │ │ │ │ ├── import.ts
│ │ │ │ │ ├── rebuild_reply_relations.ts
│ │ │ │ │ ├── recalculate_text_sizes.ts
│ │ │ │ │ ├── recalculate_top_scores.ts
│ │ │ │ │ ├── rescore.ts
│ │ │ │ │ └── send_to_scorer.ts
│ │ │ │ ├── denormalize.ts
│ │ │ │ ├── tests/
│ │ │ │ │ └── youtube.ts
│ │ │ │ └── users/
│ │ │ │ ├── create.ts
│ │ │ │ └── get_token.ts
│ │ │ ├── config.ts
│ │ │ ├── domain/
│ │ │ │ ├── articles/
│ │ │ │ │ ├── countDenormalization.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── categories/
│ │ │ │ │ ├── countDenormalization.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── commentScores/
│ │ │ │ │ └── index.ts
│ │ │ │ ├── comments/
│ │ │ │ │ ├── countDenormalization.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── textSizes.ts
│ │ │ │ └── index.ts
│ │ │ ├── index.ts
│ │ │ ├── integrations/
│ │ │ │ ├── decisions.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── youtube/
│ │ │ │ ├── actions.ts
│ │ │ │ ├── authenticate.ts
│ │ │ │ ├── channels.ts
│ │ │ │ ├── comments.ts
│ │ │ │ ├── hooks.ts
│ │ │ │ ├── objectmap.ts
│ │ │ │ ├── task.ts
│ │ │ │ └── videos.ts
│ │ │ ├── logger.ts
│ │ │ ├── models/
│ │ │ │ ├── article.ts
│ │ │ │ ├── category.ts
│ │ │ │ ├── comment.ts
│ │ │ │ ├── comment_flag.ts
│ │ │ │ ├── comment_score.ts
│ │ │ │ ├── comment_score_request.ts
│ │ │ │ ├── comment_size.ts
│ │ │ │ ├── comment_summary_score.ts
│ │ │ │ ├── comment_top_score.ts
│ │ │ │ ├── configuration.ts
│ │ │ │ ├── constants.ts
│ │ │ │ ├── csrf.ts
│ │ │ │ ├── decision.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── moderation_rule.ts
│ │ │ │ ├── moderator_assignment.ts
│ │ │ │ ├── preselect.ts
│ │ │ │ ├── tag.ts
│ │ │ │ ├── tagging_sensitivity.ts
│ │ │ │ ├── user.ts
│ │ │ │ ├── user_category_assignment.ts
│ │ │ │ └── user_social_auth.ts
│ │ │ ├── notification_router.ts
│ │ │ ├── pipeline/
│ │ │ │ ├── apiShim.ts
│ │ │ │ ├── hooks.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── pipeline.ts
│ │ │ │ ├── rules.ts
│ │ │ │ ├── shim.ts
│ │ │ │ └── state.ts
│ │ │ ├── processing/
│ │ │ │ ├── api/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── known_tasks.ts
│ │ │ │ │ └── permissions.ts
│ │ │ │ ├── dashboard.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── tasks/
│ │ │ │ │ ├── comment_actions.ts
│ │ │ │ │ ├── db_operations.ts
│ │ │ │ │ ├── heartbeat.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── process_machine_score.ts
│ │ │ │ │ ├── process_tagging.ts
│ │ │ │ │ ├── score_actions.ts
│ │ │ │ │ ├── score_tag_actions.ts
│ │ │ │ │ └── send_comment_for_scoring.ts
│ │ │ │ ├── util.ts
│ │ │ │ └── worker.ts
│ │ │ ├── processor.ts
│ │ │ ├── redis.ts
│ │ │ ├── sequelize-config.ts
│ │ │ ├── sequelize-sync.ts
│ │ │ ├── sequelize.ts
│ │ │ ├── server-management.ts
│ │ │ ├── server.ts
│ │ │ ├── test/
│ │ │ │ ├── auth/
│ │ │ │ │ ├── providers/
│ │ │ │ │ │ └── google.spec.ts
│ │ │ │ │ ├── tokens.spec.ts
│ │ │ │ │ └── users.spec.ts
│ │ │ │ ├── domain/
│ │ │ │ │ ├── comments/
│ │ │ │ │ │ ├── fixture.ts
│ │ │ │ │ │ └── textSizes.spec.ts
│ │ │ │ │ └── topScores/
│ │ │ │ │ └── calculateTopScores.spec.ts
│ │ │ │ ├── fixture.ts
│ │ │ │ ├── integration/
│ │ │ │ │ ├── api/
│ │ │ │ │ │ ├── actions.spec.ts
│ │ │ │ │ │ ├── assignments.spec.ts
│ │ │ │ │ │ ├── assistant.spec.ts
│ │ │ │ │ │ ├── authorCounts.spec.ts
│ │ │ │ │ │ ├── editComment.spec.ts
│ │ │ │ │ │ ├── histogramScores.spec.ts
│ │ │ │ │ │ ├── simple-comment.spec.ts
│ │ │ │ │ │ ├── simple-ranges.spec.ts
│ │ │ │ │ │ ├── simple-user.spec.ts
│ │ │ │ │ │ └── test_helper.ts
│ │ │ │ │ └── websocket/
│ │ │ │ │ ├── assign_moderators.spec.ts
│ │ │ │ │ ├── update_notifications.spec.ts
│ │ │ │ │ └── websocket.spec.ts
│ │ │ │ ├── pipeline/
│ │ │ │ │ ├── pipeline.spec.ts
│ │ │ │ │ ├── rules.spec.ts
│ │ │ │ │ └── state.spec.ts
│ │ │ │ ├── test_helper.ts
│ │ │ │ └── unit/
│ │ │ │ ├── services/
│ │ │ │ │ ├── authorCounts.spec.ts
│ │ │ │ │ └── histogramScores.spec.ts
│ │ │ │ └── util/
│ │ │ │ └── notifications.spec.ts
│ │ │ └── worker.ts
│ │ └── tsconfig.json
│ └── frontend-web/
│ ├── .babelrc
│ ├── LICENSE
│ ├── README.md
│ ├── package.json
│ ├── public/
│ │ ├── css/
│ │ │ ├── fonts/
│ │ │ │ └── fonts.css
│ │ │ ├── moderator.css
│ │ │ └── normalize.css
│ │ └── index.html
│ ├── src/
│ │ ├── app/
│ │ │ ├── appstate.ts
│ │ │ ├── auth.ts
│ │ │ ├── components/
│ │ │ │ ├── Arrow/
│ │ │ │ │ ├── Arrow.tsx
│ │ │ │ │ ├── ArrowStory.tsx
│ │ │ │ │ ├── __spec__/
│ │ │ │ │ │ └── .gitkeep
│ │ │ │ │ └── index.ts
│ │ │ │ ├── AspectRatio/
│ │ │ │ │ ├── AspectRatio.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── AssignModerators/
│ │ │ │ │ ├── AssignModerators.tsx
│ │ │ │ │ └── AssignModeratorsStory.tsx
│ │ │ │ ├── AssignTagsForm.tsx
│ │ │ │ ├── Avatar/
│ │ │ │ │ ├── Avatar.tsx
│ │ │ │ │ ├── AvatarStory.tsx
│ │ │ │ │ ├── __spec__/
│ │ │ │ │ │ └── .gitkeep
│ │ │ │ │ └── index.ts
│ │ │ │ ├── Button/
│ │ │ │ │ ├── Button.tsx
│ │ │ │ │ ├── ButtonStory.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── CanvasTruncate/
│ │ │ │ │ ├── CanvasTruncate.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── CheckboxRow/
│ │ │ │ │ ├── CheckboxRow.tsx
│ │ │ │ │ ├── CheckboxRowStory.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── CommentActionButton/
│ │ │ │ │ ├── CommentActionButton.tsx
│ │ │ │ │ ├── CommentActionButtonStory.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── CommentList/
│ │ │ │ │ ├── CommentList.tsx
│ │ │ │ │ ├── components/
│ │ │ │ │ │ ├── CheckboxColumn/
│ │ │ │ │ │ │ ├── CheckboxColumn.tsx
│ │ │ │ │ │ │ └── index.ts
│ │ │ │ │ │ └── SortColumn/
│ │ │ │ │ │ ├── SortColumn.tsx
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── CommentText.tsx
│ │ │ │ ├── ConfirmationCircle/
│ │ │ │ │ ├── ConfirmationCircle.tsx
│ │ │ │ │ ├── ConfirmationCircleStory.tsx
│ │ │ │ │ ├── __spec__/
│ │ │ │ │ │ └── .gitkeep
│ │ │ │ │ └── index.ts
│ │ │ │ ├── DotChart/
│ │ │ │ │ ├── DotChart.tsx
│ │ │ │ │ ├── DotChartStory.tsx
│ │ │ │ │ ├── __spec__/
│ │ │ │ │ │ └── .gitkeep
│ │ │ │ │ ├── comments-data.json
│ │ │ │ │ └── index.ts
│ │ │ │ ├── ErrorRoot.tsx
│ │ │ │ ├── ErrorRootStory.tsx
│ │ │ │ ├── FlagsSummary.tsx
│ │ │ │ ├── HeaderBar.tsx
│ │ │ │ ├── Icons/
│ │ │ │ │ ├── IconBase.tsx
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── LazyLoadComment/
│ │ │ │ │ ├── CommentBodyStory.tsx
│ │ │ │ │ ├── LazyLoadComment.tsx
│ │ │ │ │ ├── components.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── MagicTimestamp.tsx
│ │ │ │ ├── MagicTimestampStory.tsx
│ │ │ │ ├── ModerateButtons/
│ │ │ │ │ ├── ModerateButtons.tsx
│ │ │ │ │ ├── ModerateButtonsStory.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── NavigationTab/
│ │ │ │ │ ├── NavigationTab.tsx
│ │ │ │ │ ├── NavigationTabStory.tsx
│ │ │ │ │ ├── __spec__/
│ │ │ │ │ │ └── .gitkeep
│ │ │ │ │ └── index.ts
│ │ │ │ ├── OverflowContainer/
│ │ │ │ │ ├── OverflowContainer.tsx
│ │ │ │ │ ├── OverflowContainerStory.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── RuleBars/
│ │ │ │ │ ├── RuleBars.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── ScoresList/
│ │ │ │ │ ├── ScoresList.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── Scrim/
│ │ │ │ │ ├── Scrim.tsx
│ │ │ │ │ ├── ScrimStory.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── SearchAttribute/
│ │ │ │ │ ├── SearchAttribute.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── SearchHeader/
│ │ │ │ │ ├── SearchHeader.tsx
│ │ │ │ │ ├── SearchHeaderStory.tsx
│ │ │ │ │ ├── __spec__/
│ │ │ │ │ │ └── .gitkeep
│ │ │ │ │ └── index.ts
│ │ │ │ ├── SingleComment/
│ │ │ │ │ ├── SingleComment.tsx
│ │ │ │ │ ├── SingleCommentStory.tsx
│ │ │ │ │ ├── __spec__/
│ │ │ │ │ │ └── .gitkeep
│ │ │ │ │ ├── components/
│ │ │ │ │ │ ├── AnnotatedCommentText.tsx
│ │ │ │ │ │ ├── AuthorCounts.tsx
│ │ │ │ │ │ ├── CommentTags.tsx
│ │ │ │ │ │ ├── DetailRow.tsx
│ │ │ │ │ │ ├── FlagsList.tsx
│ │ │ │ │ │ └── SummaryScore.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── Slider/
│ │ │ │ │ ├── RangeBar.tsx
│ │ │ │ │ ├── Slider.tsx
│ │ │ │ │ ├── SliderStory.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── SplashRoot.tsx
│ │ │ │ ├── SplashRootStory.tsx
│ │ │ │ ├── TagLabelRow/
│ │ │ │ │ ├── TagLabelRow.tsx
│ │ │ │ │ ├── TagLabelRowStory.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── ThemeRoot.tsx
│ │ │ │ ├── Toast/
│ │ │ │ │ ├── Toast.tsx
│ │ │ │ │ ├── ToastMessage.tsx
│ │ │ │ │ ├── ToastStory.tsx
│ │ │ │ │ ├── __spec__/
│ │ │ │ │ │ └── .gitkeep
│ │ │ │ │ └── index.ts
│ │ │ │ ├── Toggle/
│ │ │ │ │ └── __spec__/
│ │ │ │ │ └── .gitkeep
│ │ │ │ ├── ToolTip/
│ │ │ │ │ ├── ToolTip.tsx
│ │ │ │ │ ├── ToolTipStory.tsx
│ │ │ │ │ ├── __spec__/
│ │ │ │ │ │ └── .gitkeep
│ │ │ │ │ └── index.ts
│ │ │ │ ├── VirtualListScrollbar.tsx
│ │ │ │ ├── article_controls.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── styles.ts
│ │ │ ├── config.ts
│ │ │ ├── injectors/
│ │ │ │ ├── articleFetchQueue.ts
│ │ │ │ ├── articleInjector.ts
│ │ │ │ ├── commentFetchQueue.ts
│ │ │ │ ├── commentInjector.ts
│ │ │ │ └── contextInjector.ts
│ │ │ ├── main.tsx
│ │ │ ├── platform/
│ │ │ │ ├── dataService.ts
│ │ │ │ ├── localStore.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── websocketService.ts
│ │ │ ├── scenes/
│ │ │ │ ├── Comments/
│ │ │ │ │ ├── Comments.tsx
│ │ │ │ │ ├── components/
│ │ │ │ │ │ ├── CommentDetail/
│ │ │ │ │ │ │ ├── CommentDetail.tsx
│ │ │ │ │ │ │ ├── components/
│ │ │ │ │ │ │ │ └── InfoButton/
│ │ │ │ │ │ │ │ ├── InfoButton.tsx
│ │ │ │ │ │ │ │ └── index.ts
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ └── store.ts
│ │ │ │ │ │ ├── ModeratedComments/
│ │ │ │ │ │ │ ├── ModeratedComments.tsx
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ └── store/
│ │ │ │ │ │ │ ├── checkedSelection.ts
│ │ │ │ │ │ │ ├── commentListLoader.ts
│ │ │ │ │ │ │ ├── currentPagingIdentifier.ts
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ └── moderatedComments.ts
│ │ │ │ │ │ ├── NewComments/
│ │ │ │ │ │ │ ├── NewComments.tsx
│ │ │ │ │ │ │ ├── components/
│ │ │ │ │ │ │ │ └── BatchSelector/
│ │ │ │ │ │ │ │ ├── BatchSelector.tsx
│ │ │ │ │ │ │ │ └── index.ts
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ └── store/
│ │ │ │ │ │ │ ├── checkedSelection.ts
│ │ │ │ │ │ │ ├── commentListLoader.ts
│ │ │ │ │ │ │ ├── commentScores.ts
│ │ │ │ │ │ │ ├── currentPagingIdentifier.ts
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ └── util.ts
│ │ │ │ │ │ ├── Shortcuts/
│ │ │ │ │ │ │ ├── Shortcuts.tsx
│ │ │ │ │ │ │ ├── ShortcutsStory.tsx
│ │ │ │ │ │ │ ├── __spec__/
│ │ │ │ │ │ │ │ └── .gitkeep
│ │ │ │ │ │ │ └── index.ts
│ │ │ │ │ │ ├── SubheaderBar.tsx
│ │ │ │ │ │ ├── TagSelector/
│ │ │ │ │ │ │ ├── TagSelector.tsx
│ │ │ │ │ │ │ └── index.ts
│ │ │ │ │ │ └── ThreadedCommentDetail/
│ │ │ │ │ │ ├── ThreadedCommentDetail.tsx
│ │ │ │ │ │ ├── components/
│ │ │ │ │ │ │ └── ThreadedComment/
│ │ │ │ │ │ │ ├── ThreadedComment.tsx
│ │ │ │ │ │ │ ├── ThreadedCommentStory.tsx
│ │ │ │ │ │ │ └── index.ts
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── scoreFilters.ts
│ │ │ │ │ └── store.ts
│ │ │ │ ├── Login/
│ │ │ │ │ ├── ConfigureOAuth.tsx
│ │ │ │ │ ├── Login.tsx
│ │ │ │ │ ├── LoginStory.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── Search/
│ │ │ │ │ ├── Search.tsx
│ │ │ │ │ ├── components/
│ │ │ │ │ │ ├── SearchResults.tsx
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── store/
│ │ │ │ │ │ ├── checkedSelection.ts
│ │ │ │ │ │ ├── commentListLoader.ts
│ │ │ │ │ │ ├── currentPagingIdentifier.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ └── searchResults.ts
│ │ │ │ │ └── types.ts
│ │ │ │ ├── Settings/
│ │ │ │ │ ├── Ranges.tsx
│ │ │ │ │ ├── Settings.tsx
│ │ │ │ │ ├── components/
│ │ │ │ │ │ ├── AddUsers.tsx
│ │ │ │ │ │ ├── ColorSelect/
│ │ │ │ │ │ │ ├── ColorSelect.tsx
│ │ │ │ │ │ │ ├── ColorSelectStory.tsx
│ │ │ │ │ │ │ └── index.ts
│ │ │ │ │ │ ├── EditUsers.tsx
│ │ │ │ │ │ ├── EditYouTubeUser.tsx
│ │ │ │ │ │ ├── LabelSettings/
│ │ │ │ │ │ │ ├── LabelSettings.tsx
│ │ │ │ │ │ │ ├── LabelSettingsStory.tsx
│ │ │ │ │ │ │ └── index.ts
│ │ │ │ │ │ ├── ManageAutomatedRules.tsx
│ │ │ │ │ │ ├── ManagePreselects.tsx
│ │ │ │ │ │ ├── ManageSensitivities.tsx
│ │ │ │ │ │ ├── ManageTags.tsx
│ │ │ │ │ │ ├── OAuthConfig.tsx
│ │ │ │ │ │ ├── RuleRow/
│ │ │ │ │ │ │ ├── RuleRow.tsx
│ │ │ │ │ │ │ ├── RuleRowStory.tsx
│ │ │ │ │ │ │ └── index.ts
│ │ │ │ │ │ ├── SaveButtons.tsx
│ │ │ │ │ │ ├── UserForm.tsx
│ │ │ │ │ │ ├── rows.tsx
│ │ │ │ │ │ └── users.tsx
│ │ │ │ │ ├── settingsStyles.ts
│ │ │ │ │ ├── store.ts
│ │ │ │ │ └── styles.ts
│ │ │ │ ├── Tables/
│ │ │ │ │ ├── ArticleTable.tsx
│ │ │ │ │ ├── CategorySidebar.tsx
│ │ │ │ │ ├── ComponentsStory.tsx
│ │ │ │ │ ├── FilterSidebar.tsx
│ │ │ │ │ ├── TableFrame.tsx
│ │ │ │ │ ├── TableFrameStory.tsx
│ │ │ │ │ ├── components.tsx
│ │ │ │ │ ├── styles.ts
│ │ │ │ │ └── utils.ts
│ │ │ │ ├── appstate.ts
│ │ │ │ ├── index.tsx
│ │ │ │ ├── routes.ts
│ │ │ │ └── store.ts
│ │ │ ├── store.ts
│ │ │ ├── stores/
│ │ │ │ ├── appstate.ts
│ │ │ │ ├── articles.ts
│ │ │ │ ├── categories.ts
│ │ │ │ ├── commentActions.ts
│ │ │ │ ├── comments.ts
│ │ │ │ ├── counts.ts
│ │ │ │ ├── globalActions.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── preselects.ts
│ │ │ │ ├── rules.ts
│ │ │ │ ├── taggingSensitivities.ts
│ │ │ │ ├── tags.ts
│ │ │ │ ├── textSizes.ts
│ │ │ │ └── users.ts
│ │ │ ├── styles/
│ │ │ │ ├── breakpoints.ts
│ │ │ │ ├── colors.ts
│ │ │ │ ├── forms.ts
│ │ │ │ ├── header.ts
│ │ │ │ ├── hoverstates.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── scrim.ts
│ │ │ │ ├── typography.ts
│ │ │ │ ├── util.ts
│ │ │ │ └── zindex.ts
│ │ │ ├── stylesx/
│ │ │ │ └── index.ts
│ │ │ ├── util/
│ │ │ │ ├── DotChartRenderer.ts
│ │ │ │ ├── color.ts
│ │ │ │ ├── csrf.ts
│ │ │ │ ├── groupByColumn.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── makeCheckedSelectionStore/
│ │ │ │ │ ├── __spec__/
│ │ │ │ │ │ └── makeCheckedSelectionStore.spec.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── makeCheckedSelectionStore.ts
│ │ │ │ ├── makeCurrentPagingIdentifierReducer.ts
│ │ │ │ ├── measureText.ts
│ │ │ │ ├── partial/
│ │ │ │ │ ├── __spec__/
│ │ │ │ │ │ ├── memoize.spec.ts
│ │ │ │ │ │ └── partial.spec.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── returnSavedCommentRow.ts
│ │ │ │ ├── returnURL.ts
│ │ │ │ ├── savedSorts.ts
│ │ │ │ ├── sortByLabel.ts
│ │ │ │ ├── time.ts
│ │ │ │ └── timeout.ts
│ │ │ └── utilx/
│ │ │ ├── cssInJs.ts
│ │ │ ├── highlightText.tsx
│ │ │ ├── hooks.ts
│ │ │ ├── index.ts
│ │ │ ├── keyCodes.tsx
│ │ │ └── sortDefinitions.tsx
│ │ ├── index.ts
│ │ ├── models/
│ │ │ ├── article.ts
│ │ │ ├── category.ts
│ │ │ ├── comment.ts
│ │ │ ├── commentFlag.ts
│ │ │ ├── commentScore.ts
│ │ │ ├── common.ts
│ │ │ ├── fake/
│ │ │ │ ├── article.ts
│ │ │ │ ├── category.ts
│ │ │ │ ├── comment.ts
│ │ │ │ ├── commentScore.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── rule.ts
│ │ │ │ ├── tag.ts
│ │ │ │ └── user.ts
│ │ │ ├── index.ts
│ │ │ ├── preselect.ts
│ │ │ ├── rule.ts
│ │ │ ├── tag.ts
│ │ │ ├── taggingSensitivity.ts
│ │ │ └── user.ts
│ │ ├── server.ts
│ │ ├── test/
│ │ │ ├── actions.ts
│ │ │ ├── apitest.ts
│ │ │ ├── notificationChecks.ts
│ │ │ ├── objectChecks.ts
│ │ │ └── pageTests.ts
│ │ └── types.ts
│ ├── tooling/
│ │ ├── storybook/
│ │ │ ├── Storyshots.test.js
│ │ │ ├── __snapshots__/
│ │ │ │ └── Storyshots.test.js.snap
│ │ │ ├── config.js
│ │ │ ├── disable-aphrodite-inject.js
│ │ │ ├── jest.config.json
│ │ │ ├── preview-head.html
│ │ │ ├── register-context.js
│ │ │ └── webpack.config.js
│ │ ├── webpack.config.js
│ │ └── webpack.config.production.js
│ └── tsconfig.json
├── seed/
│ └── initial-database.sql
└── tslint.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .circleci/config.yml
================================================
version: 2
jobs:
build:
docker:
- image: circleci/node:12.19
environment:
NODE_ENV: circle_ci
steps:
- checkout
- run: ./bin/install-circleci
- run: ./bin/lint
- run: ./bin/test
================================================
FILE: .dockerignore
================================================
.data
dist
**/dist
**/dist-commonjs
node_modules
**/node_modules
.vagrant
.dockerignore
Dockerfile
npm-debug.*
.git
.hg
.svn
config/local.json
================================================
FILE: .editorconfig
================================================
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
<!--- Provide a general summary of the issue in the Title above -->
## Context
<!--- Provide a more detailed introduction to the issue itself, and why you consider it to be a bug -->
## Expected Behavior
<!--- Tell us what should happen -->
## Actual Behavior
<!--- Tell us what happens instead -->
## Possible Fix
<!--- Not obligatory, but suggest a fix or reason for the bug -->
## Steps to Reproduce
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
<!--- reproduce this bug include code to reproduce, if relevant -->
1.
2.
3.
4.
## Context
<!--- How has this bug affected you? What were you trying to accomplish? -->
## Your Environment
<!--- Include as many relevant details about the environment you experienced the bug in -->
* Environment name and version (e.g. Chrome 39, etc):
* Operating System and version (desktop or mobile):
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!--- Provide a general summary of your changes in the Title above -->
## Description
<!--- Describe your changes in detail -->
## Motivation and Context
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->
## How Has This Been Tested?
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. -->
## Screenshots (if appropriate):
## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [ ] I have added tests to cover my changes.
================================================
FILE: .gitignore
================================================
.idea/
node_modules/
npm-debug.*
tmp/
.tmp/
dist/
dist-commonjs/
.sw[a-z]
.DS_Store
build/
*.js.map
*.css.map
.awcache
ansible_playbook.retry
.data
lerna-debug.log
source-context.json
source-contexts.json
*.rdb
================================================
FILE: CHANGELOG.md
================================================
# v1.0.5
* Fix bug where settings page ids are set to integers ([PR](https://github.com/conversationai/conversationai-moderator/pull/7))
* Update frontend validation for articleId to allow for alphanumeric ids ([PR](https://github.com/conversationai/conversationai-moderator/pull/6))
# v1.0.4
* Update data validation for publisher commentActions endpoint ([PR](https://github.com/conversationai/conversationai-moderator/pull/1))
# v1.0.0
* Open Source
================================================
FILE: CONTRIBUTING.md
================================================
# How to contribute
We'd love to accept your patches and contributions to this project. There are
just a few small guidelines you need to follow.
## Contributor License Agreement
Contributions to this project must be accompanied by a Contributor License
Agreement. You (or your employer) retain the copyright to your contribution,
this simply gives us permission to use and redistribute your contributions as
part of the project. Head over to <https://cla.developers.google.com/> to see
your current agreements on file or to sign a new one.
You generally only need to submit a CLA once, so if you've already submitted one
(even if it was for a different project), you probably don't need to do it
again.
## Code reviews
All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose. Consult [GitHub Help] for more
information on using pull requests.
[GitHub Help]: https://help.github.com/articles/about-pull-requests/
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright {2016} {Jigsaw}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: QUICKSTART.md
================================================
These are instructions on how to get a youtube instance of moderator running on a Google Cloud compute VM.
Step 0:
-------
Create a Google cloud project and a VM running 'Ubuntu 18.04 LTS Minimal' in the [Google console](https://console.cloud.google.com/compute/instances).
You'll also need to create a [firewall rule](https://console.cloud.google.com/networking/firewalls/list) to allow
HTTP traffic, and add that rule to your VM network tags.
You'll also need to allocate a domain name for your new VM - unfortunately the Google OAuth servers won't work with IP addresses.
Step 1:
-------
Create an OAuth2.0 Client ID entry in the [Google console](https://console.developers.google.com/apis/credentials).
Add the following Authorised redirect URIs:
```
http://<domain name from step 0>/api/auth/callback/google
http://<domain name from step 0>/api/youtube/callback
```
You'll be asked to enter this information when you first log into OS Moderator.
Step 2:
-------
Open a terminal to the VM and install the following packages.
```
sudo apt update
sudo apt dist-upgrade -y
sudo apt install -y nodejs npm docker.io git
sudo usermod -a -G docker `whoami`
sudo curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
exit
```
Step 3:
-------
Open a *new* terminal and download the code:
```
git clone git@github.com:conversationai/conversationai-moderator.git conversationai-moderator
cd conversationai-moderator
```
Step 4:
-------
Get a google cloud API key for the ConversationAI service. (To be documented...)
Step 5:
-------
Set the following environment variables
```
export MODERATOR_URL=http://<domain name from step 0>
export GOOGLE_CLOUD_API_KEY=<API key from step 4>
export DATABASE_PASSWORD=password
Step 6:
-------
Run the service
```
docker-compose -f deployments/local/docker-compose.yml up -d
```
When it is up and running, point your browser in the right direction:
http://<domain name from step 0>/
================================================
FILE: README.md
================================================
OSMod - The ConversationAI Moderator App
========================================
Deploying an OSMod instance
---------------------------
### Configuration
The configuration is found in packages/config/index.js. It is pretty self explanatory.
All settings can be overridden via environment variables.
Of particular note, the following have no sensible defaults, and
must be set in the environment before anything will work.
* `DATABASE_NAME`: The MySQL database name, e.g., 'os_moderator'.
* `DATABASE_USER`: The MySQL database user, e.g., 'os_moderator'.
* `DATABASE_PASSWORD`: The MySQL database password.
In a production setting, you'll also have to set the following:
* `MODERATOR_URL`: URL (including protocol, host and port) that OSMOD will listen on.
### System setup:
Install mysql, node (v10 or better), npm (v6 or better) and redis. Instructions for Ubuntu:
```bash
sudo apt install mysql-server nodejs npm redis
sudo apt install build-essential libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev
sudo npm install -g npm n
sudo n v10
hash -r
```
#### System setup -- Docker
If you want to run your moderator instances in one of the preconfigured docker containers,
you'll need to install docker. E.g., to install on Ubuntu 18.04 using apt
```bash
sudo apt install docker.io
# Add docker group to your account so you can talk to the local docker server.
# You probably need to log out and back in for groups to take effect.
sudo usermod -a -G docker `whoami`
sudo curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
# check things work
docker version
docker-compose --version.
```
### Install dependencies and run the server
Install all node dependencies and run initial typescript compile.
```bash
./bin/install
```
Setup local MySQL:
```bash
sudo mysql << EOF
CREATE DATABASE $DATABASE_NAME;
CREATE USER IF NOT EXISTS '$DATABASE_USER' IDENTIFIED BY '$DATABASE_PASSWORD';
GRANT ALL on $DATABASE_NAME.* to $DATABASE_USER;
EOF
sudo mysql $DATABASE_NAME < seed/initial-database.sql
cd packages/backend-api
npx sequelize db:migrate
cd -
# Add a service user that can talk to the Perspective API:
bin/osmod users:create --group moderator --name "PerspectiveAPI" \
--moderator-type "perspective-api" --api-key $YOUR_PERSPECTIVE_API_KEY
# Run the server
bin/watch
```
#### Alternatively, run in a docker container
To run the service in a local docker container, run the following commands:
```bash
# Make sure any local instances of MySQL and Redis are not running
# E.g., on Ubuntu, stop the services
sudo systemctl stop mysql.service redis_6379.service redis-server.service
# Create docker images and launch the service
docker-compose -f deployments/local/docker-compose.yml up -d
```
The docker-compose scripts will initialise the database and create an API service user,
so you don't need to do those steps manually.
To shut down the service and delete all your containers:
```bash
docker-compose -f deployments/local/docker-compose.yml down
```
And to see what the container is doing:
```bash
docker-compose -f deployments/local/docker-compose.yml logs
```
The `osmod` CLI
---------------
You can manage your OSMod system using the osmod commandline tool:
```bash
./bin/osmod <command> <options>
```
where `command` is one of
* `users:create` Create new OS Moderator users
* `users:get-token` Get a JWT token for a user specified by id or email
* `comments:rescore` Rescore comment.
* `comments:send-to-scorer` Send comments to Endpoint of user object to get scored.
* `comments:calculate-text-size` Using node-canvas, calculate a single comment height at a given width.
* `comments:recalculate-text-sizes` Using node-canvas, recalculate comment heights at a given width.
* `comments:recalculate-top-scores` Recalculate comment top scores.
* `comments:flag` Flag comments.
* `comments:delete` Delete all comments from the database.
* `denormalize` Re-run denormalize counts
#### Managing Users
If you are an administrator, you can create other administrators, general moderator users,
and service users via the settings pages in the OSMod UI. Also, if there are no admin users,
the UI will turn the first user to log in into an admin. But you can also create users via the commandline.
Create a human user:
```bash
./bin/osmod users:create --group general --name "Name" --email "$EMAIL_OF_USER"
```
Replace `general` with `admin` if you want to create an administrator.
To create a service user - i.e., one that can connect via the API but not via the UI:
```bash
./bin/osmod users:create --group service --name "Robot"
```
Service users will require a JWT token. You can get this via the UI, or via running the following command:
```bash
./bin/osmod users:get-token --id 4
```
### Management commands
To run a local server on `:8080` and front-end on `:8000`
```bash
./bin/watch
```
### Lint
```bash
./bin/lint
```
optionally you can run lint-fix to attempt auto-fixing most lint errors
```bash
./bin/lint-fix
```
### Storybook
To preview individual widgets and components used by the the OSMod UI:
```bash
./bin/storybook
```
The frontend unit tests also use storybook to generate a HTML snapshot of the resulting widgets.
It then compares this snapshot to a stored version, allowing you to review and approve any changes.
To update stories that need new snapshots, go to `packages/frontend-web` and run
```bash
npm run storybook:test -- -u
```
## Development
The project uses [lerna](https://www.npmjs.com/package/lerna) to help manage
development [the several npm packages](packages/README.md) that are in this
repository. Lerna sym-links package dependencies within this repository. Lerna
is also used to publish updates to all these packages at once.
## Running tests
To run the tests, you'll need to tweak your enviornment:
```bash
# Some tests need admin privileges to clean out the database
export DATABASE_NAME=os_moderator_test
export DATABASE_USER=root
# Run all the tests
NODE_ENV=test bin/test
# or you can run individual tests:
cd packages/backend-api
NODE_ENV=test npm run test
NODE_ENV=test ../../node_modules/.bin/ts-mocha 'src/test/domain/comments/*.spec.js' --recursive --timeout 10000
```
The `bin/test` script uses lerna to first compile all the typescript to javascript,
then runs all the tests.
Deleting and recreating the database schema can take a very long time, hence the long timeout above.
You may need to increase this even further if your system is particularly slow.
If you want to run a test in the debugger, add the --inspect-brk flag to the mocha invocation,
then connect using the chrome inspector (URL: `chrome://inspect`).
## What a running service looks like
While there can be many ways to setup a service, in general a deployment will
typically be a single VM instance running these services:
A MySQL database that holds all of the applications state (See
[the data model doc](docs/modeling.md)).
* Frontend-Webserver service hosting the static ReactJS site. This sends
messages to the Backend API service.
* Backend API service responsible for querying the SQL database and sending
data to the front-end service. This is also the endpoint that receives
requests from the commenting platform it is supporting moderation of; and
it sends requests back to the commenting platform with user actions (e.g. to
reject or approve comments).
* Backend Work Queue service responsible for managing concurrent queue of
asynchronous work. TODO(ldixon): add reddis stuff?
* Some number of assistant services responsible for automating tasks.
Typically this is just calling ML services like
[the Perspective API](https://perspectiveapi.com/)
================================================
FILE: bin/build
================================================
#!/bin/bash
npx lerna run build
================================================
FILE: bin/initdb
================================================
#!/bin/bash
basename=`dirname $0`
mysqlx="mysql -u root -p${DATABASE_PASSWORD}"
if [ ! -z "${DATABASE_HOST}" ]; then
mysqlx="$mysqlx -h ${DATABASE_HOST}"
fi
until $mysqlx -e "" ; do
echo "Can't configure the database:-( waiting..."
sleep 10
done
if ! $mysqlx ${DATABASE_NAME} -e "select count(*) from SequelizeMeta;"; then
echo "Creating database and API service user."
echo
$mysqlx << EOF
CREATE DATABASE ${DATABASE_NAME};
CREATE USER '${DATABASE_USER}' IDENTIFIED BY '${DATABASE_PASSWORD}';
GRANT ALL on ${DATABASE_NAME}.* to ${DATABASE_USER};
EOF
$mysqlx ${DATABASE_NAME} < ${basename}/../seed/initial-database.sql
fi
echo "Running migrations."
cd ${basename}/../packages/backend-api
npx sequelize db:migrate
cd -
================================================
FILE: bin/install
================================================
#!/bin/bash
set -e
# Running npm install blats the package-lock.json file, as it doesn't know anything about
# the sub-packages.
# So if we are not doing a clean build, we save it off and restore it after installing tools.
basename=`dirname $0`
cd "${basename}/.."
if [ -f package-lock.json.bak ]; then
rm package-lock.json.bak
fi
if [ "$1" == "clean" ]; then
echo Removing old packages so we fetch everything from scratch
for i in . packages/backend-api packages/frontend-web; do
rm -Rf "${i}/node_modules" "${i}/package-lock.json"
done
else
cp package-lock.json package-lock.json.bak
fi
# Get packages for the root of the system, in particular lerna
npm install
if [ -f package-lock.json.bak ]; then
mv package-lock.json.bak package-lock.json
fi
# Use lerna to link together sub-modules, then build
./bin/link-packages
./bin/build
================================================
FILE: bin/install-circleci
================================================
#!/bin/bash
NODE_ENV=development npm install
./node_modules/.bin/lerna bootstrap
./node_modules/.bin/lerna run build
================================================
FILE: bin/link-packages
================================================
#!/bin/bash
npx lerna bootstrap
================================================
FILE: bin/lint
================================================
#!/bin/bash
./node_modules/.bin/lerna run lint
================================================
FILE: bin/lint-fix
================================================
#!/bin/bash
./node_modules/.bin/lerna run lint:fix
================================================
FILE: bin/osmod
================================================
#!/bin/bash
basename=`dirname $0`
# Forward to ${basename}/../packages/backend-api/bin/osmod.js
C=''
for i in "$@"; do
case "$i" in
*\'*)
i=`printf "%s" "$i" | sed "s/'/'\"'\"'/g"`
;;
*) : ;;
esac
C="$C '$i'"
done
bash -c "${basename}/../packages/backend-api/bin/osmod.js$C"
================================================
FILE: bin/run
================================================
#!/bin/bash
# Script to run some moderator component inside a docker container
set -e
basename=`dirname $0`
server=$1
logs=$2
if [ -z "${server}" ]; then
echo You need to specify a server name.
exit 1
fi
if [ -z "${logs}" ]; then
logs=/tmp/logs
echo "Using default log directory ($logs)"
else
echo Logging to $logs
fi
mkdir -p ${logs}
logs=`readlink -f ${logs}`
export FRONTEND_URL=${server}
export API_URL=${server}/api
# TODO fix initdb ${basename}/initdb
cd ${basename}/../packages/backend-api
now=`date`
echo Starting: $now >> ${logs}/server.log
echo Starting: $now >> ${logs}/processor.log
echo Starting: $now >> ${logs}/worker.log
node dist/processor.js 2>&1 | tee -a ${logs}/processor.log &
node dist/worker.js 2>&1 | tee -a ${logs}/worker.log &
node dist/server.js 2>&1 | tee -a ${logs}/server.log
================================================
FILE: bin/storybook
================================================
#!/bin/bash
cd packages/frontend-web
npm run storybook &
cd -
wait
================================================
FILE: bin/sync-db
================================================
#!/bin/bash
# keyword arguments
# 1st argument is .yaml file to pull env_variables from
# This script clears the terminal, attempts to pull db dump from dev,
# and apply it to your local db, overwriting it.
parse_yaml() {
local prefix=$2
local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034')
sed -ne "s|^\($s\)\($w\)$s:$s\"\(.*\)\"$s\$|\1$fs\2$fs\3|p" \
-e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" $1 |
awk -F$fs '{
indent = length($1)/2;
vname[indent] = $2;
for (i in vname) {if (i > indent) {delete vname[i]}}
if (length($3) > 0) {
vn=""; for (i=0; i<indent; i++) {vn=(vn)(vname[i])("_")}
printf("%s%s%s=\"%s\"\n", "'$prefix'",vn, $2, $3);
}
}'
}
eval $(parse_yaml $1 "config_")
echo "Dropping your local db instance"
echo
echo "Creating new os_moderator database on local"
echo "This password is for your local db root user"
mysql -uroot -e "DROP DATABASE IF EXISTS os_moderator; CREATE DATABASE os_moderator;"
echo "Btw: This download will take a minute..."
mysqldump -h $config_env_variables_DATABASE_HOST --user=$config_env_variables_DATABASE_USER --password=$config_env_variables_DATABASE_PASSWORD --set-gtid-purged=OFF os_moderator > os_moderator.sql
echo "Finished downloading"
echo
echo "restoring db from .sql backup"
echo
mysql -uroot os_moderator -e "SOURCE os_moderator.sql"
rm os_moderator.sql
echo "mysqldump is complete"
================================================
FILE: bin/test
================================================
#!/bin/bash
npx lerna run compile
npx lerna run test
================================================
FILE: bin/watch
================================================
#!/bin/bash
set -e
export FRONTEND_URL=http://localhost:8000
export API_URL=http://localhost:8080
if [ -z "$1" ]; then
FRONTEND=1
BACKEND=1
PROCESSING=1
else
while [ -n "$1" ]; do
if [ "$1" == frontend ]; then
FRONTEND=1
elif [ "$1" == backend ]; then
BACKEND=1
elif [ "$1" == processing ]; then
BACKEND=1
PROCESSING=1
fi
shift
done
fi
if [ -n "$FRONTEND" ]; then
cd packages/frontend-web
npm run watch &
cd -
fi
if [ -n "$BACKEND" ]; then
cd packages/frontend-web
npm run compile:lib
cd -
cd packages/backend-api
npx ts-node-dev --inspect=5858 src/server.ts &
if [ -n "$PROCESSING" ]; then
npx ts-node-dev --inspect=5857 src/processor.ts &
npx ts-node-dev --inspect=5856 src/worker.ts &
fi
cd -
fi
wait
================================================
FILE: deployments/gcloud/Dockerfile
================================================
FROM gcr.io/google_appengine/nodejs
RUN install_node v8.11.1
WORKDIR /app/
COPY . /app/
RUN npm cache verify
RUN bin/install
EXPOSE 8000 8080
CMD bin/run
================================================
FILE: deployments/gcloud/README.md
================================================
# Deploying ConversationAI Moderator to Google Cloud
## Preparing for the deployment
### Install `gcloud`, `docker`, `kubectl` etc.
Instructions for installing gcloud can be found [here](https://cloud.google.com/sdk/docs/quickstart-linux).
You'll find instructions for installing docker in the [root README](../../README.md)
Once you've installed gcloud and docker, run the following commands to prepare
the system for installing moderator:
```bash
gcloud components install kubectl alpha
gcloud auth configure-docker
```
### Create a GCloud project
Before you can do anything else, you need to create a Google Cloud project,
and assign a billing account
There are many instructions on how to do this via the console. If you want
to do it via the commandline, run the following commands:
```bash
# You can see a list of your billing IDs by running
gcloud alpha billing accounts list
# Set up the project details
PROJECT=<your project ID, e.g., conversationai-moderator-your-name>
REGION=<A region close to home, e.g., europe-west2>
BILLING=<your billing ID>
gcloud projects create $PROJECT --name="Conversation AI Modereator"
gcloud alpha billing projects link $PROJECT --billing-account=$BILLING
gcloud config set project $PROJECT
gcloud config set compute/zone $REGION
# Probably not an exaustive list. Update if you discover any that are
# missing
gcloud services enable sql-component.googleapis.com sqladmin.googleapis.com
```
### Allocate a domain name and IP address
You will probably want to allocate a domain name and static IP address
for your moderator instance, especially for the production case. You can
find instructions on how to do this [here](https://cloud.google.com/kubernetes-engine/docs/tutorials/configuring-domain-name-static-ip).
(TODO: not yet integrated with the scripts. Need to add static IP address as an
environment item, and use it to set up Google OAuth.)
Once you've allocated a hostname and IP address, you'll have enough information
to set the API_URL and FRONTEND_URL environment variables, and to configure
the
### Set up the Google Cloud SQL proxy
During the deployment process, we need to connect to the Google Cloud MySQL
instance to initialise and populate the database. Also, we'll need access
to provision to proopulate users via the CLI.
To do this, we'll need to create a connection using the cloud SQL proxy.
To create the /cloudsql directory and fetch the cloud_sql_proxy script, run
the following:
```bash
sudo mkdir -p /cloudsql
sudo chmod 777 /cloudsql
wget https://dl.google.com/cloudsql/cloud_sql_proxy.linux.amd64 -O /cloudsql/cloud_sql_proxy
chmod +x /cloudsql/cloud_sql_proxy
```
You'll also need to create a service user with the necessary permissions,
then create a key file with that user's private key:
```bash
SQL_MANAGER=sql-manager
gcloud iam service-accounts create $SQL_MANAGER --display-name "SQL Manager"
gcloud projects add-iam-policy-binding $PROJECT \
--member serviceAccount:$SQL_MANAGER@$PROJECT.iam.gserviceaccount.com \
--role roles/cloudsql.client
gcloud iam service-accounts keys create /cloudsql/key.json \
--iam-account $SQL_MANAGER@$PROJECT.iam.gserviceaccount.com
```
If you want to connect from a different machine, skip this second step. Instead, once
you have set up the `/cloudsql` directory as described above, just copy the
`/cloudsql/key.json` file into place and you are good to go.
To start the SQL proxy and connect to the database identified by `$SQL_INSTANCE_NAME`,
run the following commands:
```bash
SQL_CONNECTION=`gcloud sql instances describe $SQL_INSTANCE_NAME --format "value(connectionName)"`
/cloudsql/cloud_sql_proxy -dir=/cloudsql -instances=$SQL_CONNECTION -credential_file=/cloudsql/key.json &
```
You'll then be able to access the database via the appropriate socket file in `/cloudsql`, e.g., :
```bash
DATABASE_SOCKET=/cloudsql/$SQL_CONNECTION bin/osmod users:create --group general --name "Name" --email "email@example.com"
mysql --socket=/cloudsql/$SQL_CONNECTION --user=root --password=$DATABASE_PASSWORD
```
## Do the deployment
### Generate a docker file and upload it to a registry
You can skip this step if you've got a prerolled docker image for the moderator.
(You'll need to do this step if you are rolling out a new version.
```bash
MODERATOR_IMAGE_ID=eu.gcr.io/$PROJECT/conversationai-moderator:<version>
cd <root of moderator source tree>
docker build -f deployments/gcloud/Dockerfile -t $MODERATOR_IMAGE_ID .
docker push $MODERATOR_IMAGE_ID
```
You can test out your docker image by running:
```bash
docker run --publish 8080:8080 --publish 8000:8000 \
--env DATABASE_SOCKET=/cloudsql/$SQL_CONNECTION \
--env DATABASE_NAME=$DATABASE_NAME \
--env DATABASE_USER=$DATABASE_USER \
--env DATABASE_PASSWORD=$DATABASE_PASSWORD \
--env GOOGLE_SCORE_AUTH=$GOOGLE_SCORE_AUTH \
--mount type=bind,source=/cloudsql,destination=/cloudsql/
$MODERATOR_IMAGE_ID
```
You can adjust the above environment settings to connect to the database instance
you require. The above settings assume you are connecting to the
### Set up an `ENVIRONMENT` file and run the deploy script
Subsequent steps need you to set a large number of parameters. The easiest way
to do this is to create an environment file. E.g.,
```
cat > ENVIRONMENT << EOF
export PROJECT=<as set above>
export REGION=<as set above>
export BILLING=<as set above>
export DATABASE_NAME=os_moderator
export DATABASE_USER=os_moderator
export DATABASE_PASSWORD=password
export GOOGLE_SCORE_AUTH=<get this from Jigsaw/Perspective team>
export FRONTEND_URL=http://<hostname or IP address>/
export API_URL=http://<hostname or IP address>:8080/
export MODERATOR_IMAGE_ID=eu.gcr.io/$PROJECT/conversationai-moderator:<version as above>
export SQL_INSTANCE_NAME=conversationai-moderator-db
export SQL_MANAGER=sql-manager
```
### Deploy the MySQL database
Install and configure the MySQL database.
```bash
. ENVIRONMENT
./deploy-sql.sh
```
You'll only need to do this once.
### Deploy the app using Kubernetes
First of all, create your kubernetes cluster. For normal usage, you only need to
create a cluster with one node. We assume the cluster is called
`conversationai-moderator`.
```bash
. ENVIRONMENT
gcloud container clusters create conversationai-moderator --num-nodes=1 --region=$REGION
```
Next, deploy the moderator app. You'll need to rerun this step every time
you want to upgrade the moderator.
```bash
. ENVIRONMENT
./deploy.sh
```
You can see the state of the app in the Kubernetes console, or by running
```bash
kubectl describe deployments conversationai-moderator
```
## TODO:
- Integrate statically allocated IP address
e.g., https://cloud.google.com/kubernetes-engine/docs/tutorials/configuring-domain-name-static-ip
- Separate frontend and api into separate containers?
- Enable SSH in the load balancer
================================================
FILE: deployments/gcloud/deploy-sql.sh
================================================
#!/bin/bash
# Create a managed SQL instance and populate it with initial data
# This script assumes the following environment variables have been set
# SQL_INSTANCE_NAME - Label to use for the ConversationAI MySQL instance
# DATABASE_NAME - Name of the database
# DATABASE_USER - Database user
# DATABASE_PASSWORD - Database password
# It also assumes that gcloud is configured to manage the correct Google
# Cloud project and compute region, and that an appropriate service account
# has been created. See the README for details on how to set these things
# up
set -e
set -u
if [ -z "$SQL_INSTANCE_NAME" ]; then
echo "SQL_INSTANCE_NAME is not defined"
exit;
fi
if [ -z "$DATABASE_NAME" ]; then
echo "DATABASE_NAME is not defined"
exit;
fi
if [ -z "$DATABASE_USER" ]; then
echo "DATABASE_USER is not defined"
exit;
fi
if [ -z "$DATABASE_PASSWORD" ]; then
echo "DATABASE_PASSWORD is not defined"
exit;
fi
gcloud sql instances create $SQL_INSTANCE_NAME --tier=db-g1-small --database-version=MYSQL_5_7
gcloud sql users set-password root % --instance $SQL_INSTANCE_NAME --password $DATABASE_PASSWORD
gcloud sql users create $DATABASE_USER % --instance=$SQL_INSTANCE_NAME --password=$DATABASE_PASSWORD
gcloud sql databases create $DATABASE_NAME --instance=$SQL_INSTANCE_NAME
export SQL_CONNECTION=`gcloud sql instances describe $SQL_INSTANCE_NAME --format "value(connectionName)"`
# Set up SQL proxy on local machine so we can tunnel through the firewall and access the database
/cloudsql/cloud_sql_proxy -dir=/cloudsql -instances=$SQL_CONNECTION -credential_file=/cloudsql/key.json &
mysql --socket=/cloudsql/$SQL_CONNECTION --user=root --password=$DATABASE_PASSWORD << EOF
GRANT ALL on $DATABASE_NAME.* to $DATABASE_USER
EOF
mysql --socket=/cloudsql/$SQL_CONNECTION --user=$DATABASE_USER --password=$DATABASE_PASSWORD $DATABASE_NAME < seed/initial-database.sql
================================================
FILE: deployments/gcloud/deploy.sh
================================================
#!/bin/bash
# Use kubectl to deploy the app to the
set -e
set -u
export SQL_CONNECTION=`gcloud sql instances describe $SQL_INSTANCE_NAME --format "value(connectionName)"`
# TODO: May need to destroy secrets first.
kubectl create secret generic cloudsql-instance-credentials --from-file=credentials.json=/cloudsql/key.json
kubectl create secret generic moderator-configuration \
--from-literal=DATABASE_NAME=$DATABASE_NAME \
--from-literal=DATABASE_USER=$DATABASE_USER \
--from-literal=DATABASE_PASSWORD=$DATABASE_PASSWORD \
--from-literal=GOOGLE_SCORE_AUTH=$GOOGLE_SCORE_AUTH \
--from-literal=SQL_CONNECTION=$SQL_CONNECTION
envsubst < kubernetes-deployment.yaml | kubectl apply -f -
# TODO: Use a static IP address if one is allocated.
kubectl apply -f kubernetes-networking.yaml
================================================
FILE: deployments/gcloud/kubernetes-deployment.yaml
================================================
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: conversationai-moderator
labels:
app: conversationai-moderator
spec:
template:
metadata:
labels:
app: conversationai-moderator
spec:
containers:
- name: moderator
# TODO Need to replace this with image created by deployment script
image: ${MODERATOR_IMAGE_ID}:latest
ports:
- containerPort: 8000
hostPort: 80
- containerPort: 8080
hostPort: 8080
env:
- name: DATABASE_NAME
valueFrom:
secretKeyRef:
name: moderator-configuration
key: DATABASE_NAME
- name: DATABASE_USER
valueFrom:
secretKeyRef:
name: moderator-configuration
key: DATABASE_USER
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: moderator-configuration
key: DATABASE_PASSWORD
- name: GOOGLE_SCORE_AUTH
valueFrom:
secretKeyRef:
name: moderator-configuration
key: GOOGLE_SCORE_AUTH
- name: cloudsql-proxy
image: gcr.io/cloudsql-docker/gce-proxy:1.11
command: ["/cloud_sql_proxy"]
args: ["-instances=$(SQL_CONNECTION)=tcp:3306",
"-credential_file=/secrets/cloudsql/credentials.json"]
env:
- name: SQL_CONNECTION
valueFrom:
secretKeyRef:
name: moderator-configuration
key: SQL_CONNECTION
volumeMounts:
- name: cloudsql-instance-credentials
mountPath: /secrets/cloudsql
readOnly: true
- name: redis-server
image: launcher.gcr.io/google/redis3
volumes:
- name: cloudsql-instance-credentials
secret:
secretName: cloudsql-instance-credentials
================================================
FILE: deployments/gcloud/kubernetes-networking.yaml
================================================
apiVersion: v1
kind: Service
metadata:
name: moderator-networking
spec:
type: LoadBalancer
ports:
- name: frontend
port: 80
targetPort: 8000
protocol: TCP
- name: api
port: 8080
targetPort: 8080
protocol: TCP
selector:
app: conversationai-moderator
================================================
FILE: deployments/local/Dockerfile
================================================
FROM gcr.io/google_appengine/nodejs
RUN install_node v8.11.1 && apt update && apt dist-upgrade -y && apt install -y mysql-client
WORKDIR /app/
COPY . /app/
RUN npm cache verify && bin/install
EXPOSE 80 80
CMD bin/run
================================================
FILE: deployments/local/README.md
================================================
# Run application locally in a docker collection
The scripts and configuration files contained in this directory create 3 containers:
- A MySQL database container
- A Redis datastore container
- A container for everything else
The contents of the latter container are taken from the local filesystem.
You'll find details on how to create and use these containers in the
root [README.md](../../README.md)
================================================
FILE: deployments/local/docker-compose.yml
================================================
version: '3'
services:
database:
container_name: database
image: 'mysql:5.7.16'
volumes:
- './.data/db:/var/lib/mysql'
restart: always
environment:
MYSQL_ROOT_PASSWORD: "${DATABASE_PASSWORD}"
MYSQL_DATABASE: 'os_moderator'
MYSQL_USER: 'os_moderator'
MYSQL_PASSWORD: "${DATABASE_PASSWORD}"
ports:
- '3306:3306'
redis:
container_name: redis
image: 'redis:3.2.1'
ports:
- '6379:6379'
server:
build:
context: ../..
dockerfile: "deployments/local/Dockerfile"
environment:
DATABASE_HOST: database
DATABASE_NAME: 'os_moderator'
DATABASE_USER: 'os_moderator'
DATABASE_PASSWORD: "${DATABASE_PASSWORD}"
REDIS_URL: 'redis://redis:6379'
HTTPS_LINKS_ONLY: 'false'
APP_NAME: 'Moderator'
GOOGLE_CLOUD_API_KEY: "${GOOGLE_CLOUD_API_KEY}"
PORT: 80
ports:
- "80:80"
links:
- database
- redis
================================================
FILE: deployments/standalone/.dockerignore
================================================
.data
dist
dist-commonjs
node_modules
.vagrant
.dockerignore
Dockerfile
npm-debug.*
.git
.hg
.svn
config/local.json
================================================
FILE: deployments/standalone/Dockerfile
================================================
FROM ubuntu:bionic
RUN apt update && apt --assume-yes dist-upgrade && apt --assume-yes install mysql-server nodejs npm redis supervisor && npm install -g npm
ENV DATABASE_PASSWORD=$DATABASE_PASSWORD
WORKDIR /app
COPY . /app
RUN bin/install
RUN deployments/standalone/initialise_db.sh
================================================
FILE: deployments/standalone/initialise_db.sh
================================================
#!/bin/bash
mkdir -p /var/run/mysqld
chown mysql:mysql /var/run/mysqld/
/usr/bin/mysqld_safe --skip-grant-tables --pid-file=/run/mysqld/mysqld.pid &
sleep 5
mysql -u root << EOF
DELETE FROM mysql.user WHERE User='';
DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');
DROP DATABASE IF EXISTS test;
DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';
FLUSH PRIVILEGES;
CREATE DATABASE os_moderator;
CREATE USER 'os_moderator' IDENTIFIED BY '$DATABASE_PASSWORD';
GRANT ALL on os_moderator.* to os_moderator;
EOF
mysql os_moderator < seed/initial-database.sql
mysql -u root << EOF
UPDATE mysql.user SET Password=PASSWORD('$DATABASE_PASSWORD') WHERE User='root';
EOF
cd ${basename}/../packages/backend-api
npx sequelize db:migrate
cd -
================================================
FILE: design-files/Moderator-StickerSheet-20161117-DAS/document.json
================================================
{"_class":"document","do_objectID":"FDD7F69B-2AF0-4FAE-9B51-45D7040E8FCB","assets":{"_class":"assetCollection","colors":[],"gradients":[],"imageCollection":{"_class":"imageCollection","images":{}},"images":[]},"currentPageIndex":0,"enableLayerInteraction":true,"enableSliceInteraction":true,"foreignSymbols":[],"layerStyles":{"_class":"sharedStyleContainer","objects":[{"_class":"sharedStyle","do_objectID":"DC56376C-8162-4E24-BB3C-91C5B43AD324","name":"Material\/Icon dark","value":{"_class":"style","contextSettings":{"_class":"graphicsContextSettings","blendMode":0,"opacity":0.5399999618530273},"endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":0,"green":0,"red":0},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"sharedObjectID":"DC56376C-8162-4E24-BB3C-91C5B43AD324","startDecorationType":0}},{"_class":"sharedStyle","do_objectID":"E9914C62-FB70-43EA-B840-F1BE2DEEA401","name":"Material\/Light\/Menu","value":{"_class":"style","endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":0.9803921568627451,"green":0.9803921568627451,"red":0.9803921568627451},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"shadows":[{"_class":"shadow","isEnabled":true,"blurRadius":8,"color":{"_class":"color","alpha":0.24,"blue":0,"green":0,"red":0},"contextSettings":{"_class":"graphicsContextSettings","blendMode":0,"opacity":1},"offsetX":0,"offsetY":8,"spread":0},{"_class":"shadow","isEnabled":true,"blurRadius":8,"color":{"_class":"color","alpha":0.12,"blue":0,"green":0,"red":0},"contextSettings":{"_class":"graphicsContextSettings","blendMode":0,"opacity":1},"offsetX":0,"offsetY":0,"spread":0}],"sharedObjectID":"E9914C62-FB70-43EA-B840-F1BE2DEEA401","startDecorationType":0}},{"_class":"sharedStyle","do_objectID":"ACF14791-EE9C-41D8-9F0E-C0BD9795FFE9","name":"Material\/Light\/Dialog","value":{"_class":"style","borders":[{"_class":"border","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":0.592,"green":0.592,"red":0.592},"fillType":1,"gradient":{"_class":"gradient","elipseLength":1,"from":"{0.5, 0}","gradientType":0,"shouldSmoothenOpacity":false,"stops":[{"_class":"gradientStop","color":{"_class":"color","alpha":0,"blue":0,"green":0,"red":0},"position":0},{"_class":"gradientStop","color":{"_class":"color","alpha":0,"blue":0,"green":0,"red":0},"position":0.95},{"_class":"gradientStop","color":{"_class":"color","alpha":0.04,"blue":0,"green":0,"red":0},"position":1}],"to":"{0.5, 1}"},"position":1,"thickness":0.5},{"_class":"border","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":0.592,"green":0.592,"red":0.592},"contextSettings":{"_class":"graphicsContextSettings","blendMode":8,"opacity":1},"fillType":1,"gradient":{"_class":"gradient","elipseLength":1,"from":"{0.5, 0}","gradientType":0,"shouldSmoothenOpacity":false,"stops":[{"_class":"gradientStop","color":{"_class":"color","alpha":0.8,"blue":1,"green":1,"red":1},"position":0},{"_class":"gradientStop","color":{"_class":"color","alpha":0.4,"blue":1,"green":1,"red":1},"position":0.04936005799731481},{"_class":"gradientStop","color":{"_class":"color","alpha":0,"blue":1,"green":1,"red":1},"position":0.2},{"_class":"gradientStop","color":{"_class":"color","alpha":0,"blue":1,"green":1,"red":1},"position":1}],"to":"{0.5, 1}"},"position":1,"thickness":0.5}],"endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"shadows":[{"_class":"shadow","isEnabled":true,"blurRadius":24,"color":{"_class":"color","alpha":0.3,"blue":0,"green":0,"red":0},"contextSettings":{"_class":"graphicsContextSettings","blendMode":0,"opacity":1},"offsetX":0,"offsetY":24,"spread":0},{"_class":"shadow","isEnabled":true,"blurRadius":24,"color":{"_class":"color","alpha":0.22,"blue":0,"green":0,"red":0},"contextSettings":{"_class":"graphicsContextSettings","blendMode":0,"opacity":1},"offsetX":0,"offsetY":0,"spread":0}],"sharedObjectID":"ACF14791-EE9C-41D8-9F0E-C0BD9795FFE9","startDecorationType":0}}]},"layerSymbols":{"_class":"symbolContainer","objects":[]},"layerTextStyles":{"_class":"sharedTextStyleContainer","objects":[{"_class":"sharedStyle","do_objectID":"CB2CC42C-F552-418E-A76F-6D0EDA1AD3BF","name":"Section","value":{"_class":"style","endDecorationType":0,"miterLimit":10,"sharedObjectID":"CB2CC42C-F552-418E-A76F-6D0EDA1AD3BF","startDecorationType":0,"textStyle":{"_class":"textStyle","encodedAttributes":{"MSAttributedStringFontAttribute":{"_archive":"YnBsaXN0MDDUAQIDBAUGJidYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKkHCA0XGBkaGyJVJG51bGzSCQoLDFYkY2xhc3NfEBpOU0ZvbnREZXNjcmlwdG9yQXR0cmlidXRlc4AIgALTDg8JEBMWV05TLmtleXNaTlMub2JqZWN0c6IREoADgASiFBWABYAGgAdfEBNOU0ZvbnRTaXplQXR0cmlidXRlXxATTlNGb250TmFtZUF0dHJpYnV0ZSNALAAAAAAAAF8QGUlUQ0ZyYW5rbGluR290aGljU3RkLURlbWnSHB0eH1okY2xhc3NuYW1lWCRjbGFzc2VzXxATTlNNdXRhYmxlRGljdGlvbmFyeaMeICFcTlNEaWN0aW9uYXJ5WE5TT2JqZWN00hwdIyRfEBBOU0ZvbnREZXNjcmlwdG9yoiUhXxAQTlNGb250RGVzY3JpcHRvcl8QD05TS2V5ZWRBcmNoaXZlctEoKVRyb290gAEACAARABoAIwAtADIANwBBAEcATABTAHAAcgB0AHsAgwCOAJEAkwCVAJgAmgCcAJ4AtADKANMA7wD0AP8BCAEeASIBLwE4AT0BUAFTAWYBeAF7AYAAAAAAAAACAQAAAAAAAAAqAAAAAAAAAAAAAAAAAAABgg=="},"NSColor":{"_archive":"YnBsaXN0MDDUAQIDBAUGFRZYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKMHCA9VJG51bGzTCQoLDA0OVU5TUkdCXE5TQ29sb3JTcGFjZVYkY2xhc3NPECcwLjE0OTAxOTYwNzggMC4xNDkwMTk2MDc4IDAuMTQ5MDE5NjA3OAAQAYAC0hAREhNaJGNsYXNzbmFtZVgkY2xhc3Nlc1dOU0NvbG9yohIUWE5TT2JqZWN0XxAPTlNLZXllZEFyY2hpdmVy0RcYVHJvb3SAAQgRGiMtMjc7QUhOW2KMjpCVoKmxtL3P0tcAAAAAAAABAQAAAAAAAAAZAAAAAAAAAAAAAAAAAAAA2Q=="},"NSKern":0,"NSParagraphStyle":{"_archive":"YnBsaXN0MDDUAQIDBAUGIyRYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKUHCBUZH1UkbnVsbNYJCgsMDQ4PEBEQExBfEBJOU1BhcmFncmFwaFNwYWNpbmdaTlNUYWJTdG9wc18QEk5TV3JpdGluZ0RpcmVjdGlvblxOU1RleHRCbG9ja3NWJGNsYXNzW05TVGV4dExpc3RzI0AkAAAAAAAAgAIQAYACgASAAtIWDRcYWk5TLm9iamVjdHOggAPSGhscHVokY2xhc3NuYW1lWCRjbGFzc2VzV05TQXJyYXmiHB5YTlNPYmplY3TSGhsgIV8QF05TTXV0YWJsZVBhcmFncmFwaFN0eWxloyAiHl8QEE5TUGFyYWdyYXBoU3R5bGVfEA9OU0tleWVkQXJjaGl2ZXLRJSZUcm9vdIABAAgAEQAaACMALQAyADcAPQBDAFAAZQBwAIUAkgCZAKUArgCwALIAtAC2ALgAvQDIAMkAywDQANsA5ADsAO8A+AD9ARcBGwEuAUABQwFIAAAAAAAAAgEAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAUo="}}}}},{"_class":"sharedStyle","do_objectID":"D531A342-3DE7-4169-AF05-BE4F54B37453","name":"Article title","value":{"_class":"style","contextSettings":{"_class":"graphicsContextSettings","blendMode":0,"opacity":0.86},"endDecorationType":0,"miterLimit":10,"sharedObjectID":"D531A342-3DE7-4169-AF05-BE4F54B37453","startDecorationType":0,"textStyle":{"_class":"textStyle","encodedAttributes":{"MSAttributedStringFontAttribute":{"_archive":"YnBsaXN0MDDUAQIDBAUGJidYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKkHCA0XGBkaGyJVJG51bGzSCQoLDFYkY2xhc3NfEBpOU0ZvbnREZXNjcmlwdG9yQXR0cmlidXRlc4AIgALTDg8JEBMWV05TLmtleXNaTlMub2JqZWN0c6IREoADgASiFBWABYAGgAdfEBNOU0ZvbnRTaXplQXR0cmlidXRlXxATTlNGb250TmFtZUF0dHJpYnV0ZSNAMAAAAAAAAF8QD0NoZWx0ZW5oYW0tQm9va9IcHR4fWiRjbGFzc25hbWVYJGNsYXNzZXNfEBNOU011dGFibGVEaWN0aW9uYXJ5ox4gIVxOU0RpY3Rpb25hcnlYTlNPYmplY3TSHB0jJF8QEE5TRm9udERlc2NyaXB0b3KiJSFfEBBOU0ZvbnREZXNjcmlwdG9yXxAPTlNLZXllZEFyY2hpdmVy0SgpVHJvb3SAAQAIABEAGgAjAC0AMgA3AEEARwBMAFMAcAByAHQAewCDAI4AkQCTAJUAmACaAJwAngC0AMoA0wDlAOoA9QD+ARQBGAElAS4BMwFGAUkBXAFuAXEBdgAAAAAAAAIBAAAAAAAAACoAAAAAAAAAAAAAAAAAAAF4"},"NSStrokeWidth":0,"NSKern":0,"NSColor":{"_archive":"YnBsaXN0MDDUAQIDBAUGHyBYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKUHCBEVHFUkbnVsbNQJCgsMDQ4PEFVOU1JHQlxOU0NvbG9yU3BhY2VfEBJOU0N1c3RvbUNvbG9yU3BhY2VWJGNsYXNzRjAgMCAwABABgAKABNISDBMUVE5TSUQQAYAD0hYXGBlaJGNsYXNzbmFtZVgkY2xhc3Nlc1xOU0NvbG9yU3BhY2WiGhtcTlNDb2xvclNwYWNlWE5TT2JqZWN00hYXHR5XTlNDb2xvcqIdG18QD05TS2V5ZWRBcmNoaXZlctEhIlRyb290gAEACAARABoAIwAtADIANwA9AEMATABSAF8AdAB7AIIAhACGAIgAjQCSAJQAlgCbAKYArwC8AL8AzADVANoA4gDlAPcA+gD\/AAAAAAAAAgEAAAAAAAAAIwAAAAAAAAAAAAAAAAAAAQE="},"NSStrokeColor":{"_archive":"YnBsaXN0MDDUAQIDBAUGFRZYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKMHCA9VJG51bGzTCQoLDA0OVU5TUkdCXE5TQ29sb3JTcGFjZVYkY2xhc3NMMC42IDAuNiAwLjYAEAKAAtIQERITWiRjbGFzc25hbWVYJGNsYXNzZXNXTlNDb2xvcqISFFhOU09iamVjdF8QD05TS2V5ZWRBcmNoaXZlctEXGFRyb290gAEIERojLTI3O0FITltib3FzeIOMlJegsrW6AAAAAAAAAQEAAAAAAAAAGQAAAAAAAAAAAAAAAAAAALw="},"NSParagraphStyle":{"_archive":"YnBsaXN0MDDUAQIDBAUGJSZYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKUHCBcbIVUkbnVsbNgJCgsMDQ4PEBESExQVFhYVViRjbGFzc1pOU1RhYlN0b3BzXU5TSGVhZGVyTGV2ZWxbTlNUZXh0TGlzdHNfEB9OU0FsbG93c1RpZ2h0ZW5pbmdGb3JUcnVuY2F0aW9uXxAPTlNNYXhMaW5lSGVpZ2h0XxAPTlNNaW5MaW5lSGVpZ2h0XxASTlNXcml0aW5nRGlyZWN0aW9ugASAACNACAAAAAAAAIACEAEjQDgAAAAAAADSGAkZGlpOUy5vYmplY3RzoIAD0hwdHh9aJGNsYXNzbmFtZVgkY2xhc3Nlc1dOU0FycmF5oh4gWE5TT2JqZWN00hwdIiNfEBdOU011dGFibGVQYXJhZ3JhcGhTdHlsZaMiJCBfEBBOU1BhcmFncmFwaFN0eWxlXxAPTlNLZXllZEFyY2hpdmVy0ScoVHJvb3SAAQAIABEAGgAjAC0AMgA3AD0AQwBUAFsAZgB0AIAAogC0AMYA2wDdAN8A6ADqAOwA9QD6AQUBBgEIAQ0BGAEhASkBLAE1AToBVAFYAWsBfQGAAYUAAAAAAAACAQAAAAAAAAApAAAAAAAAAAAAAAAAAAABhw=="}}}}},{"_class":"sharedStyle","do_objectID":"C25F20A1-60B3-40B1-BCDE-FC0E9111F18D","name":"Hello Lucas! Style","value":{"_class":"style","endDecorationType":0,"miterLimit":10,"sharedObjectID":"C25F20A1-60B3-40B1-BCDE-FC0E9111F18D","startDecorationType":0,"textStyle":{"_class":"textStyle","encodedAttributes":{"MSAttributedStringFontAttribute":{"_archive":"YnBsaXN0MDDUAQIDBAUGJidYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKkHCA0XGBkaGyJVJG51bGzSCQoLDFYkY2xhc3NfEBpOU0ZvbnREZXNjcmlwdG9yQXR0cmlidXRlc4AIgALTDg8JEBMWV05TLmtleXNaTlMub2JqZWN0c6IREoADgASiFBWABYAGgAdfEBNOU0ZvbnRTaXplQXR0cmlidXRlXxATTlNGb250TmFtZUF0dHJpYnV0ZSNALAAAAAAAAF8QGUlUQ0ZyYW5rbGluR290aGljU3RkLURlbWnSHB0eH1okY2xhc3NuYW1lWCRjbGFzc2VzXxATTlNNdXRhYmxlRGljdGlvbmFyeaMeICFcTlNEaWN0aW9uYXJ5WE5TT2JqZWN00hwdIyRfEBBOU0ZvbnREZXNjcmlwdG9yoiUhXxAQTlNGb250RGVzY3JpcHRvcl8QD05TS2V5ZWRBcmNoaXZlctEoKVRyb290gAEACAARABoAIwAtADIANwBBAEcATABTAHAAcgB0AHsAgwCOAJEAkwCVAJgAmgCcAJ4AtADKANMA7wD0AP8BCAEeASIBLwE4AT0BUAFTAWYBeAF7AYAAAAAAAAACAQAAAAAAAAAqAAAAAAAAAAAAAAAAAAABgg=="},"NSKern":0,"NSColor":{"_archive":"YnBsaXN0MDDUAQIDBAUGHyBYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKUHCBEVHFUkbnVsbNQJCgsMDQ4PEFVOU1JHQlxOU0NvbG9yU3BhY2VfEBJOU0N1c3RvbUNvbG9yU3BhY2VWJGNsYXNzRjEgMSAxABABgAKABNISDBMUVE5TSUQQAYAD0hYXGBlaJGNsYXNzbmFtZVgkY2xhc3Nlc1xOU0NvbG9yU3BhY2WiGhtcTlNDb2xvclNwYWNlWE5TT2JqZWN00hYXHR5XTlNDb2xvcqIdG18QD05TS2V5ZWRBcmNoaXZlctEhIlRyb290gAEACAARABoAIwAtADIANwA9AEMATABSAF8AdAB7AIIAhACGAIgAjQCSAJQAlgCbAKYArwC8AL8AzADVANoA4gDlAPcA+gD\/AAAAAAAAAgEAAAAAAAAAIwAAAAAAAAAAAAAAAAAAAQE="},"NSParagraphStyle":{"_archive":"YnBsaXN0MDDUAQIDBAUGJSZYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKUHCBcbIVUkbnVsbNcJCgsMDQ4PEBESExQVFlYkY2xhc3NaTlNUYWJTdG9wc11OU0hlYWRlckxldmVsXxASTlNQYXJhZ3JhcGhTcGFjaW5nXxAPTlNNaW5MaW5lSGVpZ2h0XxASTlNXcml0aW5nRGlyZWN0aW9uW05TQWxpZ25tZW50gASAAiM\/8AAAAAAAACNAJAAAAAAAACNAPIAAAAAAABABEATSGAkZGlpOUy5vYmplY3RzoIAD0hwdHh9aJGNsYXNzbmFtZVgkY2xhc3Nlc1dOU0FycmF5oh4gWE5TT2JqZWN00hwdIiNfEBdOU011dGFibGVQYXJhZ3JhcGhTdHlsZaMiJCBfEBBOU1BhcmFncmFwaFN0eWxlXxAPTlNLZXllZEFyY2hpdmVy0ScoVHJvb3SAAQAIABEAGgAjAC0AMgA3AD0AQwBSAFkAZAByAIcAmQCuALoAvAC+AMcA0ADZANsA3QDiAO0A7gDwAPUBAAEJAREBFAEdASIBPAFAAVMBZQFoAW0AAAAAAAACAQAAAAAAAAApAAAAAAAAAAAAAAAAAAABbw=="}}}}},{"_class":"sharedStyle","do_objectID":"63446B2F-1571-456D-89FD-2A26EA213F12","name":"Material\/Dark\/Title","value":{"_class":"style","endDecorationType":0,"miterLimit":10,"sharedObjectID":"63446B2F-1571-456D-89FD-2A26EA213F12","startDecorationType":0,"textStyle":{"_class":"textStyle","encodedAttributes":{"MSAttributedStringFontAttribute":{"_archive":"YnBsaXN0MDDUAQIDBAUGJidYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKkHCA0XGBkaGyJVJG51bGzSCQoLDFYkY2xhc3NfEBpOU0ZvbnREZXNjcmlwdG9yQXR0cmlidXRlc4AIgALTDg8JEBMWV05TLmtleXNaTlMub2JqZWN0c6IREoADgASiFBWABYAGgAdfEBNOU0ZvbnRTaXplQXR0cmlidXRlXxATTlNGb250TmFtZUF0dHJpYnV0ZSNANAAAAAAAAF1Sb2JvdG8tTWVkaXVt0hwdHh9aJGNsYXNzbmFtZVgkY2xhc3Nlc18QE05TTXV0YWJsZURpY3Rpb25hcnmjHiAhXE5TRGljdGlvbmFyeVhOU09iamVjdNIcHSMkXxAQTlNGb250RGVzY3JpcHRvcqIlIV8QEE5TRm9udERlc2NyaXB0b3JfEA9OU0tleWVkQXJjaGl2ZXLRKClUcm9vdIABAAgAEQAaACMALQAyADcAQQBHAEwAUwBwAHIAdAB7AIMAjgCRAJMAlQCYAJoAnACeALQAygDTAOEA5gDxAPoBEAEUASEBKgEvAUIBRQFYAWoBbQFyAAAAAAAAAgEAAAAAAAAAKgAAAAAAAAAAAAAAAAAAAXQ="},"NSColor":{"_archive":"YnBsaXN0MDDUAQIDBAUGFRZYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKMHCA9VJG51bGzTCQoLDA0OVU5TUkdCXE5TQ29sb3JTcGFjZVYkY2xhc3NGMSAxIDEAEAGAAtIQERITWiRjbGFzc25hbWVYJGNsYXNzZXNXTlNDb2xvcqISFFhOU09iamVjdF8QD05TS2V5ZWRBcmNoaXZlctEXGFRyb290gAEIERojLTI3O0FITltiaWttcn2GjpGarK+0AAAAAAAAAQEAAAAAAAAAGQAAAAAAAAAAAAAAAAAAALY="},"NSLigature":0,"NSParagraphStyle":{"_archive":"YnBsaXN0MDDUAQIDBAUGIiNYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKUHCBQYHlUkbnVsbNYJCgsMDQ4PEBESExFbTlNBbGlnbm1lbnRaTlNUYWJTdG9wc18QD05TTWluTGluZUhlaWdodFtOU1RleHRMaXN0c1YkY2xhc3NfEA9OU01heExpbmVIZWlnaHQQBIAAI0A8AAAAAAAAgAKABNIVDRYXWk5TLm9iamVjdHOggAPSGRobHFokY2xhc3NuYW1lWCRjbGFzc2VzV05TQXJyYXmiGx1YTlNPYmplY3TSGRofIF8QF05TTXV0YWJsZVBhcmFncmFwaFN0eWxlox8hHV8QEE5TUGFyYWdyYXBoU3R5bGVfEA9OU0tleWVkQXJjaGl2ZXLRJCVUcm9vdIABAAgAEQAaACMALQAyADcAPQBDAFAAXABnAHkAhQCMAJ4AoACiAKsArQCvALQAvwDAAMIAxwDSANsA4wDmAO8A9AEOARIBJQE3AToBPwAAAAAAAAIBAAAAAAAAACYAAAAAAAAAAAAAAAAAAAFB"}}}}},{"_class":"sharedStyle","do_objectID":"69ED5B4A-408D-4DCD-A1CB-392D2A7E41BB","name":"Material\/Light\/Body 1 secondary","value":{"_class":"style","endDecorationType":0,"miterLimit":10,"sharedObjectID":"69ED5B4A-408D-4DCD-A1CB-392D2A7E41BB","startDecorationType":0,"textStyle":{"_class":"textStyle","encodedAttributes":{"MSAttributedStringFontAttribute":{"_archive":"YnBsaXN0MDDUAQIDBAUGJidYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKkHCA0XGBkaGyJVJG51bGzSCQoLDFYkY2xhc3NfEBpOU0ZvbnREZXNjcmlwdG9yQXR0cmlidXRlc4AIgALTDg8JEBMWV05TLmtleXNaTlMub2JqZWN0c6IREoADgASiFBWABYAGgAdfEBNOU0ZvbnRTaXplQXR0cmlidXRlXxATTlNGb250TmFtZUF0dHJpYnV0ZSNALAAAAAAAAF5Sb2JvdG8tUmVndWxhctIcHR4fWiRjbGFzc25hbWVYJGNsYXNzZXNfEBNOU011dGFibGVEaWN0aW9uYXJ5ox4gIVxOU0RpY3Rpb25hcnlYTlNPYmplY3TSHB0jJF8QEE5TRm9udERlc2NyaXB0b3KiJSFfEBBOU0ZvbnREZXNjcmlwdG9yXxAPTlNLZXllZEFyY2hpdmVy0SgpVHJvb3SAAQAIABEAGgAjAC0AMgA3AEEARwBMAFMAcAByAHQAewCDAI4AkQCTAJUAmACaAJwAngC0AMoA0wDiAOcA8gD7AREBFQEiASsBMAFDAUYBWQFrAW4BcwAAAAAAAAIBAAAAAAAAACoAAAAAAAAAAAAAAAAAAAF1"},"NSColor":{"_archive":"YnBsaXN0MDDUAQIDBAUGFRZYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKMHCA9VJG51bGzTCQoLDA0OVU5TUkdCXE5TQ29sb3JTcGFjZVYkY2xhc3NPEBMwIDAgMCAwLjU0Mzg0NjI0MDkAEAGAAtIQERITWiRjbGFzc25hbWVYJGNsYXNzZXNXTlNDb2xvcqISFFhOU09iamVjdF8QD05TS2V5ZWRBcmNoaXZlctEXGFRyb290gAEIERojLTI3O0FITltieHp8gYyVnaCpu77DAAAAAAAAAQEAAAAAAAAAGQAAAAAAAAAAAAAAAAAAAMU="},"NSLigature":0,"NSParagraphStyle":{"_archive":"YnBsaXN0MDDUAQIDBAUGIiNYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKUHCBQYHlUkbnVsbNYJCgsMDQ4PEBESExFbTlNBbGlnbm1lbnRaTlNUYWJTdG9wc18QD05TTWluTGluZUhlaWdodFtOU1RleHRMaXN0c1YkY2xhc3NfEA9OU01heExpbmVIZWlnaHQQBIAAI0A0AAAAAAAAgAKABNIVDRYXWk5TLm9iamVjdHOggAPSGRobHFokY2xhc3NuYW1lWCRjbGFzc2VzV05TQXJyYXmiGx1YTlNPYmplY3TSGRofIF8QF05TTXV0YWJsZVBhcmFncmFwaFN0eWxlox8hHV8QEE5TUGFyYWdyYXBoU3R5bGVfEA9OU0tleWVkQXJjaGl2ZXLRJCVUcm9vdIABAAgAEQAaACMALQAyADcAPQBDAFAAXABnAHkAhQCMAJ4AoACiAKsArQCvALQAvwDAAMIAxwDSANsA4wDmAO8A9AEOARIBJQE3AToBPwAAAAAAAAIBAAAAAAAAACYAAAAAAAAAAAAAAAAAAAFB"}}}}},{"_class":"sharedStyle","do_objectID":"AB229DCC-1C49-4B7D-8079-F9BA9EBB787A","name":"Material\/Desktop\/Light\/Subhead","value":{"_class":"style","endDecorationType":0,"miterLimit":10,"sharedObjectID":"AB229DCC-1C49-4B7D-8079-F9BA9EBB787A","startDecorationType":0,"textStyle":{"_class":"textStyle","encodedAttributes":{"MSAttributedStringFontAttribute":{"_archive":"YnBsaXN0MDDUAQIDBAUGJidYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKkHCA0XGBkaGyJVJG51bGzSCQoLDFYkY2xhc3NfEBpOU0ZvbnREZXNjcmlwdG9yQXR0cmlidXRlc4AIgALTDg8JEBMWV05TLmtleXNaTlMub2JqZWN0c6IREoADgASiFBWABYAGgAdfEBNOU0ZvbnRTaXplQXR0cmlidXRlXxATTlNGb250TmFtZUF0dHJpYnV0ZSNALgAAAAAAAF5Sb2JvdG8tUmVndWxhctIcHR4fWiRjbGFzc25hbWVYJGNsYXNzZXNfEBNOU011dGFibGVEaWN0aW9uYXJ5ox4gIVxOU0RpY3Rpb25hcnlYTlNPYmplY3TSHB0jJF8QEE5TRm9udERlc2NyaXB0b3KiJSFfEBBOU0ZvbnREZXNjcmlwdG9yXxAPTlNLZXllZEFyY2hpdmVy0SgpVHJvb3SAAQAIABEAGgAjAC0AMgA3AEEARwBMAFMAcAByAHQAewCDAI4AkQCTAJUAmACaAJwAngC0AMoA0wDiAOcA8gD7AREBFQEiASsBMAFDAUYBWQFrAW4BcwAAAAAAAAIBAAAAAAAAACoAAAAAAAAAAAAAAAAAAAF1"},"NSColor":{"_archive":"YnBsaXN0MDDUAQIDBAUGFRZYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKMHCA9VJG51bGzTCQoLDA0OVU5TUkdCXE5TQ29sb3JTcGFjZVYkY2xhc3NLMCAwIDAgMC44NwAQAYAC0hAREhNaJGNsYXNzbmFtZVgkY2xhc3Nlc1dOU0NvbG9yohIUWE5TT2JqZWN0XxAPTlNLZXllZEFyY2hpdmVy0RcYVHJvb3SAAQgRGiMtMjc7QUhOW2JucHJ3gouTlp+xtLkAAAAAAAABAQAAAAAAAAAZAAAAAAAAAAAAAAAAAAAAuw=="},"NSLigature":0,"NSParagraphStyle":{"_archive":"YnBsaXN0MDDUAQIDBAUGIiNYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKUHCBQYHlUkbnVsbNYJCgsMDQ4PEBESExFbTlNBbGlnbm1lbnRaTlNUYWJTdG9wc18QD05TTWluTGluZUhlaWdodFtOU1RleHRMaXN0c1YkY2xhc3NfEA9OU01heExpbmVIZWlnaHQQBIAAI0A0AAAAAAAAgAKABNIVDRYXWk5TLm9iamVjdHOggAPSGRobHFokY2xhc3NuYW1lWCRjbGFzc2VzV05TQXJyYXmiGx1YTlNPYmplY3TSGRofIF8QF05TTXV0YWJsZVBhcmFncmFwaFN0eWxlox8hHV8QEE5TUGFyYWdyYXBoU3R5bGVfEA9OU0tleWVkQXJjaGl2ZXLRJCVUcm9vdIABAAgAEQAaACMALQAyADcAPQBDAFAAXABnAHkAhQCMAJ4AoACiAKsArQCvALQAvwDAAMIAxwDSANsA4wDmAO8A9AEOARIBJQE3AToBPwAAAAAAAAIBAAAAAAAAACYAAAAAAAAAAAAAAAAAAAFB"}}}}},{"_class":"sharedStyle","do_objectID":"E565A9CE-412B-4E07-B11D-9E9DB95D0595","name":"Material\/Light\/Title","value":{"_class":"style","endDecorationType":0,"miterLimit":10,"sharedObjectID":"E565A9CE-412B-4E07-B11D-9E9DB95D0595","startDecorationType":0,"textStyle":{"_class":"textStyle","encodedAttributes":{"MSAttributedStringFontAttribute":{"_archive":"YnBsaXN0MDDUAQIDBAUGJidYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKkHCA0XGBkaGyJVJG51bGzSCQoLDFYkY2xhc3NfEBpOU0ZvbnREZXNjcmlwdG9yQXR0cmlidXRlc4AIgALTDg8JEBMWV05TLmtleXNaTlMub2JqZWN0c6IREoADgASiFBWABYAGgAdfEBNOU0ZvbnRTaXplQXR0cmlidXRlXxATTlNGb250TmFtZUF0dHJpYnV0ZSNANAAAAAAAAF1Sb2JvdG8tTWVkaXVt0hwdHh9aJGNsYXNzbmFtZVgkY2xhc3Nlc18QE05TTXV0YWJsZURpY3Rpb25hcnmjHiAhXE5TRGljdGlvbmFyeVhOU09iamVjdNIcHSMkXxAQTlNGb250RGVzY3JpcHRvcqIlIV8QEE5TRm9udERlc2NyaXB0b3JfEA9OU0tleWVkQXJjaGl2ZXLRKClUcm9vdIABAAgAEQAaACMALQAyADcAQQBHAEwAUwBwAHIAdAB7AIMAjgCRAJMAlQCYAJoAnACeALQAygDTAOEA5gDxAPoBEAEUASEBKgEvAUIBRQFYAWoBbQFyAAAAAAAAAgEAAAAAAAAAKgAAAAAAAAAAAAAAAAAAAXQ="},"NSKern":0,"NSColor":{"_archive":"YnBsaXN0MDDUAQIDBAUGFRZYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKMHCA9VJG51bGzTCQoLDA0OVU5TUkdCXE5TQ29sb3JTcGFjZVYkY2xhc3NLMCAwIDAgMC44NwAQAYAC0hAREhNaJGNsYXNzbmFtZVgkY2xhc3Nlc1dOU0NvbG9yohIUWE5TT2JqZWN0XxAPTlNLZXllZEFyY2hpdmVy0RcYVHJvb3SAAQgRGiMtMjc7QUhOW2JucHJ3gouTlp+xtLkAAAAAAAABAQAAAAAAAAAZAAAAAAAAAAAAAAAAAAAAuw=="},"NSLigature":0,"NSParagraphStyle":{"_archive":"YnBsaXN0MDDUAQIDBAUGIiNYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKUHCBQYHlUkbnVsbNYJCgsMDQ4PEBESExFbTlNBbGlnbm1lbnRaTlNUYWJTdG9wc18QD05TTWluTGluZUhlaWdodFtOU1RleHRMaXN0c1YkY2xhc3NfEA9OU01heExpbmVIZWlnaHQQBIAAI0A8AAAAAAAAgAKABNIVDRYXWk5TLm9iamVjdHOggAPSGRobHFokY2xhc3NuYW1lWCRjbGFzc2VzV05TQXJyYXmiGx1YTlNPYmplY3TSGRofIF8QF05TTXV0YWJsZVBhcmFncmFwaFN0eWxlox8hHV8QEE5TUGFyYWdyYXBoU3R5bGVfEA9OU0tleWVkQXJjaGl2ZXLRJCVUcm9vdIABAAgAEQAaACMALQAyADcAPQBDAFAAXABnAHkAhQCMAJ4AoACiAKsArQCvALQAvwDAAMIAxwDSANsA4wDmAO8A9AEOARIBJQE3AToBPwAAAAAAAAIBAAAAAAAAACYAAAAAAAAAAAAAAAAAAAFB"}}}}},{"_class":"sharedStyle","do_objectID":"12DAF224-DCC7-475F-B21D-E6E75E563E9A","name":"Material\/Light\/Subhead secondary","value":{"_class":"style","endDecorationType":0,"miterLimit":10,"sharedObjectID":"12DAF224-DCC7-475F-B21D-E6E75E563E9A","startDecorationType":0,"textStyle":{"_class":"textStyle","encodedAttributes":{"MSAttributedStringFontAttribute":{"_archive":"YnBsaXN0MDDUAQIDBAUGJidYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKkHCA0XGBkaGyJVJG51bGzSCQoLDFYkY2xhc3NfEBpOU0ZvbnREZXNjcmlwdG9yQXR0cmlidXRlc4AIgALTDg8JEBMWV05TLmtleXNaTlMub2JqZWN0c6IREoADgASiFBWABYAGgAdfEBNOU0ZvbnRTaXplQXR0cmlidXRlXxATTlNGb250TmFtZUF0dHJpYnV0ZSNAMAAAAAAAAF5Sb2JvdG8tUmVndWxhctIcHR4fWiRjbGFzc25hbWVYJGNsYXNzZXNfEBNOU011dGFibGVEaWN0aW9uYXJ5ox4gIVxOU0RpY3Rpb25hcnlYTlNPYmplY3TSHB0jJF8QEE5TRm9udERlc2NyaXB0b3KiJSFfEBBOU0ZvbnREZXNjcmlwdG9yXxAPTlNLZXllZEFyY2hpdmVy0SgpVHJvb3SAAQAIABEAGgAjAC0AMgA3AEEARwBMAFMAcAByAHQAewCDAI4AkQCTAJUAmACaAJwAngC0AMoA0wDiAOcA8gD7AREBFQEiASsBMAFDAUYBWQFrAW4BcwAAAAAAAAIBAAAAAAAAACoAAAAAAAAAAAAAAAAAAAF1"},"NSParagraphStyle":{"_archive":"YnBsaXN0MDDUAQIDBAUGICFYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKUHCBIWHFUkbnVsbNUJCgsMDQ4PEBEQWk5TVGFiU3RvcHNbTlNUZXh0TGlzdHNfEA9OU01pbkxpbmVIZWlnaHRWJGNsYXNzXxAPTlNNYXhMaW5lSGVpZ2h0gACAAiNAOAAAAAAAAIAE0hMMFBVaTlMub2JqZWN0c6CAA9IXGBkaWiRjbGFzc25hbWVYJGNsYXNzZXNXTlNBcnJheaIZG1hOU09iamVjdNIXGB0eXxAXTlNNdXRhYmxlUGFyYWdyYXBoU3R5bGWjHR8bXxAQTlNQYXJhZ3JhcGhTdHlsZV8QD05TS2V5ZWRBcmNoaXZlctEiI1Ryb290gAEACAARABoAIwAtADIANwA9AEMATgBZAGUAdwB+AJAAkgCUAJ0AnwCkAK8AsACyALcAwgDLANMA1gDfAOQA\/gECARUBJwEqAS8AAAAAAAACAQAAAAAAAAAkAAAAAAAAAAAAAAAAAAABMQ=="},"NSLigature":0,"NSColor":{"_archive":"YnBsaXN0MDDUAQIDBAUGFRZYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKMHCA9VJG51bGzTCQoLDA0OVU5TUkdCXE5TQ29sb3JTcGFjZVYkY2xhc3NPEBMwIDAgMCAwLjU0MTMyNjk5MjgAEAGAAtIQERITWiRjbGFzc25hbWVYJGNsYXNzZXNXTlNDb2xvcqISFFhOU09iamVjdF8QD05TS2V5ZWRBcmNoaXZlctEXGFRyb290gAEIERojLTI3O0FITltieHp8gYyVnaCpu77DAAAAAAAAAQEAAAAAAAAAGQAAAAAAAAAAAAAAAAAAAMU="}}}}},{"_class":"sharedStyle","do_objectID":"9779A83F-D1E9-4FFE-800D-8152982B444C","name":"Material\/Light\/Subhead","value":{"_class":"style","endDecorationType":0,"miterLimit":10,"sharedObjectID":"9779A83F-D1E9-4FFE-800D-8152982B444C","startDecorationType":0,"textStyle":{"_class":"textStyle","encodedAttributes":{"MSAttributedStringFontAttribute":{"_archive":"YnBsaXN0MDDUAQIDBAUGJidYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKkHCA0XGBkaGyJVJG51bGzSCQoLDFYkY2xhc3NfEBpOU0ZvbnREZXNjcmlwdG9yQXR0cmlidXRlc4AIgALTDg8JEBMWV05TLmtleXNaTlMub2JqZWN0c6IREoADgASiFBWABYAGgAdfEBNOU0ZvbnRTaXplQXR0cmlidXRlXxATTlNGb250TmFtZUF0dHJpYnV0ZSNAMAAAAAAAAF5Sb2JvdG8tUmVndWxhctIcHR4fWiRjbGFzc25hbWVYJGNsYXNzZXNfEBNOU011dGFibGVEaWN0aW9uYXJ5ox4gIVxOU0RpY3Rpb25hcnlYTlNPYmplY3TSHB0jJF8QEE5TRm9udERlc2NyaXB0b3KiJSFfEBBOU0ZvbnREZXNjcmlwdG9yXxAPTlNLZXllZEFyY2hpdmVy0SgpVHJvb3SAAQAIABEAGgAjAC0AMgA3AEEARwBMAFMAcAByAHQAewCDAI4AkQCTAJUAmACaAJwAngC0AMoA0wDiAOcA8gD7AREBFQEiASsBMAFDAUYBWQFrAW4BcwAAAAAAAAIBAAAAAAAAACoAAAAAAAAAAAAAAAAAAAF1"},"NSColor":{"_archive":"YnBsaXN0MDDUAQIDBAUGFRZYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKMHCA9VJG51bGzTCQoLDA0OVU5TUkdCXE5TQ29sb3JTcGFjZVYkY2xhc3NLMCAwIDAgMC44NwAQAYAC0hAREhNaJGNsYXNzbmFtZVgkY2xhc3Nlc1dOU0NvbG9yohIUWE5TT2JqZWN0XxAPTlNLZXllZEFyY2hpdmVy0RcYVHJvb3SAAQgRGiMtMjc7QUhOW2JucHJ3gouTlp+xtLkAAAAAAAABAQAAAAAAAAAZAAAAAAAAAAAAAAAAAAAAuw=="},"NSLigature":0,"NSParagraphStyle":{"_archive":"YnBsaXN0MDDUAQIDBAUGIiNYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKUHCBQYHlUkbnVsbNYJCgsMDQ4PEBESExFbTlNBbGlnbm1lbnRaTlNUYWJTdG9wc18QD05TTWluTGluZUhlaWdodFtOU1RleHRMaXN0c1YkY2xhc3NfEA9OU01heExpbmVIZWlnaHQQBIAAI0A4AAAAAAAAgAKABNIVDRYXWk5TLm9iamVjdHOggAPSGRobHFokY2xhc3NuYW1lWCRjbGFzc2VzV05TQXJyYXmiGx1YTlNPYmplY3TSGRofIF8QF05TTXV0YWJsZVBhcmFncmFwaFN0eWxlox8hHV8QEE5TUGFyYWdyYXBoU3R5bGVfEA9OU0tleWVkQXJjaGl2ZXLRJCVUcm9vdIABAAgAEQAaACMALQAyADcAPQBDAFAAXABnAHkAhQCMAJ4AoACiAKsArQCvALQAvwDAAMIAxwDSANsA4wDmAO8A9AEOARIBJQE3AToBPwAAAAAAAAIBAAAAAAAAACYAAAAAAAAAAAAAAAAAAAFB"}}}}}]},"pages":[{"_class":"MSJSONFileReference","_ref_class":"MSImmutablePage","_ref":"pages\/226D6615-C16F-4B84-92E0-291B2F1B15C4"},{"_class":"MSJSONFileReference","_ref_class":"MSImmutablePage","_ref":"pages\/1B67083D-3430-4865-A36A-6C687A1EEB45"}]}
================================================
FILE: design-files/Moderator-StickerSheet-20161117-DAS/meta.json
================================================
{"commit":"335a30073fcb2dc64a0abd6148ae147d694c887d","appVersion":"43.1","build":39012,"app":"com.bohemiancoding.sketch3","pagesAndArtboards":{"226D6615-C16F-4B84-92E0-291B2F1B15C4":{"name":"Page 1","artboards":{"13915B01-7A46-4D0E-BFEA-D8170EF6C531":{"name":"Tablet 9inch"}}},"1B67083D-3430-4865-A36A-6C687A1EEB45":{"name":"Symbols","artboards":{"DDB0E68B-BEEE-4E3A-9844-0443F2E09696":{"name":"Material\/Icons black\/refresh"},"34CA2EB5-EBA4-4D7A-9C5B-1EC39EA97E4F":{"name":"Material\/Icons black\/close"},"57C2B1F6-1718-4BD9-85D3-39BEFA28613B":{"name":"Material\/Icons white\/arrow back"},"7C5A0EC0-D963-4207-80CB-49ED115BF9B5":{"name":"Material\/Android\/Navbar 1024dp black"},"A0EAD81F-5486-461D-846C-2016D95CE579":{"name":"Material\/Icons white\/close"},"76C8A3F1-FA9D-4553-9035-374EEF18CBF2":{"name":"Material\/Icons black\/arrow back"},"E33E2440-9E0D-4801-A0C4-5F0D7AA40DE6":{"name":"Material\/Icons white\/more vert"},"10E7654C-E936-403F-9CEF-AA512F39B60F":{"name":"Material\/Icons white\/arrow drop down"},"8E30A0EB-7E44-41FB-B562-98898C86C5CF":{"name":"Material\/Icons black\/arrow drop up"},"D284E6EB-A900-4B52-B3A1-0461AFBE8EFD":{"name":"Material\/Android\/Status bar 1024dp black"},"D11A2A32-7CF0-4C46-B91D-9FE1B6C7CBB3":{"name":"Material\/Icons white\/search"},"21B63B46-4C5E-4D74-9E80-2B8CCF870C32":{"name":"Material\/Icons black\/check"},"CA799155-0C43-4E3B-8162-8521128A451A":{"name":"Material\/Icons black\/more vert"},"3D9F5A8F-3429-4C85-881D-96C7B13601E9":{"name":"Material\/Android\/Status bar content light"}}}},"fonts":["ITCFranklinGothicStd-Med","ITCFranklinGothicStd-Book","ITCFranklinGothicStd-Hvy",".SFNSText","CheltenhamStd-Book","ITCFranklinGothicStd-Demi","Roboto-Medium","Cheltenham-Book"],"created":{"app":"com.bohemiancoding.sketch3","commit":"566dab740f05650c87722a04bbcb154884097f92","version":87,"appVersion":"42","variant":"NONAPPSTORE","build":36781},"version":88,"saveHistory":["NONAPPSTORE.36781","NONAPPSTORE.39012"],"autosaved":0,"variant":"NONAPPSTORE"}
================================================
FILE: design-files/Moderator-StickerSheet-20161117-DAS/pages/1B67083D-3430-4865-A36A-6C687A1EEB45.json
================================================
{"_class":"page","do_objectID":"1B67083D-3430-4865-A36A-6C687A1EEB45","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":300,"width":300,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Symbols","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"miterLimit":10,"startDecorationType":0},"hasClickThrough":true,"layers":[{"_class":"symbolMaster","do_objectID":"3D9F5A8F-3429-4C85-881D-96C7B13601E9","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":24,"width":118,"x":100,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":1,"name":"Material\/Android\/Status bar content light","nameIsFixed":true,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"fills":[{"_class":"fill","do_objectID":"B99E6927-021E-45DC-B685-DA1D14859377","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":1,"patternTileScale":1}],"miterLimit":10,"startDecorationType":0},"hasClickThrough":true,"layers":[{"_class":"group","do_objectID":"07EA6ABD-426B-469E-85BA-0D2542F1EB5B","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":16,"width":36,"x":74,"y":4},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"time","nameIsFixed":true,"originalObjectID":"07EA6ABD-426B-469E-85BA-0D2542F1EB5B","resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","contextSettings":{"_class":"graphicsContextSettings","blendMode":0,"opacity":0.9},"endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":0,"green":0,"red":0},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"text","do_objectID":"2851D932-9391-44B6-BA6A-55668EC918E6","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":16,"width":36,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"12:30","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"startDecorationType":0,"textStyle":{"_class":"textStyle","encodedAttributes":{"NSLigature":0,"MSAttributedStringFontAttribute":{"_archive":"YnBsaXN0MDDUAQIDBAUGJidYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKkHCA0XGBkaGyJVJG51bGzSCQoLDFYkY2xhc3NfEBpOU0ZvbnREZXNjcmlwdG9yQXR0cmlidXRlc4AIgALTDg8JEBMWV05TLmtleXNaTlMub2JqZWN0c6IREoADgASiFBWABYAGgAdfEBNOU0ZvbnRTaXplQXR0cmlidXRlXxATTlNGb250TmFtZUF0dHJpYnV0ZSNALAAAAAAAAF1Sb2JvdG8tTWVkaXVt0hwdHh9aJGNsYXNzbmFtZVgkY2xhc3Nlc18QE05TTXV0YWJsZURpY3Rpb25hcnmjHiAhXE5TRGljdGlvbmFyeVhOU09iamVjdNIcHSMkXxAQTlNGb250RGVzY3JpcHRvcqIlIV8QEE5TRm9udERlc2NyaXB0b3JfEA9OU0tleWVkQXJjaGl2ZXLRKClUcm9vdIABAAgAEQAaACMALQAyADcAQQBHAEwAUwBwAHIAdAB7AIMAjgCRAJMAlQCYAJoAnACeALQAygDTAOEA5gDxAPoBEAEUASEBKgEvAUIBRQFYAWoBbQFyAAAAAAAAAgEAAAAAAAAAKgAAAAAAAAAAAAAAAAAAAXQ="},"NSParagraphStyle":{"_archive":"YnBsaXN0MDDUAQIDBAUGFhdYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKMHCA9VJG51bGzTCQoLDA0OWk5TVGFiU3RvcHNWJGNsYXNzW05TQWxpZ25tZW50gACAAhAB0hAREhNaJGNsYXNzbmFtZVgkY2xhc3Nlc18QF05TTXV0YWJsZVBhcmFncmFwaFN0eWxloxIUFV8QEE5TUGFyYWdyYXBoU3R5bGVYTlNPYmplY3RfEA9OU0tleWVkQXJjaGl2ZXLRGBlUcm9vdIABCBEaIy0yNztBSFNaZmhqbHF8hZ+jtr\/R1NkAAAAAAAABAQAAAAAAAAAaAAAAAAAAAAAAAAAAAAAA2w=="}}}},"attributedString":{"_class":"MSAttributedString","archivedAttributedString":{"_archive":"YnBsaXN0MDDUAQIDBAUGS0xYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoK8QFAcIDxAcHR4fICQsLS4vMDc7QUVHVSRudWxs0wkKCwwNDlhOU1N0cmluZ1YkY2xhc3NcTlNBdHRyaWJ1dGVzgAKAE4ADVTEyOjMw0xESChMXG1dOUy5rZXlzWk5TLm9iamVjdHOjFBUWgASABYAGoxgZGoAHgAiAEIASWk5TTGlnYXR1cmVfEB9NU0F0dHJpYnV0ZWRTdHJpbmdGb250QXR0cmlidXRlXxAQTlNQYXJhZ3JhcGhTdHlsZRAA0gohIiNfEBpOU0ZvbnREZXNjcmlwdG9yQXR0cmlidXRlc4APgAnTERIKJSgroiYngAqAC6IpKoAMgA2ADl8QE05TRm9udFNpemVBdHRyaWJ1dGVfEBNOU0ZvbnROYW1lQXR0cmlidXRlI0AsAAAAAAAAXVJvYm90by1NZWRpdW3SMTIzNFokY2xhc3NuYW1lWCRjbGFzc2VzXxATTlNNdXRhYmxlRGljdGlvbmFyeaMzNTZcTlNEaWN0aW9uYXJ5WE5TT2JqZWN00jEyODlfEBBOU0ZvbnREZXNjcmlwdG9yojo2XxAQTlNGb250RGVzY3JpcHRvctM8Cj0+P0BaTlNUYWJTdG9wc1tOU0FsaWdubWVudIAAgBEQAdIxMkJDXxAXTlNNdXRhYmxlUGFyYWdyYXBoU3R5bGWjQkQ2XxAQTlNQYXJhZ3JhcGhTdHlsZdIxMjVGojU20jEySElfEBJOU0F0dHJpYnV0ZWRTdHJpbmeiSjZfEBJOU0F0dHJpYnV0ZWRTdHJpbmdfEA9OU0tleWVkQXJjaGl2ZXLRTU5Ucm9vdIABAAgAEQAaACMALQAyADcATgBUAFsAZABrAHgAegB8AH4AhACLAJMAngCiAKQApgCoAKwArgCwALIAtAC\/AOEA9AD2APsBGAEaARwBIwEmASgBKgEtAS8BMQEzAUkBXwFoAXYBewGGAY8BpQGpAbYBvwHEAdcB2gHtAfQB\/wILAg0CDwIRAhYCMAI0AkcCTAJPAlQCaQJsAoECkwKWApsAAAAAAAACAQAAAAAAAABPAAAAAAAAAAAAAAAAAAACnQ=="}},"automaticallyDrawOnUnderlyingPath":false,"dontSynchroniseWithSymbol":false,"glyphBounds":"{{0, 0}, {36, 16.40625}}","heightIsClipped":false,"lineSpacingBehaviour":0,"textBehaviour":0}]},{"_class":"group","do_objectID":"42D99C57-EF76-4D8A-928A-44B249108E0B","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":true,"height":16,"width":16,"x":55,"y":4},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"battery","nameIsFixed":true,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":0,"green":0,"red":0},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"shapeGroup","do_objectID":"948320AB-B4A4-4749-8445-86978E2DC560","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":16,"width":16,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"bounds","nameIsFixed":true,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"miterLimit":10,"startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"shapePath","do_objectID":"79A9EE07-A92C-4BE1-BE3A-2762F4D51368","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":16,"width":16,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":true,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0}","curveMode":1,"curveTo":"{0, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0}","curveMode":1,"curveTo":"{1, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 1}","curveMode":1,"curveTo":"{1, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 1}","curveMode":1,"curveTo":"{0, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 1}"}]}}],"clippingMaskMode":0,"hasClippingMask":false,"windingRule":1},{"_class":"shapeGroup","do_objectID":"FE57A042-EDF4-4C50-82B0-380EF7B28AC3","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":14,"width":9,"x":3,"y":1},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Shape","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"shapePath","do_objectID":"9DEEA577-F6E6-47F4-8439-C2A4A0045478","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":14,"width":9,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":true,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.72222222222222221, 0.12337662337662333}","curveMode":1,"curveTo":"{0.72222222222222221, 0.12337662337662333}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.66666666666666663, 0.062500000000000014}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.72222222222222221, 2.6404969480761258e-16}","curveMode":1,"curveTo":"{0.72222222222222221, 2.6404969480761258e-16}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.66666666666666663, 2.6962559169468085e-16}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.33333333333333331, 7.9301644616082606e-18}","curveMode":1,"curveTo":"{0.33333333333333331, 7.9301644616082606e-18}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.33333333333333331, 7.9301644616082606e-18}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.33333333333333331, 0.12337662337662332}","curveMode":1,"curveTo":"{0.33333333333333331, 0.12337662337662332}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.33333333333333331, 0.0625}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0.1194805194805194}","curveMode":1,"curveTo":"{0, 0.047402597402597425}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 0.0625}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 1}","curveMode":1,"curveTo":"{0, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 1}","curveMode":1,"curveTo":"{1, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0.047402597402597377}","curveMode":1,"curveTo":"{1, 0.11948051948051941}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 0.0625}"}]}}],"clippingMaskMode":0,"hasClippingMask":false,"windingRule":1}]},{"_class":"group","do_objectID":"3DA9B3DF-5C04-4A39-9C53-9969BD080CF8","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":true,"height":16,"width":16,"x":35,"y":4},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"cellular","nameIsFixed":true,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":0,"green":0,"red":0},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"shapeGroup","do_objectID":"9CE34C1D-0979-49AC-9C7A-5CC1C8AB236B","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":16,"width":16,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"bounds","nameIsFixed":true,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"miterLimit":10,"startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"shapePath","do_objectID":"167CE758-4A23-4C76-9056-9B8685747098","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":16,"width":16,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":true,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0}","curveMode":1,"curveTo":"{0, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0}","curveMode":1,"curveTo":"{1, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 1}","curveMode":1,"curveTo":"{1, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 1}","curveMode":1,"curveTo":"{0, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 1}"}]}}],"clippingMaskMode":0,"hasClippingMask":false,"windingRule":1},{"_class":"shapeGroup","do_objectID":"772A4B91-3052-43B7-9601-9EEC2B2FEAE6","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":true,"height":14,"width":14,"x":0,"y":1},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Shape","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"shapePath","do_objectID":"224429C0-2948-4BCB-8BEF-1562B4041907","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":14,"width":14,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":true,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 1}","curveMode":1,"curveTo":"{0, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 1}","curveMode":1,"curveTo":"{1, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0}","curveMode":1,"curveTo":"{1, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 0}"}]}}],"clippingMaskMode":0,"hasClippingMask":false,"windingRule":1}]},{"_class":"group","do_objectID":"A33967C6-E840-423D-ABC0-352996D1DAEC","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":true,"height":16,"width":20,"x":14,"y":4},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"wifi","nameIsFixed":true,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":0,"green":0,"red":0},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"shapeGroup","do_objectID":"DA3E3AFB-0BCC-4441-8062-925273087AF6","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":16,"width":16,"x":2,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"bounds","nameIsFixed":true,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"miterLimit":10,"startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"shapePath","do_objectID":"D15D00AC-B194-4DEB-807C-B29A7A0C30D1","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":16,"width":16,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":true,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0}","curveMode":1,"curveTo":"{0, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0}","curveMode":1,"curveTo":"{1, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 1}","curveMode":1,"curveTo":"{1, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 1}","curveMode":1,"curveTo":"{0, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 1}"}]}}],"clippingMaskMode":0,"hasClippingMask":false,"windingRule":1},{"_class":"shapeGroup","do_objectID":"9DCE5055-9A08-4B23-B1D5-943FB838D12C","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":true,"height":14,"width":18.0452558296814,"x":0.9773720851803773,"y":1},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Shape","nameIsFixed":true,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"oval","do_objectID":"17C69D78-7C2A-46AF-B6C5-2106D448D51A","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":true,"height":30,"width":30,"x":-5.9773720851804,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":false,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.77614237490000004, 1}","curveMode":2,"curveTo":"{0.22385762510000001, 1}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0.5, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0.22385762510000001}","curveMode":2,"curveTo":"{1, 0.77614237490000004}","hasCurveFrom":true,"hasCurveTo":true,"point":"{1, 0.5}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.22385762510000001, 0}","curveMode":2,"curveTo":"{0.77614237490000004, 0}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0.5, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0.77614237490000004}","curveMode":2,"curveTo":"{0, 0.22385762510000001}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0, 0.5}"}]}},{"_class":"shapePath","do_objectID":"AC4876A8-B88A-47AD-A48E-3023127CD30D","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":14,"width":23,"x":-2.4773720851804,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path 13","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":2,"edited":true,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.13043478260869393, 1.0000000000000004}","curveMode":1,"curveTo":"{0.13043478260869393, 1.0000000000000004}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.50000000000000067, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0}","curveMode":1,"curveTo":"{1, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{3.8616453030440226e-17, 0}","curveMode":1,"curveTo":"{3.8616453030440226e-17, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{3.8616453030440226e-17, 0}"}]}}],"clippingMaskMode":0,"hasClippingMask":false,"windingRule":1}]}],"backgroundColor":{"_class":"color","alpha":1,"blue":0.2588235294117647,"green":0.2588235294117647,"red":0.2588235294117647},"hasBackgroundColor":true,"horizontalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInExport":true,"includeInCloudUpload":true,"verticalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInInstance":false,"symbolID":"080C0EF9-47BD-4900-BB91-42C80173D424"},{"_class":"symbolMaster","do_objectID":"7C5A0EC0-D963-4207-80CB-49ED115BF9B5","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":48,"width":1024,"x":318,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":1,"name":"Material\/Android\/Navbar 1024dp black","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"miterLimit":10,"startDecorationType":0},"hasClickThrough":true,"layers":[{"_class":"shapeGroup","do_objectID":"FD276149-9FFE-47EE-AFEF-6E94CAAB15AB","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":48,"width":1024,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"navbar bg","nameIsFixed":true,"originalObjectID":"FD276149-9FFE-47EE-AFEF-6E94CAAB15AB","resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":0,"green":0,"red":0},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"rectangle","do_objectID":"93E17AB9-FD66-4372-A444-CA8F05F091D4","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":48,"width":1024,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Rectangle-path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":false,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0}","curveMode":1,"curveTo":"{0, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 1}","curveMode":1,"curveTo":"{0, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 1}","curveMode":1,"curveTo":"{1, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0}","curveMode":1,"curveTo":"{1, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 0}"}]},"fixedRadius":0,"hasConvertedToNewRoundCorners":true}],"clippingMaskMode":0,"hasClippingMask":false,"windingRule":1},{"_class":"shapeGroup","do_objectID":"C32AB187-22D2-4FD4-A756-372306E00456","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":true,"height":16,"width":16,"x":709,"y":16},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"recent","nameIsFixed":true,"originalObjectID":"C32AB187-22D2-4FD4-A756-372306E00456","resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","borders":[{"_class":"border","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"fillType":0,"position":1,"thickness":2}],"endDecorationType":0,"miterLimit":10,"startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"rectangle","do_objectID":"C05F4D69-B880-485E-9DEA-0445FDB5CEBE","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":16,"width":16,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":false,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":2,"curveFrom":"{0, 0}","curveMode":1,"curveTo":"{0, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 0}"},{"_class":"curvePoint","cornerRadius":2,"curveFrom":"{0, 1}","curveMode":1,"curveTo":"{0, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 1}"},{"_class":"curvePoint","cornerRadius":2,"curveFrom":"{1, 1}","curveMode":1,"curveTo":"{1, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 1}"},{"_class":"curvePoint","cornerRadius":2,"curveFrom":"{1, 0}","curveMode":1,"curveTo":"{1, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 0}"}]},"fixedRadius":2,"hasConvertedToNewRoundCorners":true}],"clippingMaskMode":0,"hasClippingMask":false,"windingRule":1},{"_class":"shapeGroup","do_objectID":"CA1CFE60-94CC-4DFE-ADB7-228099132327","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":true,"height":16,"width":16,"x":504.9975292549875,"y":16.00097492402438},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"home","nameIsFixed":true,"originalObjectID":"CA1CFE60-94CC-4DFE-ADB7-228099132327","resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","borders":[{"_class":"border","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"fillType":0,"position":1,"thickness":2}],"endDecorationType":0,"miterLimit":10,"startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"oval","do_objectID":"8873C58B-0DDD-45A4-8A28-EC4E572620DF","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":16,"width":16,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":false,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.77614237490000004, 1}","curveMode":2,"curveTo":"{0.22385762510000001, 1}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0.5, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0.22385762510000001}","curveMode":2,"curveTo":"{1, 0.77614237490000004}","hasCurveFrom":true,"hasCurveTo":true,"point":"{1, 0.5}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.22385762510000001, 0}","curveMode":2,"curveTo":"{0.77614237490000004, 0}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0.5, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0.77614237490000004}","curveMode":2,"curveTo":"{0, 0.22385762510000001}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0, 0.5}"}]}}],"clippingMaskMode":0,"hasClippingMask":false,"windingRule":1},{"_class":"shapeGroup","do_objectID":"B139CB06-4803-4C60-9112-3DEE3F51D109","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":17.45461322817407,"width":14.99505859673756,"x":301.0024707450125,"y":15.27269338591304},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"back","nameIsFixed":true,"originalObjectID":"B139CB06-4803-4C60-9112-3DEE3F51D109","resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","borders":[{"_class":"border","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"fillType":0,"position":1,"thickness":2}],"endDecorationType":0,"miterLimit":10,"startDecorationType":0},"hasClickThrough":true,"layers":[{"_class":"shapePath","do_objectID":"466F6847-C95A-4EAD-A447-C48DCC7870DC","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":17.45461322817407,"width":14.99505859673756,"x":0,"y":1.77635683940025e-15},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":true,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":2,"curveFrom":"{0.99999997746672664, -0.044212864809782863}","curveMode":1,"curveTo":"{0.99999997746672664, -0.044212864809782863}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.99999997746672664, -0.044212864809782863}"},{"_class":"curvePoint","cornerRadius":2,"curveFrom":"{0.72049598702496187, 1.1868950936476208}","curveMode":1,"curveTo":"{1.2795040182693471, 0.90175412939323807}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1.0000000026471545, 1.0443246115204294}"},{"_class":"curvePoint","cornerRadius":2,"curveFrom":"{-0.067018176924187017, 0.5000558548053492}","curveMode":1,"curveTo":"{-0.067018176924187017, 0.5000558548053492}","hasCurveFrom":false,"hasCurveTo":false,"point":"{-0.067018176924187017, 0.5000558548053492}"}]}}],"clippingMaskMode":0,"hasClippingMask":false,"windingRule":1}],"backgroundColor":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"hasBackgroundColor":false,"horizontalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInExport":true,"includeInCloudUpload":true,"verticalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInInstance":false,"symbolID":"ED9B7E91-942C-4237-9FBB-46015072DC87"},{"_class":"symbolMaster","do_objectID":"E33E2440-9E0D-4801-A0C4-5F0D7AA40DE6","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":24,"width":12,"x":1442,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":1,"name":"Material\/Icons white\/more vert","nameIsFixed":true,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"fills":[{"_class":"fill","do_objectID":"80066115-E9C9-49E6-A544-44ED048B1155","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":1,"patternTileScale":1}],"miterLimit":10,"startDecorationType":0},"hasClickThrough":true,"layers":[{"_class":"shapeGroup","do_objectID":"81A456AC-794A-4C12-82B7-424EEBB853DA","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","do_objectID":"4E763800-4F35-4D88-BD54-27F2228FFD7B","constrainProportions":false,"height":16,"width":4,"x":4,"y":4},"isFlippedHorizontal":false,"isFlippedVertical":true,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Shape","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","do_objectID":"78B24937-2B1B-4AC0-8304-D12FF139276F","endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"sharedObjectID":"A93742BB-00F9-4F61-A4D3-18497765722F","startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"shapePath","do_objectID":"4CC0D4E9-894A-4BCE-9A28-86E02BC355DD","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":4,"width":4,"x":0,"y":12},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":true,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.77499999999999991, 0}","curveMode":4,"curveTo":"{0.5, 0}","hasCurveFrom":true,"hasCurveTo":false,"point":"{0.5, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0.77499999999999991}","curveMode":2,"curveTo":"{1, 0.22500000000000009}","hasCurveFrom":true,"hasCurveTo":true,"point":"{1, 0.5}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.22500000000000009, 1}","curveMode":2,"curveTo":"{0.77499999999999991, 1}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0.5, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0.22500000000000009}","curveMode":2,"curveTo":"{0, 0.77499999999999991}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0, 0.5}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.5, 0}","curveMode":4,"curveTo":"{0.22500000000000009, 0}","hasCurveFrom":false,"hasCurveTo":true,"point":"{0.5, 0}"}]}},{"_class":"shapePath","do_objectID":"19FCD26B-6C74-46C6-95AF-4FBD48018DFA","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":4,"width":4,"x":0,"y":6},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":true,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.22500000000000009, 1}","curveMode":4,"curveTo":"{0.5, 1}","hasCurveFrom":true,"hasCurveTo":false,"point":"{0.5, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0.22500000000000009}","curveMode":2,"curveTo":"{0, 0.77499999999999991}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0, 0.5}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.77499999999999991, 0}","curveMode":2,"curveTo":"{0.22500000000000009, 0}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0.5, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0.77499999999999991}","curveMode":2,"curveTo":"{1, 0.22500000000000009}","hasCurveFrom":true,"hasCurveTo":true,"point":"{1, 0.5}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.5, 1}","curveMode":4,"curveTo":"{0.77499999999999991, 1}","hasCurveFrom":false,"hasCurveTo":true,"point":"{0.5, 1}"}]}},{"_class":"shapePath","do_objectID":"14AD854B-BD0A-4BA8-B581-7ECE76C1CA08","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":4,"width":4,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":true,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.22500000000000009, 1}","curveMode":4,"curveTo":"{0.5, 1}","hasCurveFrom":true,"hasCurveTo":false,"point":"{0.5, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0.22499999999999964}","curveMode":2,"curveTo":"{0, 0.77500000000000036}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0, 0.5}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.77499999999999991, 0}","curveMode":2,"curveTo":"{0.22500000000000009, 0}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0.5, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0.77500000000000036}","curveMode":2,"curveTo":"{1, 0.22499999999999964}","hasCurveFrom":true,"hasCurveTo":true,"point":"{1, 0.5}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.5, 1}","curveMode":4,"curveTo":"{0.77499999999999991, 1}","hasCurveFrom":false,"hasCurveTo":true,"point":"{0.5, 1}"}]}}],"clippingMaskMode":0,"hasClippingMask":false,"windingRule":1}],"backgroundColor":{"_class":"color","alpha":1,"blue":0.2588235294117647,"green":0.2588235294117647,"red":0.2588235294117647},"hasBackgroundColor":true,"horizontalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInExport":true,"includeInCloudUpload":true,"verticalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInInstance":false,"symbolID":"0E94AECA-E862-48AE-9B52-257AAE09FC3A"},{"_class":"symbolMaster","do_objectID":"57C2B1F6-1718-4BD9-85D3-39BEFA28613B","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":24,"width":24,"x":1554,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":1,"name":"Material\/Icons white\/arrow back","nameIsFixed":true,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"fills":[{"_class":"fill","do_objectID":"24C96E56-1A8A-4A56-B78D-E65C53111097","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":1,"patternTileScale":1}],"miterLimit":10,"startDecorationType":0},"hasClickThrough":true,"layers":[{"_class":"shapeGroup","do_objectID":"E5F819E4-BE46-40EB-B37C-15B389D007FE","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":16,"width":15.99999999999994,"x":3.700000000000045,"y":4},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Shape","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","do_objectID":"5A6AFA73-58B5-4893-9E5C-F87AD791FC88","endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"sharedObjectID":"A93742BB-00F9-4F61-A4D3-18497765722F","startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"shapePath","do_objectID":"7364F9DB-12C9-4030-BA13-488063868527","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":16,"width":15.99999999999994,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":true,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0.4375}","curveMode":1,"curveTo":"{1, 0.4375}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 0.4375}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.23749999999999799, 0.4375}","curveMode":1,"curveTo":"{0.23749999999999799, 0.4375}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.23749999999999799, 0.4375}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.58750000000000069, 0.087499999999998579}","curveMode":1,"curveTo":"{0.58750000000000069, 0.087499999999998579}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.58750000000000069, 0.087499999999998579}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.50000000000000178, 0}","curveMode":1,"curveTo":"{0.50000000000000178, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.50000000000000178, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0.5}","curveMode":1,"curveTo":"{0, 0.5}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 0.5}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.50000000000000178, 1}","curveMode":1,"curveTo":"{0.50000000000000178, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.50000000000000178, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.58750000000000069, 0.91250000000000142}","curveMode":1,"curveTo":"{0.58750000000000069, 0.91250000000000142}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.58750000000000069, 0.91250000000000142}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.23749999999999799, 0.5625}","curveMode":1,"curveTo":"{0.23749999999999799, 0.5625}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.23749999999999799, 0.5625}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0.5625}","curveMode":1,"curveTo":"{1, 0.5625}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 0.5625}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0.4375}","curveMode":1,"curveTo":"{1, 0.4375}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 0.4375}"}]}}],"clippingMaskMode":0,"hasClippingMask":false,"windingRule":1}],"backgroundColor":{"_class":"color","alpha":1,"blue":0.2588235294117647,"green":0.2588235294117647,"red":0.2588235294117647},"hasBackgroundColor":true,"horizontalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInExport":true,"includeInCloudUpload":true,"verticalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInInstance":false,"symbolID":"399C1B51-205C-4A6A-B53D-5715C2003838"},{"_class":"symbolMaster","do_objectID":"D284E6EB-A900-4B52-B3A1-0461AFBE8EFD","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":24,"width":1024,"x":1678,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Material\/Android\/Status bar 1024dp black","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"miterLimit":10,"startDecorationType":0},"hasClickThrough":true,"layers":[{"_class":"shapeGroup","do_objectID":"8C2C177A-6BDB-4A27-AED7-BFD6E8C28CFC","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":24,"width":1024,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"status bar bg","nameIsFixed":true,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":0,"green":0,"red":0},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"rectangle","do_objectID":"1A534E53-529F-43A2-9E31-1B2F30F8B3F9","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":24,"width":1024,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Rectangle-path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":false,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0}","curveMode":1,"curveTo":"{0, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 1}","curveMode":1,"curveTo":"{0, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 1}","curveMode":1,"curveTo":"{1, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0}","curveMode":1,"curveTo":"{1, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 0}"}]},"fixedRadius":0,"hasConvertedToNewRoundCorners":true}],"clippingMaskMode":0,"hasClippingMask":false,"windingRule":1},{"_class":"symbolInstance","do_objectID":"47930CB2-9DE7-4E64-B046-ED7FCD4A8102","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":24,"width":118,"x":906,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"status bar content","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"miterLimit":10,"startDecorationType":0},"horizontalSpacing":0,"masterInfluenceEdgeMaxXPadding":0,"masterInfluenceEdgeMaxYPadding":11,"masterInfluenceEdgeMinXPadding":0,"masterInfluenceEdgeMinYPadding":0,"symbolID":"080C0EF9-47BD-4900-BB91-42C80173D424","verticalSpacing":0,"overrides":{"0":{}}}],"backgroundColor":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"hasBackgroundColor":false,"horizontalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInExport":true,"includeInCloudUpload":true,"verticalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInInstance":false,"symbolID":"88160964-34E8-4518-8C33-9A5CE739F4A7"},{"_class":"symbolMaster","do_objectID":"D11A2A32-7CF0-4C46-B91D-9FE1B6C7CBB3","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","do_objectID":"620DB548-1D66-401B-B650-ADCC059489A4","constrainProportions":false,"height":24,"width":24,"x":2802,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Material\/Icons white\/search","nameIsFixed":true,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"fills":[{"_class":"fill","do_objectID":"1EC8BD03-31CD-4BC9-B0E7-DE236CF32471","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":1,"patternTileScale":1}],"miterLimit":10,"startDecorationType":0},"hasClickThrough":true,"layers":[{"_class":"shapeGroup","do_objectID":"B11F8503-58EE-4781-AD1E-23038435CBB8","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","do_objectID":"4D3B3EC6-B673-4CA9-B6CD-EDD0BDA8E718","constrainProportions":false,"height":17.491,"width":17.49,"x":3,"y":3},"isFlippedHorizontal":false,"isFlippedVertical":true,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Shape","nameIsFixed":true,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","do_objectID":"F1F9F873-3F66-43CC-BF1C-30FC1724183E","endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"sharedObjectID":"A93742BB-00F9-4F61-A4D3-18497765722F","startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"shapePath","do_objectID":"D72E3724-172E-4777-AEC1-7E2FFBAF73CC","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","do_objectID":"E9107034-A95A-4613-B57D-EC674C4C4836","constrainProportions":false,"height":17.491,"width":17.49,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":true,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":true,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.7148084619782733, 0.62889486021382424}","curveMode":1,"curveTo":"{0.7148084619782733, 0.62889486021382424}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.7148084619782733, 0.62889486021382424}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.66941109205260141, 0.62889486021382424}","curveMode":1,"curveTo":"{0.66941109205260141, 0.62889486021382424}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.66941109205260141, 0.62889486021382424}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.70937678673527726, 0.5482248013263965}","curveMode":4,"curveTo":"{0.65363064608347621, 0.61322966096849807}","hasCurveFrom":true,"hasCurveTo":false,"point":"{0.65363064608347621, 0.61322966096849807}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.74328187535734702, 0.16637127665656626}","curveMode":3,"curveTo":"{0.74328187535734702, 0.46395289005774398}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0.74328187535734702, 0.37161969012635071}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.16638078902229844, 0}","curveMode":2,"curveTo":"{0.57690108633504855, 0}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0.37164093767867351, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0.57686810359613516}","curveMode":2,"curveTo":"{0, 0.16637127665656626}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0, 0.37161969012635071}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.46397941680960547, 0.74323938025270142}","curveMode":3,"curveTo":"{0.16638078902229844, 0.74323938025270142}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0.37164093767867351, 0.74323938025270142}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.61320754716981118, 0.65370762106226066}","curveMode":4,"curveTo":"{0.5481989708404803, 0.70939340232119374}","hasCurveFrom":false,"hasCurveTo":true,"point":"{0.61320754716981118, 0.65370762106226066}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.62898799313893639, 0.66937282030758682}","curveMode":1,"curveTo":"{0.62898799313893639, 0.66937282030758682}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.62898799313893639, 0.66937282030758682}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.62898799313893639, 0.7146532502429821}","curveMode":1,"curveTo":"{0.62898799313893639, 0.7146532502429821}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.62898799313893639, 0.7146532502429821}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.91475128644939963, 1}","curveMode":1,"curveTo":"{0.91475128644939963, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.91475128644939963, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0.91475616031101714}","curveMode":1,"curveTo":"{1, 0.91475616031101714}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 0.91475616031101714}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.7148084619782733, 0.62889486021382424}","curveMode":1,"curveTo":"{0.7148084619782733, 0.62889486021382424}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.7148084619782733, 0.62889486021382424}"}]}},{"_class":"shapePath","do_objectID":"FA3610BD-4C4F-47C4-B7B8-440CE1981E3A","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","do_objectID":"C2BF318E-DE70-4EA3-8ABC-D8A0303D827E","constrainProportions":false,"height":9,"width":9,"x":2,"y":6.490999999999985},"isFlippedHorizontal":false,"isFlippedVertical":true,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":true,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.2237777777777778, 1}","curveMode":4,"curveTo":"{0.5, 1}","hasCurveFrom":true,"hasCurveTo":false,"point":"{0.5, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0.22388888888888894}","curveMode":2,"curveTo":"{0, 0.77622222222222226}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0, 0.5}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.77611111111111108, 0}","curveMode":2,"curveTo":"{0.2237777777777778, 0}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0.5, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0.77622222222222226}","curveMode":2,"curveTo":"{1, 0.22388888888888894}","hasCurveFrom":true,"hasCurveTo":true,"point":"{1, 0.5}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.5, 1}","curveMode":4,"curveTo":"{0.77611111111111108, 1}","hasCurveFrom":false,"hasCurveTo":true,"point":"{0.5, 1}"}]}}],"clippingMaskMode":0,"hasClippingMask":false,"windingRule":1}],"backgroundColor":{"_class":"color","alpha":1,"blue":0.2588235294117647,"green":0.2588235294117647,"red":0.2588235294117647},"hasBackgroundColor":true,"horizontalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInExport":true,"includeInCloudUpload":true,"verticalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInInstance":false,"symbolID":"0D519726-DC4C-4BF3-8DA2-C387E9DF606E"},{"_class":"symbolMaster","do_objectID":"10E7654C-E936-403F-9CEF-AA512F39B60F","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":24,"width":24,"x":2926,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":1,"name":"Material\/Icons white\/arrow drop down","nameIsFixed":true,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":1,"patternTileScale":1}],"miterLimit":10,"startDecorationType":0},"hasClickThrough":true,"layers":[{"_class":"shapeGroup","do_objectID":"57F766A9-1E61-4A19-9FC8-B5C38370035E","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":5,"width":10,"x":7,"y":10},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Shape","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"sharedObjectID":"A93742BB-00F9-4F61-A4D3-18497765722F","startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"shapePath","do_objectID":"A65159E5-B368-48D8-AAD4-6143D1C687F7","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":5,"width":10,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":true,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0}","curveMode":1,"curveTo":"{0, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.5, 1}","curveMode":1,"curveTo":"{0.5, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.5, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0}","curveMode":1,"curveTo":"{1, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 0}"}]}}],"clippingMaskMode":0,"hasClippingMask":false,"windingRule":1}],"backgroundColor":{"_class":"color","alpha":1,"blue":0.2588235294117647,"green":0.2588235294117647,"red":0.2588235294117647},"hasBackgroundColor":true,"horizontalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInExport":true,"includeInCloudUpload":true,"verticalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInInstance":false,"symbolID":"13DF2748-134F-4932-AFF1-A04B20BCEB5D"},{"_class":"symbolMaster","do_objectID":"8E30A0EB-7E44-41FB-B562-98898C86C5CF","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","do_objectID":"E92EB70E-0A8F-47D2-A9FC-AE07CFC84D88","constrainProportions":false,"height":24,"width":24,"x":3050,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":1,"name":"Material\/Icons black\/arrow drop up","nameIsFixed":true,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"miterLimit":10,"startDecorationType":0},"hasClickThrough":true,"layers":[{"_class":"shapeGroup","do_objectID":"C9FDF8B1-CADE-47D0-BA77-F1699B31B101","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","do_objectID":"7AFE403B-7BFC-4AC0-95E6-8CB29E41EE9E","constrainProportions":false,"height":5,"width":10,"x":7,"y":9},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Shape","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","do_objectID":"66D2FB6D-BAF8-4D74-A80B-82B160E929FF","endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":0,"green":0,"red":0},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"sharedObjectID":"DC56376C-8162-4E24-BB3C-91C5B43AD324","startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"shapePath","do_objectID":"C941E34F-358F-403D-8D24-FC0377A5CF66","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":5,"width":10,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":true,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 1}","curveMode":1,"curveTo":"{0, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.5, 0}","curveMode":1,"curveTo":"{0.5, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.5, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 1}","curveMode":1,"curveTo":"{1, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 1}"}]}}],"clippingMaskMode":0,"hasClippingMask":false,"windingRule":1}],"backgroundColor":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"hasBackgroundColor":false,"horizontalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInExport":true,"includeInCloudUpload":true,"verticalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInInstance":false,"symbolID":"FB5EAE15-93F8-464A-90D4-ED01D9F6980B"},{"_class":"symbolMaster","do_objectID":"A0EAD81F-5486-461D-846C-2016D95CE579","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":24,"width":24,"x":3174,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":1,"name":"Material\/Icons white\/close","nameIsFixed":true,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"fills":[{"_class":"fill","do_objectID":"F365C82D-DE5F-4804-A574-685B2F36B01D","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":1,"patternTileScale":1}],"miterLimit":10,"startDecorationType":0},"hasClickThrough":true,"layers":[{"_class":"shapeGroup","do_objectID":"1761D2BD-4632-4D21-92C3-0FC1207AAFCF","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":14,"width":14,"x":5,"y":5},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Shape","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"sharedObjectID":"A93742BB-00F9-4F61-A4D3-18497765722F","startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"shapePath","do_objectID":"D966142A-CD98-46C9-BDAD-9D321B106E74","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":14,"width":14,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":true,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0.099999999999998382}","curveMode":1,"curveTo":"{1, 0.099999999999998382}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 0.099999999999998382}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.90000000000000158, 0}","curveMode":1,"curveTo":"{0.90000000000000158, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.90000000000000158, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.5, 0.40000000000000163}","curveMode":1,"curveTo":"{0.5, 0.40000000000000163}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.5, 0.40000000000000163}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.099999999999998382, 0}","curveMode":1,"curveTo":"{0.099999999999998382, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.099999999999998382, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0.099999999999998382}","curveMode":1,"curveTo":"{0, 0.099999999999998382}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 0.099999999999998382}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.40000000000000163, 0.5}","curveMode":1,"curveTo":"{0.40000000000000163, 0.5}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.40000000000000163, 0.5}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0.90000000000000158}","curveMode":1,"curveTo":"{0, 0.90000000000000158}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 0.90000000000000158}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.099999999999998382, 1}","curveMode":1,"curveTo":"{0.099999999999998382, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.099999999999998382, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.5, 0.59999999999999842}","curveMode":1,"curveTo":"{0.5, 0.59999999999999842}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.5, 0.59999999999999842}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.90000000000000158, 1}","curveMode":1,"curveTo":"{0.90000000000000158, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.90000000000000158, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0.90000000000000158}","curveMode":1,"curveTo":"{1, 0.90000000000000158}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 0.90000000000000158}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.59999999999999842, 0.5}","curveMode":1,"curveTo":"{0.59999999999999842, 0.5}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.59999999999999842, 0.5}"}]}}],"clippingMaskMode":0,"hasClippingMask":false,"windingRule":1}],"backgroundColor":{"_class":"color","alpha":1,"blue":0.2588235294117647,"green":0.2588235294117647,"red":0.2588235294117647},"hasBackgroundColor":true,"horizontalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInExport":true,"includeInCloudUpload":true,"verticalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInInstance":false,"symbolID":"7FD66874-2EE6-4590-BBF7-EE5C8FCEA405"},{"_class":"symbolMaster","do_objectID":"21B63B46-4C5E-4D74-9E80-2B8CCF870C32","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","do_objectID":"0896C0E2-B56B-42C7-BF44-C662D526E64D","constrainProportions":false,"height":24,"width":24,"x":3298,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":1,"name":"Material\/Icons black\/check","nameIsFixed":true,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"miterLimit":10,"startDecorationType":0},"hasClickThrough":true,"layers":[{"_class":"shapeGroup","do_objectID":"80BA1CB4-1207-4D70-A6A4-B01D84837060","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","do_objectID":"FA46C3A8-545B-4470-A9E9-59CE80683639","constrainProportions":false,"height":13.39999999999998,"width":17.60000000000002,"x":3.399993896484375,"y":5.599999999999909},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Shape","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","do_objectID":"C501C759-A703-4F90-A2D6-4AB5F3E4AE55","endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":0,"green":0,"red":0},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"sharedObjectID":"DC56376C-8162-4E24-BB3C-91C5B43AD324","startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"shapePath","do_objectID":"2A9AB63B-64CF-4B9E-BE9B-7C9B8EA62134","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","do_objectID":"27C107F2-9A34-4531-B77F-B9EFFF0BEEC1","constrainProportions":false,"height":13.39999999999998,"width":17.60000000000002,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":true,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.31818181818181906, 0.79104477611940605}","curveMode":1,"curveTo":"{0.31818181818181906, 0.79104477611940605}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.31818181818181906, 0.79104477611940605}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.07954545454545961, 0.47761194029850657}","curveMode":1,"curveTo":"{0.07954545454545961, 0.47761194029850657}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.07954545454545961, 0.47761194029850657}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0.58208955223880354}","curveMode":1,"curveTo":"{0, 0.58208955223880354}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 0.58208955223880354}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.31818181818181906, 1}","curveMode":1,"curveTo":"{0.31818181818181906, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.31818181818181906, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0.10447761194029699}","curveMode":1,"curveTo":"{1, 0.10447761194029699}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 0.10447761194029699}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.92045454545454686, 0}","curveMode":1,"curveTo":"{0.92045454545454686, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.92045454545454686, 0}"}]}}],"clippingMaskMode":0,"hasClippingMask":false,"windingRule":1}],"backgroundColor":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"hasBackgroundColor":false,"horizontalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInExport":true,"includeInCloudUpload":true,"verticalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInInstance":false,"symbolID":"173D909A-EFDA-4D89-BD96-A54732F86FBC"},{"_class":"symbolMaster","do_objectID":"34CA2EB5-EBA4-4D7A-9C5B-1EC39EA97E4F","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":24,"width":24,"x":3422,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Material\/Icons black\/close","nameIsFixed":true,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"miterLimit":10,"startDecorationType":0},"hasClickThrough":true,"layers":[{"_class":"shapeGroup","do_objectID":"0F1B587E-CE5B-4C68-AE12-9259ACBAD47F","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":14,"width":14,"x":5,"y":5},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Shape","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","do_objectID":"5E67E0D0-732F-4BED-BA12-13E7712F916D","endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":0,"green":0,"red":0},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"sharedObjectID":"DC56376C-8162-4E24-BB3C-91C5B43AD324","startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"shapePath","do_objectID":"792E7CCE-2239-4F83-8C42-B81247D8DB36","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":14,"width":14,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":true,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0.099999999999998382}","curveMode":1,"curveTo":"{1, 0.099999999999998382}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 0.099999999999998382}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.90000000000000158, 0}","curveMode":1,"curveTo":"{0.90000000000000158, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.90000000000000158, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.5, 0.40000000000000163}","curveMode":1,"curveTo":"{0.5, 0.40000000000000163}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.5, 0.40000000000000163}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.099999999999998382, 0}","curveMode":1,"curveTo":"{0.099999999999998382, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.099999999999998382, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0.099999999999998382}","curveMode":1,"curveTo":"{0, 0.099999999999998382}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 0.099999999999998382}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.40000000000000163, 0.5}","curveMode":1,"curveTo":"{0.40000000000000163, 0.5}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.40000000000000163, 0.5}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0.90000000000000158}","curveMode":1,"curveTo":"{0, 0.90000000000000158}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 0.90000000000000158}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.099999999999998382, 1}","curveMode":1,"curveTo":"{0.099999999999998382, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.099999999999998382, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.5, 0.59999999999999842}","curveMode":1,"curveTo":"{0.5, 0.59999999999999842}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.5, 0.59999999999999842}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.90000000000000158, 1}","curveMode":1,"curveTo":"{0.90000000000000158, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.90000000000000158, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0.90000000000000158}","curveMode":1,"curveTo":"{1, 0.90000000000000158}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 0.90000000000000158}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.59999999999999842, 0.5}","curveMode":1,"curveTo":"{0.59999999999999842, 0.5}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.59999999999999842, 0.5}"}]}}],"clippingMaskMode":0,"hasClippingMask":false,"windingRule":1}],"backgroundColor":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"hasBackgroundColor":false,"horizontalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInExport":true,"includeInCloudUpload":true,"verticalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInInstance":false,"symbolID":"A2EB1EF8-52D0-4370-A4C9-6E97D54C71F5"},{"_class":"symbolMaster","do_objectID":"CA799155-0C43-4E3B-8162-8521128A451A","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":24,"width":12,"x":3546,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":1,"name":"Material\/Icons black\/more vert","nameIsFixed":true,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"miterLimit":10,"startDecorationType":0},"hasClickThrough":true,"layers":[{"_class":"shapeGroup","do_objectID":"428FB459-330F-4D52-AE8E-8992B42D0835","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","do_objectID":"90C406E0-0192-4A84-9910-606A3A1C3D98","constrainProportions":false,"height":16,"width":4,"x":4,"y":4},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Shape","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","do_objectID":"768DEAB1-5B2D-4B48-92DD-7E7E8A2175C1","endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":0,"green":0,"red":0},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"sharedObjectID":"DC56376C-8162-4E24-BB3C-91C5B43AD324","startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"shapePath","do_objectID":"3EBF9BF2-E838-4353-B39A-C5A0B2267E83","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":4,"width":4,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":true,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.77500000000000568, 1}","curveMode":4,"curveTo":"{0.5, 1}","hasCurveFrom":true,"hasCurveTo":false,"point":"{0.5, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0.22499999999999432}","curveMode":2,"curveTo":"{1, 0.77500000000000568}","hasCurveFrom":true,"hasCurveTo":true,"point":"{1, 0.5}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.22499999999999432, 0}","curveMode":2,"curveTo":"{0.77500000000000568, 0}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0.5, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0.77500000000000568}","curveMode":2,"curveTo":"{0, 0.22499999999999432}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0, 0.5}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.5, 1}","curveMode":4,"curveTo":"{0.22499999999999432, 1}","hasCurveFrom":false,"hasCurveTo":true,"point":"{0.5, 1}"}]}},{"_class":"shapePath","do_objectID":"E47C78F6-6493-40B8-94F4-05DCB1FED9D2","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":4,"width":4,"x":0,"y":6},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":true,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.22499999999999432, 0}","curveMode":4,"curveTo":"{0.5, 0}","hasCurveFrom":true,"hasCurveTo":false,"point":"{0.5, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0.77500000000000568}","curveMode":2,"curveTo":"{0, 0.22499999999999432}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0, 0.5}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.77500000000000568, 1}","curveMode":2,"curveTo":"{0.22499999999999432, 1}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0.5, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0.22499999999999432}","curveMode":2,"curveTo":"{1, 0.77500000000000568}","hasCurveFrom":true,"hasCurveTo":true,"point":"{1, 0.5}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.5, 0}","curveMode":4,"curveTo":"{0.77500000000000568, 0}","hasCurveFrom":false,"hasCurveTo":true,"point":"{0.5, 0}"}]}},{"_class":"shapePath","do_objectID":"5D799E6A-A1E8-440D-8ECE-3353F766E0E3","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":4,"width":4,"x":0,"y":12},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":true,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.22499999999999432, 0}","curveMode":4,"curveTo":"{0.5, 0}","hasCurveFrom":true,"hasCurveTo":false,"point":"{0.5, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0.77500000000000568}","curveMode":2,"curveTo":"{0, 0.22499999999999432}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0, 0.5}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.77500000000000568, 1}","curveMode":2,"curveTo":"{0.22499999999999432, 1}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0.5, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0.22499999999999432}","curveMode":2,"curveTo":"{1, 0.77500000000000568}","hasCurveFrom":true,"hasCurveTo":true,"point":"{1, 0.5}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.5, 0}","curveMode":4,"curveTo":"{0.77500000000000568, 0}","hasCurveFrom":false,"hasCurveTo":true,"point":"{0.5, 0}"}]}}],"clippingMaskMode":0,"hasClippingMask":false,"windingRule":1}],"backgroundColor":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"hasBackgroundColor":false,"horizontalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInExport":true,"includeInCloudUpload":true,"verticalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInInstance":false,"symbolID":"CBDCF326-5C6B-4C9C-BAA5-D47108544398"},{"_class":"symbolMaster","do_objectID":"DDB0E68B-BEEE-4E3A-9844-0443F2E09696","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":24,"width":24,"x":3658,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":1,"name":"Material\/Icons black\/refresh","nameIsFixed":true,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"miterLimit":10,"startDecorationType":0},"hasClickThrough":true,"layers":[{"_class":"shapeGroup","do_objectID":"71365292-C3BA-4844-8627-D09CBFF6B16C","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","do_objectID":"31B62D58-947D-4E7E-99F0-DD70B73B987B","constrainProportions":false,"height":16,"width":16,"x":4,"y":4},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Shape","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","do_objectID":"15A1DE9C-27EF-4D0A-81DF-E0417196D753","endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":0,"green":0,"red":0},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"sharedObjectID":"DC56376C-8162-4E24-BB3C-91C5B43AD324","startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"shapePath","do_objectID":"7A117936-C128-47F8-84D2-1A3C5DBA4C50","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":16,"width":16.00000000000023,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":true,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.22499999999999112, 0}","curveMode":3,"curveTo":"{0.63749999999997953, 0}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0.49999999999999289, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0.77499999999999858}","curveMode":2,"curveTo":"{0, 0.22500000000000142}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0, 0.5}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.73124999999999241, 1}","curveMode":3,"curveTo":"{0.22499999999999112, 1}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0.49999999999999289, 1}"},{"_class":"curvePoint","do_objectID":"261A7BB7-7918-43C1-8D27-FC39440F388B","cornerRadius":0,"curveFrom":"{0.98124999999998885, 0.625}","curveMode":4,"curveTo":"{0.92499999999998406, 0.83749999999999858}","hasCurveFrom":false,"hasCurveTo":true,"point":"{0.99999999999998579, 0.625}"},{"_class":"curvePoint","do_objectID":"27222968-0B55-472C-AE54-1407E1911126","cornerRadius":0,"curveFrom":"{0.80000000000000004, 0.76874999999999716}","curveMode":4,"curveTo":"{0.84999999999999643, 0.625}","hasCurveFrom":true,"hasCurveTo":false,"point":"{0.87499999999998757, 0.625}"},{"_class":"curvePoint","do_objectID":"DF22ECE7-5C80-41A9-A9E9-9B1D5C1C21CE","cornerRadius":0,"curveFrom":"{0.29375000000001289, 0.875}","curveMode":3,"curveTo":"{0.66249999999999909, 0.875}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0.49999999999999289, 0.875}"},{"_class":"curvePoint","do_objectID":"53943DFA-AA6A-4950-9E9C-8633972AC481","cornerRadius":0,"curveFrom":"{0.12500000000001243, 0.29375000000000284}","curveMode":2,"curveTo":"{0.12500000000001243, 0.70624999999999716}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0.12499999999999822, 0.5}"},{"_class":"curvePoint","do_objectID":"8B7B0347-6EC3-471A-8AB5-CD06B2BB0EFE","cornerRadius":0,"curveFrom":"{0.60625000000000839, 0.125}","curveMode":3,"curveTo":"{0.29375000000001289, 0.125}","hasCurveFrom":true,"hasCurveTo":true,"point":"{0.49999999999999289, 0.125}"},{"_class":"curvePoint","do_objectID":"1D91563B-F479-4C72-BAB5-46F19D15D658","cornerRadius":0,"curveFrom":"{0.76250000000000617, 0.23749999999999716}","curveMode":4,"curveTo":"{0.69374999999999865, 0.16875000000000284}","hasCurveFrom":false,"hasCurveTo":true,"point":"{0.74999999999998934, 0.25}"},{"_class":"curvePoint","do_objectID":"54ED3EEF-9F06-41E9-AF50-47EC812276C7","cornerRadius":0,"curveFrom":"{0.56250000000000622, 0.4375}","curveMode":1,"curveTo":"{0.56250000000000622, 0.4375}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.56249999999999201, 0.4375}"},{"_class":"curvePoint","do_objectID":"8438DED0-BA59-4644-A2E3-FA34C7C5033A","cornerRadius":0,"curveFrom":"{1, 0.4375}","curveMode":1,"curveTo":"{1, 0.4375}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.99999999999998579, 0.4375}"},{"_class":"curvePoint","do_objectID":"FB4E35A0-C58C-465B-BBB4-FF18EC71CC06","cornerRadius":0,"curveFrom":"{1, 0}","curveMode":1,"curveTo":"{1, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.99999999999998579, 0}"},{"_class":"curvePoint","do_objectID":"EFBA6B62-8E3C-4B84-B90E-BCFE1E58D208","cornerRadius":0,"curveFrom":"{0.76249999999997775, 0.0625}","curveMode":1,"curveTo":"{0.84999999999998221, 0.14999999999999858}","hasCurveFrom":true,"hasCurveTo":false,"point":"{0.87499999999998757, 0.125}"}]}}],"clippingMaskMode":0,"hasClippingMask":false,"windingRule":1}],"backgroundColor":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"hasBackgroundColor":false,"horizontalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInExport":true,"includeInCloudUpload":true,"verticalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInInstance":false,"symbolID":"C6E3F5B3-C968-48FF-9085-D3562D0EAA93"},{"_class":"symbolMaster","do_objectID":"76C8A3F1-FA9D-4553-9035-374EEF18CBF2","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","do_objectID":"3BBD04CE-B0B0-48A6-BCC3-E8C8934CB89B","constrainProportions":false,"height":24,"width":24,"x":3782,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":1,"name":"Material\/Icons black\/arrow back","nameIsFixed":true,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","endDecorationType":0,"miterLimit":10,"startDecorationType":0},"hasClickThrough":true,"layers":[{"_class":"shapeGroup","do_objectID":"469B41DE-EE2A-427D-BEA8-B1DA6D113C8D","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","do_objectID":"DFD20384-5EF2-48B5-BEB4-74796F3663B7","constrainProportions":false,"height":16,"width":15.99999999999994,"x":4,"y":4},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Shape","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"style":{"_class":"style","do_objectID":"B29063E0-AB00-400D-A11C-8E5670377D84","endDecorationType":0,"fills":[{"_class":"fill","isEnabled":true,"color":{"_class":"color","alpha":1,"blue":0,"green":0,"red":0},"fillType":0,"noiseIndex":0,"noiseIntensity":0,"patternFillType":0,"patternTileScale":1}],"miterLimit":10,"sharedObjectID":"DC56376C-8162-4E24-BB3C-91C5B43AD324","startDecorationType":0},"hasClickThrough":false,"layers":[{"_class":"shapePath","do_objectID":"0EEF4BF2-0C9B-44A6-AB3B-C0421BA31C82","exportOptions":{"_class":"exportOptions","exportFormats":[],"includedLayerIds":[],"layerOptions":0,"shouldTrim":false},"frame":{"_class":"rect","constrainProportions":false,"height":16,"width":15.99999999999994,"x":0,"y":0},"isFlippedHorizontal":false,"isFlippedVertical":false,"isLocked":false,"isVisible":true,"layerListExpandedType":0,"name":"Path","nameIsFixed":false,"resizingType":0,"rotation":0,"shouldBreakMaskChain":false,"booleanOperation":-1,"edited":true,"path":{"_class":"path","isClosed":true,"points":[{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0.4375}","curveMode":1,"curveTo":"{1, 0.4375}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 0.4375}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.23749999999999799, 0.4375}","curveMode":1,"curveTo":"{0.23749999999999799, 0.4375}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.23749999999999799, 0.4375}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.58750000000000069, 0.087499999999998579}","curveMode":1,"curveTo":"{0.58750000000000069, 0.087499999999998579}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.58750000000000069, 0.087499999999998579}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.50000000000000178, 0}","curveMode":1,"curveTo":"{0.50000000000000178, 0}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.50000000000000178, 0}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0, 0.5}","curveMode":1,"curveTo":"{0, 0.5}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0, 0.5}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.50000000000000178, 1}","curveMode":1,"curveTo":"{0.50000000000000178, 1}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.50000000000000178, 1}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.58750000000000069, 0.91250000000000142}","curveMode":1,"curveTo":"{0.58750000000000069, 0.91250000000000142}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.58750000000000069, 0.91250000000000142}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{0.23749999999999799, 0.5625}","curveMode":1,"curveTo":"{0.23749999999999799, 0.5625}","hasCurveFrom":false,"hasCurveTo":false,"point":"{0.23749999999999799, 0.5625}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0.5625}","curveMode":1,"curveTo":"{1, 0.5625}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 0.5625}"},{"_class":"curvePoint","cornerRadius":0,"curveFrom":"{1, 0.4375}","curveMode":1,"curveTo":"{1, 0.4375}","hasCurveFrom":false,"hasCurveTo":false,"point":"{1, 0.4375}"}]}}],"clippingMaskMode":0,"hasClippingMask":false,"windingRule":1}],"backgroundColor":{"_class":"color","alpha":1,"blue":1,"green":1,"red":1},"hasBackgroundColor":false,"horizontalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInExport":true,"includeInCloudUpload":true,"verticalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeBackgroundColorInInstance":false,"symbolID":"B18F04FF-1BB0-41C0-939B-7EB372AF033F"}],"horizontalRulerData":{"_class":"rulerData","base":0,"guides":[]},"includeInCloudUpload":true,"verticalRulerData":{"_class":"rulerData","base":0,"guides":[]}}
================================================
FILE: design-files/Moderator-StickerSheet-20161117-DAS/pages/226D6615-C16F-4B84-92E0-291B2F1B15C4.json
================================================
[File too large to display: 13.5 MB]
================================================
FILE: design-files/Moderator-StickerSheet-20161117-DAS/user.json
================================================
{"FDD7F69B-2AF0-4FAE-9B51-45D7040E8FCB":{"pageListHeight":1016,"exportableLayerSelection":["FAC6D00E-86E5-45BC-B761-CA9C39DD5233","DC571BE3-F1C5-4526-BA2A-9E4AB741EFF0","5798BAD5-8ED5-409A-87F4-567E0A63E777","66A8E8B0-AB0E-4B94-8E31-2C0F0E16F20A","0E479248-891F-42FD-8327-648B15C87E04","57C3F8E0-276C-4ACB-BDB3-8193E9EA7742","5B4982B7-8726-418E-9D14-2AEDA65B8DFF","6A87051D-B660-41EC-9BDB-488D63F0C56D","0ED4F5B1-85B9-4C2C-A3C9-B98E2D4980DA","83FDAE46-C90F-4972-A27A-53E25B1D2C95","DE991B07-B57F-4889-9D6E-D26F914CE247","759BEA3D-2EC8-4B10-BEA4-C945D63E9908","76B56D15-9C7B-41DD-B915-969F64DF243A","A4960E90-D518-4C70-ABD3-CCE07FB682AC","CD81B3A5-D489-484E-AC6B-000D7F3788D6","1A57493A-78A1-47A5-9CD8-E94EC457B557","6E06B227-EDA2-4053-89B7-92CD8D6D1A35","E2CB16E3-20B5-4210-A90E-E115BC41B9E9","AB3EA644-5829-4734-BAA8-A65E5A65C9AC","B36E8C3C-2368-48AF-9F48-BF0C929339B7","9C115BA1-01FD-4EB5-A0ED-3F302E44863B","933CC81D-2C9B-452A-AC5B-6405D4C607C4","B26015EB-FB4C-4310-A22D-DF70FE99CB1D","E18798F8-EB42-472D-8D37-4061B912D75F","971BFAFC-6721-4AD8-848B-439035F684E9","E580DDC7-059E-4509-A8C2-AA5DD98B9EFF","410F9419-53B5-49B2-8F33-917D0C005E78","D6681D07-6D98-45CC-BABB-0E966D101F3F","1A9E010D-ED70-4FEE-A2B7-AE50B4B6495D","C52C86AD-DFBD-47DB-918E-70E1E839263F","38A0707D-E950-4E99-902E-02B985A31968","131D9DA7-5413-48C7-8852-D77E2C92E339","08674E09-346B-4798-A00F-82D253CBA7A6","CF85FC69-3CEE-4AEA-8F1D-0A4D0F2447B7","CED58EDB-021E-42C9-A52E-A296F29B0E9B","C8F2A3CE-3FFE-4FCD-B841-BEFA971219A3","50080C0C-942A-4186-B601-FD70D4EB756D","9A65DFE6-0C2D-4B5E-BC48-4B30B0C39EBF","AF964B9C-3384-4A3E-B398-CDB2E3659409","4758FACA-A200-4913-8793-0D803A62EA75","711392F8-7726-4EF4-85C9-8E07F436E413","7E5FCE1E-3DC5-4048-9283-B61D8BE599B7","19986F6A-1B19-403F-B544-AF7D046D1200","6ED93965-5641-4CFF-BBCB-08B4FCAB98AC","A39A8B9A-83A0-4AF5-830A-902AEB658BE3","533F043C-236F-45E2-B277-6B683B7AB111","66256BA5-F050-482B-BFE3-0BF96B9B25E1","550873D6-3F8B-4EF2-9E4C-8C98B3405FB7","941D41CD-30D7-431B-B632-A9DD3FB56BFB","A3DC2D33-661C-4F1C-8E76-299C4C6683E0","DB4F3B63-3DDC-4A63-8B60-D4D5A00859BF"],"cloudShare":null},"1B67083D-3430-4865-A36A-6C687A1EEB45":{"scrollOrigin":"{-12647, 613}","zoomValue":4},"226D6615-C16F-4B84-92E0-291B2F1B15C4":{"scrollOrigin":"{9851, 2885}","zoomValue":1}}
================================================
FILE: docs/auth.md
================================================
# OS Moderator Authentication
OS Moderator leverages a Google OAuth 2 authentication flow to trade on our server for JWT tokens. The following outlines how it all works.
## Google OAuth Flow
Google OAuth (version 2) is used to actually authenticate users. [Passport](https://github.com/jaredhanson/passport) and [passport-google-oauth2](https://github.com/jaredhanson/passport-google-oauth2) are used on the backend and, upon receiving and verifying the access token:
1. A unique `User` model instance based on the email address, is created or retrieved
2. A unique `UserSocialAuth` model instance for the user / provider / provider id is created or retrieved
3. A JWT token is created and the user is redirected to the homepage with it in the query string, like: `/?token=(token string)`
## Protecting Resources
To make an endpoint require a valid JWT token, pass in Passport authentication middleware like so:
```js
router.get(
'/some/protected/path',
passport.authenticate('jwt', {session: false}),
(request, response) => {
response.send('Some private stuff!');
});
```
The JWT provider fetches and verifies the user encoded in the token with every request (see the `verify` function in `server/domain/auth/providers/jwt.ts`) and Passport makes a `user` object (a `User` Sequelize model instance) on the `request` object.
## Logging In
To log in, navigate to `/auth/login/google` and it will kick off the OAuth process with Google. You can optionally pass a `redirect` query string parameter of an encoded URL (e.g `encodeURIComponent('http://radsite.com')`) and it will be redirected to on successful login with a `token` query string parameter tacked on to it.
## Accessing Protected Resources
To use your JWT token to access protected resources, you must pass it into the `Authorization` HTTP header like so: `Authorization: JWT (token string)`.
## Token Expiration
Tokens expire for human users (as opposed to users whose `group` is set to `service`, which do not expire) after the number of minutes set in the configuration value `token_expiration_minutes` (`server/config/index.js`). The expiration is calculated server-side based on the `iat` (issued at timestamp) embedded in the token.
## Removing/Deactivating users
To remove/deactivate users, there is a simple `isActive` boolean field, which you can set to `false`/`0`. As long as the user record remains in the `users` table, the user should not be able to reauthenticate.
================================================
FILE: docs/comment_flow.md
================================================
# OSMOD Comment State Flow
This document describes the path of a comment through the OSMOD system.
1. The integration module (e.g., the YouTube module) pulls categories, articles and comments from the target system.
2. For each comment, we create a task to send the comment out to assistants for processing.
3. The task queue creates a `CommentScoreRequest` and submits [the request](osmod_assistant_protocol.md) to all users in the `service` group that have a configured `endpoint` path.
4. Each assistant `POST`s back to the `callback` URL they were given, which contains the specific `CommentScoreRequest` id from the original request.
5. Once all assistants have either called back, or timed out, a task is created to run automated `ModerationRule`s.
6. The rule task is handled by running all `ModerationRule`s against a single comment. If the rule results in a resolution (approve or reject), the comment is considered resolved.
7. If a rule did not resolve a comment, it is made available to the OSMOD frontend.
8. The OSMOD frontend can approve or reject comments either singularly or in bulk. The OSMOD frontend can also update the disposition of previously resolved comments.
9. For each resolved comment, the integration module updates the target system to implement the comment's final disposition.
10. Party 🎉🎉🎉
================================================
FILE: docs/modeling.md
================================================
# The Data Model for Osmod
## Connecting to remote SQL Google Cloud database
From [a cloud shell in your project](https://cloud.google.com/shell/docs/), you can run:
`gcloud beta sql connect <instance-name> --user=<username>`
To connect to your SQL instance. You'll need to create the username from the
[google cloud SQL config interface](https://pantheon.corp.google.com/sql/instances/osmod-development-branch/users).
## The SQL Data Model
[The SQL table construction file](https://github.com/conversationai/conversationai-moderator/blob/master/seed/initial-database.sql)
Default database name: `os_moderator`
The tables:
Object | Tables_in_os_moderator | Description
-----------------------|---------------------------|-------------
- | SequelizeMeta | List of migrations that have been applied
User | users | OSMod Administrators, moderators, and other user-like entities
Category | categories | Categories from the moderated system (e.g., corresponds to channels for Youtube)
Article | articles | Articles from the moderated system (e.g., videos for YouTube)
Comment | comments | Comments from the moderated system
CommentFlag | comment_flag |
CommentScoreRequest | comment_score_requests |
CommentScore | comment_scores |
CommentSize | comment_sizes |
CommentSummaryScore | comment_summary_scores |
CommentTopScore | comment_top_scores |
Decision | decisions |
ModerationRule | moderation_rules |
Preselect | preselects |
TaggingSensitivity | tagging_sensitivities |
Tag | tags |
ModeratorAssignment | moderator_assignments | (Join table) Moderators assigned to moderate comments for the given article
UserCategoryAssignment | user_category_assignments | (Join table) Moderators assigned to moderate comments for the given category
CSRF | csrfs |
UserSocialAuth | user_social_auths |
### User
Users are users of Osmod. This is the moderation team, and people who admin the
Osmod system using the UI.
- id (int) (required)
- group (enum: general, admin, service, youtube) (required)
- email (string) (required for all but users in the "service" group)
- name (string) (required)
- isActive (tinyint) (required)
- avatarURL (string)
- extra (json)
*Indexes*:
- group
- isActive
- email (unique)
Service users serve 2 purposes:
- They are used to authenticate non-humans that want to access to the system. Use the [get-token](../README.md#the-osmod-cli) CLI command to create a suitable JWT token for this purpose.
- They are used to store the configuration for a moderator endpoint.
For the latter, the required configuration is stored in the extra field in a JSON blob that looks like
```json
{
serviceType: 'moderator',
endpointType: 'perspective-api',
endpoint: <URL of endpoint>,
extra: <Any extra configuration required by this endpoint.>
}
```
### UserSocialAuth
This table holds information about the passport authentication configuration for
users in the `Users` table. It is used to allow users to login with social
networks, e.g. using their google account.
- id (int) (required)
- userId (foreign key: User) (required)
- socialId (string) (required) (provider user id)
- provider (string) (required) (name of auth provider, e.g. "google")
- extra (json)
*Indexes*:
- userId + provider (unique) (don't let the same user use the same provider multiple times)
- socialId + provider (unique) (don't allow the same provider user to authenticate on multiple accounts)
### Category
Represents a higher level collection of articles. Categories correspond to e.g., site sections, YouTube channels, or Reddit subreddits.
Moderation Rules are configured at the category level and apply to all articles in the category. Moderators can be assigned at this level.
- id (number) (required)
- sourceId (string) (optional) Original id from target system
- ownerId (foreign key: User) (optional) Service user that created this article.
- label (string) (required)
- isActive (boolean) (required, default true) Whether this category is being actively managed.
- count (int) Number of comments in this category
- unprocessedCount (int) Denormalize SUM of articles' unprocessedCount
- unmoderatedCount (int) Denormalize SUM of articles' unmoderatedCount
- moderatedCount (int) Denormalize SUM of articles' moderatedCount
- highlightedCount (int) Denormalize SUM of articles' highlightedCount
- approvedCount (int) Denormalize SUM of articles' approvedCount
- rejectedCount (int) Denormalize SUM of articles' rejectedCount
- deferredCount (int) Denormalize SUM of articles' deferredCount
- flaggedCount (int) Denormalize SUM of articles' flaggedCount
- batchedCount (int) Denormalize SUM of articles' batchedCount
- extra (json)
### Article
This table holds the articles that can be commented on.
- id (bigint) (required)
- sourceId (string) (required)
- ownerId (foreign key: User) (optional) Service user that created this article.
- sourceCreatedAt (Created ISO 8601 timestamp from target system)
- categoryId (foreign key: Category) (optional)
- title (string) (required)
- text (string) (required)
- url (string) (required)
- isAutoModerated (tinyint) (required) (Indicates whether the article is subject to automated moderation rules)
- isCommentingEnabled (boolean) (Indicates whether commenting is enabled. This field should be automatically synchronised with the host platform to enable/disable commenting.
- count (int) Number of comments in this article
- unprocessedCount (int) (Denormalized count of unprocessed comments (isScored = false))
- unmoderatedCount (int) (Denormalized count of unmoderated comments (isScored = true AND isModerated = false))
- moderatedCount (int) (Denormalized count of moderated comments (isScored = true AND isModerated = true))
- highlightedCount (int) (Denormalize COUNT of comments with highlightedCount > 0)
- approvedCount (int) (Denormalize COUNT of comments with approvedCount > 0)
- rejectedCount (int) (Denormalize COUNT of comments with rejectedCount > 0)
- deferredCount (int) (Denormalize COUNT of comments with deferredCount > 0)
- flaggedCount (int) (Denormalize COUNT of comments with flaggedCount > 0)
- batchedCount (int) (Denormalize COUNT of comments with batchedCount > 0)
- createdAt (datetime)
- modifiedAt (datetime)
- lastModeratedAt (datetime) Time when a moderation action was last done.
- extra (json)
*Indexes*:
- sourceId (unique)
- categoryId
### ModeratorAssignment
This tables holds which users are assigned to which articles.
- id (int) (required)
- user (foreign key: User) (required)
- article (foreign key: Article) (required)
*Indexes*:
- user + article (unique)
- user
### Comment
This table holds the comments, and the state of the comments.
- id (bigint) (required)
- sourceId (string) (required) (Original id from target system)
- ownerId (foreign key: User) (optional) Service user that uploaded this comment.
- replyToSourceId (string) (optional foreign key: self.sourceId)
- replyId (foreign key: Comment) (id of comment this is a reply to)
- authorSourceId (string) (required) (id of author on the target system)
- article (foreign key: Article) (required)
- author (json) (required)
- text (long text) (required)
- isScored (tinyint) (required)
- isModerated (tinyint)
- isAccepted (tinyint)
- isDeferred (tinyint)
- isHighlighted (tinyint)
- isBatchResolved (tinyint)
- isAutoResolved (tinyint) (Indicates if the comment was auto-accepted/rejected based on a rule(s))
- sourceCreatedAt (datetime) (required) (time comment created on target system)
- sentForScoring (datetime)
- sentBackToPublisher (datetime)
- extra (json)
*Indexes*
- sourceId (unique)
- isAccepted
- isDeferred
- isHighlighted
- isBatchResolved
- isAutoResolved
- sentForScoring
### CommentSize
- commentId (int) (required)
- width (int) (required)
- height (int) (required)
*Indexes*:
- commentId, width
### UserCategoryAssignment
- userId (int) (required)
- categoryId (int) (required)
*Indexes*:
- userId
- categoryId
#### Notes
- Used for hasAndBelongsToMany for category assignments on users.
#### States
- unscored: sentForScoring == null
- scored: sentForScoring != null && isScored == 1 (set as such when all related `CommentScoreRequest`s `doneAt` fields are set)
- accepted: isAccepted == 1
- rejected: isAccepted == 0
- deferred: isAccepted == null && isDeferred == 1
- highlighted: isAccepted == 1 && isHighlighted == 1
### CommentScoreRequest
- id (bigint)
- comment (foreign key: Comment) (required)
- userId (foreign key: User) (required)
- sentAt (datetime) (required)
- doneAt (datetime)
### CommentScore
- id (bigint)
- commentId (foreign key: Comment)
- sourceType (enum: User, Moderator, Machine) (required)
- sourceId (string) (optional identifier so that scores can be retracted, like for publisher recommendations)
- commentScoreRequestId (foreign key: CommentScoreRequest) (set for "machine" sources)
- score (float) (required) (0 - 1) (these get set to 1 for non-machine sources)
- annotationStart (int)
- annotationEnd (int)
- confirmedUserId (int)
- isConfirmed (tinyint)
- extra (json)
- createdAt (datetime) (required)
- updatedAt (datetime)
- TagId (int) (foreign key: Tags)
*Indexes*:
- comment
### CommentTopScore
- commentId (foreign key: Comment)
- tagId (foreign key: Tag)
- commentScoreId (foreign key: CommentScore)
*Indexes*:
- commentId/tagId
### CommentSummaryScore
- commentId (foreign key: Comment)
- tagId (foreign key: Tag)
- score (float) (required)
- confirmedUserId (int)
- isConfirmed (bool)
*Indexes*:
- commentId/tagId
### CommentFlag
An attribute of the comment indicating the comment has been flagged for some reason on the target platform.
Currently, there is no means of setting this flag in OSMod, though we display counts of flagged comments.
TODO: Document how a flagged comment appears in the UI.
- id (bigint)
- commentId (foreign key: Comment)
- sourceId (string) (optional identifier so that scores can be retracted, like for publisher recommendations)
- extra (json)
- createdAt (datetime) (required)
- updatedAt (datetime)
*Indexes*:
- commentId
### Decision
- id(int) (required)
- commentId (foreign key: Comment) (require)
- userId (foreign key: User) (optional, if source === User)
- moderationRuleId (foreign key: ModerationRule) (optional, if source === Rule)
- status (enum: Accept, Reject, Defer) (required)
- source (enum: User, Rule) (required)
- sentBackToPublisher (datetime)
#### Notes
Represents a log of decisions made by OSMod.
### ModerationRule
- id (int) (required)
- tagId (foreign key: Tag) (required)
- categoryId (foreign key: Category)
- lowerThreshold (smallint) (required)
- upperThreshold (smallint) (required)
- action (enum: Approve, Reject, Defer, Highlight) (required)
- createdBy (foreign key: User)
*Indexes*:
- categoryId
### CommentReply
- commentId (int) (required)
- replyId (int) (required)
*Indexes*:
- commentId
- replyId
#### Notes
- Used for hasAndBelongsToMany for replies on a comment.
### Tag
- id (int) (required)
- key (string) (required) (raw key for tag, e.g. `ATTACK_ON_COMMENTER`)
- label (string) (required) (display name, e.g. `Attack of Commenter`)
- color (string) (required) (hex color, e.g. `#c0ff33`)
- description (string) (optional) (short description, e.g. `A verbale attack directed towards author`)
- isInBatchView (bool) (is the tag to be shown on the front-end)
- isTaggable (bool) (tags that would show up in reason to reject or moderateor selected tags, but not the tag selector for batch view)
*Indexes*:
- key (unique)
### Preselect
- id (int) (required)
- tagId (foreign key: Tag)
- categoryId (foreign key: Category)
- lowerThreshold (smallint) (required)
- upperThreshold (smallint) (required)
- createdBy (foreign key: User)
### TaggingSensitivity
- id (int) (required)
- tagId (foreign key: Tag)
- categoryId (foreign key: Category)
- lowerThreshold (smallint) (required)
- upperThreshold (smallint) (required)
- createdBy (foreign key: User)
## Mappings
### YouTube
For YouTube we use the following map:
* YouTube Channel -> OSMod Category
* YouTube Video -> OSMod Article
* YouTube Comment -> OSMod Comment
YouTube allows comments on the channel as well as the Video. To accommodate this, there is a special article that corresponds to the channel itself, labelled "Channel comments."
You can connect multiple YouTube accounts to a single OSMod instance. Each connection has an associated user of type "youtube" that stores the necessary authentication credentials.
We set the ownerID field of each category/article/comment fetched from YouTube to point to the YouTube user that is responsible for this entity.
================================================
FILE: docs/osmod_assistant_protocol.md
================================================
# OSMOD Assistant Protocol
This document describes the protocol for interacting with the "assistant", the
component that bridges between the OSMOD backend and the machine learning
models.
The OSMOD backend provides comments and articles to the assistant, which in
turn gives scores on each comment.
## Send a Comment for Scoring
To get scores for a comment, the OSMOD backend sends a `POST` to the assistant
endpoint `/api/score-comment` in the following format:
```javascript
{
"comment": {
"commentId": "123",
// UTF-8 text of the comment.
"plainText": "We are condemned to act out this sad, once unimaginable farce. Sad!",
// Optional HTML content.
"htmlText": "We are <b>condemned</b> to act out this sad, once unimaginable farce. Sad!",
"links": {
// OSMOD API endpoint for retrieving more data about the comment.
"self": "https://osmod-backend/api/rest/comments/123",
},
},
"article": {
"articleId": "456",
// UTF-8 text of the article.
"plainText": "The beauty of me is that I'm very rich.",
// Optional HTML content.
"htmlText": "The <i>beauty</i> of me is that I'm very rich.",
"links": {
// OSMOD API endpoint for retrieving more data about the comment.
"self": "https://osmod-backend/api/rest/articles/456",
},
},
// Single comment that this comment is responding to. Often null.
"inReplyToComment": {
"commentId": "789",
// UTF-8 text of the comment.
"plainText": "We are condemned to act out this sad, once unimaginable farce. Sad!",
// Optional HTML content.
"htmlText": "We are <b>condemned</b> to act out this sad, once unimaginable farce. Sad!",
"links": {
// OSMOD API endpoint for retrieving more data about the comment.
"self": "https://osmod-backend/api/rest/comments/789"
},
},
// Whether to include summary scores for the comment. Optional: by default,
// summary scores aren't included. See documentation of response object below.
"includeSummaryScores": true,
"links": {
// Full URL of backend endpoint that the assistant should post scores to.
// See next section.
"callback": "https://osmod-backend/api/assistant/comment-scores/123"
}
}
```
Once the assistant has scores for the comment, it will `POST` them to the
endpoint specified in `links.callback`. That endpoint is described next.
## Receive Comment Scores
The OSMOD backend will provide the endpoint
`/api/assistant/comment-scores/:id`. The comment ID is embedded as the last
parameter in the URL, which is constructed and sent as the `links.callback`
field in the scoring request described above.
The POST data from the assistant is in the following format:
```javascript
{
// A map from "attribute" to list of score objects. Each score object contains
// a score value for a span of the original comment text. There may be
// multiple score objects for each attribute that describe different text
// spans.
//
// The possible attribute string values are:
// ATTACK_ON_AUTHOR
// ATTACK_ON_COMMENTER
// ATTACK_ON_PUBLISHER
// INCOHERENT
// INFLAMMATORY
// LIKELY_TO_REJECT
// OBSCENE
// OFF_TOPIC
// SPAM
// UNSUBSTANTIAL
"scores": {
"ATTACK_ON_COMMENTER": [
{
// Number between 0 and 1, inclusive. Greater values mean higher
// confidence that the attribute applies to this span of text.
"score": 0.2,
// Integer describing the span of the original comment text that
// the score applies to. The values are in UTF-16 codepoints. "end" is
// exclusive.
// Example: for the text "Hi - I have the best words!", the begin/end
// pair of (0,2) describes the string "Hi", and the pair (5,26)
// describes the string "I have the best words".
"begin": 0,
"end": 62,
},
],
"INFLAMMATORY": [
{
"score": 0.4,
"begin": 0,
"end": 62,
},
{
"score": 0.7,
"begin": 63,
"end": 66,
},
],
},
// A map from "attribute" to a single overall score for the entire comment.
// The set of keys between `summaryScores` and `scores` should be the same
// (that is, an attribute with per-span scores should also have a summary
// score, and vice versa).
//
// `summaryScores` is returned if `includeSummaryScores` was true in the
// request.
"summaryScores": {
"ATTACK_ON_COMMENTER": 0.2,
"INFLAMMATORY": 0.45
},
// String describing problems encountered during scoring. The `scores` and
// `error` fields should be mutually exclusive.
"error": "Problem scoring text: connection to ML backend timed out.",
}
```
## Assistant connection details
The assistant can be reached at https://osmod-assistant.appspot.com/.
For testing/debugging, one can specify the `sync` field in the scoring request,
and the assistant will respond with the score result, as opposed to posting the
result to the `links.callback` endpoint.
Example:
```
$ curl -H 'Content-Type: application/json' --data '{"sync": true, "comment": {"plainText": "you big darn dummy!"} }' https://osmod-assistant.appspot.com/api/score-comment
{"scores":{"ATTACK_ON_COMMENTER":[{"score":0.66,"begin":0,"end":18}],"INFLAMMATORY":[{"score":0.792,"begin":0,"end":18}],"OBSCENITY":[{"score":0.2,"begin":8,"end":11}]}}
```
================================================
FILE: docs/osmod_services_api.md
================================================
# OSMOD Services API
The OSMOD Services API allows publishing and tagging operations to comments.
This documentation covers the services:
Comment Actions
> * /api/services/commentActions/approve
> * /api/services/commentActions/reject
> * /api/services/commentActions/defer
> * /api/services/commentActions/highlight
> * /api/services/commentActions/tag/:tagid
> * /api/services/commentActions/tagCommentSummaryScores/:tagid
Comment Score Actions
> * /api/services/commentActions/:commentid/scores
> * /api/services/commentActions/:commentid/scores/:commentscoreid/reset
> * /api/services/commentActions/:commentid/scores/:commentscoreid/confirm
> * /api/services/commentActions/:commentid/scores/:commentscoreid/reject
> * /api/services/commentActions/:commentid/scores/:commentscoreid
## Comment Actions
Comment Actions allow control over a comment to approve, reject, highlight, defer, and tag.
### approve
Approve all comment id(s)
A `POST` to `/api/services/commentActions/approve` with a body containing the ID(s) of the comment(s) to trigger the action upon.
Body Example using single commentId:
```javascript
{
"data" : [
{ commentId: '12', userId: '1' }
]
}
```
Or, for a bulk command using multiple commentId
```javascript
{
"data" : [
{ commentId: '12', userId: '1' },
{ commentId: '13', userId: '1' },
{ commentId: '17', userId: '1' }
]
}
```
### reject
Reject all comment id(s)
A `POST` to `/api/services/commentActions/reject` with a body containing the ID(s) of the comment(s) to trigger the action upon.
Body Example using single commentId:
```javascript
{
"data" : [
{ commentId: '12', userId: '1' }
]
}
```
Or, for a bulk command using multiple commentId
```javascript
{
"data" : [
{ commentId: '12', userId: '1' },
{ commentId: '13', userId: '1' },
{ commentId: '17', userId: '1' }
]
}
```
### defer
Defer all comment id(s)
A `POST` to `/api/services/commentActions/defer` with a body containing the ID(s) of the comment(s) to trigger the action upon.
Body Example using single commentId:
```javascript
{
"data" : [
{ commentId: '12', userId: '1' }
]
}
```
Or, for a bulk command using multiple commentId
```javascript
{
"data" : [
{ commentId: '12', userId: '1' },
{ commentId: '13', userId: '1' },
{ commentId: '17', userId: '1' }
]
}
```
### highlight
Highlight all comment id(s)
A `POST` to `/api/services/commentActions/highlight` with a body containing the ID(s) of the comment(s) to trigger the action upon.
Body Example using single commentId:
```javascript
{
"data" : [
{ commentId: '12', userId: '1' }
]
}
```
Or, for a bulk command using multiple commentId
```javascript
{
"data" : [
{ commentId: '12', userId: '1' },
{ commentId: '13', userId: '1' },
{ commentId: '17', userId: '1' }
]
}
```
### tag
Tag all comment id(s) with the tag ID provided.
A `POST` to `/api/services/commentActions/tag/:tagid` with a body containing the ID(s) of the comment(s) to trigger the action upon.
Body Example using single commentId:
```javascript
{
"data" : [
12
]
}
```
Or, for a bulk command using multiple commentId
```javascript
{
"data" : [
12,13,17
]
}
```
### tagCommentSummaryScores
Tag all comment id(s) comment summary score with the tag ID provided.
A `POST` to `/a
gitextract_v8p2gl5q/ ├── .circleci/ │ └── config.yml ├── .dockerignore ├── .editorconfig ├── .github/ │ ├── ISSUE_TEMPLATE.md │ └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── QUICKSTART.md ├── README.md ├── bin/ │ ├── build │ ├── initdb │ ├── install │ ├── install-circleci │ ├── link-packages │ ├── lint │ ├── lint-fix │ ├── osmod │ ├── run │ ├── storybook │ ├── sync-db │ ├── test │ └── watch ├── deployments/ │ ├── gcloud/ │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── deploy-sql.sh │ │ ├── deploy.sh │ │ ├── kubernetes-deployment.yaml │ │ └── kubernetes-networking.yaml │ ├── local/ │ │ ├── Dockerfile │ │ ├── README.md │ │ └── docker-compose.yml │ └── standalone/ │ ├── .dockerignore │ ├── Dockerfile │ └── initialise_db.sh ├── design-files/ │ ├── Moderator-StickerSheet-20161117-DAS/ │ │ ├── document.json │ │ ├── meta.json │ │ ├── pages/ │ │ │ ├── 1B67083D-3430-4865-A36A-6C687A1EEB45.json │ │ │ └── 226D6615-C16F-4B84-92E0-291B2F1B15C4.json │ │ └── user.json │ └── Moderator-StickerSheet-20161117-DAS.sketch ├── docs/ │ ├── auth.md │ ├── comment_flow.md │ ├── modeling.md │ ├── osmod_assistant_protocol.md │ ├── osmod_services_api.md │ ├── osmod_task_api.md │ ├── sql_queries.sql │ ├── worker.md │ └── youtube_integration.md ├── lerna.json ├── package.json ├── packages/ │ ├── README.md │ ├── backend-api/ │ │ ├── .sequelizerc │ │ ├── LICENSE │ │ ├── README.md │ │ ├── bin/ │ │ │ ├── check_migrations.sh │ │ │ ├── make_migration.sh │ │ │ ├── osmod-test.js │ │ │ ├── osmod.js │ │ │ ├── run_sequelize_sync.js │ │ │ └── run_task │ │ ├── data/ │ │ │ ├── alice.txt │ │ │ ├── brexit.csv │ │ │ ├── climate.csv │ │ │ ├── election.csv │ │ │ └── wikipedia.csv │ │ ├── package.json │ │ ├── src/ │ │ │ ├── actions/ │ │ │ │ ├── assignment_updaters.ts │ │ │ │ └── object_updaters.ts │ │ │ ├── api/ │ │ │ │ ├── assistant/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── schema.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── router.ts │ │ │ │ ├── services/ │ │ │ │ │ ├── assignments.ts │ │ │ │ │ ├── authorCounts.ts │ │ │ │ │ ├── commentActions.ts │ │ │ │ │ ├── commentSources.ts │ │ │ │ │ ├── editComment.ts │ │ │ │ │ ├── histogramScores/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── util.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── moderatedCounts.ts │ │ │ │ │ ├── search.ts │ │ │ │ │ ├── serializer.ts │ │ │ │ │ ├── simple.ts │ │ │ │ │ ├── textSizes.ts │ │ │ │ │ └── updateNotifications.ts │ │ │ │ └── util/ │ │ │ │ ├── permissions.ts │ │ │ │ ├── server.ts │ │ │ │ ├── sortCommentIds.ts │ │ │ │ └── validation.ts │ │ │ ├── auth/ │ │ │ │ ├── config.ts │ │ │ │ ├── providers/ │ │ │ │ │ ├── google.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── jwt.ts │ │ │ │ ├── router.ts │ │ │ │ ├── tokens.ts │ │ │ │ ├── users.ts │ │ │ │ ├── utils.ts │ │ │ │ └── youtube.ts │ │ │ ├── commands/ │ │ │ │ ├── articles/ │ │ │ │ │ └── delete.ts │ │ │ │ ├── comments/ │ │ │ │ │ ├── calculate_text_size.ts │ │ │ │ │ ├── data_helpers.ts │ │ │ │ │ ├── delete.ts │ │ │ │ │ ├── flag.ts │ │ │ │ │ ├── generate.ts │ │ │ │ │ ├── import.ts │ │ │ │ │ ├── rebuild_reply_relations.ts │ │ │ │ │ ├── recalculate_text_sizes.ts │ │ │ │ │ ├── recalculate_top_scores.ts │ │ │ │ │ ├── rescore.ts │ │ │ │ │ └── send_to_scorer.ts │ │ │ │ ├── denormalize.ts │ │ │ │ ├── tests/ │ │ │ │ │ └── youtube.ts │ │ │ │ └── users/ │ │ │ │ ├── create.ts │ │ │ │ └── get_token.ts │ │ │ ├── config.ts │ │ │ ├── domain/ │ │ │ │ ├── articles/ │ │ │ │ │ ├── countDenormalization.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── categories/ │ │ │ │ │ ├── countDenormalization.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── commentScores/ │ │ │ │ │ └── index.ts │ │ │ │ ├── comments/ │ │ │ │ │ ├── countDenormalization.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── textSizes.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── integrations/ │ │ │ │ ├── decisions.ts │ │ │ │ ├── index.ts │ │ │ │ └── youtube/ │ │ │ │ ├── actions.ts │ │ │ │ ├── authenticate.ts │ │ │ │ ├── channels.ts │ │ │ │ ├── comments.ts │ │ │ │ ├── hooks.ts │ │ │ │ ├── objectmap.ts │ │ │ │ ├── task.ts │ │ │ │ └── videos.ts │ │ │ ├── logger.ts │ │ │ ├── models/ │ │ │ │ ├── article.ts │ │ │ │ ├── category.ts │ │ │ │ ├── comment.ts │ │ │ │ ├── comment_flag.ts │ │ │ │ ├── comment_score.ts │ │ │ │ ├── comment_score_request.ts │ │ │ │ ├── comment_size.ts │ │ │ │ ├── comment_summary_score.ts │ │ │ │ ├── comment_top_score.ts │ │ │ │ ├── configuration.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── csrf.ts │ │ │ │ ├── decision.ts │ │ │ │ ├── index.ts │ │ │ │ ├── moderation_rule.ts │ │ │ │ ├── moderator_assignment.ts │ │ │ │ ├── preselect.ts │ │ │ │ ├── tag.ts │ │ │ │ ├── tagging_sensitivity.ts │ │ │ │ ├── user.ts │ │ │ │ ├── user_category_assignment.ts │ │ │ │ └── user_social_auth.ts │ │ │ ├── notification_router.ts │ │ │ ├── pipeline/ │ │ │ │ ├── apiShim.ts │ │ │ │ ├── hooks.ts │ │ │ │ ├── index.ts │ │ │ │ ├── pipeline.ts │ │ │ │ ├── rules.ts │ │ │ │ ├── shim.ts │ │ │ │ └── state.ts │ │ │ ├── processing/ │ │ │ │ ├── api/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── known_tasks.ts │ │ │ │ │ └── permissions.ts │ │ │ │ ├── dashboard.ts │ │ │ │ ├── index.ts │ │ │ │ ├── tasks/ │ │ │ │ │ ├── comment_actions.ts │ │ │ │ │ ├── db_operations.ts │ │ │ │ │ ├── heartbeat.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── process_machine_score.ts │ │ │ │ │ ├── process_tagging.ts │ │ │ │ │ ├── score_actions.ts │ │ │ │ │ ├── score_tag_actions.ts │ │ │ │ │ └── send_comment_for_scoring.ts │ │ │ │ ├── util.ts │ │ │ │ └── worker.ts │ │ │ ├── processor.ts │ │ │ ├── redis.ts │ │ │ ├── sequelize-config.ts │ │ │ ├── sequelize-sync.ts │ │ │ ├── sequelize.ts │ │ │ ├── server-management.ts │ │ │ ├── server.ts │ │ │ ├── test/ │ │ │ │ ├── auth/ │ │ │ │ │ ├── providers/ │ │ │ │ │ │ └── google.spec.ts │ │ │ │ │ ├── tokens.spec.ts │ │ │ │ │ └── users.spec.ts │ │ │ │ ├── domain/ │ │ │ │ │ ├── comments/ │ │ │ │ │ │ ├── fixture.ts │ │ │ │ │ │ └── textSizes.spec.ts │ │ │ │ │ └── topScores/ │ │ │ │ │ └── calculateTopScores.spec.ts │ │ │ │ ├── fixture.ts │ │ │ │ ├── integration/ │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── actions.spec.ts │ │ │ │ │ │ ├── assignments.spec.ts │ │ │ │ │ │ ├── assistant.spec.ts │ │ │ │ │ │ ├── authorCounts.spec.ts │ │ │ │ │ │ ├── editComment.spec.ts │ │ │ │ │ │ ├── histogramScores.spec.ts │ │ │ │ │ │ ├── simple-comment.spec.ts │ │ │ │ │ │ ├── simple-ranges.spec.ts │ │ │ │ │ │ ├── simple-user.spec.ts │ │ │ │ │ │ └── test_helper.ts │ │ │ │ │ └── websocket/ │ │ │ │ │ ├── assign_moderators.spec.ts │ │ │ │ │ ├── update_notifications.spec.ts │ │ │ │ │ └── websocket.spec.ts │ │ │ │ ├── pipeline/ │ │ │ │ │ ├── pipeline.spec.ts │ │ │ │ │ ├── rules.spec.ts │ │ │ │ │ └── state.spec.ts │ │ │ │ ├── test_helper.ts │ │ │ │ └── unit/ │ │ │ │ ├── services/ │ │ │ │ │ ├── authorCounts.spec.ts │ │ │ │ │ └── histogramScores.spec.ts │ │ │ │ └── util/ │ │ │ │ └── notifications.spec.ts │ │ │ └── worker.ts │ │ └── tsconfig.json │ └── frontend-web/ │ ├── .babelrc │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── public/ │ │ ├── css/ │ │ │ ├── fonts/ │ │ │ │ └── fonts.css │ │ │ ├── moderator.css │ │ │ └── normalize.css │ │ └── index.html │ ├── src/ │ │ ├── app/ │ │ │ ├── appstate.ts │ │ │ ├── auth.ts │ │ │ ├── components/ │ │ │ │ ├── Arrow/ │ │ │ │ │ ├── Arrow.tsx │ │ │ │ │ ├── ArrowStory.tsx │ │ │ │ │ ├── __spec__/ │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ └── index.ts │ │ │ │ ├── AspectRatio/ │ │ │ │ │ ├── AspectRatio.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── AssignModerators/ │ │ │ │ │ ├── AssignModerators.tsx │ │ │ │ │ └── AssignModeratorsStory.tsx │ │ │ │ ├── AssignTagsForm.tsx │ │ │ │ ├── Avatar/ │ │ │ │ │ ├── Avatar.tsx │ │ │ │ │ ├── AvatarStory.tsx │ │ │ │ │ ├── __spec__/ │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ └── index.ts │ │ │ │ ├── Button/ │ │ │ │ │ ├── Button.tsx │ │ │ │ │ ├── ButtonStory.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── CanvasTruncate/ │ │ │ │ │ ├── CanvasTruncate.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── CheckboxRow/ │ │ │ │ │ ├── CheckboxRow.tsx │ │ │ │ │ ├── CheckboxRowStory.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── CommentActionButton/ │ │ │ │ │ ├── CommentActionButton.tsx │ │ │ │ │ ├── CommentActionButtonStory.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── CommentList/ │ │ │ │ │ ├── CommentList.tsx │ │ │ │ │ ├── components/ │ │ │ │ │ │ ├── CheckboxColumn/ │ │ │ │ │ │ │ ├── CheckboxColumn.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ └── SortColumn/ │ │ │ │ │ │ ├── SortColumn.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── CommentText.tsx │ │ │ │ ├── ConfirmationCircle/ │ │ │ │ │ ├── ConfirmationCircle.tsx │ │ │ │ │ ├── ConfirmationCircleStory.tsx │ │ │ │ │ ├── __spec__/ │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ └── index.ts │ │ │ │ ├── DotChart/ │ │ │ │ │ ├── DotChart.tsx │ │ │ │ │ ├── DotChartStory.tsx │ │ │ │ │ ├── __spec__/ │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ ├── comments-data.json │ │ │ │ │ └── index.ts │ │ │ │ ├── ErrorRoot.tsx │ │ │ │ ├── ErrorRootStory.tsx │ │ │ │ ├── FlagsSummary.tsx │ │ │ │ ├── HeaderBar.tsx │ │ │ │ ├── Icons/ │ │ │ │ │ ├── IconBase.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── LazyLoadComment/ │ │ │ │ │ ├── CommentBodyStory.tsx │ │ │ │ │ ├── LazyLoadComment.tsx │ │ │ │ │ ├── components.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── MagicTimestamp.tsx │ │ │ │ ├── MagicTimestampStory.tsx │ │ │ │ ├── ModerateButtons/ │ │ │ │ │ ├── ModerateButtons.tsx │ │ │ │ │ ├── ModerateButtonsStory.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── NavigationTab/ │ │ │ │ │ ├── NavigationTab.tsx │ │ │ │ │ ├── NavigationTabStory.tsx │ │ │ │ │ ├── __spec__/ │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ └── index.ts │ │ │ │ ├── OverflowContainer/ │ │ │ │ │ ├── OverflowContainer.tsx │ │ │ │ │ ├── OverflowContainerStory.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── RuleBars/ │ │ │ │ │ ├── RuleBars.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── ScoresList/ │ │ │ │ │ ├── ScoresList.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Scrim/ │ │ │ │ │ ├── Scrim.tsx │ │ │ │ │ ├── ScrimStory.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── SearchAttribute/ │ │ │ │ │ ├── SearchAttribute.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── SearchHeader/ │ │ │ │ │ ├── SearchHeader.tsx │ │ │ │ │ ├── SearchHeaderStory.tsx │ │ │ │ │ ├── __spec__/ │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ └── index.ts │ │ │ │ ├── SingleComment/ │ │ │ │ │ ├── SingleComment.tsx │ │ │ │ │ ├── SingleCommentStory.tsx │ │ │ │ │ ├── __spec__/ │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ ├── components/ │ │ │ │ │ │ ├── AnnotatedCommentText.tsx │ │ │ │ │ │ ├── AuthorCounts.tsx │ │ │ │ │ │ ├── CommentTags.tsx │ │ │ │ │ │ ├── DetailRow.tsx │ │ │ │ │ │ ├── FlagsList.tsx │ │ │ │ │ │ └── SummaryScore.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Slider/ │ │ │ │ │ ├── RangeBar.tsx │ │ │ │ │ ├── Slider.tsx │ │ │ │ │ ├── SliderStory.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── SplashRoot.tsx │ │ │ │ ├── SplashRootStory.tsx │ │ │ │ ├── TagLabelRow/ │ │ │ │ │ ├── TagLabelRow.tsx │ │ │ │ │ ├── TagLabelRowStory.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── ThemeRoot.tsx │ │ │ │ ├── Toast/ │ │ │ │ │ ├── Toast.tsx │ │ │ │ │ ├── ToastMessage.tsx │ │ │ │ │ ├── ToastStory.tsx │ │ │ │ │ ├── __spec__/ │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ └── index.ts │ │ │ │ ├── Toggle/ │ │ │ │ │ └── __spec__/ │ │ │ │ │ └── .gitkeep │ │ │ │ ├── ToolTip/ │ │ │ │ │ ├── ToolTip.tsx │ │ │ │ │ ├── ToolTipStory.tsx │ │ │ │ │ ├── __spec__/ │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ └── index.ts │ │ │ │ ├── VirtualListScrollbar.tsx │ │ │ │ ├── article_controls.tsx │ │ │ │ ├── index.ts │ │ │ │ └── styles.ts │ │ │ ├── config.ts │ │ │ ├── injectors/ │ │ │ │ ├── articleFetchQueue.ts │ │ │ │ ├── articleInjector.ts │ │ │ │ ├── commentFetchQueue.ts │ │ │ │ ├── commentInjector.ts │ │ │ │ └── contextInjector.ts │ │ │ ├── main.tsx │ │ │ ├── platform/ │ │ │ │ ├── dataService.ts │ │ │ │ ├── localStore.ts │ │ │ │ ├── types.ts │ │ │ │ └── websocketService.ts │ │ │ ├── scenes/ │ │ │ │ ├── Comments/ │ │ │ │ │ ├── Comments.tsx │ │ │ │ │ ├── components/ │ │ │ │ │ │ ├── CommentDetail/ │ │ │ │ │ │ │ ├── CommentDetail.tsx │ │ │ │ │ │ │ ├── components/ │ │ │ │ │ │ │ │ └── InfoButton/ │ │ │ │ │ │ │ │ ├── InfoButton.tsx │ │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── store.ts │ │ │ │ │ │ ├── ModeratedComments/ │ │ │ │ │ │ │ ├── ModeratedComments.tsx │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── store/ │ │ │ │ │ │ │ ├── checkedSelection.ts │ │ │ │ │ │ │ ├── commentListLoader.ts │ │ │ │ │ │ │ ├── currentPagingIdentifier.ts │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── moderatedComments.ts │ │ │ │ │ │ ├── NewComments/ │ │ │ │ │ │ │ ├── NewComments.tsx │ │ │ │ │ │ │ ├── components/ │ │ │ │ │ │ │ │ └── BatchSelector/ │ │ │ │ │ │ │ │ ├── BatchSelector.tsx │ │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── store/ │ │ │ │ │ │ │ ├── checkedSelection.ts │ │ │ │ │ │ │ ├── commentListLoader.ts │ │ │ │ │ │ │ ├── commentScores.ts │ │ │ │ │ │ │ ├── currentPagingIdentifier.ts │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── util.ts │ │ │ │ │ │ ├── Shortcuts/ │ │ │ │ │ │ │ ├── Shortcuts.tsx │ │ │ │ │ │ │ ├── ShortcutsStory.tsx │ │ │ │ │ │ │ ├── __spec__/ │ │ │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── SubheaderBar.tsx │ │ │ │ │ │ ├── TagSelector/ │ │ │ │ │ │ │ ├── TagSelector.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ └── ThreadedCommentDetail/ │ │ │ │ │ │ ├── ThreadedCommentDetail.tsx │ │ │ │ │ │ ├── components/ │ │ │ │ │ │ │ └── ThreadedComment/ │ │ │ │ │ │ │ ├── ThreadedComment.tsx │ │ │ │ │ │ │ ├── ThreadedCommentStory.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── scoreFilters.ts │ │ │ │ │ └── store.ts │ │ │ │ ├── Login/ │ │ │ │ │ ├── ConfigureOAuth.tsx │ │ │ │ │ ├── Login.tsx │ │ │ │ │ ├── LoginStory.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Search/ │ │ │ │ │ ├── Search.tsx │ │ │ │ │ ├── components/ │ │ │ │ │ │ ├── SearchResults.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── store/ │ │ │ │ │ │ ├── checkedSelection.ts │ │ │ │ │ │ ├── commentListLoader.ts │ │ │ │ │ │ ├── currentPagingIdentifier.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── searchResults.ts │ │ │ │ │ └── types.ts │ │ │ │ ├── Settings/ │ │ │ │ │ ├── Ranges.tsx │ │ │ │ │ ├── Settings.tsx │ │ │ │ │ ├── components/ │ │ │ │ │ │ ├── AddUsers.tsx │ │ │ │ │ │ ├── ColorSelect/ │ │ │ │ │ │ │ ├── ColorSelect.tsx │ │ │ │ │ │ │ ├── ColorSelectStory.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── EditUsers.tsx │ │ │ │ │ │ ├── EditYouTubeUser.tsx │ │ │ │ │ │ ├── LabelSettings/ │ │ │ │ │ │ │ ├── LabelSettings.tsx │ │ │ │ │ │ │ ├── LabelSettingsStory.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── ManageAutomatedRules.tsx │ │ │ │ │ │ ├── ManagePreselects.tsx │ │ │ │ │ │ ├── ManageSensitivities.tsx │ │ │ │ │ │ ├── ManageTags.tsx │ │ │ │ │ │ ├── OAuthConfig.tsx │ │ │ │ │ │ ├── RuleRow/ │ │ │ │ │ │ │ ├── RuleRow.tsx │ │ │ │ │ │ │ ├── RuleRowStory.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── SaveButtons.tsx │ │ │ │ │ │ ├── UserForm.tsx │ │ │ │ │ │ ├── rows.tsx │ │ │ │ │ │ └── users.tsx │ │ │ │ │ ├── settingsStyles.ts │ │ │ │ │ ├── store.ts │ │ │ │ │ └── styles.ts │ │ │ │ ├── Tables/ │ │ │ │ │ ├── ArticleTable.tsx │ │ │ │ │ ├── CategorySidebar.tsx │ │ │ │ │ ├── ComponentsStory.tsx │ │ │ │ │ ├── FilterSidebar.tsx │ │ │ │ │ ├── TableFrame.tsx │ │ │ │ │ ├── TableFrameStory.tsx │ │ │ │ │ ├── components.tsx │ │ │ │ │ ├── styles.ts │ │ │ │ │ └── utils.ts │ │ │ │ ├── appstate.ts │ │ │ │ ├── index.tsx │ │ │ │ ├── routes.ts │ │ │ │ └── store.ts │ │ │ ├── store.ts │ │ │ ├── stores/ │ │ │ │ ├── appstate.ts │ │ │ │ ├── articles.ts │ │ │ │ ├── categories.ts │ │ │ │ ├── commentActions.ts │ │ │ │ ├── comments.ts │ │ │ │ ├── counts.ts │ │ │ │ ├── globalActions.ts │ │ │ │ ├── index.ts │ │ │ │ ├── preselects.ts │ │ │ │ ├── rules.ts │ │ │ │ ├── taggingSensitivities.ts │ │ │ │ ├── tags.ts │ │ │ │ ├── textSizes.ts │ │ │ │ └── users.ts │ │ │ ├── styles/ │ │ │ │ ├── breakpoints.ts │ │ │ │ ├── colors.ts │ │ │ │ ├── forms.ts │ │ │ │ ├── header.ts │ │ │ │ ├── hoverstates.ts │ │ │ │ ├── index.ts │ │ │ │ ├── scrim.ts │ │ │ │ ├── typography.ts │ │ │ │ ├── util.ts │ │ │ │ └── zindex.ts │ │ │ ├── stylesx/ │ │ │ │ └── index.ts │ │ │ ├── util/ │ │ │ │ ├── DotChartRenderer.ts │ │ │ │ ├── color.ts │ │ │ │ ├── csrf.ts │ │ │ │ ├── groupByColumn.ts │ │ │ │ ├── index.ts │ │ │ │ ├── makeCheckedSelectionStore/ │ │ │ │ │ ├── __spec__/ │ │ │ │ │ │ └── makeCheckedSelectionStore.spec.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── makeCheckedSelectionStore.ts │ │ │ │ ├── makeCurrentPagingIdentifierReducer.ts │ │ │ │ ├── measureText.ts │ │ │ │ ├── partial/ │ │ │ │ │ ├── __spec__/ │ │ │ │ │ │ ├── memoize.spec.ts │ │ │ │ │ │ └── partial.spec.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── returnSavedCommentRow.ts │ │ │ │ ├── returnURL.ts │ │ │ │ ├── savedSorts.ts │ │ │ │ ├── sortByLabel.ts │ │ │ │ ├── time.ts │ │ │ │ └── timeout.ts │ │ │ └── utilx/ │ │ │ ├── cssInJs.ts │ │ │ ├── highlightText.tsx │ │ │ ├── hooks.ts │ │ │ ├── index.ts │ │ │ ├── keyCodes.tsx │ │ │ └── sortDefinitions.tsx │ │ ├── index.ts │ │ ├── models/ │ │ │ ├── article.ts │ │ │ ├── category.ts │ │ │ ├── comment.ts │ │ │ ├── commentFlag.ts │ │ │ ├── commentScore.ts │ │ │ ├── common.ts │ │ │ ├── fake/ │ │ │ │ ├── article.ts │ │ │ │ ├── category.ts │ │ │ │ ├── comment.ts │ │ │ │ ├── commentScore.ts │ │ │ │ ├── index.ts │ │ │ │ ├── rule.ts │ │ │ │ ├── tag.ts │ │ │ │ └── user.ts │ │ │ ├── index.ts │ │ │ ├── preselect.ts │ │ │ ├── rule.ts │ │ │ ├── tag.ts │ │ │ ├── taggingSensitivity.ts │ │ │ └── user.ts │ │ ├── server.ts │ │ ├── test/ │ │ │ ├── actions.ts │ │ │ ├── apitest.ts │ │ │ ├── notificationChecks.ts │ │ │ ├── objectChecks.ts │ │ │ └── pageTests.ts │ │ └── types.ts │ ├── tooling/ │ │ ├── storybook/ │ │ │ ├── Storyshots.test.js │ │ │ ├── __snapshots__/ │ │ │ │ └── Storyshots.test.js.snap │ │ │ ├── config.js │ │ │ ├── disable-aphrodite-inject.js │ │ │ ├── jest.config.json │ │ │ ├── preview-head.html │ │ │ ├── register-context.js │ │ │ └── webpack.config.js │ │ ├── webpack.config.js │ │ └── webpack.config.production.js │ └── tsconfig.json ├── seed/ │ └── initial-database.sql └── tslint.json
SYMBOL INDEX (1960 symbols across 336 files)
FILE: docs/sql_queries.sql
type articles_with_counts (line 4) | CREATE VIEW articles_with_counts AS
type category_ids_with_counts (line 13) | CREATE VIEW category_ids_with_counts AS
type categories_with_counts (line 22) | CREATE VIEW categories_with_counts AS
FILE: packages/backend-api/src/actions/assignment_updaters.ts
function getUserCategoryAssignment (line 21) | function getUserCategoryAssignment(userIds: Array<number>, categoryId: n...
function getArticleAssignmentArray (line 30) | function getArticleAssignmentArray(userIds: Array<number>, articleIdsInC...
function removeArticleAssignments (line 41) | async function removeArticleAssignments(userIds: Array<number>, articleI...
function updateCategoryAssignments (line 54) | async function updateCategoryAssignments(categoryId: number, userIds: Ar...
function updateArticleAssignments (line 107) | async function updateArticleAssignments(articleId: number, userIds: Set<...
FILE: packages/backend-api/src/actions/object_updaters.ts
type ModelType (line 20) | type ModelType = 'moderation_rule' | 'preselect' | 'tagging_sensitivity';
function checkModelType (line 22) | function checkModelType(type: string): type is ModelType {
function processRangeData (line 32) | async function processRangeData(
function createRangeObject (line 77) | async function createRangeObject(
function modifyRangeObject (line 115) | async function modifyRangeObject(
function deleteRangeObject (line 147) | async function deleteRangeObject(type: ModelType | 'tag', objectId: numb...
function createTagObject (line 165) | async function createTagObject(
function modifyTagObject (line 187) | async function modifyTagObject(
FILE: packages/backend-api/src/api/assistant/index.ts
function createAssistant (line 30) | function createAssistant(): express.Router {
function createAssistantRouter (line 76) | function createAssistantRouter(): express.Router {
FILE: packages/backend-api/src/api/constants.ts
constant REPLY_SUCCESS_VALUE (line 19) | const REPLY_SUCCESS_VALUE = 'success';
constant REPLY_SUCCESS (line 20) | const REPLY_SUCCESS = { status: REPLY_SUCCESS_VALUE};
FILE: packages/backend-api/src/api/router.ts
function createApiRouter (line 23) | function createApiRouter(authenticator: any) {
FILE: packages/backend-api/src/api/services/assignments.ts
function countAssignments (line 26) | async function countAssignments(user: User) {
function createAssignmentsService (line 31) | function createAssignmentsService(): express.Router {
FILE: packages/backend-api/src/api/services/authorCounts.ts
type IAuthorCounts (line 41) | interface IAuthorCounts {
function getAuthorCounts (line 46) | async function getAuthorCounts(authorSourceId: string): Promise<IAuthorC...
function createAuthorCountsService (line 56) | function createAuthorCountsService(): express.Router {
FILE: packages/backend-api/src/api/services/commentActions.ts
function queueMainAction (line 51) | function queueMainAction(action: CommentActions): express.RequestHandler {
function queueScoreCommentSummaryAction (line 74) | function queueScoreCommentSummaryAction(action: ScoreActions): express.R...
function queueSingleScoreAction (line 96) | function queueSingleScoreAction(action: ScoreActions): express.RequestHa...
function createCommentActionsService (line 110) | function createCommentActionsService(): express.Router {
FILE: packages/backend-api/src/api/services/commentSources.ts
constant ACTION_ACTIVATE (line 32) | const ACTION_ACTIVATE = 'activate';
constant ACTION_SYNC (line 33) | const ACTION_SYNC = 'sync';
type ISynchronizeChannelData (line 35) | interface ISynchronizeChannelData {
function _youtubeSynchronizeChannel (line 40) | async function _youtubeSynchronizeChannel(
constant ACTIONS (line 60) | const ACTIONS = new Map([
function createAction (line 65) | function createAction(actionId: string): express.RequestHandler {
function createCommentSourcesService (line 101) | function createCommentSourcesService(): express.Router {
FILE: packages/backend-api/src/api/services/editComment.ts
function createEditCommentTextService (line 38) | function createEditCommentTextService(): express.Router {
FILE: packages/backend-api/src/api/services/histogramScores/index.ts
type ICommentScoredOrDatedWithStringId (line 35) | interface ICommentScoredOrDatedWithStringId {
type ICommentScoredWithStringId (line 39) | interface ICommentScoredWithStringId extends ICommentScoredOrDatedWithSt...
type ICommentDatedWithStringId (line 43) | interface ICommentDatedWithStringId extends ICommentScoredOrDatedWithStr...
function stringifyIds (line 67) | function stringifyIds(arr: Array<any>): Array<any> {
function scoresToChart (line 75) | async function scoresToChart(
function createHistogramScoresService (line 111) | function createHistogramScoresService(): express.Router {
FILE: packages/backend-api/src/api/services/histogramScores/util.ts
type ICommentScoredOrDated (line 27) | interface ICommentScoredOrDated {
type ICommentScored (line 31) | interface ICommentScored extends ICommentScoredOrDated {
type ICommentDated (line 35) | interface ICommentDated extends ICommentScoredOrDated {
class NotFoundError (line 39) | class NotFoundError extends Error {
function sortComments (line 45) | async function sortComments<T extends ICommentScoredOrDated>(data: Array...
function getHistogramScoresForAllCategories (line 74) | async function getHistogramScoresForAllCategories(tagId: number): Promis...
function getHistogramScoresForAllCategoriesByDate (line 98) | async function getHistogramScoresForAllCategoriesByDate(): Promise<Array...
function getHistogramScoresForCategory (line 113) | async function getHistogramScoresForCategory(categoryId: number | 'all',...
function getHistogramScoresForCategoryByDate (line 148) | async function getHistogramScoresForCategoryByDate(categoryId: number | ...
function getHistogramScoresForArticle (line 174) | async function getHistogramScoresForArticle(articleId: number, tagId: nu...
function getHistogramScoresForArticleByDate (line 203) | async function getHistogramScoresForArticleByDate(articleId: number): Pr...
function getMaxSummaryScoreForArticle (line 224) | async function getMaxSummaryScoreForArticle(articleId: number): Promise<...
function getMaxHistogramScoresForAllCategories (line 247) | async function getMaxHistogramScoresForAllCategories(): Promise<Array<IC...
function getMaxSummaryScoreForCategory (line 266) | async function getMaxSummaryScoreForCategory(categoryId: number | 'all')...
constant DEFAULT_IMAGE_WIDTH (line 292) | const DEFAULT_IMAGE_WIDTH = 400;
constant DEFAULT_IMAGE_HEIGHT (line 293) | const DEFAULT_IMAGE_HEIGHT = 200;
constant DEFAULT_COLUMN_COUNT (line 294) | const DEFAULT_COLUMN_COUNT = 100;
function renderScoresToPNG (line 296) | function renderScoresToPNG(
FILE: packages/backend-api/src/api/services/index.ts
function createServicesRouter (line 31) | function createServicesRouter(): express.Router {
FILE: packages/backend-api/src/api/services/moderatedCounts.ts
type IModeratedCounts (line 25) | interface IModeratedCounts {
type IModeratedCountsAsStrings (line 35) | interface IModeratedCountsAsStrings {
function getModeratedCounts (line 57) | async function getModeratedCounts(model: any, sortQuery: string, getWher...
function createModeratedCountsService (line 109) | function createModeratedCountsService(): express.Router {
FILE: packages/backend-api/src/api/services/search.ts
constant MINIMUM_QUERY_LENGTH (line 26) | const MINIMUM_QUERY_LENGTH = 3;
type ISearchResponse (line 28) | type ISearchResponse = Array<string>;
function createSearchService (line 34) | function createSearchService(): express.Router {
FILE: packages/backend-api/src/api/services/serializer.ts
constant TAG_FIELDS (line 20) | const TAG_FIELDS = ['id', 'color', 'description', 'key', 'label', 'isInB...
constant RANGE_FIELDS (line 21) | const RANGE_FIELDS = ['id', 'categoryId', 'lowerThreshold', 'upperThresh...
constant TAGGING_SENSITIVITY_FIELDS (line 22) | const TAGGING_SENSITIVITY_FIELDS = RANGE_FIELDS;
constant RULE_FIELDS (line 23) | const RULE_FIELDS = ['action', 'createdBy', ...RANGE_FIELDS];
constant PRESELECT_FIELDS (line 24) | const PRESELECT_FIELDS = RANGE_FIELDS;
constant USER_FIELDS (line 25) | const USER_FIELDS = ['id', 'name', 'email', 'avatarURL', 'group', 'isAct...
constant COMMENTSET_FIELDS (line 27) | const COMMENTSET_FIELDS = ['id', 'updatedAt', 'allCount', 'unprocessedCo...
constant CATEGORY_FIELDS (line 30) | const CATEGORY_FIELDS = [...COMMENTSET_FIELDS, 'label', 'ownerId', 'isAc...
constant ARTICLE_FIELDS (line 31) | const ARTICLE_FIELDS = [...COMMENTSET_FIELDS, 'title', 'url', 'categoryI...
constant COMMENT_FIELDS (line 34) | const COMMENT_FIELDS = ['id', 'sourceId', 'replyToSourceId', 'replyId', ...
constant SUMMARY_SCORE_FIELDS (line 39) | const SUMMARY_SCORE_FIELDS = ['tagId', 'score'];
constant SCORE_FIELDS (line 40) | const SCORE_FIELDS = ['id', 'commentId', 'confirmedUserId', 'tagId', 'sc...
constant FLAG_FIELDS (line 42) | const FLAG_FIELDS = ['id', 'label', 'detail', 'isRecommendation', 'comme...
constant ID_FIELDS (line 45) | const ID_FIELDS = new Set(['id', 'categoryId', 'articleId', 'tagId', 'ow...
type serializedData (line 48) | type serializedData = {[key: string]: {} | Array<string> | string | numb...
function serialiseObject (line 51) | function serialiseObject(
FILE: packages/backend-api/src/api/services/simple.ts
function createSimpleRESTService (line 62) | function createSimpleRESTService(): express.Router {
FILE: packages/backend-api/src/api/services/textSizes.ts
type ITextSizesResponse (line 28) | interface ITextSizesResponse {
function createTextSizesService (line 39) | function createTextSizesService(): express.Router {
FILE: packages/backend-api/src/api/services/updateNotifications.ts
constant SEND_TEST_UPDATE_PACKETS (line 19) | const SEND_TEST_UPDATE_PACKETS = false;
type ISystemData (line 51) | interface ISystemData {
type IAllArticlesData (line 59) | interface IAllArticlesData {
type IArticleUpdateData (line 64) | interface IArticleUpdateData {
type IPerUserData (line 69) | interface IPerUserData {
type IMessage (line 73) | interface IMessage {
function getSystemData (line 78) | async function getSystemData() {
function getGlobalData (line 116) | async function getGlobalData() {
function getCategoryUpdate (line 143) | async function getCategoryUpdate(categoryId: number) {
function getArticleUpdate (line 160) | async function getArticleUpdate(articleId: number) {
function getPerUserData (line 186) | async function getPerUserData(userId: number) {
type ISocketItem (line 198) | interface ISocketItem {
function refreshSystemMessage (line 208) | async function refreshSystemMessage(): Promise<boolean> {
function removeSocket (line 223) | function removeSocket(si: ISocketItem, ws: WebSocket) {
function refreshMessages (line 233) | async function refreshMessages(alwaysSend: boolean) {
function maybeSendUpdateToUser (line 238) | async function maybeSendUpdateToUser(si: ISocketItem,
function maybeSendUpdates (line 266) | async function maybeSendUpdates() {
function sendUpdate (line 273) | async function sendUpdate(type: string, update: string) {
function sendGlobal (line 285) | async function sendGlobal() {
function sendCategoryUpdate (line 290) | async function sendCategoryUpdate(categoryId: number) {
function sendArticleUpdate (line 295) | async function sendArticleUpdate(articleId: number) {
function sendTestUpdatePackets (line 300) | function sendTestUpdatePackets(si: ISocketItem) {
function processNotification (line 359) | async function processNotification(data: INotificationData) {
function createUpdateNotificationService (line 380) | function createUpdateNotificationService(): express.Router {
function destroyUpdateNotificationService (line 426) | function destroyUpdateNotificationService() {
FILE: packages/backend-api/src/api/util/permissions.ts
function onlyAdmin (line 21) | function onlyAdmin(req: express.Request, res: express.Response, next: ex...
function onlyServices (line 35) | function onlyServices(req: express.Request, res: express.Response, next:...
function onlyAdminAndServices (line 50) | function onlyAdminAndServices(req: express.Request, res: express.Respons...
FILE: packages/backend-api/src/api/util/server.ts
function getExpressAppWithPreprocessors (line 53) | function getExpressAppWithPreprocessors(testMode?: boolean) {
function applyCommonPostprocessors (line 80) | function applyCommonPostprocessors(app: express.Application, testMode?: ...
function makeServer (line 106) | function makeServer(testMode?: boolean): {
FILE: packages/backend-api/src/api/util/sortCommentIds.ts
function sortCommentIds (line 20) | async function sortCommentIds(
FILE: packages/backend-api/src/api/util/validation.ts
function dataSchema (line 20) | function dataSchema(type: any) {
function validateRequest (line 33) | function validateRequest(schema: Joi.Schema) {
function validateAndSendResponse (line 49) | function validateAndSendResponse<T>(schema: Joi.Schema) {
FILE: packages/backend-api/src/auth/config.ts
type IGoogleOAuthConfiguration (line 19) | interface IGoogleOAuthConfiguration {
function getOAuthConfiguration (line 24) | async function getOAuthConfiguration() {
function setOAuthConfiguration (line 28) | async function setOAuthConfiguration(oauthConfig: IGoogleOAuthConfigurat...
function setOAuthGood (line 34) | async function setOAuthGood(isGood: boolean) {
function isOAuthGood (line 38) | async function isOAuthGood() {
FILE: packages/backend-api/src/auth/providers/google.ts
class AuthError (line 24) | class AuthError extends Error {
type IGoogleProfile (line 28) | interface IGoogleProfile {
function mapAuthDataToUser (line 47) | function mapAuthDataToUser(profile: IGoogleProfile) {
function mapAuthDataToUserSocialAuth (line 59) | function mapAuthDataToUserSocialAuth(accessToken: string, refreshToken: ...
function verifyGoogleToken (line 77) | async function verifyGoogleToken(accessToken: string, refreshToken: stri...
function getGoogleStrategy (line 109) | function getGoogleStrategy(
FILE: packages/backend-api/src/auth/providers/jwt.ts
function verifyJWT (line 29) | async function verifyJWT(jwtPayload: any): Promise<User> {
function getJwtStrategy (line 58) | async function getJwtStrategy() {
FILE: packages/backend-api/src/auth/router.ts
function redirectToFrontend (line 35) | function redirectToFrontend(
function createHealthcheckRouter (line 61) | function createHealthcheckRouter(oauthConfig: IGoogleOAuthConfiguration ...
function createAuthConfigRouter (line 89) | function createAuthConfigRouter(): express.Router {
function createAuthRouter (line 125) | function createAuthRouter(): express.Router {
FILE: packages/backend-api/src/auth/tokens.ts
type ITokenConfiguration (line 29) | interface ITokenConfiguration {
function getTokenConfiguration (line 37) | async function getTokenConfiguration(): Promise<ITokenConfiguration> {
type ITokenPayload (line 65) | interface ITokenPayload {
function isValidToken (line 71) | function isValidToken(tokenPayload: ITokenPayload): boolean {
function isExpired (line 83) | async function isExpired(user: User, tokenPayload: ITokenPayload): Promi...
function createToken (line 101) | async function createToken(userId: number, email?: string): Promise<stri...
function verifyToken (line 120) | async function verifyToken(token: string): Promise<ITokenPayload | null> {
function refreshToken (line 139) | async function refreshToken(token: string): Promise<string | null> {
FILE: packages/backend-api/src/auth/users.ts
function isValidUser (line 29) | function isValidUser(user: User): boolean {
function findOrCreateUserSocialAuth (line 41) | async function findOrCreateUserSocialAuth(
function isFirstUserInitialised (line 62) | async function isFirstUserInitialised() {
function ensureFirstUser (line 67) | async function ensureFirstUser({name, email}: {name: string, email: stri...
function saveYouTubeUserToken (line 96) | async function saveYouTubeUserToken({name, email}: {name: string, email:...
FILE: packages/backend-api/src/auth/utils.ts
function generateServerCSRF (line 23) | async function generateServerCSRF(req: express.Request, res: express.Res...
function getClientCSRF (line 44) | async function getClientCSRF(req: express.Request):
FILE: packages/backend-api/src/auth/youtube.ts
function createYouTubeRouter (line 25) | function createYouTubeRouter(
FILE: packages/backend-api/src/commands/articles/delete.ts
function builder (line 26) | function builder(args: yargs.Argv) {
function handler (line 31) | async function handler() {
FILE: packages/backend-api/src/commands/comments/calculate_text_size.ts
function builder (line 26) | function builder(args: yargs.Argv) {
function handler (line 37) | async function handler(argv: any) {
FILE: packages/backend-api/src/commands/comments/data_helpers.ts
function guid (line 13) | function guid() {
function createOwner (line 22) | async function createOwner(name: string) {
function createCategory (line 34) | async function createCategory(owner: User | null, label: string) {
function createArticle (line 52) | async function createArticle(
function createComment (line 81) | async function createComment(
FILE: packages/backend-api/src/commands/comments/delete.ts
function builder (line 26) | function builder(args: yargs.Argv) {
function handler (line 31) | async function handler() {
FILE: packages/backend-api/src/commands/comments/flag.ts
function builder (line 27) | function builder(args: yargs.Argv) {
function handler (line 40) | async function handler(argv: any) {
FILE: packages/backend-api/src/commands/comments/generate.ts
constant PREEXISTING_CATEGORIES (line 27) | const PREEXISTING_CATEGORIES = 5;
constant PREEXISTING_ARTICLES (line 28) | const PREEXISTING_ARTICLES = 20;
constant NEW_CATEGORIES (line 29) | const NEW_CATEGORIES = 1;
constant NEW_ARTICLES (line 30) | const NEW_ARTICLES = 5;
constant NEW_COMMENTS (line 31) | const NEW_COMMENTS = 20;
function builder (line 40) | function builder(args: yargs.Argv) {
function isAlnum (line 55) | function isAlnum(data: string, offset: number): boolean {
function isInternalPunctuation (line 66) | function isInternalPunctuation(data: string, offset: number): boolean {
function find_start_of_sentence (line 70) | function find_start_of_sentence(data: string, offset: number): number {
function find_end_of_sentence (line 77) | function find_end_of_sentence(data: string, offset: number): number {
function find_start_of_word (line 85) | function find_start_of_word(data: string, offset: number): number {
function find_end_of_word (line 92) | function find_end_of_word(data: string, offset: number): number {
function get_sentences (line 100) | function get_sentences(data: string, count: number): string {
function get_words (line 116) | function get_words(data: string, count: number): string {
function handler (line 132) | async function handler(argv: any) {
FILE: packages/backend-api/src/commands/comments/import.ts
constant FILES (line 27) | const FILES = ['brexit', 'climate', 'election', 'wikipedia'];
function builder (line 29) | function builder(args: yargs.Argv) {
constant METADATA (line 37) | const METADATA = {
function processFile (line 61) | async function processFile(owner: User, fname: string) {
function handler (line 74) | async function handler(argv: any) {
FILE: packages/backend-api/src/commands/comments/rebuild_reply_relations.ts
function builder (line 27) | function builder(args: yargs.Argv) {
function handler (line 32) | async function handler() {
FILE: packages/backend-api/src/commands/comments/recalculate_text_sizes.ts
function builder (line 27) | function builder(args: yargs.Argv) {
function handler (line 35) | async function handler(argv: any) {
FILE: packages/backend-api/src/commands/comments/recalculate_top_scores.ts
function builder (line 26) | function builder(args: yargs.Argv) {
function handler (line 31) | async function handler() {
FILE: packages/backend-api/src/commands/comments/rescore.ts
function builder (line 26) | function builder(args: yargs.Argv) {
function handler (line 31) | async function handler() {
FILE: packages/backend-api/src/commands/comments/send_to_scorer.ts
function builder (line 31) | function builder(args: yargs.Argv) {
function handler (line 51) | async function handler(argv: any) {
FILE: packages/backend-api/src/commands/denormalize.ts
function builder (line 30) | function builder(args: yargs.Argv) {
function handler (line 36) | async function handler(argv: any) {
FILE: packages/backend-api/src/commands/tests/youtube.ts
function builder (line 27) | function builder(args: yargs.Argv) {
function handler (line 32) | async function handler() {
FILE: packages/backend-api/src/commands/users/create.ts
function builder (line 35) | function builder(args: yargs.Argv) {
function handler (line 60) | async function handler(argv: any) {
FILE: packages/backend-api/src/commands/users/get_token.ts
function builder (line 27) | function builder(args: yargs.Argv) {
function handler (line 47) | async function handler(argv: any) {
FILE: packages/backend-api/src/domain/articles/countDenormalization.ts
function denormalizeCommentCountsForArticle (line 27) | async function denormalizeCommentCountsForArticle(article: Article | nul...
FILE: packages/backend-api/src/domain/categories/countDenormalization.ts
function denormalizeCommentCountsForCategory (line 22) | async function denormalizeCommentCountsForCategory(category: Category) {
FILE: packages/backend-api/src/domain/commentScores/index.ts
type ITopScore (line 30) | interface ITopScore {
type ITopScores (line 40) | interface ITopScores {
function calculateTopScore (line 47) | function calculateTopScore(scores: Array<CommentScore>): CommentScore | ...
function calculateTopScores (line 60) | async function calculateTopScores(comments: Array<Comment>, tagId: numbe...
function cacheCommentTopScore (line 86) | async function cacheCommentTopScore(comment: Comment, tag: Tag): Promise...
function cacheCommentTopScores (line 107) | async function cacheCommentTopScores(comment: Comment) {
FILE: packages/backend-api/src/domain/comments/countDenormalization.ts
function denormalizeCountsForComment (line 25) | async function denormalizeCountsForComment(comment: Comment) {
FILE: packages/backend-api/src/domain/comments/textSizes.ts
constant FONT_FAMILY (line 25) | const FONT_FAMILY = 'Georgia';
constant TYPE_DEF (line 26) | const TYPE_DEF = {
constant FONT_FILE (line 33) | const FONT_FILE = path.join(__dirname, '..', '..', '..', 'fonts', 'Georg...
function getTextHeight (line 44) | function getTextHeight(lines: Array<string>, _wordWrapWidth: number, sty...
function measureText (line 50) | function measureText(word: string): number {
function wordWrap (line 57) | function wordWrap(text: string, wordWrapWidth: number): Array<string> {
function calculateTextSize (line 94) | async function calculateTextSize(comment: Comment, width: number): Promi...
function cacheTextSize (line 100) | async function cacheTextSize(comment: Comment, width: number): Promise<n...
FILE: packages/backend-api/src/index.ts
function mountAPI (line 34) | async function mountAPI(testMode?: boolean): Promise<express.Express> {
FILE: packages/backend-api/src/integrations/decisions.ts
function getDecisionForComment (line 25) | async function getDecisionForComment(
function foreachPendingDecision (line 37) | async function foreachPendingDecision(
function markDecisionExecuted (line 54) | async function markDecisionExecuted(decision: Decision) {
FILE: packages/backend-api/src/integrations/youtube/actions.ts
function youtubeActivateChannel (line 22) | async function youtubeActivateChannel(
function youtubeSynchronizeChannel (line 32) | async function youtubeSynchronizeChannel(
FILE: packages/backend-api/src/integrations/youtube/authenticate.ts
constant SCOPES (line 24) | const SCOPES = ['https://www.googleapis.com/auth/youtube.force-ssl'];
function for_one_youtube_user (line 26) | async function for_one_youtube_user(
function for_all_youtube_users (line 42) | async function for_all_youtube_users(
FILE: packages/backend-api/src/integrations/youtube/channels.ts
function sync_page_of_channels (line 27) | async function sync_page_of_channels(owner: User, auth: OAuth2Client, pa...
function sync_channels (line 60) | async function sync_channels(
function activate_channel (line 71) | async function activate_channel(
function get_playlist_for_channel (line 120) | async function get_playlist_for_channel(owner: User, auth: OAuth2Client,...
function get_article_id_map_for_channel (line 143) | async function get_article_id_map_for_channel(
function for_each_active_channel (line 158) | async function for_each_active_channel(
FILE: packages/backend-api/src/integrations/youtube/comments.ts
function sync_page_of_comments (line 35) | async function sync_page_of_comments(
function sync_comment_threads_for_channel (line 88) | async function sync_comment_threads_for_channel(
function sync_comment_threads (line 108) | async function sync_comment_threads(
function implement_moderation_decision (line 119) | async function implement_moderation_decision(
function implement_moderation_decisions (line 149) | async function implement_moderation_decisions(
FILE: packages/backend-api/src/integrations/youtube/hooks.ts
method commentModerated (line 24) | async commentModerated(owner: User, comment: Comment) {
FILE: packages/backend-api/src/integrations/youtube/objectmap.ts
function youtubeSetTestOnly (line 34) | function youtubeSetTestOnly(c: typeof testCallback) {
function saveError (line 39) | async function saveError(owner: User, error: Error) {
function clearError (line 51) | async function clearError(owner: User) {
function mapChannelToCategory (line 58) | async function mapChannelToCategory(owner: User, channel: any) {
function setChannelActive (line 134) | async function setChannelActive(owner: User, channelId: string, branding...
function foreachActiveChannel (line 147) | async function foreachActiveChannel(owner: User, callback: (channelId: s...
function mapVideoItemToArticle (line 174) | async function mapVideoItemToArticle(
function mapCommentToComment (line 226) | async function mapCommentToComment(
function mapCommentThreadToComments (line 289) | async function mapCommentThreadToComments(
FILE: packages/backend-api/src/integrations/youtube/task.ts
constant CHANNEL_SYNC_INTERVAL (line 24) | const CHANNEL_SYNC_INTERVAL = 60 * 24;
constant COMMENT_SYNC_INTERVAL (line 25) | const COMMENT_SYNC_INTERVAL = 5;
function syncYoutubeTask (line 27) | async function syncYoutubeTask(tick: number) {
FILE: packages/backend-api/src/integrations/youtube/videos.ts
function sync_page_of_videos (line 28) | async function sync_page_of_videos(
function sync_playlists (line 68) | async function sync_playlists(
function sync_individual_videos (line 96) | async function sync_individual_videos(
function sync_known_videos (line 145) | async function sync_known_videos(
function get_article_id_from_youtube_id (line 173) | async function get_article_id_from_youtube_id(
FILE: packages/backend-api/src/models/article.ts
class Article (line 25) | class Article extends Model {
FILE: packages/backend-api/src/models/category.ts
class Category (line 23) | class Category extends Model {
FILE: packages/backend-api/src/models/comment.ts
class Decision (line 29) | class Decision extends Model {}
type IAuthorAttributes (line 31) | interface IAuthorAttributes {
constant FLAGS_COUNT (line 38) | const FLAGS_COUNT = 0;
constant UNRESOLVED_FLAGS_COUNT (line 39) | const UNRESOLVED_FLAGS_COUNT = 1;
constant RECOMMENDATIONS_COUNT (line 40) | const RECOMMENDATIONS_COUNT = 2;
type IFlagSummary (line 42) | interface IFlagSummary {
class Comment (line 46) | class Comment extends Model {
FILE: packages/backend-api/src/models/comment_flag.ts
class CommentFlag (line 23) | class CommentFlag extends Model {
FILE: packages/backend-api/src/models/comment_score.ts
constant SCORE_SOURCE_TYPES (line 25) | const SCORE_SOURCE_TYPES = [
class CommentScore (line 31) | class CommentScore extends Model {
FILE: packages/backend-api/src/models/comment_score_request.ts
class CommentScoreRequest (line 23) | class CommentScoreRequest extends Model {
FILE: packages/backend-api/src/models/comment_size.ts
class CommentSize (line 22) | class CommentSize extends Model {
FILE: packages/backend-api/src/models/comment_summary_score.ts
class CommentSummaryScore (line 24) | class CommentSummaryScore extends Model {
FILE: packages/backend-api/src/models/comment_top_score.ts
class CommentTopScore (line 21) | class CommentTopScore extends Model {
FILE: packages/backend-api/src/models/configuration.ts
constant CONFIGURATION_TOKEN (line 31) | const CONFIGURATION_TOKEN = 'token';
constant CONFIGURATION_GOOGLE_OAUTH (line 32) | const CONFIGURATION_GOOGLE_OAUTH = 'google-oauth';
class Configuration (line 34) | class Configuration extends Model {
function getConfigItem (line 54) | async function getConfigItem(itemId: string): Promise<object | null> {
function setConfigItem (line 63) | async function setConfigItem(itemId: string, data: object): Promise<void> {
FILE: packages/backend-api/src/models/constants.ts
constant MODERATION_ACTION_ACCEPT (line 17) | const MODERATION_ACTION_ACCEPT = 'Accept';
constant MODERATION_ACTION_REJECT (line 18) | const MODERATION_ACTION_REJECT = 'Reject';
constant MODERATION_ACTION_DEFER (line 19) | const MODERATION_ACTION_DEFER = 'Defer';
constant MODERATION_ACTION_HIGHLIGHT (line 20) | const MODERATION_ACTION_HIGHLIGHT = 'Highlight';
type IResolution (line 22) | type IResolution = 'Accept' | 'Reject' | 'Defer';
type IAction (line 24) | type IAction = 'Accept' | 'Reject' | 'Defer' | 'Highlight';
constant RESET_COUNTS (line 26) | const RESET_COUNTS = {
FILE: packages/backend-api/src/models/csrf.ts
class CSRF (line 21) | class CSRF extends Model {
FILE: packages/backend-api/src/models/decision.ts
class Decision (line 33) | class Decision extends Model {
FILE: packages/backend-api/src/models/moderation_rule.ts
constant MODERATION_RULE_ACTION_TYPES (line 32) | const MODERATION_RULE_ACTION_TYPES = [
constant MODERATION_RULE_ACTION_TYPES_SET (line 39) | const MODERATION_RULE_ACTION_TYPES_SET = new Set(MODERATION_RULE_ACTION_...
class ModerationRule (line 41) | class ModerationRule extends Model {
function isModerationRule (line 111) | function isModerationRule(instance: any) {
FILE: packages/backend-api/src/models/moderator_assignment.ts
class ModeratorAssignment (line 26) | class ModeratorAssignment extends Model {
FILE: packages/backend-api/src/models/preselect.ts
class Preselect (line 24) | class Preselect extends Model {
FILE: packages/backend-api/src/models/tag.ts
constant SUMMARY_SCORE_TAG (line 24) | const SUMMARY_SCORE_TAG = 'SUMMARY_SCORE';
class Tag (line 29) | class Tag extends Model {
function findOrCreateTagByKey (line 100) | async function findOrCreateTagByKey(key: string) {
FILE: packages/backend-api/src/models/tagging_sensitivity.ts
class TaggingSensitivity (line 24) | class TaggingSensitivity extends Model {
FILE: packages/backend-api/src/models/user.ts
constant USER_GROUP_GENERAL (line 24) | const USER_GROUP_GENERAL = 'general';
constant USER_GROUP_ADMIN (line 25) | const USER_GROUP_ADMIN = 'admin';
constant USER_GROUP_SERVICE (line 26) | const USER_GROUP_SERVICE = 'service';
constant USER_GROUP_YOUTUBE (line 27) | const USER_GROUP_YOUTUBE = 'youtube';
constant USER_GROUP_MODERATOR (line 28) | const USER_GROUP_MODERATOR = 'moderator';
constant USER_GROUPS (line 30) | const USER_GROUPS = [
constant ENDPOINT_TYPE_API (line 39) | const ENDPOINT_TYPE_API = 'perspective-api';
type IRequestedAttributes (line 41) | interface IRequestedAttributes {
type IScorerExtra (line 48) | interface IScorerExtra {
type IIntegrationExtra (line 56) | interface IIntegrationExtra {
type IServiceExtra (line 62) | interface IServiceExtra {
class User (line 66) | class User extends Model {
method requireEmailForHumans (line 142) | requireEmailForHumans() {
function isUser (line 159) | function isUser(instance: Model | null): boolean {
FILE: packages/backend-api/src/models/user_category_assignment.ts
class UserCategoryAssignment (line 21) | class UserCategoryAssignment extends Model {
FILE: packages/backend-api/src/models/user_social_auth.ts
class UserSocialAuth (line 22) | class UserSocialAuth extends Model {
FILE: packages/backend-api/src/notification_router.ts
constant REDIS_NOTIFICATION_CHANNEL (line 24) | const REDIS_NOTIFICATION_CHANNEL = 'update-notification';
type INotificationData (line 26) | interface INotificationData {
type IInterestListener (line 32) | interface IInterestListener {
function setTestMode (line 39) | function setTestMode() {
type NotificationObjectType (line 43) | type NotificationObjectType = 'global' | 'category' | 'article' | 'user'...
type NotificationAction (line 44) | type NotificationAction = 'create' | 'modify' | 'delete';
function createSendNotificationHook (line 46) | function createSendNotificationHook<T>(
function processNotification (line 57) | function processNotification(data: INotificationData) {
function sendNotification (line 63) | async function sendNotification(
function receiveNotifications (line 78) | async function receiveNotifications() {
function registerInterest (line 95) | function registerInterest(interestListener: IInterestListener) {
function clearInterested (line 102) | function clearInterested() {
FILE: packages/backend-api/src/pipeline/apiShim.ts
type ITextEntry (line 35) | interface ITextEntry {
type IAnalyzeCommentRequest (line 39) | interface IAnalyzeCommentRequest {
type ISpanScore (line 49) | interface ISpanScore {
type IAttributeScores (line 55) | interface IAttributeScores {
type IAnalyzeCommentResponse (line 62) | interface IAnalyzeCommentResponse {
function StripAttributeVersion (line 69) | function StripAttributeVersion(attributeName: string): string {
function createShim (line 79) | async function createShim(
FILE: packages/backend-api/src/pipeline/hooks.ts
type IPipelineHook (line 20) | interface IPipelineHook {
type IHookData (line 26) | interface IHookData {
function getOwnerData (line 31) | async function getOwnerData(ownerId: number) {
function executeCommentModeratedTask (line 42) | async function executeCommentModeratedTask(data: IHookData) {
function registerHooks (line 53) | function registerHooks(ownerType: string, hook: IPipelineHook) {
function commentModeratedHook (line 58) | async function commentModeratedHook(comment: Comment) {
FILE: packages/backend-api/src/pipeline/pipeline.ts
function sendToScorer (line 58) | async function sendToScorer(comment: Comment, scorer: User) {
function checkScoringDone (line 101) | async function checkScoringDone(comment: Comment): Promise<void> {
function sendForScoring (line 115) | async function sendForScoring(comment: Comment): Promise<void> {
function resendCutoff (line 141) | function resendCutoff() {
function getCommentsToResendForScoring (line 149) | async function getCommentsToResendForScoring(
function resendForScoring (line 171) | async function resendForScoring(comment: Comment): Promise<void> {
function processMachineScore (line 180) | async function processMachineScore(
function updateMaxSummaryScore (line 247) | async function updateMaxSummaryScore(comment: Comment): Promise<void> {
function completeMachineScoring (line 276) | async function completeMachineScoring(commentId: number): Promise<void> {
function compileScoresData (line 294) | function compileScoresData(sourceType: string, userId: number, scoreData...
function compileSummaryScoresData (line 327) | function compileSummaryScoresData(scoreData: ISummaryScores, comment: Co...
function findOrCreateTagsByKey (line 351) | async function findOrCreateTagsByKey(
function recordDecision (line 362) | async function recordDecision(
function postProcessComment (line 393) | async function postProcessComment(comment: Comment): Promise<void> {
FILE: packages/backend-api/src/pipeline/rules.ts
type ICompiledScores (line 42) | interface ICompiledScores {
function compileScores (line 50) | function compileScores(commentScores: Array<CommentSummaryScore>): IComp...
function resolveComment (line 70) | async function resolveComment(
function processRulesForComment (line 204) | async function processRulesForComment(comment: Comment): Promise<IDecisi...
FILE: packages/backend-api/src/pipeline/shim.ts
type IScore (line 19) | interface IScore {
type IScores (line 25) | interface IScores {
type ISummaryScores (line 29) | interface ISummaryScores {
type IScoreData (line 33) | interface IScoreData {
type IShim (line 38) | interface IShim {
FILE: packages/backend-api/src/pipeline/state.ts
type IDecision (line 35) | interface IDecision {
function scoresComplete (line 44) | function scoresComplete(commentScoreRequests: Array<CommentScoreRequest>...
function getIsDoneScoring (line 81) | async function getIsDoneScoring(commentId: number) {
type ICommentStateParams (line 91) | type ICommentStateParams = Pick<
function getDefaultStateData (line 99) | function getDefaultStateData(): ICommentStateParams {
function getApproveStateData (line 114) | function getApproveStateData(): Partial<ICommentStateParams> {
function getRejectStateData (line 125) | function getRejectStateData(): Partial<ICommentStateParams> {
function getDeferStateData (line 136) | function getDeferStateData(): Partial<ICommentStateParams> {
function getHighlightStateData (line 147) | function getHighlightStateData(): Partial<ICommentStateParams> {
function getUnHighlightStateData (line 156) | function getUnHighlightStateData(): Partial<ICommentStateParams> {
function setCommentState (line 166) | async function setCommentState(
function approve (line 195) | async function approve(
function reject (line 211) | async function reject(
function defer (line 227) | async function defer(
function highlight (line 243) | async function highlight(
function reset (line 266) | function reset(comment: Comment, source: User | ModerationRule | null ):...
function addScore (line 277) | async function addScore(comment: Comment, tag: Tag, user?: User | null):...
FILE: packages/backend-api/src/processing/api/index.ts
function mountTaskAPI (line 26) | function mountTaskAPI(): express.Router {
function processingTriggers (line 37) | function processingTriggers(): express.Router {
FILE: packages/backend-api/src/processing/api/known_tasks.ts
function createKnownTasksRouter (line 33) | function createKnownTasksRouter(): express.Router {
FILE: packages/backend-api/src/processing/api/permissions.ts
function verifyAppEngineCron (line 19) | function verifyAppEngineCron(req: express.Request, res: express.Response...
FILE: packages/backend-api/src/processing/dashboard.ts
function mountQueueDashboard (line 20) | function mountQueueDashboard(): Application {
FILE: packages/backend-api/src/processing/tasks/comment_actions.ts
type CommentActions (line 22) | type CommentActions =
type ICommentActionData (line 32) | interface ICommentActionData {
function executeAcceptCommentsTask (line 38) | async function executeAcceptCommentsTask(data: ICommentActionData) {
function executeAcceptCommentsAndFlagsTask (line 50) | async function executeAcceptCommentsAndFlagsTask(data: ICommentActionDat...
function executeRejectCommentsTask (line 62) | async function executeRejectCommentsTask(data: ICommentActionData) {
function executeRejectCommentsAndFlagsTask (line 74) | async function executeRejectCommentsAndFlagsTask(data: ICommentActionDat...
function executeDeferCommentsTask (line 86) | async function executeDeferCommentsTask(data: ICommentActionData) {
function executeHighlightCommentsTask (line 97) | async function executeHighlightCommentsTask(data: ICommentActionData) {
function executeResetCommentsTask (line 107) | async function executeResetCommentsTask(data: ICommentActionData) {
function executeResolveFlagsTask (line 114) | async function executeResolveFlagsTask(data: ICommentActionData) {
function enqueueCommentAction (line 132) | async function enqueueCommentAction(
FILE: packages/backend-api/src/processing/tasks/db_operations.ts
function getUser (line 32) | async function getUser(userId?: number | null | undefined) {
function getComment (line 45) | async function getComment(commentId: number) {
function getTag (line 60) | async function getTag(tagId: number) {
function resolveComment (line 70) | async function resolveComment(
function resolveFlags (line 85) | async function resolveFlags(
function resolveFlagsAndDenormalize (line 101) | async function resolveFlagsAndDenormalize(
function resolveCommentAndFlags (line 116) | async function resolveCommentAndFlags(
FILE: packages/backend-api/src/processing/tasks/heartbeat.ts
constant HEARTBEAT_INTERVAL (line 23) | const HEARTBEAT_INTERVAL = 10;
constant PROCESS_COMMENT_LIMIT (line 24) | const PROCESS_COMMENT_LIMIT = 10;
function heartbeatTask (line 26) | async function heartbeatTask(tick: number) {
function resendComments (line 32) | async function resendComments() {
FILE: packages/backend-api/src/processing/tasks/index.ts
function startProcessing (line 20) | function startProcessing() {
FILE: packages/backend-api/src/processing/tasks/process_machine_score.ts
type IProcessMachineScoreData (line 25) | interface IProcessMachineScoreData {
function executeProcessMachineScoreTask (line 31) | async function executeProcessMachineScoreTask(data: IProcessMachineScore...
function enqueueProcessMachineScoreTask (line 47) | async function enqueueProcessMachineScoreTask(commentId: number, userId:...
FILE: packages/backend-api/src/processing/tasks/process_tagging.ts
type IProcessTagData (line 25) | interface IProcessTagData {
type IProcessTagAdditionData (line 31) | interface IProcessTagAdditionData extends IProcessTagData {
function lookUpCommentBySourceId (line 35) | function lookUpCommentBySourceId(sid: string ) {
function executeProcessTagAdditionTask (line 42) | async function executeProcessTagAdditionTask(data: IProcessTagAdditionDa...
function enqueueProcessTagAdditionTask (line 85) | async function enqueueProcessTagAdditionTask(data: IProcessTagData, runI...
function executeProcessTagRevocationTask (line 89) | async function executeProcessTagRevocationTask(data: IProcessTagData) {
function enqueueProcessTagRevocationTask (line 120) | async function enqueueProcessTagRevocationTask(data: IProcessTagData, ru...
FILE: packages/backend-api/src/processing/tasks/score_actions.ts
type ScoreActions (line 27) | type ScoreActions =
type ITagCommentsData (line 33) | interface ITagCommentsData {
function executeTagCommentsTask (line 39) | async function executeTagCommentsTask(data: ITagCommentsData) {
function executeTagCommentSummaryScoresTask (line 50) | async function executeTagCommentSummaryScoresTask(data: ITagCommentsData) {
function executeConfirmCommentSummaryScoreTask (line 68) | async function executeConfirmCommentSummaryScoreTask(data: ITagCommentsD...
function executeRejectCommentSummaryScoreTask (line 90) | async function executeRejectCommentSummaryScoreTask(data: ITagCommentsDa...
function enqueueScoreAction (line 117) | async function enqueueScoreAction(
FILE: packages/backend-api/src/processing/tasks/score_tag_actions.ts
type IAddTagData (line 24) | interface IAddTagData {
type ICommentScoreData (line 32) | interface ICommentScoreData {
type IUserCommentScoreData (line 36) | interface IUserCommentScoreData {
function executeAddTagTask (line 41) | async function executeAddTagTask(data: IAddTagData) {
function executeRemoveTagTask (line 71) | async function executeRemoveTagTask(data: ICommentScoreData) {
function executeResetTagTask (line 96) | async function executeResetTagTask(data: ICommentScoreData) {
function executeConfirmTagTask (line 111) | async function executeConfirmTagTask(data: IUserCommentScoreData) {
function executeRejectTagTask (line 127) | async function executeRejectTagTask(data: IUserCommentScoreData) {
function enqueueAddTagTask (line 148) | async function enqueueAddTagTask(
function enqueueRemoveTagTask (line 159) | async function enqueueRemoveTagTask(commentScoreId: number, runImmediate...
function enqueueResetTagTask (line 163) | async function enqueueResetTagTask(commentScoreId: number, runImmediatel...
function enqueueConfirmTagTask (line 167) | async function enqueueConfirmTagTask(userId: number, commentScoreId: num...
function enqueueRejectTagTask (line 171) | async function enqueueRejectTagTask(userId: number, commentScoreId: numb...
FILE: packages/backend-api/src/processing/tasks/send_comment_for_scoring.ts
type ISendCommentForScoringTaskData (line 22) | interface ISendCommentForScoringTaskData {
function executeSendCommentForScoringTask (line 26) | async function executeSendCommentForScoringTask(data: ISendCommentForSco...
function enqueueSendCommentForScoringTask (line 53) | async function enqueueSendCommentForScoringTask(commentId: number) {
FILE: packages/backend-api/src/processing/util.ts
function getQueueSingleton (line 26) | function getQueueSingleton(): Queue {
function enqueue (line 39) | function enqueue<T>(name: string, data: T, runImmediately = false): Job ...
type IQueueHandler (line 55) | interface IQueueHandler<T> {
function registerTask (line 59) | function registerTask<T>(name: string, fn: IQueueHandler<T>) {
function processKnownTasks (line 63) | function processKnownTasks() {
FILE: packages/backend-api/src/processing/worker.ts
constant WORK_POLL_INTERVAL (line 43) | const WORK_POLL_INTERVAL = 60 * 1000;
constant REDIS_WORK_TRIGGER_CHANNEL (line 44) | const REDIS_WORK_TRIGGER_CHANNEL = 'work_trigger';
type REDIS_WORK_TRIGGER_TYPE (line 45) | type REDIS_WORK_TRIGGER_TYPE = 'kick' | 'start' | 'reset' | 'stop';
function registerWorkItem (line 49) | function registerWorkItem(itemName: string, item: (tick: number) => Prom...
function processWorkItems (line 57) | async function processWorkItems() {
function startWorking (line 81) | async function startWorking() {
function stopWorking (line 88) | async function stopWorking() {
function startWorker (line 93) | async function startWorker() {
function kickWorker (line 115) | async function kickWorker(type: REDIS_WORK_TRIGGER_TYPE) {
function runTask (line 119) | async function runTask(task: string) {
FILE: packages/backend-api/src/server-management.ts
function registerServer (line 25) | function registerServer(iserver: https.Server | http.Server) {
function registerInit (line 29) | function registerInit(iinit: () => void) {
function restartService (line 33) | function restartService() {
FILE: packages/backend-api/src/server.ts
constant STANDALONE (line 62) | const STANDALONE = api_url.startsWith(frontend_url);
function init (line 67) | async function init() {
FILE: packages/backend-api/src/test/auth/tokens.spec.ts
function getExpiredTimestamp (line 49) | function getExpiredTimestamp() {
function getRefreshableTimestamp (line 151) | function getRefreshableTimestamp() {
FILE: packages/backend-api/src/test/auth/users.spec.ts
function assertUser (line 202) | async function assertUser(user: any) {
FILE: packages/backend-api/src/test/domain/comments/fixture.ts
type IAttributes (line 42) | interface IAttributes {
function getCategoryData (line 47) | function getCategoryData(data: IAttributes = {}): IAttributes {
function createCategory (line 55) | async function createCategory(obj: Partial<IAttributes> = {}): Promise<C...
function getArticleData (line 60) | function getArticleData(data: Partial<IAttributes> = {}): IAttributes {
function createArticle (line 74) | async function createArticle(obj: Partial<IAttributes> = {}): Promise<Ar...
function getCommentData (line 78) | function getCommentData(data: Partial<IAttributes> = {}): IAttributes {
function createComment (line 89) | async function createComment(data?: any): Promise<Comment> {
function getCommentScoreRequestData (line 95) | function getCommentScoreRequestData(data: Partial<IAttributes> = {}): IA...
function createCommentScoreRequest (line 105) | async function createCommentScoreRequest(data?: object): Promise<Comment...
function createUser (line 111) | async function createUser(data: IAttributes = {}): Promise<User> {
function createServiceUser (line 121) | async function createServiceUser(data: IAttributes = {}): Promise<User> {
function createModeratorUser (line 130) | async function createModeratorUser(data: IAttributes = {}): Promise<User> {
function getCommentScoreData (line 146) | function getCommentScoreData(data: IAttributes = {}): IAttributes {
function createCommentScore (line 157) | async function createCommentScore(data?: object): Promise<CommentScore> {
function getCommentSummaryScoreData (line 163) | function getCommentSummaryScoreData(data: IAttributes = {}): IAttributes {
function createCommentSummaryScore (line 172) | async function createCommentSummaryScore(data?: object): Promise<Comment...
function getModerationRuleData (line 178) | function getModerationRuleData(data: IAttributes = {}): IAttributes {
function createModerationRule (line 191) | async function createModerationRule(data?: object): Promise<ModerationRu...
function getTagData (line 197) | function getTagData(data: IAttributes = {}): IAttributes {
function createTag (line 208) | async function createTag(data?: object): Promise<Tag> {
FILE: packages/backend-api/src/test/fixture.ts
function makeArticle (line 43) | async function makeArticle(obj = {}): Promise<Article> {
function makeUser (line 57) | async function makeUser(obj = {}): Promise<User> {
function makeTag (line 67) | async function makeTag(obj = {}) {
function makeTaggingSensitivity (line 75) | async function makeTaggingSensitivity(obj = {}): Promise<TaggingSensitiv...
function makeComment (line 84) | async function makeComment(obj = {}): Promise<Comment> {
function makeCommentScore (line 100) | async function makeCommentScore(obj = {}): Promise<CommentScore> {
function makeCommentTopScore (line 112) | async function makeCommentTopScore(score: CommentScore): Promise<void> {
function makeCommentSummaryScore (line 120) | async function makeCommentSummaryScore(
function makeCategory (line 129) | async function makeCategory(obj = {}): Promise<Category> {
function makeRule (line 137) | async function makeRule(tag: Tag, obj = {}): Promise<ModerationRule> {
function makePreselect (line 147) | async function makePreselect(obj = {}): Promise<Preselect> {
function makeFlag (line 155) | async function makeFlag(obj: {}): Promise<CommentFlag> {
function sleep (line 163) | async function sleep(ms: number) {
function assertSystemMessage (line 167) | function assertSystemMessage(body: any) {
function assertGlobalMessage (line 170) | function assertGlobalMessage(body: any) {
function assertArticleUpdateMessage (line 173) | function assertArticleUpdateMessage(body: any) {
function assertUserMessage (line 176) | function assertUserMessage(body: any) {
function listenForMessages (line 180) | async function listenForMessages(
FILE: packages/backend-api/src/test/integration/api/actions.spec.ts
constant BASE_URL (line 39) | const BASE_URL = `/services/commentActions`;
function checkArticle (line 52) | async function checkArticle(
function checkComment (line 66) | async function checkComment(
function checkFlag (line 87) | async function checkFlag(x: string, id: number, resolved: boolean, resol...
function actOnAComment (line 93) | async function actOnAComment(url: string) {
FILE: packages/backend-api/src/test/integration/api/assignments.spec.ts
constant BASE_URL (line 42) | const BASE_URL = `/services/assignments`;
FILE: packages/backend-api/src/test/integration/api/assistant.spec.ts
constant BASE_URL (line 34) | const BASE_URL = `/assistant`;
FILE: packages/backend-api/src/test/integration/api/authorCounts.spec.ts
constant BASE_URL (line 31) | const BASE_URL = `/services/authorCounts`;
function fakeAuthor (line 33) | async function fakeAuthor(authorSourceId: string, approvedCount: number,...
FILE: packages/backend-api/src/test/integration/api/editComment.spec.ts
constant URL (line 32) | const URL = `/services/editComment`;
FILE: packages/backend-api/src/test/integration/api/histogramScores.spec.ts
constant BASE_URL (line 39) | const BASE_URL = `/services/histogramScores`;
FILE: packages/backend-api/src/test/integration/api/simple-comment.spec.ts
constant BASE_URL (line 40) | const BASE_URL = `/services/simple`;
FILE: packages/backend-api/src/test/integration/api/simple-ranges.spec.ts
constant BASE_URL (line 36) | const BASE_URL = `/services/simple`;
FILE: packages/backend-api/src/test/integration/api/simple-user.spec.ts
constant BASE_URL (line 23) | const BASE_URL = `/services/simple`;
FILE: packages/backend-api/src/test/integration/api/test_helper.ts
function setAuthenticatedUser (line 41) | function setAuthenticatedUser(u: User) {
FILE: packages/backend-api/src/test/integration/websocket/assign_moderators.spec.ts
function assignCategoryModerator (line 78) | async function assignCategoryModerator(data: Array<number>) {
function assignArticleModerator (line 128) | async function assignArticleModerator(data: Array<number>) {
FILE: packages/backend-api/src/test/integration/websocket/update_notifications.spec.ts
function setArticleAttributes (line 76) | async function setArticleAttributes(isCommentingEnabled: boolean, isAuto...
FILE: packages/backend-api/src/test/pipeline/state.spec.ts
function shouldRecordDecision (line 52) | async function shouldRecordDecision(
FILE: packages/backend-api/src/test/test_helper.ts
constant TEST_ENVS (line 24) | const TEST_ENVS = ['test', 'circle_ci'];
function isTestEnv (line 26) | function isTestEnv() {
function cleanDatabase (line 41) | async function cleanDatabase(isRoot?: boolean) {
function dropDatabase (line 55) | async function dropDatabase() {
FILE: packages/backend-api/src/test/unit/services/histogramScores.spec.ts
function createScore (line 54) | async function createScore(categoryLabel: string, scoreValue: number) {
function createScore (line 100) | async function createScore(scoreValue: number, category: Category) {
function createScore (line 163) | async function createScore(scoreValue: number, article: Article) {
FILE: packages/backend-api/src/test/unit/util/notifications.spec.ts
function awaitNotification (line 55) | async function awaitNotification(action: () => Promise<void>): Promise<A...
FILE: packages/frontend-web/src/app/appstate.ts
type IAppState (line 22) | type IAppState = Readonly<{
type IThunkAction (line 27) | type IThunkAction<R> = ThunkAction<R, IAppState, undefined, Action>;
type IAppDispatch (line 28) | type IAppDispatch = ThunkDispatch<IAppState, undefined, Action>;
FILE: packages/frontend-web/src/app/auth.ts
function setAxiosToken (line 37) | function setAxiosToken(token: string): void {
function decodeToken (line 52) | function decodeToken(token: string): any {
function connectWebsocket (line 56) | async function connectWebsocket(
function completeAuthentication (line 98) | async function completeAuthentication(
function verifyCSRF (line 112) | function verifyCSRF(csrf?: string): void {
function handleLoginRedirect (line 127) | async function handleLoginRedirect(
function start (line 150) | async function start(
function logout (line 218) | function logout() {
FILE: packages/frontend-web/src/app/components/Arrow/Arrow.tsx
constant STYLES (line 22) | const STYLES = stylesheet({
type IArrowProps (line 70) | interface IArrowProps {
class Arrow (line 80) | class Arrow extends React.PureComponent<IArrowProps> {
method render (line 82) | render() {
FILE: packages/frontend-web/src/app/components/AspectRatio/AspectRatio.tsx
type IAspectRatioProps (line 22) | interface IAspectRatioProps extends React.HTMLProps<any> {
type IAspectRatioState (line 27) | interface IAspectRatioState {
class AspectRatio (line 32) | class AspectRatio extends React.PureComponent<
method render (line 40) | render() {
method componentDidMount (line 54) | componentDidMount() {
method componentWillUnmount (line 60) | componentWillUnmount() {
method onResize (line 65) | private onResize() {
FILE: packages/frontend-web/src/app/components/AssignModerators/AssignModerators.tsx
constant STYLES (line 33) | const STYLES = stylesheet({
function ModeratorListItem (line 53) | function ModeratorListItem(props: {
function ModeratorList (line 86) | function ModeratorList(props: {
type IAssignModeratorsProps (line 116) | interface IAssignModeratorsProps {
function AssignModerators (line 126) | function AssignModerators(props: IAssignModeratorsProps) {
FILE: packages/frontend-web/src/app/components/AssignTagsForm.tsx
constant STYLES (line 46) | const STYLES = stylesheet({
type IAssignTagsFormProps (line 84) | interface IAssignTagsFormProps {
function AssignTagsForm (line 91) | function AssignTagsForm(props: IAssignTagsFormProps) {
FILE: packages/frontend-web/src/app/components/Avatar/Avatar.tsx
constant AVATAR_TEXT_SCALE_FACTOR (line 27) | const AVATAR_TEXT_SCALE_FACTOR = 2;
function Avatar (line 38) | function Avatar(props: {
function PseudoAvatar (line 74) | function PseudoAvatar(props: {
FILE: packages/frontend-web/src/app/components/Button/Button.tsx
constant STYLES (line 29) | const STYLES = stylesheet({
type IButtonProps (line 50) | interface IButtonProps {
class Button (line 57) | class Button extends React.PureComponent<IButtonProps> {
method render (line 58) | render() {
FILE: packages/frontend-web/src/app/components/CanvasTruncate/CanvasTruncate.tsx
function clampLines (line 26) | function clampLines(text: string, width: number, fontStyles: ITypeStyle,...
function memoizedClampLines (line 50) | function memoizedClampLines(text: string, width: number, fontStyles: ITy...
type ICanvasTruncateProps (line 60) | interface ICanvasTruncateProps {
type ICanvasTruncateState (line 67) | interface ICanvasTruncateState {
class CanvasTruncate (line 71) | class CanvasTruncate extends React.PureComponent<ICanvasTruncateProps, I...
method componentDidMount (line 78) | componentDidMount() {
method componentWillUnmount (line 85) | componentWillUnmount() {
method onResize (line 90) | onResize() {
method saveElementRef (line 97) | saveElementRef(elem: HTMLDivElement) {
method render (line 101) | render() {
FILE: packages/frontend-web/src/app/components/CheckboxRow/CheckboxRow.tsx
constant GOOD_IMAGE_SIZE (line 29) | const GOOD_IMAGE_SIZE = 46;
constant STYLES (line 31) | const STYLES = stylesheet({
type ICheckboxRowProps (line 68) | interface ICheckboxRowProps<T> {
function CheckboxRow (line 77) | function CheckboxRow<T>(props: ICheckboxRowProps<T>) {
FILE: packages/frontend-web/src/app/components/CommentActionButton/CommentActionButton.tsx
constant ICON_SIZE (line 31) | const ICON_SIZE = 24;
constant STYLES (line 33) | const STYLES = stylesheet({
type ICommentActionProps (line 84) | interface ICommentActionProps {
type ICommentActionState (line 96) | interface ICommentActionState {
class CommentActionButton (line 101) | class CommentActionButton
method render (line 109) | render() {
method onMouseEnter (line 159) | onMouseEnter() {
method onMouseLeave (line 164) | onMouseLeave() {
method onFocus (line 169) | onFocus() {
method onBlur (line 174) | onBlur() {
FILE: packages/frontend-web/src/app/components/CommentList/CommentList.tsx
constant ARROW_SIZE (line 44) | const ARROW_SIZE = 6;
constant STYLES (line 46) | const STYLES = stylesheet({
constant ROW_FLEX_STYLE (line 88) | const ROW_FLEX_STYLE = {
constant DEFAULT_ROW_HEIGHT (line 94) | const DEFAULT_ROW_HEIGHT = 180;
constant ROW_PADDING_WITH_TITLE (line 95) | const ROW_PADDING_WITH_TITLE = 200;
constant ROW_PADDING_WITHOUT_TITLE (line 96) | const ROW_PADDING_WITHOUT_TITLE = 130;
constant SELECT_ALL_ID (line 98) | const SELECT_ALL_ID = 'select-all-checkbox';
type ICommentListProps (line 100) | interface ICommentListProps {
function CommentList (line 129) | function CommentList(props: ICommentListProps) {
FILE: packages/frontend-web/src/app/components/CommentList/components/CheckboxColumn/CheckboxColumn.tsx
constant STYLES (line 32) | const STYLES = stylesheet({
type ICheckboxColumnProps (line 51) | interface ICheckboxColumnProps {
function CheckboxColumn (line 59) | function CheckboxColumn(props: ICheckboxColumnProps) {
FILE: packages/frontend-web/src/app/components/CommentList/components/SortColumn/SortColumn.tsx
type ISortColumnProps (line 30) | interface ISortColumnProps extends React.HTMLProps<any> {
function SortColumn (line 37) | function SortColumn(props: ISortColumnProps) {
FILE: packages/frontend-web/src/app/components/CommentText.tsx
function linkifyLink (line 29) | function linkifyLink(decoratedHref: string, decoratedText: string, key: ...
function CommentText (line 37) | function CommentText(props: {
FILE: packages/frontend-web/src/app/components/ConfirmationCircle/ConfirmationCircle.tsx
constant STYLES (line 28) | const STYLES = stylesheet({
type IConfirmationCircleProps (line 47) | interface IConfirmationCircleProps {
class ConfirmationCircle (line 54) | class ConfirmationCircle extends React.PureComponent<IConfirmationCircle...
method render (line 55) | render() {
FILE: packages/frontend-web/src/app/components/ConfirmationCircle/ConfirmationCircleStory.tsx
constant APPROVE_COLOR (line 20) | const APPROVE_COLOR = '#27d073';
constant DEFER_COLOR (line 21) | const DEFER_COLOR = '#999999';
constant HIGHLIGHT_COLOR (line 22) | const HIGHLIGHT_COLOR = '#f9b453';
constant REJECT_COLOR (line 23) | const REJECT_COLOR = '#fc4a79';
type IConfirmationCircleProps (line 25) | interface IConfirmationCircleProps {
FILE: packages/frontend-web/src/app/components/DotChart/DotChart.tsx
constant ICON_SIZE (line 27) | const ICON_SIZE = 24;
constant STYLES (line 29) | const STYLES = stylesheet({
type IAppliedRule (line 67) | interface IAppliedRule {
type IDotChartProps (line 74) | interface IDotChartProps {
type IDotChartState (line 83) | interface IDotChartState {
class DotChart (line 87) | class DotChart extends React.PureComponent<IDotChartProps, IDotChartStat...
method constructor (line 94) | constructor(props: IDotChartProps) {
method toggleShowAll (line 109) | toggleShowAll() {
method saveCanvasRef (line 114) | saveCanvasRef(canvas: HTMLCanvasElement) {
method render (line 120) | render() {
FILE: packages/frontend-web/src/app/components/DotChart/DotChartStory.tsx
constant COLCOUNT (line 35) | const COLCOUNT = 100;
function randomTaggedComments (line 37) | function randomTaggedComments(count: number) {
function randomDatedComments (line 50) | function randomDatedComments(count: number, dateAge: number): Array<ICom...
function generateRules (line 63) | function generateRules() {
FILE: packages/frontend-web/src/app/components/ErrorRoot.tsx
type IErrorRootProps (line 23) | interface IErrorRootProps {
function ErrorRoot (line 28) | function ErrorRoot(props: React.PropsWithChildren<IErrorRootProps>) {
FILE: packages/frontend-web/src/app/components/ErrorRootStory.tsx
function noop (line 20) | function noop() {/**/}
FILE: packages/frontend-web/src/app/components/FlagsSummary.tsx
function FlagsSummary (line 21) | function FlagsSummary(props: {
FILE: packages/frontend-web/src/app/components/HeaderBar.tsx
constant STYLES (line 37) | const STYLES = stylesheet({
type IHeaderBarProps (line 91) | interface IHeaderBarProps {
function HeaderBar (line 98) | function HeaderBar(props: IHeaderBarProps) {
FILE: packages/frontend-web/src/app/components/Icons/IconBase.tsx
type IIconBaseProps (line 24) | interface IIconBaseProps {
FILE: packages/frontend-web/src/app/components/Icons/index.tsx
function makeIcon (line 22) | function makeIcon(contents: JSX.Element): React.ComponentClass<any> {
function renderSwatch (line 120) | function renderSwatch() {
FILE: packages/frontend-web/src/app/components/LazyLoadComment/CommentBodyStory.tsx
function doNothing (line 60) | async function doNothing() {/**/}
FILE: packages/frontend-web/src/app/components/LazyLoadComment/LazyLoadComment.tsx
constant AVATAR_SIZE (line 54) | const AVATAR_SIZE = 24;
type ILinkTargetGetter (line 56) | type ILinkTargetGetter = (commentId: ModelId) => string;
type IBasicBodyProps (line 58) | interface IBasicBodyProps {
type IBasicBodyState (line 71) | interface IBasicBodyState {
class BasicBody (line 76) | class BasicBody extends React.PureComponent<IBasicBodyProps, IBasicBodyS...
method popupOpen (line 83) | popupOpen(isOpen: boolean) {
method mouseEnter (line 88) | mouseEnter() {
method mouseLeave (line 93) | mouseLeave() {
method onModerateButtonClick (line 98) | onModerateButtonClick(comment: ICommentModel, action: IConfirmationAct...
method getActiveButtons (line 102) | getActiveButtons(): List<IModerationAction> {
method isModerated (line 114) | isModerated() {
method onClickModerateActions (line 121) | onClickModerateActions(action: IConfirmationAction): void {
method render (line 138) | render() {
type ILinkedBasicBodyProps (line 299) | interface ILinkedBasicBodyProps extends Omit<IBasicBodyProps, 'comment'> {
function LinkedBasicBody (line 304) | function LinkedBasicBody(props: ILinkedBasicBodyProps) {
FILE: packages/frontend-web/src/app/components/LazyLoadComment/components.tsx
function ArticleTitle (line 28) | function ArticleTitle({articleId}: {articleId: ModelId}) {
FILE: packages/frontend-web/src/app/components/MagicTimestamp.tsx
function MagicTimestamp (line 21) | function MagicTimestamp(props: {
FILE: packages/frontend-web/src/app/components/ModerateButtons/ModerateButtons.tsx
constant STYLES (line 43) | const STYLES = stylesheet({
type IModerateButtonsProps (line 88) | interface IModerateButtonsProps {
function ModerateButtons (line 102) | function ModerateButtons(props: IModerateButtonsProps) {
FILE: packages/frontend-web/src/app/components/NavigationTab/NavigationTab.tsx
constant STYLES (line 30) | const STYLES = stylesheet({
type INavigationTabProps (line 92) | interface INavigationTabProps {
class NavigationTab (line 101) | class NavigationTab
method render (line 104) | render() {
FILE: packages/frontend-web/src/app/components/NavigationTab/NavigationTabStory.tsx
constant STYLES (line 37) | const STYLES = {
FILE: packages/frontend-web/src/app/components/OverflowContainer/OverflowContainer.tsx
constant STYLES (line 34) | const STYLES = stylesheet({
type IContainerHeaderProps (line 81) | interface IContainerHeaderProps {
function ContainerHeader (line 86) | function ContainerHeader(props: IContainerHeaderProps) {
type IContainerFooterProps (line 97) | interface IContainerFooterProps {
function ContainerFooter (line 102) | function ContainerFooter(props: IContainerFooterProps) {
type IOverflowContainerProps (line 117) | interface IOverflowContainerProps {
class OverflowContainer (line 123) | class OverflowContainer extends React.PureComponent<IOverflowContainerPr...
method render (line 124) | render() {
FILE: packages/frontend-web/src/app/components/RuleBars/RuleBars.tsx
constant STYLES (line 29) | const STYLES = stylesheet({
function differentiateRules (line 68) | function differentiateRules(rules: List<IRuleModel>): Array<IRuleModel> {
type IRuleBarsProps (line 84) | interface IRuleBarsProps {
class RuleBars (line 89) | class RuleBars extends React.Component<IRuleBarsProps> {
method render (line 90) | render() {
FILE: packages/frontend-web/src/app/components/ScoresList/ScoresList.tsx
constant SCORE_ROW_STYLES (line 36) | const SCORE_ROW_STYLES = stylesheet({
type IScoreRowProps (line 52) | interface IScoreRowProps {
constant STYLES (line 72) | const STYLES = stylesheet({
type IScoresListProps (line 106) | interface IScoresListProps {
function ScoresList (line 113) | function ScoresList(props: IScoresListProps) {
FILE: packages/frontend-web/src/app/components/Scrim/Scrim.tsx
constant STYLES (line 23) | const STYLES = stylesheet({
type IScrimProps (line 39) | interface IScrimProps extends React.HTMLProps<any> {
class Scrim (line 46) | class Scrim extends React.PureComponent<IScrimProps> {
method render (line 47) | render() {
FILE: packages/frontend-web/src/app/components/Scrim/ScrimStory.tsx
constant STYLES (line 23) | const STYLES = {
FILE: packages/frontend-web/src/app/components/SearchAttribute/SearchAttribute.tsx
constant STYLES (line 24) | const STYLES = stylesheet({
type ISearchAttributeProps (line 53) | interface ISearchAttributeProps {
class SearchAttribute (line 58) | class SearchAttribute extends React.PureComponent<ISearchAttributeProps> {
method render (line 59) | render() {
FILE: packages/frontend-web/src/app/components/SearchHeader/SearchHeader.tsx
constant STYLES (line 29) | const STYLES = stylesheet({
function HeaderItem (line 79) | function HeaderItem(props: React.PropsWithChildren<{
function SearchHeader (line 95) | function SearchHeader(props: React.PropsWithChildren<{
FILE: packages/frontend-web/src/app/components/SearchHeader/SearchHeaderStory.tsx
constant STORY_STYLES (line 34) | const STORY_STYLES = {
FILE: packages/frontend-web/src/app/components/SingleComment/SingleComment.tsx
constant AVATAR_SIZE (line 80) | const AVATAR_SIZE = 60;
constant REPLY_WIDTH (line 82) | const REPLY_WIDTH = 642;
constant COMMENT_BODY_STYLES (line 86) | const COMMENT_BODY_STYLES = `
constant STYLES (line 143) | const STYLES = stylesheet({
constant PROFILE_STYLES (line 218) | const PROFILE_STYLES = stylesheet({
constant COMMENT_STYLES (line 292) | const COMMENT_STYLES = stylesheet({
type ISingleCommentProps (line 356) | interface ISingleCommentProps {
type ISingleCommentState (line 375) | interface ISingleCommentState {
class SingleComment (line 381) | class SingleComment extends React.PureComponent<ISingleCommentProps, ISi...
method handleEditCommentClick (line 394) | handleEditCommentClick() {
method saveAuthorLocationRef (line 401) | saveAuthorLocationRef(elem: HTMLDivElement) {
method saveAuthorNameRef (line 406) | saveAuthorNameRef(elem: HTMLSpanElement) {
method saveCommentTextRef (line 411) | saveCommentTextRef(elem: HTMLDivElement) {
method saveEditedCommentText (line 416) | saveEditedCommentText(e: React.FormEvent<any>) {
method cancelEditedCommentText (line 438) | cancelEditedCommentText(e: React.FormEvent<any>) {
method onEditMouseEnter (line 446) | onEditMouseEnter() {
method onEditMouseLeave (line 451) | onEditMouseLeave() {
method onEditFocus (line 456) | onEditFocus() {
method onEditBlur (line 461) | onEditBlur() {
method focusText (line 466) | focusText() {
method focusName (line 471) | focusName() {
method focusLocation (line 476) | focusLocation() {
method renderAuthor (line 480) | renderAuthor() {
method render (line 548) | render() {
FILE: packages/frontend-web/src/app/components/SingleComment/SingleCommentStory.tsx
constant STORY_STYLES (line 49) | const STORY_STYLES = {
FILE: packages/frontend-web/src/app/components/SingleComment/components/AnnotatedCommentText.tsx
constant TOOLTIP_ARROW_SIZE (line 46) | const TOOLTIP_ARROW_SIZE = 16;
constant STYLES (line 48) | const STYLES = stylesheet({
type IAnnotatedCommentTextProps (line 133) | interface IAnnotatedCommentTextProps {
type IAnnotatedCommentTextState (line 143) | interface IAnnotatedCommentTextState {
class AnnotatedCommentText (line 163) | class AnnotatedCommentText extends React.PureComponent<IAnnotatedComment...
method componentDidMount (line 192) | componentDidMount() {
method componentWillUnmount (line 199) | componentWillUnmount() {
method render (line 205) | render() {
method saveConfirmationRef (line 327) | saveConfirmationRef(el: HTMLElement) {
method saveTaggingRef (line 332) | saveTaggingRef(el: HTMLElement) {
method onMouseDown (line 337) | onMouseDown() {
method onMouseUp (line 343) | onMouseUp() {
method onTouchStart (line 350) | onTouchStart() {
method onTouchMove (line 356) | onTouchMove() {
method onTouchEnd (line 361) | onTouchEnd() {
method onPressEscape (line 368) | onPressEscape() {
method onGlobalClick (line 375) | onGlobalClick(e: any) {
method closeToolTips (line 390) | closeToolTips() {
method closeTaggingToolTip (line 401) | closeTaggingToolTip() {
method clearSelection (line 407) | clearSelection() {
method toggleConfirmationToolTip (line 416) | toggleConfirmationToolTip(score: ICommentScoreModel, event: React.Mous...
method closeConfirmationToolTip (line 445) | closeConfirmationToolTip() {
method handleSelection (line 454) | handleSelection(): void {
method positionToolTip (line 477) | positionToolTip(selection: Selection): void {
method tagText (line 495) | async tagText(id: string) {
method confirmTag (line 506) | async confirmTag() {
method undoTag (line 512) | async undoTag() {
method removeTag (line 527) | async removeTag() {
method taggifyText (line 532) | taggifyText(text: string, scores: Array<ICommentScoreModel>): JSX.Elem...
method addRange (line 575) | addRange(
FILE: packages/frontend-web/src/app/components/SingleComment/components/AuthorCounts.tsx
type IAuthorCountsProps (line 26) | interface IAuthorCountsProps {
function AuthorCounts (line 30) | function AuthorCounts(props: IAuthorCountsProps) {
FILE: packages/frontend-web/src/app/components/SingleComment/components/CommentTags.tsx
constant ICON_SIZE (line 42) | const ICON_SIZE = 24;
constant STYLES (line 44) | const STYLES = stylesheet({
type ICommentTagsProps (line 132) | interface ICommentTagsProps {
type ICommentTagsState (line 139) | interface ICommentTagsState {
class CommentTags (line 149) | class CommentTags extends React.PureComponent<ICommentTagsProps, ICommen...
method saveTaggingTooltipButtonRef (line 164) | saveTaggingTooltipButtonRef(ref: HTMLButtonElement) {
method saveTaggingTooltipRef (line 169) | saveTaggingTooltipRef(ref: HTMLDivElement) {
method render (line 173) | render() {
method calculateTaggingTriggerPosition (line 288) | calculateTaggingTriggerPosition(ref?: HTMLElement) {
method onPressEscape (line 304) | onPressEscape() {
method componentDidMount (line 308) | componentDidMount() {
method componentWillUnmount (line 314) | componentWillUnmount() {
method toggleTaggingToolTip (line 320) | toggleTaggingToolTip() {
method openTaggingTooltip (line 329) | openTaggingTooltip() {
method closeTaggingTooltip (line 337) | closeTaggingTooltip() {
method checkTaggingTooltipStatus (line 344) | checkTaggingTooltipStatus(e: Event) {
method tagComment (line 355) | tagComment(id: string): void {
method onMetaTagMouseEnter (line 361) | onMetaTagMouseEnter() {
method onMetaTagMouseLeave (line 366) | onMetaTagMouseLeave() {
method onMetaTagFocus (line 371) | onMetaTagFocus() {
method onMetaTagBlur (line 376) | onMetaTagBlur() {
FILE: packages/frontend-web/src/app/components/SingleComment/components/DetailRow.tsx
constant DETAIL_STYLES (line 25) | const DETAIL_STYLES = stylesheet({
constant DETAIL_LINK (line 56) | const DETAIL_LINK = {
constant ICON_SIZE (line 65) | const ICON_SIZE = 20;
type IDetailRowProps (line 67) | interface IDetailRowProps {
function EmailRow (line 80) | function EmailRow({author}: {author: IAuthorAttributes}) {
function SourceIdRow (line 100) | function SourceIdRow({authorSourceId}: {authorSourceId: string}) {
function ApprovalRatingRow (line 120) | function ApprovalRatingRow({approvalRating}: {approvalRating: string}) {
function IsSubscriberRow (line 135) | function IsSubscriberRow() {
FILE: packages/frontend-web/src/app/components/SingleComment/components/FlagsList.tsx
constant FLAGS_STYLES (line 23) | const FLAGS_STYLES = stylesheet({
function Flag (line 47) | function Flag(props: {flag: ICommentFlagModel}) {
function FlagsList (line 67) | function FlagsList(props: {commentId: ModelId}) {
FILE: packages/frontend-web/src/app/components/SingleComment/components/SummaryScore.tsx
constant STYLES (line 39) | const STYLES = stylesheet({
type ISummaryScoreProps (line 80) | interface ISummaryScoreProps {
function SummaryScore (line 86) | function SummaryScore(props: ISummaryScoreProps) {
type ISummaryScoresProps (line 110) | interface ISummaryScoresProps {
function SummaryScores (line 115) | function SummaryScores(props: ISummaryScoresProps) {
FILE: packages/frontend-web/src/app/components/Slider/RangeBar.tsx
constant BAR_HEIGHT (line 25) | const BAR_HEIGHT = 6;
constant STYLES (line 27) | const STYLES = stylesheet({
type IRange (line 36) | interface IRange {
type IRangeBarProps (line 41) | interface IRangeBarProps extends React.HTMLProps<any> {
type IRangeBarState (line 45) | interface IRangeBarState {
class RangeBar (line 49) | class RangeBar extends
method render (line 55) | render() {
method componentDidMount (line 71) | componentDidMount() {
method componentWillUnmount (line 77) | componentWillUnmount() {
method onResize (line 82) | onResize() {
FILE: packages/frontend-web/src/app/components/Slider/Slider.tsx
constant HANDLE_SIZE (line 38) | const HANDLE_SIZE = 22;
constant HANDLE_HIT_CONTAINER_SIZE (line 39) | const HANDLE_HIT_CONTAINER_SIZE = 44;
constant BAR_HEIGHT (line 40) | const BAR_HEIGHT = 6;
constant HANDLE_STYLES (line 42) | const HANDLE_STYLES = stylesheet({
type IHandleProps (line 95) | interface IHandleProps extends React.HTMLProps<any> {
type IHandleState (line 102) | interface IHandleState {
class Handle (line 106) | class Handle extends React.PureComponent<IHandleProps, IHandleState> {
method render (line 111) | render() {
method onMouseEnter (line 152) | onMouseEnter() {
method onMouseLeave (line 159) | onMouseLeave() {
method onFocus (line 166) | onFocus() {
method onBlur (line 171) | onBlur() {
type IDraggableHandleProps (line 176) | interface IDraggableHandleProps {
type IDraggableHandleState (line 184) | interface IDraggableHandleState {
class DraggableHandle (line 188) | class DraggableHandle extends
method saveSliderRef (line 197) | saveSliderRef(ref: HTMLDivElement) {
method render (line 201) | render() {
method onKeyUp (line 224) | onKeyUp(e: React.KeyboardEvent<any>) {
method componentDidMount (line 232) | componentDidMount() {
method componentWillUnmount (line 238) | componentWillUnmount() {
method onResize (line 243) | onResize() {
method onDragStart (line 253) | onDragStart(_: any, { x }: { x: number }) {
method onDragUpdate (line 262) | onDragUpdate(_: any, { x }: { x: number }) {
method onDragEnd (line 271) | onDragEnd(_: any, { x }: { x: number }) {
method convertToPercentage (line 283) | convertToPercentage(x: number): number {
constant SLIDER_STYLES (line 288) | const SLIDER_STYLES = {
type ISliderProps (line 297) | interface ISliderProps extends React.HTMLProps<any> {
class Slider (line 301) | class Slider extends React.PureComponent<ISliderProps> {
method render (line 302) | render() {
FILE: packages/frontend-web/src/app/components/SplashRoot.tsx
constant SPLASH_STYLES (line 21) | const SPLASH_STYLES = stylesheet({
function Bubbles (line 73) | function Bubbles() {
function SplashFrame (line 99) | function SplashFrame(props: React.PropsWithChildren<{}>) {
function SplashRoot (line 119) | function SplashRoot(props: React.PropsWithChildren<{}>) {
FILE: packages/frontend-web/src/app/components/TagLabelRow/TagLabelRow.tsx
constant STYLES (line 35) | const STYLES = stylesheet({
type ITagLabelRowProps (line 95) | interface ITagLabelRowProps extends IContextPathParams {
type ITagLabelRowState (line 104) | interface ITagLabelRowState {
class TagLabelRow (line 108) | class TagLabelRow
method handleRowEnter (line 116) | handleRowEnter() {
method handleRowLeave (line 123) | handleRowLeave() {
method render (line 129) | render() {
FILE: packages/frontend-web/src/app/components/TagLabelRow/TagLabelRowStory.tsx
constant ACTION_STYLES (line 40) | const ACTION_STYLES = {
constant SNAPSHOT_WIDTH (line 49) | const SNAPSHOT_WIDTH = 264;
constant SNAPSHOT_HEIGHT (line 50) | const SNAPSHOT_HEIGHT = 76;
FILE: packages/frontend-web/src/app/components/ThemeRoot.tsx
class ThemeRoot (line 40) | class ThemeRoot extends React.Component {
method render (line 41) | render() {
FILE: packages/frontend-web/src/app/components/Toast/Toast.tsx
constant STYLES (line 21) | const STYLES = stylesheet({
type IToastProps (line 46) | interface IToastProps {
class Toast (line 51) | class Toast extends React.PureComponent<IToastProps> {
method render (line 52) | render() {
FILE: packages/frontend-web/src/app/components/Toast/ToastMessage.tsx
constant STYLES (line 28) | const STYLES = stylesheet({
type IToastMessageProps (line 58) | interface IToastMessageProps {
class ToastMessage (line 64) | class ToastMessage extends React.PureComponent<IToastMessageProps> {
method render (line 65) | render() {
FILE: packages/frontend-web/src/app/components/Toast/ToastStory.tsx
constant STORY_STYLES (line 37) | const STORY_STYLES = {
FILE: packages/frontend-web/src/app/components/ToolTip/ToolTip.tsx
constant BUFFER (line 25) | const BUFFER = 16;
function makeArrowStyles (line 33) | function makeArrowStyles(direction: string, color: string, size: number)...
function setTranslation (line 178) | function setTranslation(direction = 'topCenter', size: number) {
constant STYLES (line 245) | const STYLES = stylesheet({
type ArrowPosition (line 254) | type ArrowPosition = 'topLeft' | 'topCenter' | 'topRight' | 'rightTop' |
type IToolTipProps (line 258) | interface IToolTipProps {
type IToolTipState (line 273) | interface IToolTipState {
class ToolTip (line 277) | class ToolTip extends React.PureComponent<IToolTipProps, IToolTipState> {
method componentDidMount (line 284) | componentDidMount() {
method componentWillUpdate (line 290) | componentWillUpdate(nextProps: IToolTipProps) {
method componentWillUnmount (line 298) | componentWillUnmount() {
method saveContainerRef (line 303) | saveContainerRef(el: HTMLDivElement) {
method checkClick (line 308) | checkClick(e: any) {
method render (line 321) | render() {
FILE: packages/frontend-web/src/app/components/ToolTip/ToolTipStory.tsx
type IToolTipTagProps (line 30) | interface IToolTipTagProps {
constant GREY_COLOR (line 34) | const GREY_COLOR = '#efefef';
constant TARGET_POSITION (line 36) | const TARGET_POSITION = {
constant CONTAINER_STYLES (line 41) | const CONTAINER_STYLES = {
constant MULTIPLE_TAG_STYLES (line 53) | const MULTIPLE_TAG_STYLES = {
constant SINGLE_TAG_STYLES (line 79) | const SINGLE_TAG_STYLES = {
constant INFO_TOOLTIP_STYLES (line 102) | const INFO_TOOLTIP_STYLES = {
class ToolTipWithTag (line 114) | class ToolTipWithTag extends React.PureComponent<IToolTipTagProps> {
method render (line 115) | render() {
class ToolTipWithTags (line 144) | class ToolTipWithTags extends React.PureComponent<object> {
method render (line 145) | render() {
class InfoToolTip (line 209) | class InfoToolTip extends React.PureComponent<object> {
method render (line 210) | render() {
constant TARGET_STYLE (line 221) | const TARGET_STYLE = {
class ToolTipTarget (line 232) | class ToolTipTarget extends React.PureComponent<object> {
method render (line 233) | render() {
FILE: packages/frontend-web/src/app/components/article_controls.tsx
type IControlFlagProps (line 40) | interface IControlFlagProps {
class ControlFlag (line 45) | class ControlFlag extends React.Component<IControlFlagProps> {
method render (line 46) | render() {
type IControlPopupProps (line 68) | interface IControlPopupProps {
type IControlPopupState (line 74) | interface IControlPopupState {
class ArticleControlPopup (line 79) | class ArticleControlPopup extends React.Component<IControlPopupProps, IC...
method constructor (line 80) | constructor(props: Readonly<IControlPopupProps>) {
method handleCommentingEnabledClicked (line 89) | handleCommentingEnabledClicked() {
method handleAutoModeratedClicked (line 94) | handleAutoModeratedClicked() {
method saveControls (line 102) | saveControls() {
method render (line 106) | render() {
type IArticleControlIconProps (line 151) | interface IArticleControlIconProps {
class ArticleControlIcon (line 163) | class ArticleControlIcon extends React.Component<IArticleControlIconProp...
method setAnchorElement (line 167) | setAnchorElement(node: any) {
method setOpen (line 172) | setOpen() {
method render (line 182) | render() {
FILE: packages/frontend-web/src/app/components/styles.ts
constant COMMENT_HEADER_BACKGROUND_COLOR (line 12) | const COMMENT_HEADER_BACKGROUND_COLOR = '#F5F7F9';
constant ROW_STYLES (line 14) | const ROW_STYLES = stylesheet({
FILE: packages/frontend-web/src/app/config.ts
function get_config (line 30) | function get_config() {
function getString (line 37) | function getString(key: string, fallback: string): string {
function getBoolean (line 55) | function getBoolean(key: string, fallback: boolean): boolean {
constant DEVELOPMENT (line 77) | const DEVELOPMENT = (typeof __DEVELOPMENT__ !== 'undefined') ? __DEVELOP...
constant API_URL (line 78) | const API_URL = getString('API_URL', (typeof ENV_API_URL !== 'undefined'...
constant APP_NAME (line 79) | const APP_NAME = getString('APP_NAME', (typeof ENV_APP_NAME !== 'undefin...
constant REQUIRE_REASON_TO_REJECT (line 80) | const REQUIRE_REASON_TO_REJECT = getBoolean('REQUIRE_REASON_TO_REJECT', ...
constant COMMENTS_EDITABLE_FLAG (line 81) | const COMMENTS_EDITABLE_FLAG = getBoolean('COMMENTS_EDITABLE_FLAG', (typ...
constant RESTRICT_TO_SESSION (line 82) | const RESTRICT_TO_SESSION = getBoolean('RESTRICT_TO_SESSION', (typeof EN...
constant MODERATOR_GUIDELINES_URL (line 83) | const MODERATOR_GUIDELINES_URL = getString('MODERATOR_GUIDELINES_URL', (...
constant SUBMIT_FEEDBACK_URL (line 84) | const SUBMIT_FEEDBACK_URL = getString('SUBMIT_FEEDBACK_URL', (typeof ENV...
constant DEFAULT_DRAG_HANDLE_POS1 (line 85) | const DEFAULT_DRAG_HANDLE_POS1 = 0;
constant DEFAULT_DRAG_HANDLE_POS2 (line 86) | const DEFAULT_DRAG_HANDLE_POS2 = 0.2;
constant DATE_FORMAT_LONG (line 87) | const DATE_FORMAT_LONG = 'MMM. d, yyyy h:mm a';
constant DATE_FORMAT_MDY (line 88) | const DATE_FORMAT_MDY = 'MMM. d, yyyy';
constant DATE_FORMAT_HM (line 89) | const DATE_FORMAT_HM = 'h:mm a';
constant COLCOUNT (line 90) | const COLCOUNT = 100;
FILE: packages/frontend-web/src/app/injectors/articleFetchQueue.ts
type IArticleCacheProps (line 24) | interface IArticleCacheProps {
function fetchArticle (line 29) | async function fetchArticle(articleId: ModelId) {
function ensureCache (line 34) | function ensureCache(articleId: ModelId) {
function getCachedArticle (line 41) | function getCachedArticle(state: IAppState, articleId: ModelId): IArticl...
FILE: packages/frontend-web/src/app/injectors/articleInjector.ts
function useCachedArticle (line 22) | function useCachedArticle(articleId: ModelId): IArticleCacheProps {
FILE: packages/frontend-web/src/app/injectors/commentFetchQueue.ts
type ICommentCacheProps (line 21) | interface ICommentCacheProps {
function getCachedComment (line 26) | function getCachedComment(state: IAppState, commentId: ModelId): ICommen...
FILE: packages/frontend-web/src/app/injectors/commentInjector.ts
type ICommentInjectorInputProps (line 24) | interface ICommentInjectorInputProps {
function getCommentFromCommentId (line 28) | function getCommentFromCommentId(state: IAppState, {commentId}: IComment...
function getCommentFromRoute (line 34) | function getCommentFromRoute(state: IAppState, props: RouteComponentProp...
function useCachedComment (line 40) | function useCachedComment(commentId: ModelId): ICommentCacheProps {
FILE: packages/frontend-web/src/app/injectors/contextInjector.ts
type IContextInjectorProps (line 26) | interface IContextInjectorProps {
function mapStateToProps (line 35) | function mapStateToProps(
function useRouteContext (line 62) | function useRouteContext() {
FILE: packages/frontend-web/src/app/main.tsx
function _Root (line 40) | function _Root(props: React.PropsWithChildren<RouteComponentProps<{}>>) {
FILE: packages/frontend-web/src/app/platform/dataService.ts
type IValidModelNames (line 46) | type IValidModelNames =
function serializeParams (line 55) | function serializeParams(originalParams?: Partial<IParams> | null): stri...
constant BASE_RANGE_ATTRIBUTES (line 70) | const BASE_RANGE_ATTRIBUTES = ['categoryId', 'tagId', 'lowerThreshold', ...
constant AUTH_URL (line 75) | const AUTH_URL = `/auth`;
constant SERVICES_URL (line 80) | const SERVICES_URL = `/services`;
function serviceURL (line 85) | function serviceURL(service: string, path?: string | null, params?: Part...
function listTextSizesByIds (line 89) | async function listTextSizesByIds(
function packCommentScoreData (line 102) | function packCommentScoreData(data: Array<{ commentId: ModelId, score: n...
function packCommentDateData (line 106) | function packCommentDateData(data: Array<{ commentId: ModelId, date: str...
function listHistogramScoresByArticle (line 113) | async function listHistogramScoresByArticle(
function listMaxSummaryScoreByArticle (line 125) | async function listMaxSummaryScoreByArticle(
function listHistogramScoresByArticleByDate (line 136) | async function listHistogramScoresByArticleByDate(
function listHistogramScoresByCategory (line 147) | async function listHistogramScoresByCategory(
function listMaxHistogramScoresByCategory (line 159) | async function listMaxHistogramScoresByCategory(
function listHistogramScoresByCategoryByDate (line 170) | async function listHistogramScoresByCategoryByDate(
function getComments (line 181) | async function getComments(
function search (line 192) | async function search(
function editAndRescoreCommentRequest (line 209) | async function editAndRescoreCommentRequest(
function updateCategoryModerators (line 227) | async function updateCategoryModerators(categoryId: ModelId, moderatorId...
function updateArticleModerators (line 232) | async function updateArticleModerators(articleId: ModelId, moderatorIds:...
type IModeratedComments (line 237) | type IModeratedComments = Readonly<{
function getModeratedCommentIdsForArticle (line 248) | async function getModeratedCommentIdsForArticle(
function getModeratedCommentIdsForCategory (line 259) | async function getModeratedCommentIdsForCategory(
function getArticles (line 270) | async function getArticles(ids: Array<ModelId>): Promise<Array<IArticleM...
function getArticleText (line 276) | async function getArticleText(id: ModelId) {
function updateArticle (line 282) | async function updateArticle(id: string, isCommentingEnabled: boolean, i...
function createThing (line 287) | async function createThing(url: string, attributes: {[key: string]: stri...
function updateThing (line 298) | async function updateThing(url: string, attributes: {[key: string]: stri...
function createUser (line 309) | async function createUser(user: IUserModel) {
function createTag (line 315) | async function createTag(tag: ITagModel) {
function createRule (line 322) | async function createRule(rule: IRuleModel) {
function createPreselect (line 328) | async function createPreselect(preselect: IPreselectModel) {
function createSensitivity (line 334) | async function createSensitivity(sensitivity: ITaggingSensitivityModel) {
function updateUser (line 340) | async function updateUser(user: IUserModel) {
function updateTag (line 346) | async function updateTag(tag: ITagModel) {
function updateRule (line 353) | async function updateRule(rule: IRuleModel) {
function updatePreselect (line 359) | async function updatePreselect(preselect: IPreselectModel) {
function updateSensitivity (line 365) | async function updateSensitivity(sensitivity: ITaggingSensitivityModel) {
function destroyModel (line 374) | async function destroyModel(
function getCommentScores (line 381) | async function getCommentScores(commentId: string): Promise<Array<IComme...
function getCommentFlags (line 387) | async function getCommentFlags(commentId: string): Promise<Array<ICommen...
function checkServerStatus (line 393) | async function checkServerStatus(): Promise<ServerStates> {
function checkAuthorization (line 413) | async function checkAuthorization(): Promise<void> {
function makeCommentAction (line 419) | async function makeCommentAction(path: string, ids: Array<string>): Prom...
function makeCommentActionForId (line 430) | async function makeCommentActionForId(path: string, commentId: string): ...
function deleteCommentScoreRequest (line 435) | async function deleteCommentScoreRequest(commentId: string, commentScore...
function highlightCommentsRequest (line 440) | function highlightCommentsRequest(ids: Array<string>): Promise<void> {
function resetCommentsRequest (line 444) | function resetCommentsRequest(ids: Array<string>): Promise<void> {
function approveCommentsRequest (line 448) | function approveCommentsRequest(ids: Array<string>): Promise<void> {
function approveFlagsAndCommentsRequest (line 452) | function approveFlagsAndCommentsRequest(ids: Array<string>): Promise<voi...
function resolveFlagsRequest (line 456) | function resolveFlagsRequest(ids: Array<string>): Promise<void> {
function deferCommentsRequest (line 460) | function deferCommentsRequest(ids: Array<string>): Promise<void> {
function rejectCommentsRequest (line 464) | function rejectCommentsRequest(ids: Array<string>): Promise<void> {
function rejectFlagsAndCommentsRequest (line 468) | function rejectFlagsAndCommentsRequest(ids: Array<string>): Promise<void> {
function tagCommentsRequest (line 472) | function tagCommentsRequest(ids: Array<string>, tagId: string): Promise<...
function tagCommentSummaryScoresRequest (line 476) | function tagCommentSummaryScoresRequest(ids: Array<string>, tagId: strin...
function confirmCommentSummaryScoreRequest (line 480) | async function confirmCommentSummaryScoreRequest(commentId: string, tagI...
function rejectCommentSummaryScoreRequest (line 485) | async function rejectCommentSummaryScoreRequest(commentId: string, tagId...
function tagCommentsAnnotationRequest (line 489) | async function tagCommentsAnnotationRequest(commentId: string, tagId: st...
function confirmCommentScoreRequest (line 501) | async function confirmCommentScoreRequest(commentId: string, commentScor...
function rejectCommentScoreRequest (line 506) | async function rejectCommentScoreRequest(commentId: string, commentScore...
function resetCommentScoreRequest (line 511) | async function resetCommentScoreRequest(commentId: string, commentScoreI...
function listAuthorCounts (line 516) | async function listAuthorCounts(
function listSystemUsers (line 527) | async function listSystemUsers(type: string): Promise<List<IUserModel>> {
function kickProcessor (line 535) | async function kickProcessor(type: string): Promise<void> {
function activateCommentSource (line 539) | async function activateCommentSource(categoryId: ModelId, activate: bool...
function syncCommentSource (line 543) | async function syncCommentSource(categoryId: ModelId): Promise<void> {
type IApiConfiguration (line 547) | interface IApiConfiguration {
function getOAuthConfig (line 552) | async function getOAuthConfig(): Promise<IApiConfiguration> {
function updateOAuthConfig (line 557) | async function updateOAuthConfig(config: IApiConfiguration): Promise<voi...
FILE: packages/frontend-web/src/app/platform/localStore.ts
constant LOCAL_STORAGE_AUTH_TOKEN_KEY (line 24) | const LOCAL_STORAGE_AUTH_TOKEN_KEY = 'moderator/auth_token';
function getToken (line 40) | function getToken(): string | undefined {
function saveToken (line 44) | function saveToken(token: string | null): string {
function versionKey (line 55) | function versionKey(key: string) {
function dataKey (line 58) | function dataKey(key: string) {
function getStoreItem (line 62) | function getStoreItem(key: string, version: number) {
function saveStoreItem (line 76) | function saveStoreItem(key: string, version: number, data: string) {
FILE: packages/frontend-web/src/app/platform/types.ts
type IParams (line 17) | interface IParams {
FILE: packages/frontend-web/src/app/platform/websocketService.ts
constant STATUS_DOWN (line 54) | const STATUS_DOWN = 'down';
constant STATUS_UP (line 55) | const STATUS_UP = 'up';
constant STATUS_RESET (line 56) | const STATUS_RESET = 'reset';
type ISystemData (line 58) | interface ISystemData {
type IAllArticlesData (line 66) | interface IAllArticlesData {
type IArticleUpdate (line 71) | interface IArticleUpdate {
type IPerUserData (line 76) | interface IPerUserData {
function packSystemData (line 82) | function packSystemData(data: any): ISystemData {
function packArticleData (line 102) | function packArticleData(data: any): IAllArticlesData {
function packArticleUpdate (line 118) | function packArticleUpdate(data: any): IArticleUpdate {
function connectNotifier (line 144) | function connectNotifier(
function disconnectNotifier (line 213) | function disconnectNotifier() {
FILE: packages/frontend-web/src/app/scenes/Comments/Comments.tsx
function redirect (line 37) | function redirect(to: string) {
constant STYLES (line 43) | const STYLES = stylesheet({
function Comments (line 51) | function Comments(_props: { }) {
FILE: packages/frontend-web/src/app/scenes/Comments/components/CommentDetail/CommentDetail.tsx
constant COMMENT_WRAPPER_WIDTH (line 119) | const COMMENT_WRAPPER_WIDTH = 696;
constant KEYBOARD_SHORTCUTS_POPUP_ID (line 120) | const KEYBOARD_SHORTCUTS_POPUP_ID = 'keyboard-shortcuts';
constant SCORES_POPUP_ID (line 121) | const SCORES_POPUP_ID = 'scores-popup';
constant CONFIRMATION_POPUP_ID (line 122) | const CONFIRMATION_POPUP_ID = 'confirmation-popup';
constant INFO_DROPDOWN_ID (line 123) | const INFO_DROPDOWN_ID = 'info-dropdown';
constant APPROVE_SHORTCUT (line 124) | const APPROVE_SHORTCUT = 'alt + a';
constant REJECT_SHORTCUT (line 125) | const REJECT_SHORTCUT = 'alt + r';
constant DEFER_SHORTCUT (line 126) | const DEFER_SHORTCUT = 'alt + d';
constant HIGHLIGHT_SHORTCUT (line 127) | const HIGHLIGHT_SHORTCUT = 'alt + h';
constant ESCAPE_SHORTCUT (line 128) | const ESCAPE_SHORTCUT = 'escape';
constant PREV_SHORTCUT (line 129) | const PREV_SHORTCUT = 'alt + up';
constant NEXT_SHORTCUT (line 130) | const NEXT_SHORTCUT = 'alt + down';
constant STYLES (line 132) | const STYLES = stylesheet({
type IReplyLinkProps (line 305) | interface IReplyLinkProps extends RouteComponentProps<ICommentDetailsPat...
function _ReplyLink (line 310) | function _ReplyLink(props: IReplyLinkProps) {
type ICommentDetailProps (line 333) | interface ICommentDetailProps extends RouteComponentProps<ICommentDetail...
type ICommentDetailState (line 350) | interface ICommentDetailState {
class CommentDetail (line 373) | class CommentDetail extends React.Component<ICommentDetailProps, ICommen...
method componentDidMount (line 395) | componentDidMount() {
method componentWillUnmount (line 399) | componentWillUnmount() {
method getDerivedStateFromProps (line 403) | static getDerivedStateFromProps(nextProps: ICommentDetailProps, prevSt...
method onFocusUpArrow (line 423) | onFocusUpArrow() {
method onBlurUpArrow (line 428) | onBlurUpArrow() {
method onFocusDownArrow (line 433) | onFocusDownArrow() {
method onBlurDownArrow (line 438) | onBlurDownArrow() {
method onFocusInfoIcon (line 443) | onFocusInfoIcon() {
method onBlurInfoIcon (line 448) | onBlurInfoIcon() {
method saveButtonRef (line 453) | saveButtonRef(ref: HTMLButtonElement) {
method saveReturnRow (line 458) | saveReturnRow(commentId: string): void {
method handleAssignTagsSubmit (line 463) | async handleAssignTagsSubmit(commentId: ModelId, selectedTagIds: Set<M...
method render (line 473) | render() {
method generatePagingLink (line 760) | generatePagingLink(commentId: string) {
method calculateInfoTrigger (line 773) | calculateInfoTrigger(ref: any) {
method onResize (line 790) | onResize() {
method onKeyboardOpen (line 795) | onKeyboardOpen() {
method onKeyboardClose (line 800) | onKeyboardClose() {
method onDropdownOpen (line 805) | onDropdownOpen() {
method onDropdownClose (line 811) | onDropdownClose() {
method onScoresModalClose (line 816) | onScoresModalClose() {
method attachEvents (line 821) | attachEvents() {
method detachEvents (line 834) | detachEvents() {
method onBackClick (line 846) | onBackClick() {
method moderateComment (line 851) | async moderateComment(action: IModerationAction) {
method approveComment (line 878) | approveComment() {
method rejectComment (line 883) | rejectComment() {
method deferComment (line 888) | deferComment() {
method highlightComment (line 893) | highlightComment() {
method goToPrevComment (line 898) | goToPrevComment() {
method goToNextComment (line 910) | goToNextComment() {
method onTagButtonClick (line 922) | async onTagButtonClick(tagId: string) {
method onAnnotateTagButtonClick (line 929) | async onAnnotateTagButtonClick(tag: string, start: number, end: number...
method onCommentTagClick (line 936) | async onCommentTagClick(commentScore: ICommentScoreModel) {
method closeToast (line 942) | closeToast() {
method getActiveButtons (line 946) | getActiveButtons(comment: ICommentModel): List<IModerationAction> {
method handleScoreClick (line 969) | handleScoreClick(scoreClicked: ICommentSummaryScoreModel) {
method onPressEscape (line 984) | onPressEscape() {
FILE: packages/frontend-web/src/app/scenes/Comments/components/CommentDetail/index.ts
type ICommentDetailOwnProps (line 41) | type ICommentDetailOwnProps = Pick<ICommentDetailProps, 'match' | 'locat...
type ICommentDetailDispatchProps (line 43) | type ICommentDetailDispatchProps = Pick<
function getPagingIdentifier (line 49) | function getPagingIdentifier(location: Location): string | null {
function mapDispatchToProps (line 95) | function mapDispatchToProps(dispatch: IAppDispatch): ICommentDetailDispa...
FILE: packages/frontend-web/src/app/scenes/Comments/components/CommentDetail/store.ts
function loadScores (line 40) | async function loadScores(dispatch: IAppDispatch, id: string) {
type ICommentScoreState (line 46) | interface ICommentScoreState {
type ICommentPagingState (line 89) | interface ICommentPagingState {
type ICommentPagingStateRecord (line 98) | type ICommentPagingStateRecord = Readonly<ICommentPagingState>;
function hashString (line 110) | function hashString(str: string): string {
function getCommentPagingRecord (line 151) | function getCommentPagingRecord(state: IAppState) {
function getPagingIsFromBatch (line 155) | function getPagingIsFromBatch(state: IAppState) {
function getPagingSource (line 160) | function getPagingSource(state: IAppState, currentHash: string) {
function getPagingLink (line 167) | function getPagingLink(state: IAppState, currentHash: string) {
function getPagingHash (line 174) | function getPagingHash(state: IAppState) {
function getPagingCommentIds (line 179) | function getPagingCommentIds(state: IAppState) {
function getPagingCommentIndexes (line 184) | function getPagingCommentIndexes(state: IAppState) {
function getCurrentCommentIndex (line 189) | function getCurrentCommentIndex(state: IAppState, currentHash: string, c...
function getNextCommentId (line 202) | function getNextCommentId(state: IAppState, currentHash: string, comment...
function getPreviousCommentId (line 218) | function getPreviousCommentId(state: IAppState, currentHash: string, com...
type ICommentDetailState (line 234) | type ICommentDetailState = Readonly<{
function getScores (line 246) | function getScores(state: IAppState) {
FILE: packages/frontend-web/src/app/scenes/Comments/components/ModeratedComments/ModeratedComments.tsx
constant ARROW_SIZE (line 83) | const ARROW_SIZE = 6;
constant MODERATION_CONTAINER_HEIGHT (line 85) | const MODERATION_CONTAINER_HEIGHT = 269;
constant MODERATION_CONTAINER_HEIGHT_SHORT (line 86) | const MODERATION_CONTAINER_HEIGHT_SHORT = 202;
constant TOAST_DELAY (line 87) | const TOAST_DELAY = 6000;
constant ACTION_PLURAL (line 107) | const ACTION_PLURAL: {
constant STYLES (line 117) | const STYLES = stylesheet({
constant LOADING_COMMENTS_MESSAGING (line 245) | const LOADING_COMMENTS_MESSAGING = 'Loading comments.';
constant NO_COMMENTS_MESSAGING (line 246) | const NO_COMMENTS_MESSAGING = 'No matching comments found.';
type IModeratedCommentsProps (line 265) | interface IModeratedCommentsProps extends RouteComponentProps<IModerated...
type IModeratedCommentsState (line 285) | interface IModeratedCommentsState {
class ModeratedComments (line 309) | class ModeratedComments
method componentDidMount (line 334) | componentDidMount() {
method componentWillUnmount (line 338) | componentWillUnmount() {
method getDerivedStateFromProps (line 342) | static getDerivedStateFromProps(props: IModeratedCommentsProps, state:...
method render (line 373) | render() {
method matchAction (line 597) | matchAction(action: string): any {
method onPressEscape (line 616) | onPressEscape() {
method getSelectedIDs (line 624) | getSelectedIDs(): Array<string> {
method confirmationClose (line 632) | confirmationClose() {
method triggerActionToast (line 637) | triggerActionToast(action: ICommentAction, count: number, callback?: (...
method onTagButtonClick (line 664) | onTagButtonClick(tagId: string) {
method onTableScroll (line 671) | onTableScroll(position: number) {
method calculateTaggingTriggerPosition (line 677) | calculateTaggingTriggerPosition(ref: any) {
method toggleTaggingToolTip (line 693) | toggleTaggingToolTip() {
method handleAssignTagsSubmit (line 700) | async handleAssignTagsSubmit(commentId: ModelId, selectedTagIds: Set<M...
method dispatchConfirmedAction (line 708) | async dispatchConfirmedAction(action: IConfirmationAction, ids: Array<...
method onConfirmationClose (line 731) | onConfirmationClose() {
method handleUndoClick (line 736) | handleUndoClick() {
method onSortChange (line 742) | onSortChange(event: React.FormEvent<any>) {
method getSortOptions (line 758) | getSortOptions(): List<ITagModel> {
method onSelectAllChange (line 776) | async onSelectAllChange() {
method onSelectionChange (line 781) | async onSelectionChange(id: string) {
method openPopup (line 786) | openPopup() {
method closePopup (line 791) | closePopup() {
method applyRules (line 796) | applyRules(isCommentingEnabled: boolean, isAutoModerated: boolean): vo...
FILE: packages/frontend-web/src/app/scenes/Comments/components/ModeratedComments/index.ts
type IModeratedCommentsDispatchProps (line 44) | type IModeratedCommentsDispatchProps = Pick<
function mapDispatchToProps (line 72) | function mapDispatchToProps(dispatch: IAppDispatch): IModeratedCommentsD...
FILE: packages/frontend-web/src/app/scenes/Comments/components/ModeratedComments/store/commentListLoader.ts
function loadCommentList (line 35) | function loadCommentList(
FILE: packages/frontend-web/src/app/scenes/Comments/components/ModeratedComments/store/index.ts
type IModeratedCommentsGlobalState (line 24) | type IModeratedCommentsGlobalState = Readonly<{
FILE: packages/frontend-web/src/app/scenes/Comments/components/ModeratedComments/store/moderatedComments.ts
constant ACTION_PLURAL (line 30) | const ACTION_PLURAL: {
type ILoadModeratedCommentsForArticleCompletePayload (line 44) | type ILoadModeratedCommentsForArticleCompletePayload = {
type ILoadModeratedCommentsForCategoriesCompletePayload (line 52) | type ILoadModeratedCommentsForCategoriesCompletePayload = {
type ISetCommentsModerationForArticlesPayload (line 60) | type ISetCommentsModerationForArticlesPayload = {
type ISetCommentsModerationForCategoriesPayload (line 70) | type ISetCommentsModerationForCategoriesPayload = {
function loadModeratedCommentsForArticle (line 80) | async function loadModeratedCommentsForArticle(
function loadModeratedCommentsForCategory (line 90) | async function loadModeratedCommentsForCategory(
function setCommentsModerationStatus (line 100) | function setCommentsModerationStatus(
function shouldRemoveFromList (line 125) | function shouldRemoveFromList(currentModeration: string, moderationActio...
type IModeratedCommentsState (line 134) | type IModeratedCommentsState = Readonly<{
function getRecord (line 252) | function getRecord(state: IAppState) {
function getIsLoading (line 256) | function getIsLoading(state: IAppState) {
function getModeratedComments (line 261) | function getModeratedComments(state: IAppState, params: IModeratedCommen...
FILE: packages/frontend-web/src/app/scenes/Comments/components/NewComments/NewComments.tsx
constant ARROW_SIZE (line 106) | const ARROW_SIZE = 6;
constant TOAST_DELAY (line 107) | const TOAST_DELAY = 6000;
constant ACTION_PLURAL (line 109) | const ACTION_PLURAL: {
constant LOADING_COMMENTS_MESSAGING (line 119) | const LOADING_COMMENTS_MESSAGING = 'Loading comments.';
constant NO_COMMENTS_MESSAGING (line 120) | const NO_COMMENTS_MESSAGING = 'No matching comments found.';
constant STYLES (line 122) | const STYLES = stylesheet({
type INewCommentsProps (line 263) | interface INewCommentsProps extends RouteComponentProps<INewCommentsPath...
type INewCommentsState (line 281) | interface INewCommentsState {
class NewComments (line 312) | class NewComments extends React.Component<INewCommentsProps, INewComment...
method getDerivedStateFromProps (line 337) | static getDerivedStateFromProps(props: INewCommentsProps, state: INewC...
method componentDidUpdate (line 421) | async componentDidUpdate(_prevProps: INewCommentsProps) {
method componentDidMount (line 434) | componentDidMount() {
method componentWillUnmount (line 438) | componentWillUnmount() {
method onPressEscape (line 443) | onPressEscape() {
method saveListContainerRef (line 452) | saveListContainerRef(ref: HTMLDivElement) {
method handleAssignTagsSubmit (line 457) | async handleAssignTagsSubmit(commentId: ModelId, selectedTagIds: Set<M...
method render (line 469) | render() {
method matchAction (line 806) | matchAction(action: ICommentAction) {
method triggerActionToast (line 823) | triggerActionToast(action: ICommentAction, count: number, callback: (a...
method handleRemoveAutomatedRule (line 849) | handleRemoveAutomatedRule(rule: IRuleModel) {
method calculateTaggingTriggerPosition (line 860) | calculateTaggingTriggerPosition(ref: any) {
method onTagButtonClick (line 876) | onTagButtonClick(tagId: string) {
method toggleTaggingToolTip (line 883) | toggleTaggingToolTip() {
method dispatchConfirmedAction (line 890) | async dispatchConfirmedAction(action: ICommentAction, ids?: Array<stri...
method getSelectedIDs (line 902) | getSelectedIDs(): Array<string> {
method confirmationClose (line 908) | confirmationClose() {
method onRuleInfoClose (line 913) | onRuleInfoClose() {
method handleUndoClick (line 918) | handleUndoClick() {
method setQueryStringParam (line 924) | setQueryStringParam(pos1: number, pos2: number, sort: string): void {
method saveCommentRow (line 943) | saveCommentRow(commentId: string): void {
method onSortChange (line 948) | onSortChange(event: React.FormEvent<any>) {
method onBatchCommentsChangeEnd (line 955) | onBatchCommentsChangeEnd(_commentIds: Array<number>, pos1: number, pos...
method onSelectAllChange (line 960) | async onSelectAllChange() {
method onSelectionChange (line 965) | async onSelectionChange(id: string) {
method onTableScroll (line 970) | onTableScroll(position: number) {
method openPopup (line 976) | openPopup() {
method closePopup (line 981) | closePopup() {
method applyRules (line 986) | applyRules(isCommentingEnabled: boolean, isAutoModerated: boolean): vo...
FILE: packages/frontend-web/src/app/scenes/Comments/components/NewComments/components/BatchSelector/BatchSelector.tsx
constant ARROW_SIZE (line 46) | const ARROW_SIZE = 6;
constant STYLES (line 48) | const STYLES = stylesheet({
type IBatchSelectorProps (line 88) | interface IBatchSelectorProps {
type IBatchSelectorState (line 100) | interface IBatchSelectorState {
class BatchSelector (line 107) | class BatchSelector
method componentWillUpdate (line 119) | componentWillUpdate(nextProps: IBatchSelectorProps) {
method render (line 142) | render() {
method getSelectedComments (line 213) | private getSelectedComments(selectionPosition1: number, selectionPosit...
method onHandle1Change (line 237) | onHandle1Change(num: number) {
method onHandle2Change (line 248) | onHandle2Change(num: number) {
method onHandle1ChangeEnd (line 259) | onHandle1ChangeEnd(num: number) {
method onHandle2ChangeEnd (line 266) | onHandle2ChangeEnd(num: number) {
method onDataChange (line 272) | onDataChange(pos1: number, pos2: number): void {
method onDataChangeEnd (line 282) | onDataChangeEnd(pos1: number, pos2: number): void {
method convertPositionToLabel (line 292) | private convertPositionToLabel(x: number) {
FILE: packages/frontend-web/src/app/scenes/Comments/components/NewComments/index.ts
function mapDispatchToProps (line 49) | function mapDispatchToProps(dispatch: IAppDispatch): Partial<INewComment...
FILE: packages/frontend-web/src/app/scenes/Comments/components/NewComments/store/commentListLoader.ts
function loadCommentList (line 38) | function loadCommentList(
FILE: packages/frontend-web/src/app/scenes/Comments/components/NewComments/store/commentScores.ts
type ILoadCommentScoresCompletePayload (line 33) | type ILoadCommentScoresCompletePayload = {
function loadCommentScoresForArticle (line 44) | async function loadCommentScoresForArticle(
function loadCommentScoresForCategory (line 70) | async function loadCommentScoresForCategory(
type ICommentScoresState (line 96) | type ICommentScoresState = Readonly<{
function getStoreRecord (line 130) | function getStoreRecord(state: IAppState) {
function getIsLoading (line 134) | function getIsLoading(state: IAppState) {
function getCommentScores (line 139) | function getCommentScores(state: IAppState) {
FILE: packages/frontend-web/src/app/scenes/Comments/components/NewComments/store/index.ts
type INewCommentsState (line 24) | type INewCommentsState = Readonly<{
FILE: packages/frontend-web/src/app/scenes/Comments/components/NewComments/store/util.ts
function getTagsWithDateAndSummary (line 39) | function getTagsWithDateAndSummary(state: IAppState): List<ITagModel> {
function getSelectedTag (line 43) | function getSelectedTag(state: IAppState, tag?: string): ITagModel | null {
function getCommentIDsInRange (line 47) | function getCommentIDsInRange(
FILE: packages/frontend-web/src/app/scenes/Comments/components/Shortcuts/Shortcuts.tsx
constant KEY (line 38) | const KEY = 'alt';
constant STYLES (line 40) | const STYLES = stylesheet({
type IShortcutProps (line 106) | interface IShortcutProps {
class Shortcuts (line 110) | class Shortcuts extends React.Component<IShortcutProps> {
method render (line 111) | render() {
FILE: packages/frontend-web/src/app/scenes/Comments/components/SubheaderBar.tsx
constant STYLES (line 36) | const STYLES = stylesheet({
constant CELLS (line 83) | const CELLS = [
function SubheaderBar (line 93) | function SubheaderBar(_props: {}) {
function SettingsSubheaderBar (line 144) | function SettingsSubheaderBar(_props: {}) {
FILE: packages/frontend-web/src/app/scenes/Comments/components/TagSelector/TagSelector.tsx
constant ACTION_STYLES (line 43) | const ACTION_STYLES = {
constant SNAPSHOT_WIDTH (line 76) | const SNAPSHOT_WIDTH = 264;
constant SNAPSHOT_HEIGHT (line 77) | const SNAPSHOT_HEIGHT = 76;
function getImagePath (line 86) | function getImagePath(base: string, id: string, tagId: string) {
type ITagSelectorProps (line 105) | interface ITagSelectorProps extends RouteComponentProps<ITagSelectorPath...
class TagSelector (line 109) | class TagSelector extends React.Component<ITagSelectorProps> {
method onCloseClick (line 111) | onCloseClick(e: React.MouseEvent<any>) {
method render (line 116) | render() {
FILE: packages/frontend-web/src/app/scenes/Comments/components/ThreadedCommentDetail/ThreadedCommentDetail.tsx
constant HEADER_HEIGHT (line 44) | const HEADER_HEIGHT = 75;
constant STYLES (line 46) | const STYLES = stylesheet({
function ThreadedCommentDetail (line 91) | function ThreadedCommentDetail() {
FILE: packages/frontend-web/src/app/scenes/Comments/components/ThreadedCommentDetail/components/ThreadedComment/ThreadedComment.tsx
constant STYLES (line 44) | const STYLES = stylesheet({
type IReplyItemProps (line 96) | interface IReplyItemProps {
function ReplyItem (line 105) | function ReplyItem(props: IReplyItemProps) {
type IThreadedCommentProps (line 134) | interface IThreadedCommentProps {
function ThreadedComment (line 139) | function ThreadedComment(props: IThreadedCommentProps) {
FILE: packages/frontend-web/src/app/scenes/Comments/components/ThreadedCommentDetail/components/ThreadedComment/ThreadedCommentStory.tsx
function doNothing (line 26) | async function doNothing() {/**/}
constant STORY_STYLES (line 102) | const STORY_STYLES = {
FILE: packages/frontend-web/src/app/scenes/Comments/scoreFilters.ts
function getSensitivitiesForCategory (line 26) | function getSensitivitiesForCategory(
function isSummaryAboveThreshold (line 35) | function isSummaryAboveThreshold(
function isScoreAboveThreshold (line 51) | function isScoreAboveThreshold(
function getSummaryScoresAboveThreshold (line 67) | function getSummaryScoresAboveThreshold(
function getSummaryScoresBelowThreshold (line 78) | function getSummaryScoresBelowThreshold(
function dedupeScoreTypes (line 89) | function dedupeScoreTypes(scores: Array<ICommentScoreModel>): Array<ICom...
function getScoresAboveThreshold (line 103) | function getScoresAboveThreshold(
function getScoresBelowThreshold (line 112) | function getScoresBelowThreshold(
function getReducedScoresAboveThreshold (line 126) | function getReducedScoresAboveThreshold(
function getReducedScoresBelowThreshold (line 133) | function getReducedScoresBelowThreshold(
FILE: packages/frontend-web/src/app/scenes/Comments/store.ts
type ICommentsGlobalState (line 23) | type ICommentsGlobalState = Readonly<{
FILE: packages/frontend-web/src/app/scenes/Login/ConfigureOAuth.tsx
constant STYLES (line 24) | const STYLES = stylesheet({
type IConfigureOAuthProps (line 43) | interface IConfigureOAuthProps {
function ConfigureOAuth (line 47) | function ConfigureOAuth(props: IConfigureOAuthProps) {
FILE: packages/frontend-web/src/app/scenes/Login/Login.tsx
constant STYLES (line 28) | const STYLES = stylesheet({
type ILoginProps (line 45) | interface ILoginProps {
function Login (line 51) | function Login(props: ILoginProps) {
FILE: packages/frontend-web/src/app/scenes/Search/Search.tsx
constant HEADER_STYLES (line 38) | const HEADER_STYLES = stylesheet({
function Search (line 70) | function Search(_props: {}) {
FILE: packages/frontend-web/src/app/scenes/Search/components/SearchResults.tsx
constant TOAST_DELAY (line 70) | const TOAST_DELAY = 6000;
constant ACTION_PLURAL (line 73) | const ACTION_PLURAL: any = {
constant RESULTS_HEADER_HEIGHT (line 110) | const RESULTS_HEADER_HEIGHT = 50;
constant STYLES (line 112) | const STYLES = stylesheet({
type ISearchResultsProps (line 203) | interface ISearchResultsProps extends RouteComponentProps<{}> {
type ISearchResultsState (line 222) | interface ISearchResultsState {
class SearchResults (line 239) | class SearchResults extends React.Component<ISearchResultsProps, ISearch...
method componentDidMount (line 259) | componentDidMount() {
method componentWillUnmount (line 263) | componentWillUnmount() {
method onPressEscape (line 268) | onPressEscape() {
method updateScope (line 278) | updateScope(sort: any) {
method onSortChange (line 287) | onSortChange(event: React.FormEvent<any>) {
method onSelectAllChange (line 294) | async onSelectAllChange() {
method onSelectionChange (line 299) | async onSelectionChange(id: string) {
method matchAction (line 303) | matchAction(action: ICommentAction) {
method onConfirmationClose (line 320) | onConfirmationClose() {
method handleUndoClick (line 325) | handleUndoClick() {
method dispatchConfirmedAction (line 331) | async dispatchConfirmedAction(action: ICommentAction, ids?: Array<stri...
method getSelectedIDs (line 337) | getSelectedIDs(): Array<string> {
method calculateTaggingTriggerPosition (line 344) | calculateTaggingTriggerPosition(ref: HTMLElement) {
method toggleTaggingToolTip (line 360) | toggleTaggingToolTip() {
method confirmationClose (line 367) | confirmationClose() {
method onTagButtonClick (line 372) | onTagButtonClick(tagId: string) {
method triggerActionToast (line 379) | triggerActionToast(action: ICommentAction, count: number, callback: (a...
method handleAssignTagsSubmit (line 406) | async handleAssignTagsSubmit(commentId: ModelId, selectedTagIds: Set<M...
method render (line 413) | render() {
FILE: packages/frontend-web/src/app/scenes/Search/components/index.ts
function mapDispatchToProps (line 50) | function mapDispatchToProps(dispatch: IAppDispatch): Partial<ISearchResu...
type ISearchResultPublicProps (line 62) | type ISearchResultPublicProps = Pick<ISearchResultsProps, 'searchTerm' |...
FILE: packages/frontend-web/src/app/scenes/Search/store/checkedSelection.ts
function getSelectedCount (line 37) | function getSelectedCount(state: IAppState): number {
FILE: packages/frontend-web/src/app/scenes/Search/store/commentListLoader.ts
function loadCommentList (line 29) | async function loadCommentList(
FILE: packages/frontend-web/src/app/scenes/Search/store/index.ts
type ISearchState (line 23) | type ISearchState = Readonly<{
FILE: packages/frontend-web/src/app/scenes/Search/store/searchResults.ts
type ILoadAllCommentIdsCompletePayload (line 25) | type ILoadAllCommentIdsCompletePayload = Array<ModelId>;
type IAllCommentIDsState (line 35) | type IAllCommentIDsState = Readonly<{
function getStateRecord (line 62) | function getStateRecord(state: IAppState) {
function getAllCommentIds (line 66) | function getAllCommentIds(state: IAppState) {
function getIsLoading (line 70) | function getIsLoading(state: IAppState) {
FILE: packages/frontend-web/src/app/scenes/Search/types.ts
type ISearchScope (line 17) | interface ISearchScope {
FILE: packages/frontend-web/src/app/scenes/Settings/Ranges.tsx
function StatusScrim (line 37) | function StatusScrim(props: {visible: boolean, submitStatus: string}) {
type IRangesProps (line 51) | interface IRangesProps {
type IRangesState (line 54) | interface IRangesState {
class Ranges (line 59) | class Ranges extends React.Component<IRangesProps, IRangesState> {
method componentWillReceiveProps (line 64) | componentWillReceiveProps(_: Readonly<IRangesProps>) {
method setSaving (line 73) | setSaving(isSaving: boolean) {
method setError (line 88) | setError(message: string) {
method render (line 95) | render() {
FILE: packages/frontend-web/src/app/scenes/Settings/Settings.tsx
function StatusScrim (line 79) | function StatusScrim(props: {visible: boolean, submitStatus: string}) {
function loadSystemUsers (line 93) | async function loadSystemUsers(dispatch: IAppDispatch, type: string): Pr...
function Settings (line 99) | function Settings(_props: {}) {
FILE: packages/frontend-web/src/app/scenes/Settings/components/AddUsers.tsx
type IAddUsersProps (line 37) | interface IAddUsersProps {
type IAddUsersState (line 43) | interface IAddUsersState {
class AddUsers (line 48) | class AddUsers extends React.Component<IAddUsersProps, IAddUsersState> {
method isNewUserValid (line 55) | isNewUserValid(user: IUserModel): boolean {
method onInputChange (line 73) | onInputChange(inputType: 'name' | 'email' | 'group' | 'isActive', valu...
method onSubmit (line 89) | onSubmit() {
method render (line 93) | render() {
FILE: packages/frontend-web/src/app/scenes/Settings/components/ColorSelect/ColorSelect.tsx
constant STYLES (line 26) | const STYLES = stylesheet({
type IColorSelectProps (line 53) | interface IColorSelectProps {
class ColorSelect (line 59) | class ColorSelect extends React.Component<IColorSelectProps> {
method onChange (line 61) | onChange(e: React.ChangeEvent<HTMLInputElement>) {
method render (line 67) | render() {
FILE: packages/frontend-web/src/app/scenes/Settings/components/EditUsers.tsx
constant STYLES (line 37) | const STYLES = stylesheet({
type IEditUsersProps (line 44) | interface IEditUsersProps {
type IEditUsersState (line 50) | interface IEditUsersState {
class EditUsers (line 55) | class EditUsers extends React.Component<IEditUsersProps, IEditUsersState> {
method isUserValid (line 63) | isUserValid(user: IUserModel): boolean {
method onInputChange (line 81) | onInputChange(inputType: 'name' | 'email' | 'group' | 'isActive', valu...
method iseEditedUserValid (line 97) | iseEditedUserValid(user: IUserModel): boolean {
method onSubmit (line 102) | onSubmit() {
method render (line 106) | render() {
FILE: packages/frontend-web/src/app/scenes/Settings/components/EditYouTubeUser.tsx
constant STYLES (line 38) | const STYLES = stylesheet({
type IYoutubeCategoryProps (line 96) | interface IYoutubeCategoryProps {
function YoutubeCategory (line 100) | function YoutubeCategory(props: IYoutubeCategoryProps) {
type IEditYouTubeUserProps (line 145) | interface IEditYouTubeUserProps {
function EditYouTubeUser (line 151) | function EditYouTubeUser(props: IEditYouTubeUserProps) {
FILE: packages/frontend-web/src/app/scenes/Settings/components/LabelSettings/LabelSettings.tsx
constant SMALLER_SCREEN (line 40) | const SMALLER_SCREEN = window.innerWidth < 1200;
constant STYLES (line 41) | const STYLES = stylesheet({
type ILabelSettingsProps (line 75) | interface ILabelSettingsProps {
class LabelSettings (line 84) | class LabelSettings extends React.Component<ILabelSettingsProps> {
method onLabelChange (line 86) | onLabelChange(e: React.ChangeEvent<HTMLInputElement>) {
method onDescriptionChange (line 92) | onDescriptionChange(e: React.ChangeEvent<HTMLInputElement>) {
method onColorChange (line 98) | onColorChange(color: string) {
method onTagIsInBatchViewChange (line 103) | onTagIsInBatchViewChange(e: React.MouseEvent<HTMLElement>) {
method onTagIsTaggableChange (line 110) | onTagIsTaggableChange(e: React.MouseEvent<HTMLElement>) {
method onTagInSummaryScoreChange (line 117) | onTagInSummaryScoreChange(e: React.MouseEvent<HTMLElement>) {
method onDeletePress (line 124) | onDeletePress(e: React.MouseEvent<HTMLElement>) {
method render (line 129) | render() {
FILE: packages/frontend-web/src/app/scenes/Settings/components/ManageAutomatedRules.tsx
function ManageAutomatedRules (line 42) | function ManageAutomatedRules(props: {
FILE: packages/frontend-web/src/app/scenes/Settings/components/ManagePreselects.tsx
function ManagePreselects (line 42) | function ManagePreselects(props: {
FILE: packages/frontend-web/src/app/scenes/Settings/components/ManageSensitivities.tsx
function ManageSensitivities (line 42) | function ManageSensitivities(props: {
FILE: packages/frontend-web/src/app/scenes/Settings/components/ManageTags.tsx
constant SMALLER_SCREEN (line 34) | const SMALLER_SCREEN = window.innerWidth < 1200;
constant LOCAL_STYLES (line 35) | const LOCAL_STYLES: any = stylesheet({
function validateColor (line 58) | function validateColor(color: string): boolean {
function ManageTags (line 68) | function ManageTags(props: {
FILE: packages/frontend-web/src/app/scenes/Settings/components/OAuthConfig.tsx
type IOAuthConfigProps (line 32) | interface IOAuthConfigProps extends IApiConfiguration {
function OAuthConfig (line 44) | function OAuthConfig(props: IOAuthConfigProps) {
function EditOAuthScrim (line 119) | function EditOAuthScrim(props: {
FILE: packages/frontend-web/src/app/scenes/Settings/components/RuleRow/RuleRow.tsx
constant INPUT_HEIGHT (line 53) | const INPUT_HEIGHT = 36;
constant STYLES (line 54) | const STYLES = stylesheet({
type IRuleRowProps (line 86) | interface IRuleRowProps {
class RuleRow (line 108) | class RuleRow extends React.Component<IRuleRowProps> {
method onNumberFieldChange (line 111) | onNumberFieldChange(callback: ((value: number) => any), e: React.Chang...
method onCategoryFieldChange (line 117) | onCategoryFieldChange(callback: ((value: string) => any), e: React.Cha...
method notifyWrapperOfActionChange (line 123) | notifyWrapperOfActionChange(action: IModerationAction) {
method render (line 134) | render() {
FILE: packages/frontend-web/src/app/scenes/Settings/components/SaveButtons.tsx
constant STYLES (line 25) | const STYLES: any = stylesheet({
function SaveButtons (line 34) | function SaveButtons(props: {
FILE: packages/frontend-web/src/app/scenes/Settings/components/UserForm.tsx
type IAddUsersProps (line 31) | interface IAddUsersProps {
constant GROUPS (line 36) | const GROUPS = List([
class UserForm (line 41) | class UserForm extends React.Component<IAddUsersProps> {
method onValueChange (line 44) | onValueChange(property: 'name' | 'email' | 'group' , e: React.ChangeEv...
method onIsActiveChange (line 50) | onIsActiveChange(e: React.ChangeEvent<HTMLInputElement>) {
method render (line 54) | render() {
FILE: packages/frontend-web/src/app/scenes/Settings/components/rows.tsx
type IUserProps (line 34) | interface IUserProps {
function UserRow (line 39) | function UserRow({ user, handleEditUser }: IUserProps) {
function ServiceUserRow (line 69) | function ServiceUserRow({ user, handleEditUser }: IUserProps) {
function ModeratorUserRow (line 110) | function ModeratorUserRow({ user }: {user: IUserModel}) {
function YoutubeUserRow (line 129) | function YoutubeUserRow({ user, handleEditUser }: IUserProps) {
FILE: packages/frontend-web/src/app/scenes/Settings/components/users.tsx
function UserSettings (line 42) | function UserSettings(props: {
function ServiceUserSettings (line 97) | function ServiceUserSettings(props: {
function ModeratorSettings (line 151) | function ModeratorSettings(props: {
function YouTubeUsersSettings (line 195) | function YouTubeUsersSettings(props: {
function AddUserScrim (line 260) | function AddUserScrim(props: {
function EditUserScrim (line 294) | function EditUserScrim(props: {
function EditYouTubeScrim (line 328) | function EditYouTubeScrim(props: {
FILE: packages/frontend-web/src/app/scenes/Settings/settingsStyles.ts
constant ARROW_SIZE (line 28) | const ARROW_SIZE = 6;
constant ROW_HEIGHT (line 29) | const ROW_HEIGHT = 42;
constant SETTINGS_STYLES (line 31) | const SETTINGS_STYLES = {
FILE: packages/frontend-web/src/app/scenes/Settings/store.ts
function diff (line 43) | function diff<T extends {id: ModelId}>(original: List<T>, current: List<...
function addUser (line 68) | async function addUser(user: IUserModel): Promise<void> {
function modifyUser (line 72) | async function modifyUser(user: IUserModel): Promise<void> {
function addTag (line 76) | async function addTag(tag: ITagModel): Promise<void> {
function modifyTag (line 83) | async function modifyTag(tag: ITagModel): Promise<void> {
function deleteTag (line 87) | async function deleteTag(tagId: ModelId): Promise<void> {
function updateTags (line 91) | async function updateTags(oldTags: List<ITagModel>, newTags: List<ITagMo...
function addRule (line 100) | async function addRule(rule: IRuleModel): Promise<void> {
function modifyRule (line 104) | async function modifyRule(rule: IRuleModel): Promise<void> {
function deleteRule (line 108) | async function deleteRule(ruleId: ModelId): Promise<void> {
function updateRules (line 112) | async function updateRules(oldRules: List<IRuleModel>, newRules: List<IR...
function addPreselect (line 121) | async function addPreselect(preselect: IPreselectModel): Promise<void> {
function modifyPreselect (line 125) | async function modifyPreselect(preselect: IPreselectModel): Promise<void> {
function deletePreselect (line 129) | async function deletePreselect(preselectId: ModelId): Promise<void> {
function updatePreselects (line 133) | async function updatePreselects(oldPreselects: List<IPreselectModel>, ne...
function addTaggingSensitivity (line 142) | async function addTaggingSensitivity(taggingSensitivity: ITaggingSensiti...
function modifyTaggingSensitivity (line 146) | async function modifyTaggingSensitivity(taggingSensitivity: ITaggingSens...
function deleteTaggingSensitivity (line 150) | async function deleteTaggingSensitivity(taggingSensitivityId: ModelId): ...
function updateTaggingSensitivities (line 154) | async function updateTaggingSensitivities(oldRules: List<ITaggingSensiti...
FILE: packages/frontend-web/src/app/scenes/Settings/styles.ts
constant STYLES (line 26) | const STYLES: any = stylesheet({
FILE: packages/frontend-web/src/app/scenes/Tables/ArticleTable.tsx
constant STYLES (line 83) | const STYLES = stylesheet({
constant POPUP_MODERATORS (line 108) | const POPUP_MODERATORS = 'moderators';
constant POPUP_CONTROLS (line 109) | const POPUP_CONTROLS = 'controls';
constant POPUP_FILTERS (line 110) | const POPUP_FILTERS = 'filters';
constant POPUP_SAVING (line 111) | const POPUP_SAVING = 'saving';
function renderTime (line 113) | function renderTime(time: string | null) {
type ICountsInfoProps (line 120) | interface ICountsInfoProps {
function CountsInfo (line 126) | function CountsInfo(props: ICountsInfoProps) {
type IRowActions (line 165) | interface IRowActions {
type IArticleRowProps (line 177) | interface IArticleRowProps extends IRowActions {
function ArticleRow (line 182) | function ArticleRow(props: IArticleRowProps) {
type ISummaryRowProps (line 247) | interface ISummaryRowProps extends IRowActions {
function SummaryRow (line 251) | function SummaryRow(props: ISummaryRowProps) {
function DirectionIndicatorUp (line 301) | function DirectionIndicatorUp() {
function DirectionIndicatorDown (line 309) | function DirectionIndicatorDown() {
type IArticleTableProps (line 317) | interface IArticleTableProps {
function calculateSummaryCounts (line 320) | function calculateSummaryCounts(articles: Array<IArticleModel>) {
function sortArticles (line 346) | function sortArticles(articles: Array<IArticleModel>, sort: Array<string...
function filterArticles (line 351) | function filterArticles(
function ArticleTable (line 363) | function ArticleTable(_props: IArticleTableProps) {
FILE: packages/frontend-web/src/app/scenes/Tables/CategorySidebar.tsx
constant SIDEBAR_HEADER_HEIGHT (line 40) | const SIDEBAR_HEADER_HEIGHT = 159;
constant SIDEBAR_ROW_HEIGHT (line 41) | const SIDEBAR_ROW_HEIGHT = 55;
constant SIDEBAR_ICON_SIZE (line 42) | const SIDEBAR_ICON_SIZE = 36;
constant SIDEBAR_XPAD (line 43) | const SIDEBAR_XPAD = 17;
constant SIDEBAR_WIDTH (line 44) | const SIDEBAR_WIDTH = 280;
constant STYLES (line 46) | const STYLES = stylesheet({
type ICategorySidebarProps (line 154) | interface ICategorySidebarProps {
class CategorySidebar (line 163) | class CategorySidebar extends React.PureComponent<ICategorySidebarProps> {
method componentDidMount (line 166) | componentDidMount(): void {
method render (line 174) | render() {
FILE: packages/frontend-web/src/app/scenes/Tables/FilterSidebar.tsx
constant SIDEBAR_WIDTH (line 75) | const SIDEBAR_WIDTH = 350;
constant STYLES (line 77) | const STYLES = stylesheet({
type IFilterSidebarProps (line 124) | interface IFilterSidebarProps {
type IFilterSidebarState (line 136) | interface IFilterSidebarState {
constant DATE_FILTER_RANGE (line 153) | const DATE_FILTER_RANGE = 'custom';
class FilterSidebar (line 155) | class FilterSidebar extends React.Component<IFilterSidebarProps, IFilter...
method getDerivedStateFromProps (line 169) | static getDerivedStateFromProps(props: Readonly<IFilterSidebarProps>, ...
method setFilter (line 232) | setFilter(key: string) {
method changeTitleFilter (line 239) | changeTitleFilter(e: SyntheticEvent<any>) {
method setTitleFilter (line 244) | setTitleFilter() {
method checkForTitleEnter (line 249) | checkForTitleEnter(e: KeyboardEvent<any>) {
method changeDateFilter (line 256) | changeDateFilter(currentFilter: Array<IFilterItem>, key: string, value...
method changeDateFilterKey (line 261) | changeDateFilterKey(e: React.ChangeEvent<HTMLSelectElement>) {
method changeDateFilterSelect (line 286) | changeDateFilterSelect(e: React.ChangeEvent<HTMLSelectElement>) {
method changeDateFilterFrom (line 313) | changeDateFilterFrom(e: SyntheticEvent<any>) {
method changeDateFilterTo (line 319) | changeDateFilterTo(e: SyntheticEvent<any>) {
method setModerator (line 325) | setModerator(id: string) {
method setModeratorUnassigned (line 335) | setModeratorUnassigned() {
method clearFilters (line 340) | clearFilters() {
method renderModerator (line 345) | renderModerator(u: IUserModel) {
method renderCustomDateControls (line 361) | renderCustomDateControls() {
method render (line 391) | render() {
FILE: packages/frontend-web/src/app/scenes/Tables/TableFrame.tsx
type ITableFrameProps (line 49) | interface ITableFrameProps extends RouteComponentProps<IDashboardPathPar...
type ITableFrameState (line 56) | interface ITableFrameState {
function fixedSidebar (line 61) | function fixedSidebar() {
class PureTableFrame (line 65) | class PureTableFrame extends React.Component<ITableFrameProps, ITableFra...
method constructor (line 66) | constructor(props: ITableFrameProps) {
method showSidebar (line 75) | showSidebar() {
method hideSidebar (line 80) | hideSidebar() {
method componentWillMount (line 84) | componentWillMount() {
method componentWillUnmount (line 88) | componentWillUnmount() {
method updateWindowDimensions (line 93) | updateWindowDimensions() {
method renderSidebarPopup (line 97) | renderSidebarPopup(category?: ICategoryModel) {
method render (line 121) | render() {
FILE: packages/frontend-web/src/app/scenes/Tables/TableFrameStory.tsx
function hide (line 75) | function hide() { console.log('hide clicked'); }
function show (line 95) | function show() { console.log('show clicked'); }
FILE: packages/frontend-web/src/app/scenes/Tables/components.tsx
type IModeratorsWidgetProps (line 28) | interface IModeratorsWidgetProps {
constant MODERATOR_WIDGET_STYLES (line 35) | const MODERATOR_WIDGET_STYLES = stylesheet({
function ModeratorsWidget (line 43) | function ModeratorsWidget(props: IModeratorsWidgetProps) {
constant TITLE_CELL_STYLES (line 96) | const TITLE_CELL_STYLES = stylesheet({
type ITitleCellProps (line 118) | interface ITitleCellProps {
function TitleCell (line 124) | function TitleCell(props: ITitleCellProps) {
FILE: packages/frontend-web/src/app/scenes/Tables/styles.ts
constant CELL_HEIGHT (line 26) | const CELL_HEIGHT = 96;
constant ARTICLE_TABLE_STYLES (line 28) | const ARTICLE_TABLE_STYLES = stylesheet({
FILE: packages/frontend-web/src/app/scenes/Tables/utils.ts
type IFilterItem (line 19) | interface IFilterItem {
constant NOT_SET (line 24) | const NOT_SET = '~';
function parseFilter (line 26) | function parseFilter(filter: string | undefined): Array<IFilterItem> {
function updateFilter (line 43) | function updateFilter(filterList: Array<IFilterItem>, newKey: string, ne...
function getFilterString (line 51) | function getFilterString(filterList: Array<IFilterItem>): string {
function getFilterValue (line 59) | function getFilterValue(filterList: Array<IFilterItem>, key: string) {
type IFilterContext (line 67) | interface IFilterContext {
constant FILTER_TITLE (line 71) | const FILTER_TITLE = 'title';
constant FILTER_CATEGORY (line 72) | const FILTER_CATEGORY = 'category';
constant FILTER_CATEGORY_NONE (line 73) | const FILTER_CATEGORY_NONE = 'none';
constant FILTER_MODERATORS (line 74) | const FILTER_MODERATORS = 'moderators';
constant FILTER_MODERATORS_UNASSIGNED (line 75) | const FILTER_MODERATORS_UNASSIGNED = 'unassigned';
constant FILTER_TOGGLE_ON (line 78) | const FILTER_TOGGLE_ON = 'yes';
constant FILTER_TOGGLE_OFF (line 79) | const FILTER_TOGGLE_OFF = 'no';
constant FILTER_TO_REVIEW (line 80) | const FILTER_TO_REVIEW = 'commentsToReview';
constant FILTER_TO_REVIEW_ANY (line 81) | const FILTER_TO_REVIEW_ANY = 'any';
constant FILTER_TO_REVIEW_NEW (line 82) | const FILTER_TO_REVIEW_NEW = 'new';
constant FILTER_TO_REVIEW_DEFERRED (line 83) | const FILTER_TO_REVIEW_DEFERRED = 'deferred';
constant FILTER_DATE_SINCE (line 87) | const FILTER_DATE_SINCE = 'since-';
constant FILTER_DATE_PRIOR (line 88) | const FILTER_DATE_PRIOR = 'prior-';
function articleMatchesModerators (line 90) | function articleMatchesModerators(context: IFilterContext, article: IArt...
function executeFilter (line 106) | function executeFilter(filterList: Array<IFilterItem>, context: IFilterC...
function resetFilterToRoot (line 221) | function resetFilterToRoot(filter: Array<IFilterItem>): Array<IFilterIte...
function isFilterActive (line 227) | function isFilterActive(filter: Array<IFilterItem>): boolean {
function filterDateSince (line 237) | function filterDateSince(hours: number) {
function filterDatePrior (line 241) | function filterDatePrior(hours: number) {
function filterDateRangeValues (line 245) | function filterDateRangeValues(value: string) {
function filterDateRange (line 253) | function filterDateRange(from: string, to: string) {
function parseSort (line 259) | function parseSort(sort: string | undefined) {
function updateSort (line 266) | function updateSort(sortList: Array<string>, newSort: string): Array<str...
function getSortString (line 280) | function getSortString(sl: Array<string>) {
constant SORT_TITLE (line 287) | const SORT_TITLE = 'title';
constant SORT_NEW (line 288) | const SORT_NEW = 'new';
constant SORT_APPROVED (line 289) | const SORT_APPROVED = 'approved';
constant SORT_REJECTED (line 290) | const SORT_REJECTED = 'rejected';
constant SORT_DEFERRED (line 291) | const SORT_DEFERRED = 'deferred';
constant SORT_HIGHLIGHTED (line 292) | const SORT_HIGHLIGHTED = 'highlighted';
constant SORT_FLAGGED (line 293) | const SORT_FLAGGED = 'flagged';
constant SORT_SOURCE_CREATED (line 294) | const SORT_SOURCE_CREATED = 'sourceCreatedAt';
constant SORT_UPDATED (line 295) | const SORT_UPDATED = 'updatedAt';
constant SORT_LAST_MODERATED (line 296) | const SORT_LAST_MODERATED = 'lastModeratedAt';
function executeSort (line 298) | function executeSort(sortList: Array<string>) {
FILE: packages/frontend-web/src/app/scenes/appstate.ts
type IScenesState (line 20) | type IScenesState = Readonly<{
FILE: packages/frontend-web/src/app/scenes/index.tsx
function redirect (line 38) | function redirect(to: string) {
function AppRoot (line 44) | function AppRoot() {
FILE: packages/frontend-web/src/app/scenes/routes.ts
type IDashboardPathParams (line 21) | interface IDashboardPathParams {
function dashboardLink (line 27) | function dashboardLink(params: IDashboardPathParams) {
function settingsLink (line 44) | function settingsLink() {
function rangesLink (line 49) | function rangesLink() {
type ISearchQueryParams (line 53) | interface ISearchQueryParams {
function searchLink (line 61) | function searchLink(params: ISearchQueryParams) {
type IContextPathParams (line 78) | interface IContextPathParams {
function isArticleContext (line 83) | function isArticleContext(params: IContextPathParams) {
type INewCommentsPathParams (line 87) | interface INewCommentsPathParams extends IContextPathParams {
type INewCommentsQueryParams (line 91) | interface INewCommentsQueryParams {
type IModeratedCommentsPathParams (line 97) | interface IModeratedCommentsPathParams extends IContextPathParams {
type IModeratedCommentsQueryParams (line 101) | interface IModeratedCommentsQueryParams {
constant NEW_COMMENTS_DEFAULT_TAG (line 107) | const NEW_COMMENTS_DEFAULT_TAG = 'SUMMARY_SCORE';
function newCommentsPageLink (line 108) | function newCommentsPageLink(
function moderatedCommentsPageLink (line 116) | function moderatedCommentsPageLink(
type ITagSelectorPathParams (line 124) | interface ITagSelectorPathParams extends IContextPathParams {
function tagSelectorLink (line 129) | function tagSelectorLink({context, contextId, tag}: ITagSelectorPathPara...
type ICommentDetailsPathParams (line 133) | interface ICommentDetailsPathParams extends IContextPathParams {
type ICommentDetailsQueryParams (line 137) | interface ICommentDetailsQueryParams {
function commentDetailsPageLink (line 141) | function commentDetailsPageLink (
function commentRepliesDetailsLink (line 149) | function commentRepliesDetailsLink(
function commentSearchDetailsPageLink (line 155) | function commentSearchDetailsPageLink(
FILE: packages/frontend-web/src/app/stores/appstate.ts
type IGlobalState (line 28) | type IGlobalState = Readonly<{
FILE: packages/frontend-web/src/app/stores/articles.ts
function getArticleMap (line 25) | function getArticleMap(state: IAppState): Map<ModelId, IArticleModel> {
function getArticles (line 29) | function getArticles(state: IAppState): Array<IArticleModel> {
function getArticle (line 33) | function getArticle(state: IAppState, articleId: ModelId): IArticleModel {
type IArticlesState (line 37) | interface IArticlesState {
FILE: packages/frontend-web/src/app/stores/categories.ts
function getCategoryMap (line 25) | function getCategoryMap(state: IAppState): Map<ModelId, ICategoryModel> {
function getCategories (line 29) | function getCategories(state: IAppState): Array<ICategoryModel> {
function getActiveCategories (line 33) | function getActiveCategories(state: IAppState): Array<ICategoryModel> {
function getCategory (line 37) | function getCategory(state: IAppState, categoryId: ModelId): ICategoryMo...
type ISummaryCounts (line 41) | interface ISummaryCounts {
function getGlobalCounts (line 52) | function getGlobalCounts(state: IAppState): ISummaryCounts {
type ICategoriesState (line 78) | interface ICategoriesState {
FILE: packages/frontend-web/src/app/stores/commentActions.ts
function fetchComments (line 55) | async function fetchComments(commentIds: Array<ModelId>) {
function highlightComments (line 60) | async function highlightComments(commentIds: Array<ModelId>) {
function resetComments (line 65) | async function resetComments(commentIds: Array<ModelId>) {
function approveComments (line 70) | async function approveComments(commentIds: Array<ModelId>) {
function approveFlagsAndComments (line 75) | async function approveFlagsAndComments(commentIds: Array<ModelId>) {
function resolveFlags (line 80) | async function resolveFlags(commentIds: Array<ModelId>) {
function deferComments (line 85) | async function deferComments(commentIds: Array<ModelId>) {
function rejectComments (line 90) | async function rejectComments(commentIds: Array<ModelId>) {
function rejectFlagsAndComments (line 95) | async function rejectFlagsAndComments(commentIds: Array<ModelId>) {
function sendAddScoreAction (line 100) | function sendAddScoreAction(commentId: ModelId, tagId: ModelId, start?: ...
function tagComment (line 119) | async function tagComment(commentId: ModelId, tagId: ModelId) {
function tagCommentWithAnnotation (line 124) | async function tagCommentWithAnnotation(commentId: string, tagId: string...
function untagComment (line 129) | async function untagComment(commentId: ModelId, commentScoreId: string) {
function tagCommentSummaryScores (line 134) | async function tagCommentSummaryScores(commentIds: Array<ModelId>, tagId...
function confirmCommentSummaryScore (line 138) | async function confirmCommentSummaryScore(commentId: ModelId, tagId: str...
function rejectCommentSummaryScore (line 142) | async function rejectCommentSummaryScore(commentId: ModelId, tagId: stri...
function resetCommentScore (line 146) | async function resetCommentScore(commentId: ModelId, commentScoreId: str...
function confirmCommentScore (line 155) | async function confirmCommentScore(commentId: ModelId, commentScoreId: s...
function rejectCommentScore (line 164) | async function rejectCommentScore(commentId: ModelId, commentScoreId: st...
function editAndRescoreComment (line 173) | async function editAndRescoreComment(
type ICommentActionFunction (line 185) | type ICommentActionFunction = (ids: Array<string>, tagId?: string) => Pr...
FILE: packages/frontend-web/src/app/stores/comments.ts
function executeFetch (line 28) | function executeFetch(commentFetcher: (commentIds: Array<ModelId>) => vo...
function ensureCache (line 35) | function ensureCache(commentId: ModelId, commentFetcher: (commentIds: Ar...
function clearCommentFetchQueue (line 47) | function clearCommentFetchQueue() {
function resolveFlags (line 55) | function resolveFlags(flagsSummary?: Map<string, Array<number>>) {
type ICommentsState (line 66) | interface ICommentsState {
function getComment (line 114) | function getComment(state: IAppState, commentId: ModelId) {
FILE: packages/frontend-web/src/app/stores/counts.ts
type ICountsState (line 23) | type ICountsState = Readonly<{
function getAssignments (line 27) | function getAssignments(state: IAppState) {
FILE: packages/frontend-web/src/app/stores/globalActions.ts
type ICommentAttributesUpdateDetails (line 23) | type ICommentAttributesUpdateDetails = {
type ICommentAttributesUpdate (line 29) | interface ICommentAttributesUpdate {
constant ATTRIBUTES_HIGHLIGHTED (line 35) | const ATTRIBUTES_HIGHLIGHTED: ICommentAttributesUpdateDetails = {
constant ATTRIBUTES_RESET (line 42) | const ATTRIBUTES_RESET: ICommentAttributesUpdateDetails = {
constant ATTRIBUTES_APPROVED (line 49) | const ATTRIBUTES_APPROVED: ICommentAttributesUpdateDetails = {
constant ATTRIBUTES_REJECTED (line 56) | const ATTRIBUTES_REJECTED: ICommentAttributesUpdateDetails = {
constant ATTRIBUTES_DEFERRED (line 63) | const ATTRIBUTES_DEFERRED: ICommentAttributesUpdateDetails = {
FILE: packages/frontend-web/src/app/stores/preselects.ts
function getPreselects (line 26) | function getPreselects(state: IAppState): List<IPreselectModel> {
type IPreselectsState (line 30) | interface IPreselectsState {
FILE: packages/frontend-web/src/app/stores/rules.ts
function getRules (line 27) | function getRules(state: IAppState): List<IRuleModel> {
type IRulesState (line 31) | interface IRulesState {
FILE: packages/frontend-web/src/app/stores/taggingSensitivities.ts
function getTaggingSensitivities (line 27) | function getTaggingSensitivities(state: IAppState): List<ITaggingSensiti...
type ITaggingSensitivitiesState (line 31) | interface ITaggingSensitivitiesState {
FILE: packages/frontend-web/src/app/stores/tags.ts
function getTags (line 27) | function getTags(state: IAppState): List<ITagModel> {
function getTaggableTags (line 31) | function getTaggableTags(state: IAppState) {
type ITagsState (line 35) | interface ITagsState {
FILE: packages/frontend-web/src/app/stores/textSizes.ts
type ILoadTestSizesCompletePayload (line 27) | type ILoadTestSizesCompletePayload = {
type ITextSizesState (line 34) | type ITextSizesState = Readonly<{
function getStateRecord (line 58) | function getStateRecord(state: IAppState) {
function getTextSizesHasData (line 62) | function getTextSizesHasData(state: IAppState) {
function getTextSizes (line 67) | function getTextSizes(state: IAppState) {
function getTextSizesIsLoading (line 72) | function getTextSizesIsLoading(state: IAppState) {
function loadTextSizesByIds (line 77) | function loadTextSizesByIds(ids: Array<ModelId>, width: number): IThunkA...
FILE: packages/frontend-web/src/app/stores/users.ts
function setMyUserId (line 25) | function setMyUserId(uid: string | null) {
function getMyUserId (line 29) | function getMyUserId(): string | null {
constant USER_GROUP_GENERAL (line 33) | const USER_GROUP_GENERAL = 'general';
constant USER_GROUP_ADMIN (line 34) | const USER_GROUP_ADMIN = 'admin';
constant USER_GROUP_SERVICE (line 35) | const USER_GROUP_SERVICE = 'service';
constant USER_GROUP_MODERATOR (line 36) | const USER_GROUP_MODERATOR = 'moderator';
constant USER_GROUP_YOUTUBE (line 37) | const USER_GROUP_YOUTUBE = 'youtube';
type ILoadSystemUsers (line 43) | interface ILoadSystemUsers { type: string; users: List<IUserModel>; }
function getUsers (line 49) | function getUsers(state: IAppState): Map<ModelId, IUserModel> {
function getUser (line 53) | function getUser(state: IAppState, id: ModelId): IUserModel | null {
function getCurrentUser (line 57) | function getCurrentUser(state: IAppState): IUserModel | null {
function userIsAdmin (line 65) | function userIsAdmin(user: IUserModel | null): boolean {
function getCurrentUserIsAdmin (line 69) | function getCurrentUserIsAdmin(state: IAppState): boolean {
function getSystemUsers (line 73) | function getSystemUsers(type: string, state: IAppState): List<IUserModel> {
type IUsersState (line 82) | interface IUsersState {
FILE: packages/frontend-web/src/app/styles/breakpoints.ts
constant TABLET_WIDTH (line 18) | const TABLET_WIDTH = 1023;
constant HEADER_HEIGHT (line 19) | const HEADER_HEIGHT = 64;
constant TABLET_PORTRAIT_BREAKPOINT (line 22) | const TABLET_PORTRAIT_BREAKPOINT =
FILE: packages/frontend-web/src/app/styles/colors.ts
constant DARK_COLOR (line 18) | const DARK_COLOR = '#255271';
constant MEDIUM_COLOR (line 19) | const MEDIUM_COLOR = '#326891';
constant LIGHT_COLOR (line 20) | const LIGHT_COLOR = '#46779c';
constant DIVIDER_COLOR (line 21) | const DIVIDER_COLOR = '#ededed';
constant PALE_COLOR (line 22) | const PALE_COLOR = '#f4f7f9';
constant WHITE_COLOR (line 23) | const WHITE_COLOR = '#ffffff';
constant ALMOST_WHITE (line 24) | const ALMOST_WHITE = '#fafafa';
constant GREY_COLOR (line 25) | const GREY_COLOR = '#999999';
constant NICE_DARK_BLUE (line 26) | const NICE_DARK_BLUE = '#0f4a92';
constant NICE_MIDDLE_BLUE (line 27) | const NICE_MIDDLE_BLUE = '#185bac';
constant NICE_LIGHT_BLUE (line 28) | const NICE_LIGHT_BLUE = '#95b3d8';
constant NICE_LIGHTEST_BLUE (line 29) | const NICE_LIGHTEST_BLUE = '#e1e9f4';
constant NICE_CONTROL_BLUE (line 30) | const NICE_CONTROL_BLUE = '#2e84ed';
constant NICE_BLUE_GREY (line 31) | const NICE_BLUE_GREY = '#eff0f0';
constant SIDEBAR_BLUE (line 32) | const SIDEBAR_BLUE = '#0f4a92';
constant NICE_LIGHT_HIGHLIGHT_BLUE (line 33) | const NICE_LIGHT_HIGHLIGHT_BLUE = '#2e83ed';
constant DARK_PRIMARY_TEXT_COLOR (line 36) | const DARK_PRIMARY_TEXT_COLOR = 'rgba(0, 0, 0, 0.87)';
constant DARK_SECONDARY_TEXT_COLOR (line 37) | const DARK_SECONDARY_TEXT_COLOR = 'rgba(0, 0, 0, 0.54)';
constant DARK_TERTIARY_TEXT_COLOR (line 38) | const DARK_TERTIARY_TEXT_COLOR = 'rgba(0, 0, 0, 0.38)';
constant DARK_LINK_TEXT_COLOR (line 39) | const DARK_LINK_TEXT_COLOR = 'rgba(50, 104, 145, 1)';
constant LIGHT_COLOR_BASE (line 41) | const LIGHT_COLOR_BASE = 'rgba(255, 255, 255, 1)';
constant LIGHT_PRIMARY_TEXT_COLOR (line 42) | const LIGHT_PRIMARY_TEXT_COLOR = LIGHT_COLOR_BASE;
constant LIGHT_SECONDARY_TEXT_COLOR (line 43) | const LIGHT_SECONDARY_TEXT_COLOR = 'rgba(255, 255, 255, 0.7)';
constant LIGHT_TERTIARY_TEXT_COLOR (line 44) | const LIGHT_TERTIARY_TEXT_COLOR = 'rgba(255, 255, 255, 0.5)';
constant LIGHT_HIGHLIGHT_COLOR (line 45) | const LIGHT_HIGHLIGHT_COLOR = 'rgba(255, 255, 255, 0.2)';
constant LIGHT_LINKS_TEXT_COLOR (line 46) | const LIGHT_LINKS_TEXT_COLOR = LIGHT_COLOR_BASE;
constant TAG_OBSCENE_COLOR (line 49) | const TAG_OBSCENE_COLOR = '#d71b60';
constant TAG_INCOHERENT_COLOR (line 50) | const TAG_INCOHERENT_COLOR = '#9c28b1';
constant TAG_SPAM_COLOR (line 51) | const TAG_SPAM_COLOR = '#673bb8';
constant TAG_OFF_TOPIC_COLOR (line 52) | const TAG_OFF_TOPIC_COLOR = '#3f51b5';
constant TAG_INFLAMMATORY_COLOR (line 53) | const TAG_INFLAMMATORY_COLOR = '#1976d3';
constant TAG_UNSUBSTANTIAL_COLOR (line 54) | const TAG_UNSUBSTANTIAL_COLOR = '#3d5afe';
constant TAG_OTHER_COLOR (line 55) | const TAG_OTHER_COLOR = '#01828f';
FILE: packages/frontend-web/src/app/styles/forms.ts
constant SELECT_ELEMENT (line 24) | const SELECT_ELEMENT = {
constant BUTTON_RESET (line 38) | const BUTTON_RESET = {
FILE: packages/frontend-web/src/app/styles/header.ts
constant ARTICLE_HEADER (line 26) | const ARTICLE_HEADER = {
FILE: packages/frontend-web/src/app/styles/hoverstates.ts
constant OPACITY_TRANSITION (line 21) | const OPACITY_TRANSITION = {
constant BOTTOM_BORDER_TRANSITION (line 38) | const BOTTOM_BORDER_TRANSITION = {
FILE: packages/frontend-web/src/app/styles/scrim.ts
constant SCRIM_STYLE (line 23) | const SCRIM_STYLE = {
FILE: packages/frontend-web/src/app/styles/typography.ts
type ITypeStyle (line 17) | type ITypeStyle = {
function makeTypeStyle (line 24) | function makeTypeStyle(
constant BODY_FONT_STACK (line 38) | const BODY_FONT_STACK = 'Georgia, serif';
constant LOGIN_TITLE_TYPE (line 41) | const LOGIN_TITLE_TYPE =
constant HEADLINE_TYPE (line 45) | const HEADLINE_TYPE =
constant SEMI_BOLD_TYPE (line 48) | const SEMI_BOLD_TYPE =
constant ARTICLE_HEADLINE_TYPE (line 52) | const ARTICLE_HEADLINE_TYPE =
constant BODY_TEXT_TYPE (line 56) | const BODY_TEXT_TYPE =
constant COMMENT_DETAIL_BODY_TEXT_TYPE (line 59) | const COMMENT_DETAIL_BODY_TEXT_TYPE =
constant ARTICLE_CATEGORY_TYPE (line 63) | const ARTICLE_CATEGORY_TYPE =
constant COMMENT_DETAIL_DATE_TYPE (line 66) | const COMMENT_DETAIL_DATE_TYPE =
constant BUTTON_LINK_TYPE (line 70) | const BUTTON_LINK_TYPE =
constant COMMENT_DETAIL_TAG_LIST_BUTTON_TYPE (line 74) | const COMMENT_DETAIL_TAG_LIST_BUTTON_TYPE =
constant ARTICLE_CAPTION_TYPE (line 78) | const ARTICLE_CAPTION_TYPE =
constant CAPTION_TYPE (line 82) | const CAPTION_TYPE =
constant HANDLE_LABEL_TYPE (line 86) | const HANDLE_LABEL_TYPE =
FILE: packages/frontend-web/src/app/styles/util.ts
constant DEFAULT_OPACITY (line 17) | const DEFAULT_OPACITY = 1;
constant MEDIUM_OPACITY (line 18) | const MEDIUM_OPACITY = 0.54;
constant LIGHT_OPACITY (line 19) | const LIGHT_OPACITY = 0.12;
constant TEXT_OFFSET_DEFAULT_SPACING (line 22) | const TEXT_OFFSET_DEFAULT_SPACING = 72;
constant GUTTER_DEFAULT_SPACING (line 23) | const GUTTER_DEFAULT_SPACING = 24;
constant BOX_DEFAULT_SPACING (line 24) | const BOX_DEFAULT_SPACING = 10;
constant SHORT_SCREEN_QUERY (line 26) | const SHORT_SCREEN_QUERY = '@media (max-height: 769px)';
constant OFFSCREEN (line 29) | const OFFSCREEN = {
constant MODAL_DROP_SHADOW (line 39) | const MODAL_DROP_SHADOW = [
constant INPUT_DROP_SHADOW (line 44) | const INPUT_DROP_SHADOW = [
constant CENTER_CONTENT (line 49) | const CENTER_CONTENT = {
constant VISUALLY_HIDDEN (line 55) | const VISUALLY_HIDDEN = {
FILE: packages/frontend-web/src/app/styles/zindex.ts
constant BASE_Z_INDEX (line 20) | const BASE_Z_INDEX = nextIndexLevel();
constant SELECT_Z_INDEX (line 21) | const SELECT_Z_INDEX = nextIndexLevel();
constant TOOLTIP_Z_INDEX (line 22) | const TOOLTIP_Z_INDEX = nextIndexLevel();
constant STICKY_Z_INDEX (line 23) | const STICKY_Z_INDEX = nextIndexLevel();
constant SCRIM_Z_INDEX (line 24) | const SCRIM_Z_INDEX = nextIndexLevel();
constant ARTICLE_PREVIEW_Z_INDEX (line 25) | const ARTICLE_PREVIEW_Z_INDEX = nextIndexLevel();
constant ACCOUNT_SETTINGS_MENU_Z_INDEX (line 26) | const ACCOUNT_SETTINGS_MENU_Z_INDEX = nextIndexLevel();
FILE: packages/frontend-web/src/app/stylesx/index.ts
constant IMAGE_BASE (line 20) | const IMAGE_BASE = 40;
constant ICON_STYLES (line 37) | const ICON_STYLES = stylesheet({
constant COMMON_STYLES (line 76) | const COMMON_STYLES = stylesheet({
FILE: packages/frontend-web/src/app/util/DotChartRenderer.ts
constant OVERDRAW (line 23) | const OVERDRAW = 1.2;
type IRange (line 25) | interface IRange {
type ICommentsByColumn (line 30) | interface ICommentsByColumn {
constant DIAMETER (line 34) | const DIAMETER = 2 / 3;
constant MARGIN (line 35) | const MARGIN = 1 - DIAMETER;
class DotChartRenderer (line 37) | class DotChartRenderer {
method constructor (line 53) | constructor(makeCanvas: (width: number, height: number) => any) {
method setProps (line 57) | setProps(props: {
method setProp (line 82) | setProp(key: string, value: any) {
method render (line 89) | render() {
method renderAll (line 121) | private renderAll(): void {
method renderMax (line 190) | private renderMax(): void {
method makeSprite (line 259) | private makeSprite(radius: number, isSelected: boolean): any {
FILE: packages/frontend-web/src/app/util/color.ts
function hashCode (line 18) | function hashCode(str: string) {
function intToRGB (line 26) | function intToRGB(i: number) {
function randomDarkColor (line 30) | function randomDarkColor(seed: string) {
function randomLightColor (line 34) | function randomLightColor(seed: string) {
FILE: packages/frontend-web/src/app/util/csrf.ts
constant NONCE_PATH (line 18) | const NONCE_PATH = 'moderator/csrf-nonce';
function getCSRF (line 22) | function getCSRF(): string { return storage()[NONCE_PATH]; }
function setCSRF (line 23) | function setCSRF(random: string): void { storage()[NONCE_PATH] = random; }
function clearCSRF (line 24) | function clearCSRF(): void { delete storage()[NONCE_PATH]; }
FILE: packages/frontend-web/src/app/util/groupByColumn.ts
type IGroupedComments (line 19) | interface IGroupedComments {
type IGroupableComment (line 23) | interface IGroupableComment {
type ITaggedComment (line 28) | interface ITaggedComment extends IGroupableComment {
type IDatedComment (line 32) | interface IDatedComment extends IGroupableComment {
function groupByKey (line 36) | function groupByKey(comments: Array<IGroupableComment>, key: string, sta...
function groupByScoreColumns (line 61) | function groupByScoreColumns<T extends ITaggedComment>(comments: Array<T...
function groupByDateColumns (line 65) | function groupByDateColumns<T extends IDatedComment>(comments: Array<T>,...
FILE: packages/frontend-web/src/app/util/makeCheckedSelectionStore/makeCheckedSelectionStore.ts
type ICheckedSelectionStoreOptions (line 23) | interface ICheckedSelectionStoreOptions {
type IOverrides (line 27) | type IOverrides = Map<string, boolean>;
type ICheckedSelectionState (line 29) | type ICheckedSelectionState = Readonly<{
type ICheckedSelectionPayloads (line 35) | type ICheckedSelectionPayloads =
function makeCheckedSelectionStore (line 40) | function makeCheckedSelectionStore(
FILE: packages/frontend-web/src/app/util/makeCurrentPagingIdentifierReducer.ts
type ICurrentPagingIdentifierState (line 23) | type ICurrentPagingIdentifierState = Readonly<{
type ICurrentPagingIdentifierPayload (line 31) | type ICurrentPagingIdentifierPayload = { currentPagingIdentifier: string };
function makeCurrentPagingIdentifierReducer (line 33) | function makeCurrentPagingIdentifierReducer(
FILE: packages/frontend-web/src/app/util/measureText.ts
function getFontStyle (line 19) | function getFontStyle({ fontWeight, fontSize, fontFamily }: ITypeStyle):...
function setupContext (line 23) | function setupContext(canvas: any, styles: ITypeStyle): void {
function measureLine (line 34) | function measureLine(canvas: any, text: string, styles: ITypeStyle): num...
function getTextHeight (line 41) | function getTextHeight(lines: Array<string>, _wordWrapWidth: number, sty...
function wordWrap (line 47) | function wordWrap(canvas: any, text: string, wordWrapWidth: number, styl...
FILE: packages/frontend-web/src/app/util/partial/__spec__/memoize.spec.ts
function makeMemoized (line 27) | function makeMemoized(useEqualityForMutableObjects = false) {
FILE: packages/frontend-web/src/app/util/partial/__spec__/partial.spec.ts
function identity (line 22) | function identity(param: any): any {
FILE: packages/frontend-web/src/app/util/partial/index.ts
type INode (line 27) | interface INode<T> {
function makeNode (line 32) | function makeNode<T>(): INode<T> {
function stringifyIfNecessary (line 45) | function stringifyIfNecessary(o: any, useEqualityForMutableObjects: bool...
class Cache (line 78) | class Cache<T> {
method constructor (line 82) | constructor(useEqualityForMutableObjects: boolean) {
method has (line 86) | has(args: Array<any>): boolean {
method get (line 90) | get(args: Array<any>): T | undefined {
method set (line 109) | set(args: Array<any>, value: T): void {
method toJS (line 132) | toJS(): object {
method toString (line 170) | toString() {
function memoize (line 175) | function memoize<T extends Function>(fn: T, useEqualityForMutableObjects...
function maybeCallback (line 195) | function maybeCallback<T>(fn?: T | null) {
FILE: packages/frontend-web/src/app/util/returnSavedCommentRow.ts
constant RETURN_ROW_PATH (line 17) | const RETURN_ROW_PATH = 'moderator/return-comment-row';
function getReturnSavedCommentRow (line 19) | function getReturnSavedCommentRow(): string {
function setReturnSavedCommentRow (line 27) | function setReturnSavedCommentRow(commentId: string): void {
function clearReturnSavedCommentRow (line 31) | function clearReturnSavedCommentRow(): void {
FILE: packages/frontend-web/src/app/util/returnURL.ts
constant RETURN_URL_PATH (line 17) | const RETURN_URL_PATH = 'moderator/return-url';
type IReturnURL (line 19) | interface IReturnURL {
function getReturnURL (line 24) | function getReturnURL(): IReturnURL {
function setReturnURL (line 32) | function setReturnURL(data: IReturnURL): void {
function clearReturnURL (line 36) | function clearReturnURL(): void {
FILE: packages/frontend-web/src/app/util/savedSorts.ts
constant LOCAL_STORAGE_KEY (line 19) | const LOCAL_STORAGE_KEY = 'comment-sorts';
function loadSorts (line 21) | function loadSorts(): Map<string, string> {
function getDefaultSort (line 31) | function getDefaultSort(categoryId: ModelId, page: string, tag: string) {
function putDefaultSort (line 55) | function putDefaultSort(categoryId: ModelId, page: string, tag: string, ...
FILE: packages/frontend-web/src/app/util/sortByLabel.ts
function sortByLabel (line 20) | function sortByLabel(list: List<ICategoryModel | ITagModel>): List<ICate...
FILE: packages/frontend-web/src/app/util/time.ts
function maybeS (line 17) | function maybeS(val: number) {
function getTimestring (line 21) | function getTimestring(timestamp: string, inFuture: boolean) {
FILE: packages/frontend-web/src/app/util/timeout.ts
function timeout (line 17) | function timeout(delay: number): Promise<void> {
FILE: packages/frontend-web/src/app/utilx/cssInJs.ts
type IStyleProps (line 26) | interface IStyleProps {
function stylesheet (line 33) | function stylesheet<T>(styles: T): T {
function knownStyle (line 46) | function knownStyle(obj: object) {
type IStyle (line 50) | interface IStyle { [key: string]: IStyle | string | number; }
type IPossibleStyle (line 51) | type IPossibleStyle = IStyle | null | undefined;
function flattenStyleReducer (line 53) | function flattenStyleReducer(sum: object, style: object): object {
function flattenStyles (line 59) | function flattenStyles(styles: Array<object>): object {
function originalCSS (line 63) | function originalCSS(...styles: Array<IPossibleStyle>): IStyleProps {
FILE: packages/frontend-web/src/app/utilx/highlightText.tsx
type ITextNode (line 22) | interface ITextNode {
function getTextNodes (line 28) | function getTextNodes(searchStr: string, str: string) {
function addRange (line 49) | function addRange(arr: Array<JSX.Element>, originalString: string, start...
function highlightText (line 63) | function highlightText(searchTerm: string, originalString: string) {
FILE: packages/frontend-web/src/app/utilx/hooks.ts
function useBindEscape (line 20) | function useBindEscape(action: () => void) {
FILE: packages/frontend-web/src/app/utilx/keyCodes.tsx
constant ESCAPE_KEY (line 17) | const ESCAPE_KEY = 27;
constant LEFT_ARROW_KEY (line 18) | const LEFT_ARROW_KEY = 37;
constant UP_ARROW_KEY (line 19) | const UP_ARROW_KEY = 38;
constant RIGHT_ARROW_KEY (line 20) | const RIGHT_ARROW_KEY = 39;
constant DOWN_ARROW_KEY (line 21) | const DOWN_ARROW_KEY = 40;
FILE: packages/frontend-web/src/app/utilx/sortDefinitions.tsx
type IColumnSortDefinition (line 17) | interface IColumnSortDefinition {
FILE: packages/frontend-web/src/models/article.ts
type IArticleAttributes (line 19) | interface IArticleAttributes {
type IArticleModel (line 44) | type IArticleModel = Readonly<IArticleAttributes>;
function ArticleModel (line 46) | function ArticleModel(articleData: IArticleAttributes): IArticleModel {
FILE: packages/frontend-web/src/models/category.ts
type ICategoryAttributes (line 19) | interface ICategoryAttributes {
type ICategoryModel (line 39) | type ICategoryModel = Readonly<ICategoryAttributes>;
function CategoryModel (line 41) | function CategoryModel(categoryData?: Partial<ICategoryAttributes>): ICa...
FILE: packages/frontend-web/src/models/comment.ts
type ICommentListItem (line 19) | type ICommentListItem = Readonly<{
type ICommentDate (line 23) | type ICommentDate = Readonly<{
type ICommentScore (line 27) | type ICommentScore = Readonly<{
type IAuthorAttributes (line 31) | interface IAuthorAttributes {
type IAuthorModel (line 40) | type IAuthorModel = Readonly<IAuthorAttributes>;
type IAuthorCountsAttributes (line 42) | interface IAuthorCountsAttributes {
type IAuthorCountsModel (line 47) | type IAuthorCountsModel = Readonly<IAuthorCountsAttributes>;
type ITopScore (line 49) | interface ITopScore {
type ICommentSummaryScoreAttributes (line 55) | interface ICommentSummaryScoreAttributes {
type ICommentSummaryScoreModel (line 61) | type ICommentSummaryScoreModel = Readonly<ICommentSummaryScoreAttributes>;
constant FLAGS_COUNT (line 63) | const FLAGS_COUNT = 0;
constant UNRESOLVED_FLAGS_COUNT (line 64) | const UNRESOLVED_FLAGS_COUNT = 1;
constant RECOMMENDATIONS_COUNT (line 65) | const RECOMMENDATIONS_COUNT = 2;
type ICommentAttributes (line 67) | interface ICommentAttributes {
type ICommentModel (line 100) | type ICommentModel = Readonly<ICommentAttributes>;
function CommentModel (line 102) | function CommentModel(commentData: ICommentAttributes): ICommentModel {
function getTopScore (line 121) | function getTopScore(comment: ICommentModel) {
function getSummaryForTag (line 125) | function getSummaryForTag(comment: ICommentModel, tagId: ModelId): IComm...
function getTopScoreForTag (line 132) | function getTopScoreForTag(comment: ICommentModel, tagId?: ModelId) {
FILE: packages/frontend-web/src/models/commentFlag.ts
type ICommentFlagAttributes (line 17) | interface ICommentFlagAttributes {
type ICommentFlagModel (line 30) | type ICommentFlagModel = Readonly<ICommentFlagAttributes>;
function CommentFlagModel (line 32) | function CommentFlagModel(flagData?: ICommentFlagAttributes): ICommentFl...
FILE: packages/frontend-web/src/models/commentScore.ts
type ICommentScoreAttributes (line 19) | interface ICommentScoreAttributes {
type ICommentScoreModel (line 31) | type ICommentScoreModel = Readonly<ICommentScoreAttributes>;
function CommentScoreModel (line 33) | function CommentScoreModel(scoreData?: ICommentScoreAttributes): ICommen...
FILE: packages/frontend-web/src/models/common.ts
type ModelId (line 19) | type ModelId = string;
constant SERVER_ACTION_ACCEPT (line 21) | const SERVER_ACTION_ACCEPT = 'Accept';
constant SERVER_ACTION_REJECT (line 22) | const SERVER_ACTION_REJECT = 'Reject';
constant SERVER_ACTION_DEFER (line 23) | const SERVER_ACTION_DEFER = 'Defer';
constant SERVER_ACTION_HIGHLIGHT (line 24) | const SERVER_ACTION_HIGHLIGHT = 'Highlight';
type IServerAction (line 26) | type IServerAction = 'Accept' | 'Reject' | 'Defer' | 'Highlight';
function convertServerAction (line 28) | function convertServerAction(saction: IServerAction): IModerationAction {
function convertClientAction (line 41) | function convertClientAction(action: IModerationAction): IServerAction {
FILE: packages/frontend-web/src/models/fake/article.ts
function fakeArticleModel (line 21) | function fakeArticleModel(overrides: Partial<IArticleAttributes> = {}): ...
FILE: packages/frontend-web/src/models/fake/category.ts
function fakeCategoryModel (line 20) | function fakeCategoryModel(overrides: Partial<ICategoryAttributes> = {})...
FILE: packages/frontend-web/src/models/fake/comment.ts
function fakeCommentModel (line 21) | function fakeCommentModel(overrides: Partial<ICommentAttributes> = {}): ...
function fakeCommentFlagModel (line 53) | function fakeCommentFlagModel(overrides: Partial<ICommentFlagAttributes>...
FILE: packages/frontend-web/src/models/fake/commentScore.ts
function fakeCommentScoreModel (line 20) | function fakeCommentScoreModel(overrides: Partial<ICommentScoreAttribute...
FILE: packages/frontend-web/src/models/fake/rule.ts
function fakeRuleModel (line 31) | function fakeRuleModel(overrides: Partial<IRuleAttributes> = {}): IRuleM...
FILE: packages/frontend-web/src/models/fake/tag.ts
function fakeTagModel (line 20) | function fakeTagModel(overrides: Partial<ITagAttributes> = {}): ITagModel {
FILE: packages/frontend-web/src/models/fake/user.ts
function fakeUserModel (line 20) | function fakeUserModel(overrides: Partial<IUserAttributes> = {}): IUserM...
FILE: packages/frontend-web/src/models/preselect.ts
type IPreselectAttributes (line 19) | interface IPreselectAttributes {
type IPreselectModel (line 27) | type IPreselectModel = Readonly<IPreselectAttributes>;
function PreselectModel (line 29) | function PreselectModel(keyValuePairs: IPreselectAttributes): IPreselect...
FILE: packages/frontend-web/src/models/rule.ts
type IRuleAttributes (line 19) | interface IRuleAttributes {
type IRuleModel (line 29) | type IRuleModel = Readonly<IRuleAttributes>;
function RuleModel (line 31) | function RuleModel(keyValuePairs: IRuleAttributes): IRuleModel {
FILE: packages/frontend-web/src/models/tag.ts
type ITagAttributes (line 19) | interface ITagAttributes {
type ITagModel (line 30) | type ITagModel = Readonly<ITagAttributes>;
function TagModel (line 32) | function TagModel(keyValuePairs: Partial<ITagAttributes>): ITagModel {
FILE: packages/frontend-web/src/models/taggingSensitivity.ts
type ITaggingSensitivityAttributes (line 19) | interface ITaggingSensitivityAttributes {
type ITaggingSensitivityModel (line 27) | type ITaggingSensitivityModel = Readonly<ITaggingSensitivityAttributes>;
function TaggingSensitivityModel (line 29) | function TaggingSensitivityModel(keyValuePairs: ITaggingSensitivityAttri...
FILE: packages/frontend-web/src/models/user.ts
type IUserAttributes (line 19) | interface IUserAttributes {
type IUserModel (line 29) | type IUserModel = Readonly<IUserAttributes>;
function UserModel (line 31) | function UserModel(userAttributes: IUserAttributes): IUserModel {
FILE: packages/frontend-web/src/server.ts
function mountWebFrontend (line 21) | function mountWebFrontend(modifyOutput?: (output: string) => string): Ap...
FILE: packages/frontend-web/src/test/actions.ts
function listenForMessages (line 28) | async function listenForMessages(
function checkTypeIsUpdate (line 59) | function checkTypeIsUpdate(type: string) {
function checkExpectations (line 65) | function checkExpectations(
function approveComment (line 82) | async function approveComment(
function rejectComment (line 96) | async function rejectComment(
function setArticleState (line 110) | async function setArticleState(
function setArticleModerators (line 129) | async function setArticleModerators(
FILE: packages/frontend-web/src/test/apitest.ts
function usage (line 17) | function usage() {
function websocketStateHandler (line 70) | function websocketStateHandler(status: string): void {
FILE: packages/frontend-web/src/test/notificationChecks.ts
class ArticleMessages (line 36) | class ArticleMessages {
method notificationHandler (line 55) | notificationHandler(data: IAllArticlesData) {
method updateHandler (line 68) | updateHandler(data: IArticleUpdate) {
method dataCheck (line 78) | dataCheck() {
method stateCheck (line 104) | stateCheck() {
class SystemData (line 124) | class SystemData {
method notificationHandler (line 139) | notificationHandler(data: ISystemData) {
method usersCheck (line 151) | usersCheck() {
method tagsCheck (line 159) | tagsCheck() {
method stateCheck (line 175) | stateCheck() {
class UserData (line 203) | class UserData {
method notificationHandler (line 208) | notificationHandler(data: IPerUserData) {
method stateCheck (line 214) | stateCheck() {
FILE: packages/frontend-web/src/test/objectChecks.ts
function date_string (line 29) | function date_string(val: any) {
function date_string_or_null (line 38) | function date_string_or_null(val: any) {
function email (line 46) | function email(val: any) {
function url (line 53) | function url(val: any) {
function url_or_null (line 62) | function url_or_null(val: any) {
function moderator_group (line 69) | function moderator_group(val: any) {
function category_id_or_null (line 77) | function category_id_or_null(val: any) {
function tag_id_or_null (line 86) | function tag_id_or_null(val: any) {
function is_user (line 95) | function is_user(u: any) {
function array_of_users (line 108) | function array_of_users(val: any) {
function action (line 120) | function action(val: any) {
function checkArrayOf (line 133) | function checkArrayOf(itemChecker: (i: any) => boolean, o: any) {
function checkListNumber (line 146) | function checkListNumber(o: any) {
function checkMap (line 161) | function checkMap(o: any, type: string, keyType: (o: any) => boolean, va...
function checkObject (line 299) | function checkObject(o: any, type: string, fields: any): boolean {
function checkCategory (line 323) | function checkCategory(o: any) {
function checkArticle (line 332) | function checkArticle(o: any) {
function checkUser (line 336) | function checkUser(o: any) {
function checkTag (line 345) | function checkTag(o: any) {
function checkTaggingSensitivity (line 353) | function checkTaggingSensitivity(o: any) {
function checkRule (line 357) | function checkRule(o: any) {
function checkPreselect (line 361) | function checkPreselect(o: any) {
function checkModeratedComments (line 365) | function checkModeratedComments(o: any) {
function checkTextSizes (line 369) | function checkTextSizes(o: any) {
function checkComment (line 373) | function checkComment(o: any) {
function checkListComments (line 377) | function checkListComments(o: any) {
function checkSingleComment (line 390) | function checkSingleComment(o: any) {
function checkCommentFlag (line 394) | function checkCommentFlag(o: any) {
function checkCommentScore (line 398) | function checkCommentScore(o: any) {
function checkHistogramScores (line 402) | function checkHistogramScores(o: any) {
FILE: packages/frontend-web/src/test/pageTests.ts
function fetchArticleText (line 43) | async function fetchArticleText(articleId: ModelId) {
function listCommentsPage (line 48) | async function listCommentsPage(comments: Array<ModelId>) {
function listNewCommentsPage_SUMMARY_SCORE (line 55) | async function listNewCommentsPage_SUMMARY_SCORE(
function listModeratedCommentsPage (line 80) | async function listModeratedCommentsPage(
function commentDetailsPage (line 104) | async function commentDetailsPage(commentId: string) {
FILE: packages/frontend-web/src/types.ts
type ServerStates (line 17) | type ServerStates =
type AuthenticationStates (line 25) | type AuthenticationStates = 'initialising' | 'check_token' | 'unauthenti...
type WebsocketStates (line 26) | type WebsocketStates = 'ws_connecting' | 'ws_gtg';
type SystemStates (line 27) | type SystemStates = ServerStates | AuthenticationStates | WebsocketStates;
type IModerationAction (line 32) | type IModerationAction = 'approve' | 'defer' | 'highlight' | 'reject';
type ICommentAction (line 35) | type ICommentAction = IModerationAction | 'tag';
type IConfirmationAction (line 38) | type IConfirmationAction = ICommentAction | 'reset';
FILE: packages/frontend-web/tooling/storybook/config.js
function loadStories (line 27) | function loadStories() {
FILE: seed/initial-database.sql
type `SequelizeMeta` (line 25) | CREATE TABLE `SequelizeMeta` (
type `articles` (line 49) | CREATE TABLE `articles` (
type `categories` (line 99) | CREATE TABLE `categories` (
type `comment_flags` (line 141) | CREATE TABLE `comment_flags` (
type `comment_score_requests` (line 179) | CREATE TABLE `comment_score_requests` (
type `comment_scores` (line 211) | CREATE TABLE `comment_scores` (
type `comment_sizes` (line 259) | CREATE TABLE `comment_sizes` (
type `comment_summary_scores` (line 287) | CREATE TABLE `comment_summary_scores` (
type `comment_top_scores` (line 319) | CREATE TABLE `comment_top_scores` (
type `comments` (line 349) | CREATE TABLE `comments` (
type `configuration_items` (line 414) | CREATE TABLE `configuration_items` (
type `csrfs` (line 439) | CREATE TABLE `csrfs` (
type `decisions` (line 466) | CREATE TABLE `decisions` (
type `last_updates` (line 503) | CREATE TABLE `last_updates` (
type `moderation_rules` (line 528) | CREATE TABLE `moderation_rules` (
type `moderator_assignments` (line 562) | CREATE TABLE `moderator_assignments` (
type `preselects` (line 593) | CREATE TABLE `preselects` (
type `tagging_sensitivities` (line 629) | CREATE TABLE `tagging_sensitivities` (
type `tags` (line 665) | CREATE TABLE `tags` (
type `user_category_assignments` (line 697) | CREATE TABLE `user_category_assignments` (
type `user_social_auths` (line 725) | CREATE TABLE `user_social_auths` (
type `users` (line 757) | CREATE TABLE `users` (
Condensed preview — 535 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,449K chars).
[
{
"path": ".circleci/config.yml",
"chars": 236,
"preview": "version: 2\njobs:\n build:\n docker:\n - image: circleci/node:12.19\n environment:\n NODE_ENV: circle"
},
{
"path": ".dockerignore",
"chars": 143,
"preview": ".data\ndist\n**/dist\n**/dist-commonjs\nnode_modules\n**/node_modules\n.vagrant\n.dockerignore\nDockerfile\nnpm-debug.*\n.git\n.hg\n"
},
{
"path": ".editorconfig",
"chars": 207,
"preview": "# editorconfig.org\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_"
},
{
"path": ".github/ISSUE_TEMPLATE.md",
"chars": 878,
"preview": "<!--- Provide a general summary of the issue in the Title above -->\n\n## Context\n<!--- Provide a more detailed introducti"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 1136,
"preview": "<!--- Provide a general summary of your changes in the Title above -->\n\n## Description\n<!--- Describe your changes in de"
},
{
"path": ".gitignore",
"chars": 211,
"preview": ".idea/\nnode_modules/\nnpm-debug.*\ntmp/\n.tmp/\ndist/\ndist-commonjs/\n.sw[a-z]\n.DS_Store\nbuild/\n*.js.map\n*.css.map\n.awcache\na"
},
{
"path": "CHANGELOG.md",
"chars": 459,
"preview": "# v1.0.5\n\n* Fix bug where settings page ids are set to integers ([PR](https://github.com/conversationai/conversationai-m"
},
{
"path": "CONTRIBUTING.md",
"chars": 984,
"preview": "# How to contribute\n\nWe'd love to accept your patches and contributions to this project. There are\njust a few small guid"
},
{
"path": "LICENSE",
"chars": 10755,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "QUICKSTART.md",
"chars": 2069,
"preview": "These are instructions on how to get a youtube instance of moderator running on a Google Cloud compute VM.\n\nStep 0:\n----"
},
{
"path": "README.md",
"chars": 7968,
"preview": "OSMod - The ConversationAI Moderator App\n========================================\n\nDeploying an OSMod instance\n---------"
},
{
"path": "bin/build",
"chars": 32,
"preview": "#!/bin/bash\nnpx lerna run build\n"
},
{
"path": "bin/initdb",
"chars": 737,
"preview": "#!/bin/bash\n\nbasename=`dirname $0`\nmysqlx=\"mysql -u root -p${DATABASE_PASSWORD}\"\nif [ ! -z \"${DATABASE_HOST}\" ]; then\n "
},
{
"path": "bin/install",
"chars": 855,
"preview": "#!/bin/bash\nset -e\n# Running npm install blats the package-lock.json file, as it doesn't know anything about\n# the sub-p"
},
{
"path": "bin/install-circleci",
"chars": 118,
"preview": "#!/bin/bash\n\nNODE_ENV=development npm install\n./node_modules/.bin/lerna bootstrap\n./node_modules/.bin/lerna run build\n"
},
{
"path": "bin/link-packages",
"chars": 32,
"preview": "#!/bin/bash\nnpx lerna bootstrap\n"
},
{
"path": "bin/lint",
"chars": 48,
"preview": "#!/bin/bash\n\n./node_modules/.bin/lerna run lint\n"
},
{
"path": "bin/lint-fix",
"chars": 52,
"preview": "#!/bin/bash\n\n./node_modules/.bin/lerna run lint:fix\n"
},
{
"path": "bin/osmod",
"chars": 331,
"preview": "#!/bin/bash\n\nbasename=`dirname $0`\n# Forward to ${basename}/../packages/backend-api/bin/osmod.js\n\nC=''\n\nfor i in \"$@\"; d"
},
{
"path": "bin/run",
"chars": 827,
"preview": "#!/bin/bash\n# Script to run some moderator component inside a docker container\nset -e\n\nbasename=`dirname $0`\nserver=$1\nl"
},
{
"path": "bin/storybook",
"chars": 69,
"preview": "#!/bin/bash\n\ncd packages/frontend-web\nnpm run storybook &\ncd -\n\nwait\n"
},
{
"path": "bin/sync-db",
"chars": 1436,
"preview": "#!/bin/bash\n\n# keyword arguments\n# 1st argument is .yaml file to pull env_variables from\n\n# This script clears the termi"
},
{
"path": "bin/test",
"chars": 55,
"preview": "#!/bin/bash\n\nnpx lerna run compile\nnpx lerna run test\n\n"
},
{
"path": "bin/watch",
"chars": 793,
"preview": "#!/bin/bash\nset -e\n\nexport FRONTEND_URL=http://localhost:8000\nexport API_URL=http://localhost:8080\n\nif [ -z \"$1\" ]; then"
},
{
"path": "deployments/gcloud/Dockerfile",
"chars": 160,
"preview": "FROM gcr.io/google_appengine/nodejs\n\nRUN install_node v8.11.1\n\nWORKDIR /app/\nCOPY . /app/\n\nRUN npm cache verify\n\nRUN bin"
},
{
"path": "deployments/gcloud/README.md",
"chars": 6878,
"preview": "# Deploying ConversationAI Moderator to Google Cloud\n\n## Preparing for the deployment\n\n### Install `gcloud`, `docker`, `"
},
{
"path": "deployments/gcloud/deploy-sql.sh",
"chars": 1886,
"preview": "#!/bin/bash\n# Create a managed SQL instance and populate it with initial data\n# This script assumes the following enviro"
},
{
"path": "deployments/gcloud/deploy.sh",
"chars": 798,
"preview": "#!/bin/bash\n# Use kubectl to deploy the app to the\nset -e\nset -u\n\nexport SQL_CONNECTION=`gcloud sql instances describe $"
},
{
"path": "deployments/gcloud/kubernetes-deployment.yaml",
"chars": 2067,
"preview": "apiVersion: extensions/v1beta1\nkind: Deployment\nmetadata:\n name: conversationai-moderator\n labels:\n app: conversati"
},
{
"path": "deployments/gcloud/kubernetes-networking.yaml",
"chars": 304,
"preview": "apiVersion: v1\nkind: Service\nmetadata:\n name: moderator-networking\nspec:\n type: LoadBalancer\n ports:\n - name: fron"
},
{
"path": "deployments/local/Dockerfile",
"chars": 222,
"preview": "FROM gcr.io/google_appengine/nodejs\n\nRUN install_node v8.11.1 && apt update && apt dist-upgrade -y && apt install -y mys"
},
{
"path": "deployments/local/README.md",
"chars": 410,
"preview": "# Run application locally in a docker collection\n\nThe scripts and configuration files contained in this directory create"
},
{
"path": "deployments/local/docker-compose.yml",
"chars": 961,
"preview": "version: '3'\nservices:\n database:\n container_name: database\n image: 'mysql:5.7.16'\n volumes:\n - './.data/"
},
{
"path": "deployments/standalone/.dockerignore",
"chars": 116,
"preview": ".data\ndist\ndist-commonjs\nnode_modules\n.vagrant\n.dockerignore\nDockerfile\nnpm-debug.*\n.git\n.hg\n.svn\nconfig/local.json\n"
},
{
"path": "deployments/standalone/Dockerfile",
"chars": 290,
"preview": "FROM ubuntu:bionic\n\nRUN apt update && apt --assume-yes dist-upgrade && apt --assume-yes install mysql-server nodejs npm "
},
{
"path": "deployments/standalone/initialise_db.sh",
"chars": 782,
"preview": "#!/bin/bash\n\nmkdir -p /var/run/mysqld\nchown mysql:mysql /var/run/mysqld/\n\n/usr/bin/mysqld_safe --skip-grant-tables --pid"
},
{
"path": "design-files/Moderator-StickerSheet-20161117-DAS/document.json",
"chars": 23598,
"preview": "{\"_class\":\"document\",\"do_objectID\":\"FDD7F69B-2AF0-4FAE-9B51-45D7040E8FCB\",\"assets\":{\"_class\":\"assetCollection\",\"colors\":"
},
{
"path": "design-files/Moderator-StickerSheet-20161117-DAS/meta.json",
"chars": 1996,
"preview": "{\"commit\":\"335a30073fcb2dc64a0abd6148ae147d694c887d\",\"appVersion\":\"43.1\",\"build\":39012,\"app\":\"com.bohemiancoding.sketch3"
},
{
"path": "design-files/Moderator-StickerSheet-20161117-DAS/pages/1B67083D-3430-4865-A36A-6C687A1EEB45.json",
"chars": 95096,
"preview": "{\"_class\":\"page\",\"do_objectID\":\"1B67083D-3430-4865-A36A-6C687A1EEB45\",\"exportOptions\":{\"_class\":\"exportOptions\",\"exportF"
},
{
"path": "design-files/Moderator-StickerSheet-20161117-DAS/user.json",
"chars": 2271,
"preview": "{\"FDD7F69B-2AF0-4FAE-9B51-45D7040E8FCB\":{\"pageListHeight\":1016,\"exportableLayerSelection\":[\"FAC6D00E-86E5-45BC-B761-CA9C"
},
{
"path": "docs/auth.md",
"chars": 2480,
"preview": "# OS Moderator Authentication\n\nOS Moderator leverages a Google OAuth 2 authentication flow to trade on our server for JW"
},
{
"path": "docs/comment_flow.md",
"chars": 1337,
"preview": "# OSMOD Comment State Flow\n\nThis document describes the path of a comment through the OSMOD system.\n\n1. The integration "
},
{
"path": "docs/modeling.md",
"chars": 13049,
"preview": "# The Data Model for Osmod\n\n## Connecting to remote SQL Google Cloud database\n\nFrom [a cloud shell in your project](http"
},
{
"path": "docs/osmod_assistant_protocol.md",
"chars": 5372,
"preview": "# OSMOD Assistant Protocol\n\nThis document describes the protocol for interacting with the \"assistant\", the\ncomponent tha"
},
{
"path": "docs/osmod_services_api.md",
"chars": 4981,
"preview": "# OSMOD Services API\n\nThe OSMOD Services API allows publishing and tagging operations to comments.\n\nThis documentation c"
},
{
"path": "docs/osmod_task_api.md",
"chars": 1510,
"preview": "#OSMODTaskAPI\n\nThe OSMOD Task API exposes the worker tasks on HTTP endpoints to support\n`push`-style message consumption"
},
{
"path": "docs/sql_queries.sql",
"chars": 2087,
"preview": "\n -- articles with counts of real comments, and the table's counts cache for\n -- and unmoderated and moderated.\nCREATE V"
},
{
"path": "docs/worker.md",
"chars": 4060,
"preview": "# OS Moderator Worker/Task Queue\n\nWe have a worker/task queue using [Kue](https://github.com/Automattic/kue) and [Kue Sc"
},
{
"path": "docs/youtube_integration.md",
"chars": 2228,
"preview": "# OS Moderator YouTube integration\n\n## Synchronisation of Channels and Videos\n\nIn the OSMod UI, Channels are mapped to c"
},
{
"path": "lerna.json",
"chars": 25,
"preview": "{\n \"version\": \"1.1.0\"\n}\n"
},
{
"path": "package.json",
"chars": 405,
"preview": "{\n \"name\": \"osmod\",\n \"license\": \"Apache-2.0\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/conv"
},
{
"path": "packages/README.md",
"chars": 241,
"preview": "# The Osmod Packages\n\nThere are several parts to Osmod:\n\n* `backend-api`: the codebase of the API server for Osmod.\n * "
},
{
"path": "packages/backend-api/.sequelizerc",
"chars": 211,
"preview": "const path = require('path');\n\nmodule.exports = {\n 'config': path.resolve('./dist/sequelize-config.js'),\n 'migrations-"
},
{
"path": "packages/backend-api/LICENSE",
"chars": 10755,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "packages/backend-api/README.md",
"chars": 1894,
"preview": "# Moderator Backend\nThe Moderator Backend is a Node.js/Express-backed service that provides several APIs for both the fr"
},
{
"path": "packages/backend-api/bin/check_migrations.sh",
"chars": 762,
"preview": "#!/bin/bash\n\n# Do a sequelize sync and dump the resulting schema:\n\nexport DATABASE_NAME=os_moderator_schema_test_sync\nsu"
},
{
"path": "packages/backend-api/bin/make_migration.sh",
"chars": 53,
"preview": "#!/bin/bash\nnpx sequelize migration:create --name $1\n"
},
{
"path": "packages/backend-api/bin/osmod-test.js",
"chars": 1169,
"preview": "#!/usr/bin/env node\n\n/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou "
},
{
"path": "packages/backend-api/bin/osmod.js",
"chars": 1892,
"preview": "#!/usr/bin/env node\n\n/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou "
},
{
"path": "packages/backend-api/bin/run_sequelize_sync.js",
"chars": 758,
"preview": "#!/usr/bin/env node\n\n/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou "
},
{
"path": "packages/backend-api/bin/run_task",
"chars": 666,
"preview": "#!/usr/bin/env node\n\n/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou "
},
{
"path": "packages/backend-api/data/alice.txt",
"chars": 53483,
"preview": "Taken from project Gutenberg\nhttp://www.gutenberg.org/ebooks/11\n\n _THE \"STORYLAND\" SERIES_\n\n\n\n "
},
{
"path": "packages/backend-api/data/brexit.csv",
"chars": 7912,
"preview": "Brexit,score\r\nAnti democratic assholes,0.95\r\nIgnorant and stupid,0.93\r\nThey are stupid and ignorant with no class,0.91\r\n"
},
{
"path": "packages/backend-api/data/climate.csv",
"chars": 5160,
"preview": "Climate change,Score\r\nThey have their heads up their ass.,0.93\r\nHow can you be so stupid?,0.91\r\nThey are liberal idiots "
},
{
"path": "packages/backend-api/data/election.csv",
"chars": 4032,
"preview": "US Election,score\r\nIf they voted for Hilary they are idiots,0.90\r\nIt was awful. People are stupid.,0.90\r\nScrew you trump"
},
{
"path": "packages/backend-api/data/wikipedia.csv",
"chars": 69362,
"preview": "content\n\"This article lists her first worldwide single as \"\"Energy\"\" being released in 2009, then states that her third "
},
{
"path": "packages/backend-api/package.json",
"chars": 2572,
"preview": "{\n \"name\": \"@conversationai/moderator-backend-api\",\n \"description\": \"API Endpoints for OSMod\",\n \"main\": \"src/server.t"
},
{
"path": "packages/backend-api/src/actions/assignment_updaters.ts",
"chars": 4085,
"preview": "/*\nCopyright 2021 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/actions/object_updaters.ts",
"chars": 5975,
"preview": "/*\nCopyright 2021 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/api/assistant/index.ts",
"chars": 2454,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/api/assistant/schema.ts",
"chars": 1153,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/api/constants.ts",
"chars": 772,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/api/router.ts",
"chars": 1684,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/api/services/assignments.ts",
"chars": 1846,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/api/services/authorCounts.ts",
"chars": 2241,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/api/services/commentActions.ts",
"chars": 6350,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/api/services/commentSources.ts",
"chars": 3144,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/api/services/editComment.ts",
"chars": 2324,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/api/services/histogramScores/index.ts",
"chars": 8323,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/api/services/histogramScores/util.ts",
"chars": 10315,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/api/services/index.ts",
"chars": 2195,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/api/services/moderatedCounts.ts",
"chars": 4928,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/api/services/search.ts",
"chars": 3894,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/api/services/serializer.ts",
"chars": 3315,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/api/services/simple.ts",
"chars": 9806,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/api/services/textSizes.ts",
"chars": 2505,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/api/services/updateNotifications.ts",
"chars": 11687,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/api/util/permissions.ts",
"chars": 1882,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/api/util/server.ts",
"chars": 3323,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/api/util/sortCommentIds.ts",
"chars": 1185,
"preview": "/*\nCopyright 2020 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/api/util/validation.ts",
"chars": 1824,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/auth/config.ts",
"chars": 1194,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/auth/providers/google.ts",
"chars": 3779,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/auth/providers/index.ts",
"chars": 608,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/auth/providers/jwt.ts",
"chars": 2322,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/auth/router.ts",
"chars": 5538,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/auth/tokens.ts",
"chars": 3650,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/auth/users.ts",
"chars": 3012,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/auth/utils.ts",
"chars": 2064,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/auth/youtube.ts",
"chars": 2901,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/commands/articles/delete.ts",
"chars": 1487,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/commands/comments/calculate_text_size.ts",
"chars": 1855,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/commands/comments/data_helpers.ts",
"chars": 2172,
"preview": "import { logger } from '../../logger';\nimport {\n Article,\n Category,\n Comment,\n IAuthorAttributes,\n RESET_COUNTS,\n "
},
{
"path": "packages/backend-api/src/commands/comments/delete.ts",
"chars": 1485,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/commands/comments/flag.ts",
"chars": 1936,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/commands/comments/generate.ts",
"chars": 5980,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/commands/comments/import.ts",
"chars": 2855,
"preview": "/*\nCopyright 2012 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/commands/comments/rebuild_reply_relations.ts",
"chars": 2335,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/commands/comments/recalculate_text_sizes.ts",
"chars": 1804,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/commands/comments/recalculate_top_scores.ts",
"chars": 1698,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/commands/comments/rescore.ts",
"chars": 1531,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/commands/comments/send_to_scorer.ts",
"chars": 2321,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/commands/denormalize.ts",
"chars": 1709,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/commands/tests/youtube.ts",
"chars": 2374,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/commands/users/create.ts",
"chars": 5032,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/commands/users/get_token.ts",
"chars": 2162,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/config.ts",
"chars": 3491,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/domain/articles/countDenormalization.ts",
"chars": 2631,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/domain/articles/index.ts",
"chars": 599,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/domain/categories/countDenormalization.ts",
"chars": 1658,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/domain/categories/index.ts",
"chars": 599,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/domain/commentScores/index.ts",
"chars": 2753,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/domain/comments/countDenormalization.ts",
"chars": 1405,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/domain/comments/index.ts",
"chars": 628,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/domain/comments/textSizes.ts",
"chars": 3048,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/domain/index.ts",
"chars": 678,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/index.ts",
"chars": 2725,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/integrations/decisions.ts",
"chars": 1613,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/integrations/index.ts",
"chars": 703,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/integrations/youtube/actions.ts",
"chars": 1434,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/integrations/youtube/authenticate.ts",
"chars": 1714,
"preview": "/*\nCopyright 2018 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/integrations/youtube/channels.ts",
"chars": 5064,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/integrations/youtube/comments.ts",
"chars": 4562,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/integrations/youtube/hooks.ts",
"chars": 1183,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/integrations/youtube/objectmap.ts",
"chars": 8212,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/integrations/youtube/task.ts",
"chars": 1416,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/integrations/youtube/videos.ts",
"chars": 5400,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/logger.ts",
"chars": 767,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/models/article.ts",
"chars": 4109,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/models/category.ts",
"chars": 3329,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/models/comment.ts",
"chars": 5805,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/models/comment_flag.ts",
"chars": 2136,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/models/comment_score.ts",
"chars": 3125,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/models/comment_score_request.ts",
"chars": 1493,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/models/comment_size.ts",
"chars": 1344,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/models/comment_summary_score.ts",
"chars": 1989,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/models/comment_top_score.ts",
"chars": 1282,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/models/configuration.ts",
"chars": 1907,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/models/constants.ts",
"chars": 1126,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/models/csrf.ts",
"chars": 1148,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/models/decision.ts",
"chars": 2068,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/models/index.ts",
"chars": 3100,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/models/moderation_rule.ts",
"chars": 2694,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/models/moderator_assignment.ts",
"chars": 1513,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/models/preselect.ts",
"chars": 1764,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/models/tag.ts",
"chars": 2707,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/models/tagging_sensitivity.ts",
"chars": 1819,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/models/user.ts",
"chars": 3934,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/models/user_category_assignment.ts",
"chars": 1097,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/models/user_social_auth.ts",
"chars": 1632,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/notification_router.ts",
"chars": 3060,
"preview": "/*\nCopyright 2021 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/pipeline/apiShim.ts",
"chars": 5010,
"preview": "/*\nCopyright 2018 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/pipeline/hooks.ts",
"chars": 2088,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/pipeline/index.ts",
"chars": 587,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/pipeline/pipeline.ts",
"chars": 11420,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/pipeline/rules.ts",
"chars": 6752,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/pipeline/shim.ts",
"chars": 1293,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/pipeline/state.ts",
"chars": 7996,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/processing/api/index.ts",
"chars": 1361,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/processing/api/known_tasks.ts",
"chars": 1911,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/processing/api/permissions.ts",
"chars": 841,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/processing/dashboard.ts",
"chars": 695,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/processing/index.ts",
"chars": 874,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/processing/tasks/comment_actions.ts",
"chars": 4340,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/processing/tasks/db_operations.ts",
"chars": 3191,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/processing/tasks/heartbeat.ts",
"chars": 1768,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/processing/tasks/index.ts",
"chars": 2171,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/processing/tasks/process_machine_score.ts",
"chars": 1601,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/processing/tasks/process_tagging.ts",
"chars": 3616,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/processing/tasks/score_actions.ts",
"chars": 3561,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/processing/tasks/score_tag_actions.ts",
"chars": 4733,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/processing/tasks/send_comment_for_scoring.ts",
"chars": 1675,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/processing/util.ts",
"chars": 1960,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/processing/worker.ts",
"chars": 3646,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/processor.ts",
"chars": 627,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/redis.ts",
"chars": 893,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/sequelize-config.ts",
"chars": 1247,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/sequelize-sync.ts",
"chars": 634,
"preview": "#!/usr/bin/env node\n\n/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou "
},
{
"path": "packages/backend-api/src/sequelize.ts",
"chars": 774,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/server-management.ts",
"chars": 1180,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/server.ts",
"chars": 3451,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/test/auth/providers/google.spec.ts",
"chars": 5138,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/test/auth/tokens.spec.ts",
"chars": 5915,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/test/auth/users.spec.ts",
"chars": 8400,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/test/domain/comments/fixture.ts",
"chars": 5301,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/test/domain/comments/textSizes.spec.ts",
"chars": 1976,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/test/domain/topScores/calculateTopScores.spec.ts",
"chars": 1857,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/test/fixture.ts",
"chars": 5200,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/test/integration/api/actions.spec.ts",
"chars": 9147,
"preview": "/*\nCopyright 2019 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
},
{
"path": "packages/backend-api/src/test/integration/api/assignments.spec.ts",
"chars": 3129,
"preview": "/*\nCopyright 2017 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
}
]
// ... and 335 more files (download for full content)
About this extraction
This page contains the full source code of the conversationai/conversationai-moderator GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 535 files (15.7 MB), approximately 604.0k tokens, and a symbol index with 1960 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.