about summary refs log tree commit diff
path: root/app/javascript/themes/glitch
diff options
context:
space:
mode:
authorbeatrix <beatrix.bitrot@gmail.com>2017-12-06 17:44:07 -0500
committerGitHub <noreply@github.com>2017-12-06 17:44:07 -0500
commit81b01457598459c42a7b14d9aa14f91ba60dcae1 (patch)
tree7d3e6dadb75f3be95e5a5ed8b7ecfe90e7711831 /app/javascript/themes/glitch
parentf1cbea77a4a52929244198dcbde26d63d837489a (diff)
parent017fc81caf8f265e5c5543186877437485625795 (diff)
Merge pull request #229 from glitch-soc/glitch-theme
Advanced Next-Level Flavours And Skins For Mastodon™
Diffstat (limited to 'app/javascript/themes/glitch')
-rw-r--r--app/javascript/themes/glitch/actions/accounts.js661
-rw-r--r--app/javascript/themes/glitch/actions/alerts.js24
-rw-r--r--app/javascript/themes/glitch/actions/blocks.js82
-rw-r--r--app/javascript/themes/glitch/actions/bundles.js25
-rw-r--r--app/javascript/themes/glitch/actions/cards.js52
-rw-r--r--app/javascript/themes/glitch/actions/columns.js40
-rw-r--r--app/javascript/themes/glitch/actions/compose.js398
-rw-r--r--app/javascript/themes/glitch/actions/domain_blocks.js117
-rw-r--r--app/javascript/themes/glitch/actions/emojis.js14
-rw-r--r--app/javascript/themes/glitch/actions/favourites.js83
-rw-r--r--app/javascript/themes/glitch/actions/height_cache.js17
-rw-r--r--app/javascript/themes/glitch/actions/interactions.js313
-rw-r--r--app/javascript/themes/glitch/actions/local_settings.js24
-rw-r--r--app/javascript/themes/glitch/actions/modal.js16
-rw-r--r--app/javascript/themes/glitch/actions/mutes.js103
-rw-r--r--app/javascript/themes/glitch/actions/notifications.js265
-rw-r--r--app/javascript/themes/glitch/actions/onboarding.js14
-rw-r--r--app/javascript/themes/glitch/actions/pin_statuses.js40
-rw-r--r--app/javascript/themes/glitch/actions/push_notifications.js52
-rw-r--r--app/javascript/themes/glitch/actions/reports.js80
-rw-r--r--app/javascript/themes/glitch/actions/search.js73
-rw-r--r--app/javascript/themes/glitch/actions/settings.js31
-rw-r--r--app/javascript/themes/glitch/actions/statuses.js217
-rw-r--r--app/javascript/themes/glitch/actions/store.js17
-rw-r--r--app/javascript/themes/glitch/actions/streaming.js54
-rw-r--r--app/javascript/themes/glitch/actions/timelines.js208
-rw-r--r--app/javascript/themes/glitch/components/account.js116
-rw-r--r--app/javascript/themes/glitch/components/attachment_list.js33
-rw-r--r--app/javascript/themes/glitch/components/autosuggest_emoji.js42
-rw-r--r--app/javascript/themes/glitch/components/autosuggest_textarea.js222
-rw-r--r--app/javascript/themes/glitch/components/avatar.js72
-rw-r--r--app/javascript/themes/glitch/components/avatar_overlay.js30
-rw-r--r--app/javascript/themes/glitch/components/button.js64
-rw-r--r--app/javascript/themes/glitch/components/collapsable.js22
-rw-r--r--app/javascript/themes/glitch/components/column.js54
-rw-r--r--app/javascript/themes/glitch/components/column_back_button.js29
-rw-r--r--app/javascript/themes/glitch/components/column_back_button_slim.js31
-rw-r--r--app/javascript/themes/glitch/components/column_header.js214
-rw-r--r--app/javascript/themes/glitch/components/display_name.js20
-rw-r--r--app/javascript/themes/glitch/components/dropdown_menu.js211
-rw-r--r--app/javascript/themes/glitch/components/extended_video_player.js54
-rw-r--r--app/javascript/themes/glitch/components/icon_button.js137
-rw-r--r--app/javascript/themes/glitch/components/intersection_observer_article.js130
-rw-r--r--app/javascript/themes/glitch/components/load_more.js26
-rw-r--r--app/javascript/themes/glitch/components/loading_indicator.js11
-rw-r--r--app/javascript/themes/glitch/components/media_gallery.js255
-rw-r--r--app/javascript/themes/glitch/components/missing_indicator.js12
-rw-r--r--app/javascript/themes/glitch/components/notification_purge_buttons.js58
-rw-r--r--app/javascript/themes/glitch/components/permalink.js34
-rw-r--r--app/javascript/themes/glitch/components/relative_timestamp.js147
-rw-r--r--app/javascript/themes/glitch/components/scrollable_list.js198
-rw-r--r--app/javascript/themes/glitch/components/setting_text.js34
-rw-r--r--app/javascript/themes/glitch/components/status.js440
-rw-r--r--app/javascript/themes/glitch/components/status_action_bar.js188
-rw-r--r--app/javascript/themes/glitch/components/status_content.js245
-rw-r--r--app/javascript/themes/glitch/components/status_header.js120
-rw-r--r--app/javascript/themes/glitch/components/status_list.js72
-rw-r--r--app/javascript/themes/glitch/components/status_prepend.js83
-rw-r--r--app/javascript/themes/glitch/components/status_visibility_icon.js48
-rw-r--r--app/javascript/themes/glitch/containers/account_container.js72
-rw-r--r--app/javascript/themes/glitch/containers/card_container.js18
-rw-r--r--app/javascript/themes/glitch/containers/compose_container.js38
-rw-r--r--app/javascript/themes/glitch/containers/dropdown_menu_container.js16
-rw-r--r--app/javascript/themes/glitch/containers/intersection_observer_article_container.js17
-rw-r--r--app/javascript/themes/glitch/containers/mastodon.js70
-rw-r--r--app/javascript/themes/glitch/containers/media_gallery_container.js34
-rw-r--r--app/javascript/themes/glitch/containers/notification_purge_buttons_container.js49
-rw-r--r--app/javascript/themes/glitch/containers/status_container.js151
-rw-r--r--app/javascript/themes/glitch/containers/timeline_container.js48
-rw-r--r--app/javascript/themes/glitch/containers/video_container.js26
-rw-r--r--app/javascript/themes/glitch/features/account/components/action_bar.js145
-rw-r--r--app/javascript/themes/glitch/features/account/components/header.js99
-rw-r--r--app/javascript/themes/glitch/features/account_gallery/components/media_item.js39
-rw-r--r--app/javascript/themes/glitch/features/account_gallery/index.js111
-rw-r--r--app/javascript/themes/glitch/features/account_timeline/components/header.js95
-rw-r--r--app/javascript/themes/glitch/features/account_timeline/containers/header_container.js104
-rw-r--r--app/javascript/themes/glitch/features/account_timeline/index.js77
-rw-r--r--app/javascript/themes/glitch/features/blocks/index.js70
-rw-r--r--app/javascript/themes/glitch/features/community_timeline/components/column_settings.js35
-rw-r--r--app/javascript/themes/glitch/features/community_timeline/containers/column_settings_container.js17
-rw-r--r--app/javascript/themes/glitch/features/community_timeline/index.js107
-rw-r--r--app/javascript/themes/glitch/features/compose/components/advanced_options.js62
-rw-r--r--app/javascript/themes/glitch/features/compose/components/advanced_options_toggle.js35
-rw-r--r--app/javascript/themes/glitch/features/compose/components/attach_options.js131
-rw-r--r--app/javascript/themes/glitch/features/compose/components/autosuggest_account.js24
-rw-r--r--app/javascript/themes/glitch/features/compose/components/character_counter.js25
-rw-r--r--app/javascript/themes/glitch/features/compose/components/compose_form.js286
-rw-r--r--app/javascript/themes/glitch/features/compose/components/dropdown.js77
-rw-r--r--app/javascript/themes/glitch/features/compose/components/emoji_picker_dropdown.js376
-rw-r--r--app/javascript/themes/glitch/features/compose/components/navigation_bar.js38
-rw-r--r--app/javascript/themes/glitch/features/compose/components/privacy_dropdown.js200
-rw-r--r--app/javascript/themes/glitch/features/compose/components/reply_indicator.js63
-rw-r--r--app/javascript/themes/glitch/features/compose/components/search.js129
-rw-r--r--app/javascript/themes/glitch/features/compose/components/search_results.js65
-rw-r--r--app/javascript/themes/glitch/features/compose/components/text_icon_button.js29
-rw-r--r--app/javascript/themes/glitch/features/compose/components/upload.js96
-rw-r--r--app/javascript/themes/glitch/features/compose/components/upload_button.js77
-rw-r--r--app/javascript/themes/glitch/features/compose/components/upload_form.js29
-rw-r--r--app/javascript/themes/glitch/features/compose/components/upload_progress.js42
-rw-r--r--app/javascript/themes/glitch/features/compose/components/warning.js26
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/advanced_options_container.js20
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/autosuggest_account_container.js15
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/compose_form_container.js71
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/emoji_picker_dropdown_container.js82
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/navigation_container.js11
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/privacy_dropdown_container.js24
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/reply_indicator_container.js24
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/search_container.js35
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/search_results_container.js8
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/sensitive_button_container.js71
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/spoiler_button_container.js25
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/upload_button_container.js18
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/upload_container.js21
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/upload_form_container.js8
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/upload_progress_container.js9
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/warning_container.js24
-rw-r--r--app/javascript/themes/glitch/features/compose/index.js126
-rw-r--r--app/javascript/themes/glitch/features/direct_timeline/containers/column_settings_container.js17
-rw-r--r--app/javascript/themes/glitch/features/direct_timeline/index.js107
-rw-r--r--app/javascript/themes/glitch/features/favourited_statuses/index.js94
-rw-r--r--app/javascript/themes/glitch/features/favourites/index.js60
-rw-r--r--app/javascript/themes/glitch/features/follow_requests/components/account_authorize.js49
-rw-r--r--app/javascript/themes/glitch/features/follow_requests/containers/account_authorize_container.js26
-rw-r--r--app/javascript/themes/glitch/features/follow_requests/index.js71
-rw-r--r--app/javascript/themes/glitch/features/followers/index.js93
-rw-r--r--app/javascript/themes/glitch/features/following/index.js93
-rw-r--r--app/javascript/themes/glitch/features/generic_not_found/index.js11
-rw-r--r--app/javascript/themes/glitch/features/getting_started/index.js145
-rw-r--r--app/javascript/themes/glitch/features/hashtag_timeline/index.js118
-rw-r--r--app/javascript/themes/glitch/features/home_timeline/components/column_settings.js46
-rw-r--r--app/javascript/themes/glitch/features/home_timeline/containers/column_settings_container.js21
-rw-r--r--app/javascript/themes/glitch/features/home_timeline/index.js90
-rw-r--r--app/javascript/themes/glitch/features/local_settings/index.js68
-rw-r--r--app/javascript/themes/glitch/features/local_settings/navigation/index.js74
-rw-r--r--app/javascript/themes/glitch/features/local_settings/navigation/item/index.js69
-rw-r--r--app/javascript/themes/glitch/features/local_settings/navigation/item/style.scss27
-rw-r--r--app/javascript/themes/glitch/features/local_settings/navigation/style.scss10
-rw-r--r--app/javascript/themes/glitch/features/local_settings/page/index.js212
-rw-r--r--app/javascript/themes/glitch/features/local_settings/page/item/index.js90
-rw-r--r--app/javascript/themes/glitch/features/local_settings/page/item/style.scss7
-rw-r--r--app/javascript/themes/glitch/features/local_settings/page/style.scss9
-rw-r--r--app/javascript/themes/glitch/features/local_settings/style.scss34
-rw-r--r--app/javascript/themes/glitch/features/mutes/index.js70
-rw-r--r--app/javascript/themes/glitch/features/notifications/components/clear_column_button.js17
-rw-r--r--app/javascript/themes/glitch/features/notifications/components/column_settings.js86
-rw-r--r--app/javascript/themes/glitch/features/notifications/components/follow.js98
-rw-r--r--app/javascript/themes/glitch/features/notifications/components/notification.js93
-rw-r--r--app/javascript/themes/glitch/features/notifications/components/overlay.js57
-rw-r--r--app/javascript/themes/glitch/features/notifications/components/setting_toggle.js34
-rw-r--r--app/javascript/themes/glitch/features/notifications/containers/column_settings_container.js44
-rw-r--r--app/javascript/themes/glitch/features/notifications/containers/notification_container.js26
-rw-r--r--app/javascript/themes/glitch/features/notifications/containers/overlay_container.js18
-rw-r--r--app/javascript/themes/glitch/features/notifications/index.js193
-rw-r--r--app/javascript/themes/glitch/features/pinned_statuses/index.js59
-rw-r--r--app/javascript/themes/glitch/features/public_timeline/containers/column_settings_container.js17
-rw-r--r--app/javascript/themes/glitch/features/public_timeline/index.js107
-rw-r--r--app/javascript/themes/glitch/features/reblogs/index.js60
-rw-r--r--app/javascript/themes/glitch/features/report/components/status_check_box.js37
-rw-r--r--app/javascript/themes/glitch/features/report/containers/status_check_box_container.js19
-rw-r--r--app/javascript/themes/glitch/features/standalone/compose/index.js20
-rw-r--r--app/javascript/themes/glitch/features/standalone/hashtag_timeline/index.js70
-rw-r--r--app/javascript/themes/glitch/features/standalone/public_timeline/index.js76
-rw-r--r--app/javascript/themes/glitch/features/status/components/action_bar.js129
-rw-r--r--app/javascript/themes/glitch/features/status/components/card.js125
-rw-r--r--app/javascript/themes/glitch/features/status/components/detailed_status.js130
-rw-r--r--app/javascript/themes/glitch/features/status/containers/card_container.js8
-rw-r--r--app/javascript/themes/glitch/features/status/index.js358
-rw-r--r--app/javascript/themes/glitch/features/ui/components/actions_modal.js74
-rw-r--r--app/javascript/themes/glitch/features/ui/components/boost_modal.js84
-rw-r--r--app/javascript/themes/glitch/features/ui/components/bundle.js102
-rw-r--r--app/javascript/themes/glitch/features/ui/components/bundle_column_error.js44
-rw-r--r--app/javascript/themes/glitch/features/ui/components/bundle_modal_error.js53
-rw-r--r--app/javascript/themes/glitch/features/ui/components/column.js74
-rw-r--r--app/javascript/themes/glitch/features/ui/components/column_header.js35
-rw-r--r--app/javascript/themes/glitch/features/ui/components/column_link.js39
-rw-r--r--app/javascript/themes/glitch/features/ui/components/column_loading.js30
-rw-r--r--app/javascript/themes/glitch/features/ui/components/column_subheading.js16
-rw-r--r--app/javascript/themes/glitch/features/ui/components/columns_area.js174
-rw-r--r--app/javascript/themes/glitch/features/ui/components/confirmation_modal.js53
-rw-r--r--app/javascript/themes/glitch/features/ui/components/doodle_modal.js614
-rw-r--r--app/javascript/themes/glitch/features/ui/components/drawer_loading.js11
-rw-r--r--app/javascript/themes/glitch/features/ui/components/embed_modal.js84
-rw-r--r--app/javascript/themes/glitch/features/ui/components/image_loader.js152
-rw-r--r--app/javascript/themes/glitch/features/ui/components/media_modal.js126
-rw-r--r--app/javascript/themes/glitch/features/ui/components/modal_loading.js20
-rw-r--r--app/javascript/themes/glitch/features/ui/components/modal_root.js131
-rw-r--r--app/javascript/themes/glitch/features/ui/components/mute_modal.js105
-rw-r--r--app/javascript/themes/glitch/features/ui/components/onboarding_modal.js323
-rw-r--r--app/javascript/themes/glitch/features/ui/components/report_modal.js105
-rw-r--r--app/javascript/themes/glitch/features/ui/components/tabs_bar.js84
-rw-r--r--app/javascript/themes/glitch/features/ui/components/upload_area.js52
-rw-r--r--app/javascript/themes/glitch/features/ui/components/video_modal.js33
-rw-r--r--app/javascript/themes/glitch/features/ui/containers/bundle_container.js19
-rw-r--r--app/javascript/themes/glitch/features/ui/containers/columns_area_container.js8
-rw-r--r--app/javascript/themes/glitch/features/ui/containers/loading_bar_container.js8
-rw-r--r--app/javascript/themes/glitch/features/ui/containers/modal_container.js16
-rw-r--r--app/javascript/themes/glitch/features/ui/containers/notifications_container.js18
-rw-r--r--app/javascript/themes/glitch/features/ui/containers/status_list_container.js73
-rw-r--r--app/javascript/themes/glitch/features/ui/index.js443
-rw-r--r--app/javascript/themes/glitch/features/video/index.js288
-rw-r--r--app/javascript/themes/glitch/index.js14
-rw-r--r--app/javascript/themes/glitch/middleware/errors.js31
-rw-r--r--app/javascript/themes/glitch/middleware/loading_bar.js25
-rw-r--r--app/javascript/themes/glitch/middleware/sounds.js46
-rw-r--r--app/javascript/themes/glitch/reducers/accounts.js135
-rw-r--r--app/javascript/themes/glitch/reducers/accounts_counters.js138
-rw-r--r--app/javascript/themes/glitch/reducers/alerts.js25
-rw-r--r--app/javascript/themes/glitch/reducers/cards.js14
-rw-r--r--app/javascript/themes/glitch/reducers/compose.js307
-rw-r--r--app/javascript/themes/glitch/reducers/contexts.js61
-rw-r--r--app/javascript/themes/glitch/reducers/custom_emojis.js16
-rw-r--r--app/javascript/themes/glitch/reducers/height_cache.js23
-rw-r--r--app/javascript/themes/glitch/reducers/index.js54
-rw-r--r--app/javascript/themes/glitch/reducers/local_settings.js45
-rw-r--r--app/javascript/themes/glitch/reducers/media_attachments.js15
-rw-r--r--app/javascript/themes/glitch/reducers/meta.js16
-rw-r--r--app/javascript/themes/glitch/reducers/modal.js17
-rw-r--r--app/javascript/themes/glitch/reducers/mutes.js29
-rw-r--r--app/javascript/themes/glitch/reducers/notifications.js191
-rw-r--r--app/javascript/themes/glitch/reducers/push_notifications.js51
-rw-r--r--app/javascript/themes/glitch/reducers/relationships.js46
-rw-r--r--app/javascript/themes/glitch/reducers/reports.js60
-rw-r--r--app/javascript/themes/glitch/reducers/search.js42
-rw-r--r--app/javascript/themes/glitch/reducers/settings.js119
-rw-r--r--app/javascript/themes/glitch/reducers/status_lists.js75
-rw-r--r--app/javascript/themes/glitch/reducers/statuses.js148
-rw-r--r--app/javascript/themes/glitch/reducers/timelines.js149
-rw-r--r--app/javascript/themes/glitch/reducers/user_lists.js80
-rw-r--r--app/javascript/themes/glitch/selectors/index.js87
-rw-r--r--app/javascript/themes/glitch/service_worker/entry.js10
-rw-r--r--app/javascript/themes/glitch/service_worker/web_push_notifications.js159
-rw-r--r--app/javascript/themes/glitch/store/configureStore.js15
-rw-r--r--app/javascript/themes/glitch/styles/_mixins.scss52
-rw-r--r--app/javascript/themes/glitch/styles/about.scss822
-rw-r--r--app/javascript/themes/glitch/styles/accounts.scss589
-rw-r--r--app/javascript/themes/glitch/styles/admin.scss349
-rw-r--r--app/javascript/themes/glitch/styles/basics.scss122
-rw-r--r--app/javascript/themes/glitch/styles/boost.scss28
-rw-r--r--app/javascript/themes/glitch/styles/compact_header.scss34
-rw-r--r--app/javascript/themes/glitch/styles/components.scss4832
-rw-r--r--app/javascript/themes/glitch/styles/containers.scss116
-rw-r--r--app/javascript/themes/glitch/styles/doodle.scss86
-rw-r--r--app/javascript/themes/glitch/styles/emoji_picker.scss199
-rw-r--r--app/javascript/themes/glitch/styles/footer.scss30
-rw-r--r--app/javascript/themes/glitch/styles/forms.scss540
-rw-r--r--app/javascript/themes/glitch/styles/index.scss22
-rw-r--r--app/javascript/themes/glitch/styles/landing_strip.scss36
-rw-r--r--app/javascript/themes/glitch/styles/lists.scss19
-rw-r--r--app/javascript/themes/glitch/styles/reset copy.scss91
-rw-r--r--app/javascript/themes/glitch/styles/reset.scss91
-rw-r--r--app/javascript/themes/glitch/styles/rtl.scss254
-rw-r--r--app/javascript/themes/glitch/styles/stream_entries.scss335
-rw-r--r--app/javascript/themes/glitch/styles/tables.scss76
-rw-r--r--app/javascript/themes/glitch/styles/variables.scss35
-rw-r--r--app/javascript/themes/glitch/theme.yml18
-rw-r--r--app/javascript/themes/glitch/util/api.js26
-rw-r--r--app/javascript/themes/glitch/util/async-components.js115
-rw-r--r--app/javascript/themes/glitch/util/base_polyfills.js18
-rw-r--r--app/javascript/themes/glitch/util/bio_metadata.js331
-rw-r--r--app/javascript/themes/glitch/util/counter.js9
-rw-r--r--app/javascript/themes/glitch/util/emoji/emoji_compressed.js93
-rw-r--r--app/javascript/themes/glitch/util/emoji/emoji_map.json1
-rw-r--r--app/javascript/themes/glitch/util/emoji/emoji_mart_data_light.js41
-rw-r--r--app/javascript/themes/glitch/util/emoji/emoji_mart_search_light.js157
-rw-r--r--app/javascript/themes/glitch/util/emoji/emoji_picker.js7
-rw-r--r--app/javascript/themes/glitch/util/emoji/emoji_unicode_mapping_light.js35
-rw-r--r--app/javascript/themes/glitch/util/emoji/emoji_utils.js258
-rw-r--r--app/javascript/themes/glitch/util/emoji/index.js95
-rw-r--r--app/javascript/themes/glitch/util/emoji/unicode_to_filename.js26
-rw-r--r--app/javascript/themes/glitch/util/emoji/unicode_to_unified_name.js17
-rw-r--r--app/javascript/themes/glitch/util/extra_polyfills.js5
-rw-r--r--app/javascript/themes/glitch/util/fullscreen.js46
-rw-r--r--app/javascript/themes/glitch/util/get_rect_from_entry.js21
-rw-r--r--app/javascript/themes/glitch/util/initial_state.js21
-rw-r--r--app/javascript/themes/glitch/util/intersection_observer_wrapper.js57
-rw-r--r--app/javascript/themes/glitch/util/is_mobile.js34
-rw-r--r--app/javascript/themes/glitch/util/link_header.js33
-rw-r--r--app/javascript/themes/glitch/util/load_polyfills.js39
-rw-r--r--app/javascript/themes/glitch/util/main.js39
-rw-r--r--app/javascript/themes/glitch/util/optional_motion.js5
-rw-r--r--app/javascript/themes/glitch/util/performance.js31
-rw-r--r--app/javascript/themes/glitch/util/react_router_helpers.js64
-rw-r--r--app/javascript/themes/glitch/util/ready.js7
-rw-r--r--app/javascript/themes/glitch/util/reduced_motion.js44
-rw-r--r--app/javascript/themes/glitch/util/rtl.js31
-rw-r--r--app/javascript/themes/glitch/util/schedule_idle_task.js29
-rw-r--r--app/javascript/themes/glitch/util/scroll.js30
-rw-r--r--app/javascript/themes/glitch/util/stream.js73
-rw-r--r--app/javascript/themes/glitch/util/url_regex.js196
-rw-r--r--app/javascript/themes/glitch/util/uuid.js3
-rw-r--r--app/javascript/themes/glitch/util/web_push_subscription.js105
291 files changed, 0 insertions, 30556 deletions
diff --git a/app/javascript/themes/glitch/actions/accounts.js b/app/javascript/themes/glitch/actions/accounts.js
deleted file mode 100644
index f1a8c5471..000000000
--- a/app/javascript/themes/glitch/actions/accounts.js
+++ /dev/null
@@ -1,661 +0,0 @@
-import api, { getLinks } from 'themes/glitch/util/api';
-
-export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST';
-export const ACCOUNT_FETCH_SUCCESS = 'ACCOUNT_FETCH_SUCCESS';
-export const ACCOUNT_FETCH_FAIL    = 'ACCOUNT_FETCH_FAIL';
-
-export const ACCOUNT_FOLLOW_REQUEST = 'ACCOUNT_FOLLOW_REQUEST';
-export const ACCOUNT_FOLLOW_SUCCESS = 'ACCOUNT_FOLLOW_SUCCESS';
-export const ACCOUNT_FOLLOW_FAIL    = 'ACCOUNT_FOLLOW_FAIL';
-
-export const ACCOUNT_UNFOLLOW_REQUEST = 'ACCOUNT_UNFOLLOW_REQUEST';
-export const ACCOUNT_UNFOLLOW_SUCCESS = 'ACCOUNT_UNFOLLOW_SUCCESS';
-export const ACCOUNT_UNFOLLOW_FAIL    = 'ACCOUNT_UNFOLLOW_FAIL';
-
-export const ACCOUNT_BLOCK_REQUEST = 'ACCOUNT_BLOCK_REQUEST';
-export const ACCOUNT_BLOCK_SUCCESS = 'ACCOUNT_BLOCK_SUCCESS';
-export const ACCOUNT_BLOCK_FAIL    = 'ACCOUNT_BLOCK_FAIL';
-
-export const ACCOUNT_UNBLOCK_REQUEST = 'ACCOUNT_UNBLOCK_REQUEST';
-export const ACCOUNT_UNBLOCK_SUCCESS = 'ACCOUNT_UNBLOCK_SUCCESS';
-export const ACCOUNT_UNBLOCK_FAIL    = 'ACCOUNT_UNBLOCK_FAIL';
-
-export const ACCOUNT_MUTE_REQUEST = 'ACCOUNT_MUTE_REQUEST';
-export const ACCOUNT_MUTE_SUCCESS = 'ACCOUNT_MUTE_SUCCESS';
-export const ACCOUNT_MUTE_FAIL    = 'ACCOUNT_MUTE_FAIL';
-
-export const ACCOUNT_UNMUTE_REQUEST = 'ACCOUNT_UNMUTE_REQUEST';
-export const ACCOUNT_UNMUTE_SUCCESS = 'ACCOUNT_UNMUTE_SUCCESS';
-export const ACCOUNT_UNMUTE_FAIL    = 'ACCOUNT_UNMUTE_FAIL';
-
-export const FOLLOWERS_FETCH_REQUEST = 'FOLLOWERS_FETCH_REQUEST';
-export const FOLLOWERS_FETCH_SUCCESS = 'FOLLOWERS_FETCH_SUCCESS';
-export const FOLLOWERS_FETCH_FAIL    = 'FOLLOWERS_FETCH_FAIL';
-
-export const FOLLOWERS_EXPAND_REQUEST = 'FOLLOWERS_EXPAND_REQUEST';
-export const FOLLOWERS_EXPAND_SUCCESS = 'FOLLOWERS_EXPAND_SUCCESS';
-export const FOLLOWERS_EXPAND_FAIL    = 'FOLLOWERS_EXPAND_FAIL';
-
-export const FOLLOWING_FETCH_REQUEST = 'FOLLOWING_FETCH_REQUEST';
-export const FOLLOWING_FETCH_SUCCESS = 'FOLLOWING_FETCH_SUCCESS';
-export const FOLLOWING_FETCH_FAIL    = 'FOLLOWING_FETCH_FAIL';
-
-export const FOLLOWING_EXPAND_REQUEST = 'FOLLOWING_EXPAND_REQUEST';
-export const FOLLOWING_EXPAND_SUCCESS = 'FOLLOWING_EXPAND_SUCCESS';
-export const FOLLOWING_EXPAND_FAIL    = 'FOLLOWING_EXPAND_FAIL';
-
-export const RELATIONSHIPS_FETCH_REQUEST = 'RELATIONSHIPS_FETCH_REQUEST';
-export const RELATIONSHIPS_FETCH_SUCCESS = 'RELATIONSHIPS_FETCH_SUCCESS';
-export const RELATIONSHIPS_FETCH_FAIL    = 'RELATIONSHIPS_FETCH_FAIL';
-
-export const FOLLOW_REQUESTS_FETCH_REQUEST = 'FOLLOW_REQUESTS_FETCH_REQUEST';
-export const FOLLOW_REQUESTS_FETCH_SUCCESS = 'FOLLOW_REQUESTS_FETCH_SUCCESS';
-export const FOLLOW_REQUESTS_FETCH_FAIL    = 'FOLLOW_REQUESTS_FETCH_FAIL';
-
-export const FOLLOW_REQUESTS_EXPAND_REQUEST = 'FOLLOW_REQUESTS_EXPAND_REQUEST';
-export const FOLLOW_REQUESTS_EXPAND_SUCCESS = 'FOLLOW_REQUESTS_EXPAND_SUCCESS';
-export const FOLLOW_REQUESTS_EXPAND_FAIL    = 'FOLLOW_REQUESTS_EXPAND_FAIL';
-
-export const FOLLOW_REQUEST_AUTHORIZE_REQUEST = 'FOLLOW_REQUEST_AUTHORIZE_REQUEST';
-export const FOLLOW_REQUEST_AUTHORIZE_SUCCESS = 'FOLLOW_REQUEST_AUTHORIZE_SUCCESS';
-export const FOLLOW_REQUEST_AUTHORIZE_FAIL    = 'FOLLOW_REQUEST_AUTHORIZE_FAIL';
-
-export const FOLLOW_REQUEST_REJECT_REQUEST = 'FOLLOW_REQUEST_REJECT_REQUEST';
-export const FOLLOW_REQUEST_REJECT_SUCCESS = 'FOLLOW_REQUEST_REJECT_SUCCESS';
-export const FOLLOW_REQUEST_REJECT_FAIL    = 'FOLLOW_REQUEST_REJECT_FAIL';
-
-export function fetchAccount(id) {
-  return (dispatch, getState) => {
-    dispatch(fetchRelationships([id]));
-
-    if (getState().getIn(['accounts', id], null) !== null) {
-      return;
-    }
-
-    dispatch(fetchAccountRequest(id));
-
-    api(getState).get(`/api/v1/accounts/${id}`).then(response => {
-      dispatch(fetchAccountSuccess(response.data));
-    }).catch(error => {
-      dispatch(fetchAccountFail(id, error));
-    });
-  };
-};
-
-export function fetchAccountRequest(id) {
-  return {
-    type: ACCOUNT_FETCH_REQUEST,
-    id,
-  };
-};
-
-export function fetchAccountSuccess(account) {
-  return {
-    type: ACCOUNT_FETCH_SUCCESS,
-    account,
-  };
-};
-
-export function fetchAccountFail(id, error) {
-  return {
-    type: ACCOUNT_FETCH_FAIL,
-    id,
-    error,
-    skipAlert: true,
-  };
-};
-
-export function followAccount(id, reblogs = true) {
-  return (dispatch, getState) => {
-    const alreadyFollowing = getState().getIn(['relationships', id, 'following']);
-    dispatch(followAccountRequest(id));
-
-    api(getState).post(`/api/v1/accounts/${id}/follow`, { reblogs }).then(response => {
-      dispatch(followAccountSuccess(response.data, alreadyFollowing));
-    }).catch(error => {
-      dispatch(followAccountFail(error));
-    });
-  };
-};
-
-export function unfollowAccount(id) {
-  return (dispatch, getState) => {
-    dispatch(unfollowAccountRequest(id));
-
-    api(getState).post(`/api/v1/accounts/${id}/unfollow`).then(response => {
-      dispatch(unfollowAccountSuccess(response.data, getState().get('statuses')));
-    }).catch(error => {
-      dispatch(unfollowAccountFail(error));
-    });
-  };
-};
-
-export function followAccountRequest(id) {
-  return {
-    type: ACCOUNT_FOLLOW_REQUEST,
-    id,
-  };
-};
-
-export function followAccountSuccess(relationship, alreadyFollowing) {
-  return {
-    type: ACCOUNT_FOLLOW_SUCCESS,
-    relationship,
-    alreadyFollowing,
-  };
-};
-
-export function followAccountFail(error) {
-  return {
-    type: ACCOUNT_FOLLOW_FAIL,
-    error,
-  };
-};
-
-export function unfollowAccountRequest(id) {
-  return {
-    type: ACCOUNT_UNFOLLOW_REQUEST,
-    id,
-  };
-};
-
-export function unfollowAccountSuccess(relationship, statuses) {
-  return {
-    type: ACCOUNT_UNFOLLOW_SUCCESS,
-    relationship,
-    statuses,
-  };
-};
-
-export function unfollowAccountFail(error) {
-  return {
-    type: ACCOUNT_UNFOLLOW_FAIL,
-    error,
-  };
-};
-
-export function blockAccount(id) {
-  return (dispatch, getState) => {
-    dispatch(blockAccountRequest(id));
-
-    api(getState).post(`/api/v1/accounts/${id}/block`).then(response => {
-      // Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers
-      dispatch(blockAccountSuccess(response.data, getState().get('statuses')));
-    }).catch(error => {
-      dispatch(blockAccountFail(id, error));
-    });
-  };
-};
-
-export function unblockAccount(id) {
-  return (dispatch, getState) => {
-    dispatch(unblockAccountRequest(id));
-
-    api(getState).post(`/api/v1/accounts/${id}/unblock`).then(response => {
-      dispatch(unblockAccountSuccess(response.data));
-    }).catch(error => {
-      dispatch(unblockAccountFail(id, error));
-    });
-  };
-};
-
-export function blockAccountRequest(id) {
-  return {
-    type: ACCOUNT_BLOCK_REQUEST,
-    id,
-  };
-};
-
-export function blockAccountSuccess(relationship, statuses) {
-  return {
-    type: ACCOUNT_BLOCK_SUCCESS,
-    relationship,
-    statuses,
-  };
-};
-
-export function blockAccountFail(error) {
-  return {
-    type: ACCOUNT_BLOCK_FAIL,
-    error,
-  };
-};
-
-export function unblockAccountRequest(id) {
-  return {
-    type: ACCOUNT_UNBLOCK_REQUEST,
-    id,
-  };
-};
-
-export function unblockAccountSuccess(relationship) {
-  return {
-    type: ACCOUNT_UNBLOCK_SUCCESS,
-    relationship,
-  };
-};
-
-export function unblockAccountFail(error) {
-  return {
-    type: ACCOUNT_UNBLOCK_FAIL,
-    error,
-  };
-};
-
-
-export function muteAccount(id, notifications) {
-  return (dispatch, getState) => {
-    dispatch(muteAccountRequest(id));
-
-    api(getState).post(`/api/v1/accounts/${id}/mute`, { notifications }).then(response => {
-      // Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers
-      dispatch(muteAccountSuccess(response.data, getState().get('statuses')));
-    }).catch(error => {
-      dispatch(muteAccountFail(id, error));
-    });
-  };
-};
-
-export function unmuteAccount(id) {
-  return (dispatch, getState) => {
-    dispatch(unmuteAccountRequest(id));
-
-    api(getState).post(`/api/v1/accounts/${id}/unmute`).then(response => {
-      dispatch(unmuteAccountSuccess(response.data));
-    }).catch(error => {
-      dispatch(unmuteAccountFail(id, error));
-    });
-  };
-};
-
-export function muteAccountRequest(id) {
-  return {
-    type: ACCOUNT_MUTE_REQUEST,
-    id,
-  };
-};
-
-export function muteAccountSuccess(relationship, statuses) {
-  return {
-    type: ACCOUNT_MUTE_SUCCESS,
-    relationship,
-    statuses,
-  };
-};
-
-export function muteAccountFail(error) {
-  return {
-    type: ACCOUNT_MUTE_FAIL,
-    error,
-  };
-};
-
-export function unmuteAccountRequest(id) {
-  return {
-    type: ACCOUNT_UNMUTE_REQUEST,
-    id,
-  };
-};
-
-export function unmuteAccountSuccess(relationship) {
-  return {
-    type: ACCOUNT_UNMUTE_SUCCESS,
-    relationship,
-  };
-};
-
-export function unmuteAccountFail(error) {
-  return {
-    type: ACCOUNT_UNMUTE_FAIL,
-    error,
-  };
-};
-
-
-export function fetchFollowers(id) {
-  return (dispatch, getState) => {
-    dispatch(fetchFollowersRequest(id));
-
-    api(getState).get(`/api/v1/accounts/${id}/followers`).then(response => {
-      const next = getLinks(response).refs.find(link => link.rel === 'next');
-
-      dispatch(fetchFollowersSuccess(id, response.data, next ? next.uri : null));
-      dispatch(fetchRelationships(response.data.map(item => item.id)));
-    }).catch(error => {
-      dispatch(fetchFollowersFail(id, error));
-    });
-  };
-};
-
-export function fetchFollowersRequest(id) {
-  return {
-    type: FOLLOWERS_FETCH_REQUEST,
-    id,
-  };
-};
-
-export function fetchFollowersSuccess(id, accounts, next) {
-  return {
-    type: FOLLOWERS_FETCH_SUCCESS,
-    id,
-    accounts,
-    next,
-  };
-};
-
-export function fetchFollowersFail(id, error) {
-  return {
-    type: FOLLOWERS_FETCH_FAIL,
-    id,
-    error,
-  };
-};
-
-export function expandFollowers(id) {
-  return (dispatch, getState) => {
-    const url = getState().getIn(['user_lists', 'followers', id, 'next']);
-
-    if (url === null) {
-      return;
-    }
-
-    dispatch(expandFollowersRequest(id));
-
-    api(getState).get(url).then(response => {
-      const next = getLinks(response).refs.find(link => link.rel === 'next');
-
-      dispatch(expandFollowersSuccess(id, response.data, next ? next.uri : null));
-      dispatch(fetchRelationships(response.data.map(item => item.id)));
-    }).catch(error => {
-      dispatch(expandFollowersFail(id, error));
-    });
-  };
-};
-
-export function expandFollowersRequest(id) {
-  return {
-    type: FOLLOWERS_EXPAND_REQUEST,
-    id,
-  };
-};
-
-export function expandFollowersSuccess(id, accounts, next) {
-  return {
-    type: FOLLOWERS_EXPAND_SUCCESS,
-    id,
-    accounts,
-    next,
-  };
-};
-
-export function expandFollowersFail(id, error) {
-  return {
-    type: FOLLOWERS_EXPAND_FAIL,
-    id,
-    error,
-  };
-};
-
-export function fetchFollowing(id) {
-  return (dispatch, getState) => {
-    dispatch(fetchFollowingRequest(id));
-
-    api(getState).get(`/api/v1/accounts/${id}/following`).then(response => {
-      const next = getLinks(response).refs.find(link => link.rel === 'next');
-
-      dispatch(fetchFollowingSuccess(id, response.data, next ? next.uri : null));
-      dispatch(fetchRelationships(response.data.map(item => item.id)));
-    }).catch(error => {
-      dispatch(fetchFollowingFail(id, error));
-    });
-  };
-};
-
-export function fetchFollowingRequest(id) {
-  return {
-    type: FOLLOWING_FETCH_REQUEST,
-    id,
-  };
-};
-
-export function fetchFollowingSuccess(id, accounts, next) {
-  return {
-    type: FOLLOWING_FETCH_SUCCESS,
-    id,
-    accounts,
-    next,
-  };
-};
-
-export function fetchFollowingFail(id, error) {
-  return {
-    type: FOLLOWING_FETCH_FAIL,
-    id,
-    error,
-  };
-};
-
-export function expandFollowing(id) {
-  return (dispatch, getState) => {
-    const url = getState().getIn(['user_lists', 'following', id, 'next']);
-
-    if (url === null) {
-      return;
-    }
-
-    dispatch(expandFollowingRequest(id));
-
-    api(getState).get(url).then(response => {
-      const next = getLinks(response).refs.find(link => link.rel === 'next');
-
-      dispatch(expandFollowingSuccess(id, response.data, next ? next.uri : null));
-      dispatch(fetchRelationships(response.data.map(item => item.id)));
-    }).catch(error => {
-      dispatch(expandFollowingFail(id, error));
-    });
-  };
-};
-
-export function expandFollowingRequest(id) {
-  return {
-    type: FOLLOWING_EXPAND_REQUEST,
-    id,
-  };
-};
-
-export function expandFollowingSuccess(id, accounts, next) {
-  return {
-    type: FOLLOWING_EXPAND_SUCCESS,
-    id,
-    accounts,
-    next,
-  };
-};
-
-export function expandFollowingFail(id, error) {
-  return {
-    type: FOLLOWING_EXPAND_FAIL,
-    id,
-    error,
-  };
-};
-
-export function fetchRelationships(accountIds) {
-  return (dispatch, getState) => {
-    const loadedRelationships = getState().get('relationships');
-    const newAccountIds = accountIds.filter(id => loadedRelationships.get(id, null) === null);
-
-    if (newAccountIds.length === 0) {
-      return;
-    }
-
-    dispatch(fetchRelationshipsRequest(newAccountIds));
-
-    api(getState).get(`/api/v1/accounts/relationships?${newAccountIds.map(id => `id[]=${id}`).join('&')}`).then(response => {
-      dispatch(fetchRelationshipsSuccess(response.data));
-    }).catch(error => {
-      dispatch(fetchRelationshipsFail(error));
-    });
-  };
-};
-
-export function fetchRelationshipsRequest(ids) {
-  return {
-    type: RELATIONSHIPS_FETCH_REQUEST,
-    ids,
-    skipLoading: true,
-  };
-};
-
-export function fetchRelationshipsSuccess(relationships) {
-  return {
-    type: RELATIONSHIPS_FETCH_SUCCESS,
-    relationships,
-    skipLoading: true,
-  };
-};
-
-export function fetchRelationshipsFail(error) {
-  return {
-    type: RELATIONSHIPS_FETCH_FAIL,
-    error,
-    skipLoading: true,
-  };
-};
-
-export function fetchFollowRequests() {
-  return (dispatch, getState) => {
-    dispatch(fetchFollowRequestsRequest());
-
-    api(getState).get('/api/v1/follow_requests').then(response => {
-      const next = getLinks(response).refs.find(link => link.rel === 'next');
-      dispatch(fetchFollowRequestsSuccess(response.data, next ? next.uri : null));
-    }).catch(error => dispatch(fetchFollowRequestsFail(error)));
-  };
-};
-
-export function fetchFollowRequestsRequest() {
-  return {
-    type: FOLLOW_REQUESTS_FETCH_REQUEST,
-  };
-};
-
-export function fetchFollowRequestsSuccess(accounts, next) {
-  return {
-    type: FOLLOW_REQUESTS_FETCH_SUCCESS,
-    accounts,
-    next,
-  };
-};
-
-export function fetchFollowRequestsFail(error) {
-  return {
-    type: FOLLOW_REQUESTS_FETCH_FAIL,
-    error,
-  };
-};
-
-export function expandFollowRequests() {
-  return (dispatch, getState) => {
-    const url = getState().getIn(['user_lists', 'follow_requests', 'next']);
-
-    if (url === null) {
-      return;
-    }
-
-    dispatch(expandFollowRequestsRequest());
-
-    api(getState).get(url).then(response => {
-      const next = getLinks(response).refs.find(link => link.rel === 'next');
-      dispatch(expandFollowRequestsSuccess(response.data, next ? next.uri : null));
-    }).catch(error => dispatch(expandFollowRequestsFail(error)));
-  };
-};
-
-export function expandFollowRequestsRequest() {
-  return {
-    type: FOLLOW_REQUESTS_EXPAND_REQUEST,
-  };
-};
-
-export function expandFollowRequestsSuccess(accounts, next) {
-  return {
-    type: FOLLOW_REQUESTS_EXPAND_SUCCESS,
-    accounts,
-    next,
-  };
-};
-
-export function expandFollowRequestsFail(error) {
-  return {
-    type: FOLLOW_REQUESTS_EXPAND_FAIL,
-    error,
-  };
-};
-
-export function authorizeFollowRequest(id) {
-  return (dispatch, getState) => {
-    dispatch(authorizeFollowRequestRequest(id));
-
-    api(getState)
-      .post(`/api/v1/follow_requests/${id}/authorize`)
-      .then(() => dispatch(authorizeFollowRequestSuccess(id)))
-      .catch(error => dispatch(authorizeFollowRequestFail(id, error)));
-  };
-};
-
-export function authorizeFollowRequestRequest(id) {
-  return {
-    type: FOLLOW_REQUEST_AUTHORIZE_REQUEST,
-    id,
-  };
-};
-
-export function authorizeFollowRequestSuccess(id) {
-  return {
-    type: FOLLOW_REQUEST_AUTHORIZE_SUCCESS,
-    id,
-  };
-};
-
-export function authorizeFollowRequestFail(id, error) {
-  return {
-    type: FOLLOW_REQUEST_AUTHORIZE_FAIL,
-    id,
-    error,
-  };
-};
-
-
-export function rejectFollowRequest(id) {
-  return (dispatch, getState) => {
-    dispatch(rejectFollowRequestRequest(id));
-
-    api(getState)
-      .post(`/api/v1/follow_requests/${id}/reject`)
-      .then(() => dispatch(rejectFollowRequestSuccess(id)))
-      .catch(error => dispatch(rejectFollowRequestFail(id, error)));
-  };
-};
-
-export function rejectFollowRequestRequest(id) {
-  return {
-    type: FOLLOW_REQUEST_REJECT_REQUEST,
-    id,
-  };
-};
-
-export function rejectFollowRequestSuccess(id) {
-  return {
-    type: FOLLOW_REQUEST_REJECT_SUCCESS,
-    id,
-  };
-};
-
-export function rejectFollowRequestFail(id, error) {
-  return {
-    type: FOLLOW_REQUEST_REJECT_FAIL,
-    id,
-    error,
-  };
-};
diff --git a/app/javascript/themes/glitch/actions/alerts.js b/app/javascript/themes/glitch/actions/alerts.js
deleted file mode 100644
index f37fdeeb6..000000000
--- a/app/javascript/themes/glitch/actions/alerts.js
+++ /dev/null
@@ -1,24 +0,0 @@
-export const ALERT_SHOW    = 'ALERT_SHOW';
-export const ALERT_DISMISS = 'ALERT_DISMISS';
-export const ALERT_CLEAR   = 'ALERT_CLEAR';
-
-export function dismissAlert(alert) {
-  return {
-    type: ALERT_DISMISS,
-    alert,
-  };
-};
-
-export function clearAlert() {
-  return {
-    type: ALERT_CLEAR,
-  };
-};
-
-export function showAlert(title, message) {
-  return {
-    type: ALERT_SHOW,
-    title,
-    message,
-  };
-};
diff --git a/app/javascript/themes/glitch/actions/blocks.js b/app/javascript/themes/glitch/actions/blocks.js
deleted file mode 100644
index 6ba9460f0..000000000
--- a/app/javascript/themes/glitch/actions/blocks.js
+++ /dev/null
@@ -1,82 +0,0 @@
-import api, { getLinks } from 'themes/glitch/util/api';
-import { fetchRelationships } from './accounts';
-
-export const BLOCKS_FETCH_REQUEST = 'BLOCKS_FETCH_REQUEST';
-export const BLOCKS_FETCH_SUCCESS = 'BLOCKS_FETCH_SUCCESS';
-export const BLOCKS_FETCH_FAIL    = 'BLOCKS_FETCH_FAIL';
-
-export const BLOCKS_EXPAND_REQUEST = 'BLOCKS_EXPAND_REQUEST';
-export const BLOCKS_EXPAND_SUCCESS = 'BLOCKS_EXPAND_SUCCESS';
-export const BLOCKS_EXPAND_FAIL    = 'BLOCKS_EXPAND_FAIL';
-
-export function fetchBlocks() {
-  return (dispatch, getState) => {
-    dispatch(fetchBlocksRequest());
-
-    api(getState).get('/api/v1/blocks').then(response => {
-      const next = getLinks(response).refs.find(link => link.rel === 'next');
-      dispatch(fetchBlocksSuccess(response.data, next ? next.uri : null));
-      dispatch(fetchRelationships(response.data.map(item => item.id)));
-    }).catch(error => dispatch(fetchBlocksFail(error)));
-  };
-};
-
-export function fetchBlocksRequest() {
-  return {
-    type: BLOCKS_FETCH_REQUEST,
-  };
-};
-
-export function fetchBlocksSuccess(accounts, next) {
-  return {
-    type: BLOCKS_FETCH_SUCCESS,
-    accounts,
-    next,
-  };
-};
-
-export function fetchBlocksFail(error) {
-  return {
-    type: BLOCKS_FETCH_FAIL,
-    error,
-  };
-};
-
-export function expandBlocks() {
-  return (dispatch, getState) => {
-    const url = getState().getIn(['user_lists', 'blocks', 'next']);
-
-    if (url === null) {
-      return;
-    }
-
-    dispatch(expandBlocksRequest());
-
-    api(getState).get(url).then(response => {
-      const next = getLinks(response).refs.find(link => link.rel === 'next');
-      dispatch(expandBlocksSuccess(response.data, next ? next.uri : null));
-      dispatch(fetchRelationships(response.data.map(item => item.id)));
-    }).catch(error => dispatch(expandBlocksFail(error)));
-  };
-};
-
-export function expandBlocksRequest() {
-  return {
-    type: BLOCKS_EXPAND_REQUEST,
-  };
-};
-
-export function expandBlocksSuccess(accounts, next) {
-  return {
-    type: BLOCKS_EXPAND_SUCCESS,
-    accounts,
-    next,
-  };
-};
-
-export function expandBlocksFail(error) {
-  return {
-    type: BLOCKS_EXPAND_FAIL,
-    error,
-  };
-};
diff --git a/app/javascript/themes/glitch/actions/bundles.js b/app/javascript/themes/glitch/actions/bundles.js
deleted file mode 100644
index ecc9c8f7d..000000000
--- a/app/javascript/themes/glitch/actions/bundles.js
+++ /dev/null
@@ -1,25 +0,0 @@
-export const BUNDLE_FETCH_REQUEST = 'BUNDLE_FETCH_REQUEST';
-export const BUNDLE_FETCH_SUCCESS = 'BUNDLE_FETCH_SUCCESS';
-export const BUNDLE_FETCH_FAIL = 'BUNDLE_FETCH_FAIL';
-
-export function fetchBundleRequest(skipLoading) {
-  return {
-    type: BUNDLE_FETCH_REQUEST,
-    skipLoading,
-  };
-}
-
-export function fetchBundleSuccess(skipLoading) {
-  return {
-    type: BUNDLE_FETCH_SUCCESS,
-    skipLoading,
-  };
-}
-
-export function fetchBundleFail(error, skipLoading) {
-  return {
-    type: BUNDLE_FETCH_FAIL,
-    error,
-    skipLoading,
-  };
-}
diff --git a/app/javascript/themes/glitch/actions/cards.js b/app/javascript/themes/glitch/actions/cards.js
deleted file mode 100644
index 2a1bc369a..000000000
--- a/app/javascript/themes/glitch/actions/cards.js
+++ /dev/null
@@ -1,52 +0,0 @@
-import api from 'themes/glitch/util/api';
-
-export const STATUS_CARD_FETCH_REQUEST = 'STATUS_CARD_FETCH_REQUEST';
-export const STATUS_CARD_FETCH_SUCCESS = 'STATUS_CARD_FETCH_SUCCESS';
-export const STATUS_CARD_FETCH_FAIL    = 'STATUS_CARD_FETCH_FAIL';
-
-export function fetchStatusCard(id) {
-  return (dispatch, getState) => {
-    if (getState().getIn(['cards', id], null) !== null) {
-      return;
-    }
-
-    dispatch(fetchStatusCardRequest(id));
-
-    api(getState).get(`/api/v1/statuses/${id}/card`).then(response => {
-      if (!response.data.url) {
-        return;
-      }
-
-      dispatch(fetchStatusCardSuccess(id, response.data));
-    }).catch(error => {
-      dispatch(fetchStatusCardFail(id, error));
-    });
-  };
-};
-
-export function fetchStatusCardRequest(id) {
-  return {
-    type: STATUS_CARD_FETCH_REQUEST,
-    id,
-    skipLoading: true,
-  };
-};
-
-export function fetchStatusCardSuccess(id, card) {
-  return {
-    type: STATUS_CARD_FETCH_SUCCESS,
-    id,
-    card,
-    skipLoading: true,
-  };
-};
-
-export function fetchStatusCardFail(id, error) {
-  return {
-    type: STATUS_CARD_FETCH_FAIL,
-    id,
-    error,
-    skipLoading: true,
-    skipAlert: true,
-  };
-};
diff --git a/app/javascript/themes/glitch/actions/columns.js b/app/javascript/themes/glitch/actions/columns.js
deleted file mode 100644
index bcb0cdf98..000000000
--- a/app/javascript/themes/glitch/actions/columns.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import { saveSettings } from './settings';
-
-export const COLUMN_ADD    = 'COLUMN_ADD';
-export const COLUMN_REMOVE = 'COLUMN_REMOVE';
-export const COLUMN_MOVE   = 'COLUMN_MOVE';
-
-export function addColumn(id, params) {
-  return dispatch => {
-    dispatch({
-      type: COLUMN_ADD,
-      id,
-      params,
-    });
-
-    dispatch(saveSettings());
-  };
-};
-
-export function removeColumn(uuid) {
-  return dispatch => {
-    dispatch({
-      type: COLUMN_REMOVE,
-      uuid,
-    });
-
-    dispatch(saveSettings());
-  };
-};
-
-export function moveColumn(uuid, direction) {
-  return dispatch => {
-    dispatch({
-      type: COLUMN_MOVE,
-      uuid,
-      direction,
-    });
-
-    dispatch(saveSettings());
-  };
-};
diff --git a/app/javascript/themes/glitch/actions/compose.js b/app/javascript/themes/glitch/actions/compose.js
deleted file mode 100644
index 07c469477..000000000
--- a/app/javascript/themes/glitch/actions/compose.js
+++ /dev/null
@@ -1,398 +0,0 @@
-import api from 'themes/glitch/util/api';
-import { throttle } from 'lodash';
-import { search as emojiSearch } from 'themes/glitch/util/emoji/emoji_mart_search_light';
-import { useEmoji } from './emojis';
-
-import {
-  updateTimeline,
-  refreshHomeTimeline,
-  refreshCommunityTimeline,
-  refreshPublicTimeline,
-  refreshDirectTimeline,
-} from './timelines';
-
-export const COMPOSE_CHANGE          = 'COMPOSE_CHANGE';
-export const COMPOSE_SUBMIT_REQUEST  = 'COMPOSE_SUBMIT_REQUEST';
-export const COMPOSE_SUBMIT_SUCCESS  = 'COMPOSE_SUBMIT_SUCCESS';
-export const COMPOSE_SUBMIT_FAIL     = 'COMPOSE_SUBMIT_FAIL';
-export const COMPOSE_REPLY           = 'COMPOSE_REPLY';
-export const COMPOSE_REPLY_CANCEL    = 'COMPOSE_REPLY_CANCEL';
-export const COMPOSE_MENTION         = 'COMPOSE_MENTION';
-export const COMPOSE_RESET           = 'COMPOSE_RESET';
-export const COMPOSE_UPLOAD_REQUEST  = 'COMPOSE_UPLOAD_REQUEST';
-export const COMPOSE_UPLOAD_SUCCESS  = 'COMPOSE_UPLOAD_SUCCESS';
-export const COMPOSE_UPLOAD_FAIL     = 'COMPOSE_UPLOAD_FAIL';
-export const COMPOSE_UPLOAD_PROGRESS = 'COMPOSE_UPLOAD_PROGRESS';
-export const COMPOSE_UPLOAD_UNDO     = 'COMPOSE_UPLOAD_UNDO';
-
-export const COMPOSE_SUGGESTIONS_CLEAR = 'COMPOSE_SUGGESTIONS_CLEAR';
-export const COMPOSE_SUGGESTIONS_READY = 'COMPOSE_SUGGESTIONS_READY';
-export const COMPOSE_SUGGESTION_SELECT = 'COMPOSE_SUGGESTION_SELECT';
-
-export const COMPOSE_MOUNT   = 'COMPOSE_MOUNT';
-export const COMPOSE_UNMOUNT = 'COMPOSE_UNMOUNT';
-
-export const COMPOSE_ADVANCED_OPTIONS_CHANGE = 'COMPOSE_ADVANCED_OPTIONS_CHANGE';
-export const COMPOSE_SENSITIVITY_CHANGE = 'COMPOSE_SENSITIVITY_CHANGE';
-export const COMPOSE_SPOILERNESS_CHANGE = 'COMPOSE_SPOILERNESS_CHANGE';
-export const COMPOSE_SPOILER_TEXT_CHANGE = 'COMPOSE_SPOILER_TEXT_CHANGE';
-export const COMPOSE_VISIBILITY_CHANGE  = 'COMPOSE_VISIBILITY_CHANGE';
-export const COMPOSE_LISTABILITY_CHANGE = 'COMPOSE_LISTABILITY_CHANGE';
-export const COMPOSE_COMPOSING_CHANGE = 'COMPOSE_COMPOSING_CHANGE';
-
-export const COMPOSE_EMOJI_INSERT = 'COMPOSE_EMOJI_INSERT';
-
-export const COMPOSE_UPLOAD_CHANGE_REQUEST     = 'COMPOSE_UPLOAD_UPDATE_REQUEST';
-export const COMPOSE_UPLOAD_CHANGE_SUCCESS     = 'COMPOSE_UPLOAD_UPDATE_SUCCESS';
-export const COMPOSE_UPLOAD_CHANGE_FAIL        = 'COMPOSE_UPLOAD_UPDATE_FAIL';
-
-export const COMPOSE_DOODLE_SET        = 'COMPOSE_DOODLE_SET';
-
-export function changeCompose(text) {
-  return {
-    type: COMPOSE_CHANGE,
-    text: text,
-  };
-};
-
-export function replyCompose(status, router) {
-  return (dispatch, getState) => {
-    dispatch({
-      type: COMPOSE_REPLY,
-      status: status,
-    });
-
-    if (!getState().getIn(['compose', 'mounted'])) {
-      router.push('/statuses/new');
-    }
-  };
-};
-
-export function cancelReplyCompose() {
-  return {
-    type: COMPOSE_REPLY_CANCEL,
-  };
-};
-
-export function resetCompose() {
-  return {
-    type: COMPOSE_RESET,
-  };
-};
-
-export function mentionCompose(account, router) {
-  return (dispatch, getState) => {
-    dispatch({
-      type: COMPOSE_MENTION,
-      account: account,
-    });
-
-    if (!getState().getIn(['compose', 'mounted'])) {
-      router.push('/statuses/new');
-    }
-  };
-};
-
-export function submitCompose() {
-  return function (dispatch, getState) {
-    let status = getState().getIn(['compose', 'text'], '');
-
-    if (!status || !status.length) {
-      return;
-    }
-
-    dispatch(submitComposeRequest());
-    if (getState().getIn(['compose', 'advanced_options', 'do_not_federate'])) {
-      status = status + ' 👁️';
-    }
-    api(getState).post('/api/v1/statuses', {
-      status,
-      in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
-      media_ids: getState().getIn(['compose', 'media_attachments']).map(item => item.get('id')),
-      sensitive: getState().getIn(['compose', 'sensitive']),
-      spoiler_text: getState().getIn(['compose', 'spoiler_text'], ''),
-      visibility: getState().getIn(['compose', 'privacy']),
-    }, {
-      headers: {
-        'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']),
-      },
-    }).then(function (response) {
-      dispatch(submitComposeSuccess({ ...response.data }));
-
-      // To make the app more responsive, immediately get the status into the columns
-
-      const insertOrRefresh = (timelineId, refreshAction) => {
-        if (getState().getIn(['timelines', timelineId, 'online'])) {
-          dispatch(updateTimeline(timelineId, { ...response.data }));
-        } else if (getState().getIn(['timelines', timelineId, 'loaded'])) {
-          dispatch(refreshAction());
-        }
-      };
-
-      insertOrRefresh('home', refreshHomeTimeline);
-
-      if (response.data.in_reply_to_id === null && response.data.visibility === 'public') {
-        insertOrRefresh('community', refreshCommunityTimeline);
-        insertOrRefresh('public', refreshPublicTimeline);
-      } else if (response.data.visibility === 'direct') {
-        insertOrRefresh('direct', refreshDirectTimeline);
-      }
-    }).catch(function (error) {
-      dispatch(submitComposeFail(error));
-    });
-  };
-};
-
-export function submitComposeRequest() {
-  return {
-    type: COMPOSE_SUBMIT_REQUEST,
-  };
-};
-
-export function submitComposeSuccess(status) {
-  return {
-    type: COMPOSE_SUBMIT_SUCCESS,
-    status: status,
-  };
-};
-
-export function submitComposeFail(error) {
-  return {
-    type: COMPOSE_SUBMIT_FAIL,
-    error: error,
-  };
-};
-
-export function doodleSet(options) {
-  return {
-    type: COMPOSE_DOODLE_SET,
-    options: options,
-  };
-};
-
-export function uploadCompose(files) {
-  return function (dispatch, getState) {
-    if (getState().getIn(['compose', 'media_attachments']).size > 3) {
-      return;
-    }
-
-    dispatch(uploadComposeRequest());
-
-    let data = new FormData();
-    data.append('file', files[0]);
-
-    api(getState).post('/api/v1/media', data, {
-      onUploadProgress: function (e) {
-        dispatch(uploadComposeProgress(e.loaded, e.total));
-      },
-    }).then(function (response) {
-      dispatch(uploadComposeSuccess(response.data));
-    }).catch(function (error) {
-      dispatch(uploadComposeFail(error));
-    });
-  };
-};
-
-export function changeUploadCompose(id, description) {
-  return (dispatch, getState) => {
-    dispatch(changeUploadComposeRequest());
-
-    api(getState).put(`/api/v1/media/${id}`, { description }).then(response => {
-      dispatch(changeUploadComposeSuccess(response.data));
-    }).catch(error => {
-      dispatch(changeUploadComposeFail(id, error));
-    });
-  };
-};
-
-export function changeUploadComposeRequest() {
-  return {
-    type: COMPOSE_UPLOAD_CHANGE_REQUEST,
-    skipLoading: true,
-  };
-};
-export function changeUploadComposeSuccess(media) {
-  return {
-    type: COMPOSE_UPLOAD_CHANGE_SUCCESS,
-    media: media,
-    skipLoading: true,
-  };
-};
-
-export function changeUploadComposeFail(error) {
-  return {
-    type: COMPOSE_UPLOAD_CHANGE_FAIL,
-    error: error,
-    skipLoading: true,
-  };
-};
-
-export function uploadComposeRequest() {
-  return {
-    type: COMPOSE_UPLOAD_REQUEST,
-    skipLoading: true,
-  };
-};
-
-export function uploadComposeProgress(loaded, total) {
-  return {
-    type: COMPOSE_UPLOAD_PROGRESS,
-    loaded: loaded,
-    total: total,
-  };
-};
-
-export function uploadComposeSuccess(media) {
-  return {
-    type: COMPOSE_UPLOAD_SUCCESS,
-    media: media,
-    skipLoading: true,
-  };
-};
-
-export function uploadComposeFail(error) {
-  return {
-    type: COMPOSE_UPLOAD_FAIL,
-    error: error,
-    skipLoading: true,
-  };
-};
-
-export function undoUploadCompose(media_id) {
-  return {
-    type: COMPOSE_UPLOAD_UNDO,
-    media_id: media_id,
-  };
-};
-
-export function clearComposeSuggestions() {
-  return {
-    type: COMPOSE_SUGGESTIONS_CLEAR,
-  };
-};
-
-const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) => {
-  api(getState).get('/api/v1/accounts/search', {
-    params: {
-      q: token.slice(1),
-      resolve: false,
-      limit: 4,
-    },
-  }).then(response => {
-    dispatch(readyComposeSuggestionsAccounts(token, response.data));
-  });
-}, 200, { leading: true, trailing: true });
-
-const fetchComposeSuggestionsEmojis = (dispatch, getState, token) => {
-  const results = emojiSearch(token.replace(':', ''), { maxResults: 5 });
-  dispatch(readyComposeSuggestionsEmojis(token, results));
-};
-
-export function fetchComposeSuggestions(token) {
-  return (dispatch, getState) => {
-    if (token[0] === ':') {
-      fetchComposeSuggestionsEmojis(dispatch, getState, token);
-    } else {
-      fetchComposeSuggestionsAccounts(dispatch, getState, token);
-    }
-  };
-};
-
-export function readyComposeSuggestionsEmojis(token, emojis) {
-  return {
-    type: COMPOSE_SUGGESTIONS_READY,
-    token,
-    emojis,
-  };
-};
-
-export function readyComposeSuggestionsAccounts(token, accounts) {
-  return {
-    type: COMPOSE_SUGGESTIONS_READY,
-    token,
-    accounts,
-  };
-};
-
-export function selectComposeSuggestion(position, token, suggestion) {
-  return (dispatch, getState) => {
-    let completion, startPosition;
-
-    if (typeof suggestion === 'object' && suggestion.id) {
-      completion    = suggestion.native || suggestion.colons;
-      startPosition = position - 1;
-
-      dispatch(useEmoji(suggestion));
-    } else {
-      completion    = getState().getIn(['accounts', suggestion, 'acct']);
-      startPosition = position;
-    }
-
-    dispatch({
-      type: COMPOSE_SUGGESTION_SELECT,
-      position: startPosition,
-      token,
-      completion,
-    });
-  };
-};
-
-export function mountCompose() {
-  return {
-    type: COMPOSE_MOUNT,
-  };
-};
-
-export function unmountCompose() {
-  return {
-    type: COMPOSE_UNMOUNT,
-  };
-};
-
-export function toggleComposeAdvancedOption(option) {
-  return {
-    type: COMPOSE_ADVANCED_OPTIONS_CHANGE,
-    option: option,
-  };
-}
-
-export function changeComposeSensitivity() {
-  return {
-    type: COMPOSE_SENSITIVITY_CHANGE,
-  };
-};
-
-export function changeComposeSpoilerness() {
-  return {
-    type: COMPOSE_SPOILERNESS_CHANGE,
-  };
-};
-
-export function changeComposeSpoilerText(text) {
-  return {
-    type: COMPOSE_SPOILER_TEXT_CHANGE,
-    text,
-  };
-};
-
-export function changeComposeVisibility(value) {
-  return {
-    type: COMPOSE_VISIBILITY_CHANGE,
-    value,
-  };
-};
-
-export function insertEmojiCompose(position, emoji) {
-  return {
-    type: COMPOSE_EMOJI_INSERT,
-    position,
-    emoji,
-  };
-};
-
-export function changeComposing(value) {
-  return {
-    type: COMPOSE_COMPOSING_CHANGE,
-    value,
-  };
-}
diff --git a/app/javascript/themes/glitch/actions/domain_blocks.js b/app/javascript/themes/glitch/actions/domain_blocks.js
deleted file mode 100644
index 0a880394a..000000000
--- a/app/javascript/themes/glitch/actions/domain_blocks.js
+++ /dev/null
@@ -1,117 +0,0 @@
-import api, { getLinks } from 'themes/glitch/util/api';
-
-export const DOMAIN_BLOCK_REQUEST = 'DOMAIN_BLOCK_REQUEST';
-export const DOMAIN_BLOCK_SUCCESS = 'DOMAIN_BLOCK_SUCCESS';
-export const DOMAIN_BLOCK_FAIL    = 'DOMAIN_BLOCK_FAIL';
-
-export const DOMAIN_UNBLOCK_REQUEST = 'DOMAIN_UNBLOCK_REQUEST';
-export const DOMAIN_UNBLOCK_SUCCESS = 'DOMAIN_UNBLOCK_SUCCESS';
-export const DOMAIN_UNBLOCK_FAIL    = 'DOMAIN_UNBLOCK_FAIL';
-
-export const DOMAIN_BLOCKS_FETCH_REQUEST = 'DOMAIN_BLOCKS_FETCH_REQUEST';
-export const DOMAIN_BLOCKS_FETCH_SUCCESS = 'DOMAIN_BLOCKS_FETCH_SUCCESS';
-export const DOMAIN_BLOCKS_FETCH_FAIL    = 'DOMAIN_BLOCKS_FETCH_FAIL';
-
-export function blockDomain(domain, accountId) {
-  return (dispatch, getState) => {
-    dispatch(blockDomainRequest(domain));
-
-    api(getState).post('/api/v1/domain_blocks', { domain }).then(() => {
-      dispatch(blockDomainSuccess(domain, accountId));
-    }).catch(err => {
-      dispatch(blockDomainFail(domain, err));
-    });
-  };
-};
-
-export function blockDomainRequest(domain) {
-  return {
-    type: DOMAIN_BLOCK_REQUEST,
-    domain,
-  };
-};
-
-export function blockDomainSuccess(domain, accountId) {
-  return {
-    type: DOMAIN_BLOCK_SUCCESS,
-    domain,
-    accountId,
-  };
-};
-
-export function blockDomainFail(domain, error) {
-  return {
-    type: DOMAIN_BLOCK_FAIL,
-    domain,
-    error,
-  };
-};
-
-export function unblockDomain(domain, accountId) {
-  return (dispatch, getState) => {
-    dispatch(unblockDomainRequest(domain));
-
-    api(getState).delete('/api/v1/domain_blocks', { params: { domain } }).then(() => {
-      dispatch(unblockDomainSuccess(domain, accountId));
-    }).catch(err => {
-      dispatch(unblockDomainFail(domain, err));
-    });
-  };
-};
-
-export function unblockDomainRequest(domain) {
-  return {
-    type: DOMAIN_UNBLOCK_REQUEST,
-    domain,
-  };
-};
-
-export function unblockDomainSuccess(domain, accountId) {
-  return {
-    type: DOMAIN_UNBLOCK_SUCCESS,
-    domain,
-    accountId,
-  };
-};
-
-export function unblockDomainFail(domain, error) {
-  return {
-    type: DOMAIN_UNBLOCK_FAIL,
-    domain,
-    error,
-  };
-};
-
-export function fetchDomainBlocks() {
-  return (dispatch, getState) => {
-    dispatch(fetchDomainBlocksRequest());
-
-    api(getState).get().then(response => {
-      const next = getLinks(response).refs.find(link => link.rel === 'next');
-      dispatch(fetchDomainBlocksSuccess(response.data, next ? next.uri : null));
-    }).catch(err => {
-      dispatch(fetchDomainBlocksFail(err));
-    });
-  };
-};
-
-export function fetchDomainBlocksRequest() {
-  return {
-    type: DOMAIN_BLOCKS_FETCH_REQUEST,
-  };
-};
-
-export function fetchDomainBlocksSuccess(domains, next) {
-  return {
-    type: DOMAIN_BLOCKS_FETCH_SUCCESS,
-    domains,
-    next,
-  };
-};
-
-export function fetchDomainBlocksFail(error) {
-  return {
-    type: DOMAIN_BLOCKS_FETCH_FAIL,
-    error,
-  };
-};
diff --git a/app/javascript/themes/glitch/actions/emojis.js b/app/javascript/themes/glitch/actions/emojis.js
deleted file mode 100644
index 7cd9d4b7b..000000000
--- a/app/javascript/themes/glitch/actions/emojis.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { saveSettings } from './settings';
-
-export const EMOJI_USE = 'EMOJI_USE';
-
-export function useEmoji(emoji) {
-  return dispatch => {
-    dispatch({
-      type: EMOJI_USE,
-      emoji,
-    });
-
-    dispatch(saveSettings());
-  };
-};
diff --git a/app/javascript/themes/glitch/actions/favourites.js b/app/javascript/themes/glitch/actions/favourites.js
deleted file mode 100644
index e9b3559af..000000000
--- a/app/javascript/themes/glitch/actions/favourites.js
+++ /dev/null
@@ -1,83 +0,0 @@
-import api, { getLinks } from 'themes/glitch/util/api';
-
-export const FAVOURITED_STATUSES_FETCH_REQUEST = 'FAVOURITED_STATUSES_FETCH_REQUEST';
-export const FAVOURITED_STATUSES_FETCH_SUCCESS = 'FAVOURITED_STATUSES_FETCH_SUCCESS';
-export const FAVOURITED_STATUSES_FETCH_FAIL    = 'FAVOURITED_STATUSES_FETCH_FAIL';
-
-export const FAVOURITED_STATUSES_EXPAND_REQUEST = 'FAVOURITED_STATUSES_EXPAND_REQUEST';
-export const FAVOURITED_STATUSES_EXPAND_SUCCESS = 'FAVOURITED_STATUSES_EXPAND_SUCCESS';
-export const FAVOURITED_STATUSES_EXPAND_FAIL    = 'FAVOURITED_STATUSES_EXPAND_FAIL';
-
-export function fetchFavouritedStatuses() {
-  return (dispatch, getState) => {
-    dispatch(fetchFavouritedStatusesRequest());
-
-    api(getState).get('/api/v1/favourites').then(response => {
-      const next = getLinks(response).refs.find(link => link.rel === 'next');
-      dispatch(fetchFavouritedStatusesSuccess(response.data, next ? next.uri : null));
-    }).catch(error => {
-      dispatch(fetchFavouritedStatusesFail(error));
-    });
-  };
-};
-
-export function fetchFavouritedStatusesRequest() {
-  return {
-    type: FAVOURITED_STATUSES_FETCH_REQUEST,
-  };
-};
-
-export function fetchFavouritedStatusesSuccess(statuses, next) {
-  return {
-    type: FAVOURITED_STATUSES_FETCH_SUCCESS,
-    statuses,
-    next,
-  };
-};
-
-export function fetchFavouritedStatusesFail(error) {
-  return {
-    type: FAVOURITED_STATUSES_FETCH_FAIL,
-    error,
-  };
-};
-
-export function expandFavouritedStatuses() {
-  return (dispatch, getState) => {
-    const url = getState().getIn(['status_lists', 'favourites', 'next'], null);
-
-    if (url === null) {
-      return;
-    }
-
-    dispatch(expandFavouritedStatusesRequest());
-
-    api(getState).get(url).then(response => {
-      const next = getLinks(response).refs.find(link => link.rel === 'next');
-      dispatch(expandFavouritedStatusesSuccess(response.data, next ? next.uri : null));
-    }).catch(error => {
-      dispatch(expandFavouritedStatusesFail(error));
-    });
-  };
-};
-
-export function expandFavouritedStatusesRequest() {
-  return {
-    type: FAVOURITED_STATUSES_EXPAND_REQUEST,
-  };
-};
-
-export function expandFavouritedStatusesSuccess(statuses, next) {
-  return {
-    type: FAVOURITED_STATUSES_EXPAND_SUCCESS,
-    statuses,
-    next,
-  };
-};
-
-export function expandFavouritedStatusesFail(error) {
-  return {
-    type: FAVOURITED_STATUSES_EXPAND_FAIL,
-    error,
-  };
-};
diff --git a/app/javascript/themes/glitch/actions/height_cache.js b/app/javascript/themes/glitch/actions/height_cache.js
deleted file mode 100644
index 4c752993f..000000000
--- a/app/javascript/themes/glitch/actions/height_cache.js
+++ /dev/null
@@ -1,17 +0,0 @@
-export const HEIGHT_CACHE_SET = 'HEIGHT_CACHE_SET';
-export const HEIGHT_CACHE_CLEAR = 'HEIGHT_CACHE_CLEAR';
-
-export function setHeight (key, id, height) {
-  return {
-    type: HEIGHT_CACHE_SET,
-    key,
-    id,
-    height,
-  };
-};
-
-export function clearHeight () {
-  return {
-    type: HEIGHT_CACHE_CLEAR,
-  };
-};
diff --git a/app/javascript/themes/glitch/actions/interactions.js b/app/javascript/themes/glitch/actions/interactions.js
deleted file mode 100644
index d61a7ba2a..000000000
--- a/app/javascript/themes/glitch/actions/interactions.js
+++ /dev/null
@@ -1,313 +0,0 @@
-import api from 'themes/glitch/util/api';
-
-export const REBLOG_REQUEST = 'REBLOG_REQUEST';
-export const REBLOG_SUCCESS = 'REBLOG_SUCCESS';
-export const REBLOG_FAIL    = 'REBLOG_FAIL';
-
-export const FAVOURITE_REQUEST = 'FAVOURITE_REQUEST';
-export const FAVOURITE_SUCCESS = 'FAVOURITE_SUCCESS';
-export const FAVOURITE_FAIL    = 'FAVOURITE_FAIL';
-
-export const UNREBLOG_REQUEST = 'UNREBLOG_REQUEST';
-export const UNREBLOG_SUCCESS = 'UNREBLOG_SUCCESS';
-export const UNREBLOG_FAIL    = 'UNREBLOG_FAIL';
-
-export const UNFAVOURITE_REQUEST = 'UNFAVOURITE_REQUEST';
-export const UNFAVOURITE_SUCCESS = 'UNFAVOURITE_SUCCESS';
-export const UNFAVOURITE_FAIL    = 'UNFAVOURITE_FAIL';
-
-export const REBLOGS_FETCH_REQUEST = 'REBLOGS_FETCH_REQUEST';
-export const REBLOGS_FETCH_SUCCESS = 'REBLOGS_FETCH_SUCCESS';
-export const REBLOGS_FETCH_FAIL    = 'REBLOGS_FETCH_FAIL';
-
-export const FAVOURITES_FETCH_REQUEST = 'FAVOURITES_FETCH_REQUEST';
-export const FAVOURITES_FETCH_SUCCESS = 'FAVOURITES_FETCH_SUCCESS';
-export const FAVOURITES_FETCH_FAIL    = 'FAVOURITES_FETCH_FAIL';
-
-export const PIN_REQUEST = 'PIN_REQUEST';
-export const PIN_SUCCESS = 'PIN_SUCCESS';
-export const PIN_FAIL    = 'PIN_FAIL';
-
-export const UNPIN_REQUEST = 'UNPIN_REQUEST';
-export const UNPIN_SUCCESS = 'UNPIN_SUCCESS';
-export const UNPIN_FAIL    = 'UNPIN_FAIL';
-
-export function reblog(status) {
-  return function (dispatch, getState) {
-    dispatch(reblogRequest(status));
-
-    api(getState).post(`/api/v1/statuses/${status.get('id')}/reblog`).then(function (response) {
-      // The reblog API method returns a new status wrapped around the original. In this case we are only
-      // interested in how the original is modified, hence passing it skipping the wrapper
-      dispatch(reblogSuccess(status, response.data.reblog));
-    }).catch(function (error) {
-      dispatch(reblogFail(status, error));
-    });
-  };
-};
-
-export function unreblog(status) {
-  return (dispatch, getState) => {
-    dispatch(unreblogRequest(status));
-
-    api(getState).post(`/api/v1/statuses/${status.get('id')}/unreblog`).then(response => {
-      dispatch(unreblogSuccess(status, response.data));
-    }).catch(error => {
-      dispatch(unreblogFail(status, error));
-    });
-  };
-};
-
-export function reblogRequest(status) {
-  return {
-    type: REBLOG_REQUEST,
-    status: status,
-  };
-};
-
-export function reblogSuccess(status, response) {
-  return {
-    type: REBLOG_SUCCESS,
-    status: status,
-    response: response,
-  };
-};
-
-export function reblogFail(status, error) {
-  return {
-    type: REBLOG_FAIL,
-    status: status,
-    error: error,
-  };
-};
-
-export function unreblogRequest(status) {
-  return {
-    type: UNREBLOG_REQUEST,
-    status: status,
-  };
-};
-
-export function unreblogSuccess(status, response) {
-  return {
-    type: UNREBLOG_SUCCESS,
-    status: status,
-    response: response,
-  };
-};
-
-export function unreblogFail(status, error) {
-  return {
-    type: UNREBLOG_FAIL,
-    status: status,
-    error: error,
-  };
-};
-
-export function favourite(status) {
-  return function (dispatch, getState) {
-    dispatch(favouriteRequest(status));
-
-    api(getState).post(`/api/v1/statuses/${status.get('id')}/favourite`).then(function (response) {
-      dispatch(favouriteSuccess(status, response.data));
-    }).catch(function (error) {
-      dispatch(favouriteFail(status, error));
-    });
-  };
-};
-
-export function unfavourite(status) {
-  return (dispatch, getState) => {
-    dispatch(unfavouriteRequest(status));
-
-    api(getState).post(`/api/v1/statuses/${status.get('id')}/unfavourite`).then(response => {
-      dispatch(unfavouriteSuccess(status, response.data));
-    }).catch(error => {
-      dispatch(unfavouriteFail(status, error));
-    });
-  };
-};
-
-export function favouriteRequest(status) {
-  return {
-    type: FAVOURITE_REQUEST,
-    status: status,
-  };
-};
-
-export function favouriteSuccess(status, response) {
-  return {
-    type: FAVOURITE_SUCCESS,
-    status: status,
-    response: response,
-  };
-};
-
-export function favouriteFail(status, error) {
-  return {
-    type: FAVOURITE_FAIL,
-    status: status,
-    error: error,
-  };
-};
-
-export function unfavouriteRequest(status) {
-  return {
-    type: UNFAVOURITE_REQUEST,
-    status: status,
-  };
-};
-
-export function unfavouriteSuccess(status, response) {
-  return {
-    type: UNFAVOURITE_SUCCESS,
-    status: status,
-    response: response,
-  };
-};
-
-export function unfavouriteFail(status, error) {
-  return {
-    type: UNFAVOURITE_FAIL,
-    status: status,
-    error: error,
-  };
-};
-
-export function fetchReblogs(id) {
-  return (dispatch, getState) => {
-    dispatch(fetchReblogsRequest(id));
-
-    api(getState).get(`/api/v1/statuses/${id}/reblogged_by`).then(response => {
-      dispatch(fetchReblogsSuccess(id, response.data));
-    }).catch(error => {
-      dispatch(fetchReblogsFail(id, error));
-    });
-  };
-};
-
-export function fetchReblogsRequest(id) {
-  return {
-    type: REBLOGS_FETCH_REQUEST,
-    id,
-  };
-};
-
-export function fetchReblogsSuccess(id, accounts) {
-  return {
-    type: REBLOGS_FETCH_SUCCESS,
-    id,
-    accounts,
-  };
-};
-
-export function fetchReblogsFail(id, error) {
-  return {
-    type: REBLOGS_FETCH_FAIL,
-    error,
-  };
-};
-
-export function fetchFavourites(id) {
-  return (dispatch, getState) => {
-    dispatch(fetchFavouritesRequest(id));
-
-    api(getState).get(`/api/v1/statuses/${id}/favourited_by`).then(response => {
-      dispatch(fetchFavouritesSuccess(id, response.data));
-    }).catch(error => {
-      dispatch(fetchFavouritesFail(id, error));
-    });
-  };
-};
-
-export function fetchFavouritesRequest(id) {
-  return {
-    type: FAVOURITES_FETCH_REQUEST,
-    id,
-  };
-};
-
-export function fetchFavouritesSuccess(id, accounts) {
-  return {
-    type: FAVOURITES_FETCH_SUCCESS,
-    id,
-    accounts,
-  };
-};
-
-export function fetchFavouritesFail(id, error) {
-  return {
-    type: FAVOURITES_FETCH_FAIL,
-    error,
-  };
-};
-
-export function pin(status) {
-  return (dispatch, getState) => {
-    dispatch(pinRequest(status));
-
-    api(getState).post(`/api/v1/statuses/${status.get('id')}/pin`).then(response => {
-      dispatch(pinSuccess(status, response.data));
-    }).catch(error => {
-      dispatch(pinFail(status, error));
-    });
-  };
-};
-
-export function pinRequest(status) {
-  return {
-    type: PIN_REQUEST,
-    status,
-  };
-};
-
-export function pinSuccess(status, response) {
-  return {
-    type: PIN_SUCCESS,
-    status,
-    response,
-  };
-};
-
-export function pinFail(status, error) {
-  return {
-    type: PIN_FAIL,
-    status,
-    error,
-  };
-};
-
-export function unpin (status) {
-  return (dispatch, getState) => {
-    dispatch(unpinRequest(status));
-
-    api(getState).post(`/api/v1/statuses/${status.get('id')}/unpin`).then(response => {
-      dispatch(unpinSuccess(status, response.data));
-    }).catch(error => {
-      dispatch(unpinFail(status, error));
-    });
-  };
-};
-
-export function unpinRequest(status) {
-  return {
-    type: UNPIN_REQUEST,
-    status,
-  };
-};
-
-export function unpinSuccess(status, response) {
-  return {
-    type: UNPIN_SUCCESS,
-    status,
-    response,
-  };
-};
-
-export function unpinFail(status, error) {
-  return {
-    type: UNPIN_FAIL,
-    status,
-    error,
-  };
-};
diff --git a/app/javascript/themes/glitch/actions/local_settings.js b/app/javascript/themes/glitch/actions/local_settings.js
deleted file mode 100644
index 28660a4e8..000000000
--- a/app/javascript/themes/glitch/actions/local_settings.js
+++ /dev/null
@@ -1,24 +0,0 @@
-export const LOCAL_SETTING_CHANGE = 'LOCAL_SETTING_CHANGE';
-
-export function changeLocalSetting(key, value) {
-  return dispatch => {
-    dispatch({
-      type: LOCAL_SETTING_CHANGE,
-      key,
-      value,
-    });
-
-    dispatch(saveLocalSettings());
-  };
-};
-
-//  __TODO :__
-//  Right now `saveLocalSettings()` doesn't keep track of which user
-//  is currently signed in, but it might be better to give each user
-//  their *own* local settings.
-export function saveLocalSettings() {
-  return (_, getState) => {
-    const localSettings = getState().get('local_settings').toJS();
-    localStorage.setItem('mastodon-settings', JSON.stringify(localSettings));
-  };
-};
diff --git a/app/javascript/themes/glitch/actions/modal.js b/app/javascript/themes/glitch/actions/modal.js
deleted file mode 100644
index 80e15c28e..000000000
--- a/app/javascript/themes/glitch/actions/modal.js
+++ /dev/null
@@ -1,16 +0,0 @@
-export const MODAL_OPEN  = 'MODAL_OPEN';
-export const MODAL_CLOSE = 'MODAL_CLOSE';
-
-export function openModal(type, props) {
-  return {
-    type: MODAL_OPEN,
-    modalType: type,
-    modalProps: props,
-  };
-};
-
-export function closeModal() {
-  return {
-    type: MODAL_CLOSE,
-  };
-};
diff --git a/app/javascript/themes/glitch/actions/mutes.js b/app/javascript/themes/glitch/actions/mutes.js
deleted file mode 100644
index bb19e8657..000000000
--- a/app/javascript/themes/glitch/actions/mutes.js
+++ /dev/null
@@ -1,103 +0,0 @@
-import api, { getLinks } from 'themes/glitch/util/api';
-import { fetchRelationships } from './accounts';
-import { openModal } from 'themes/glitch/actions/modal';
-
-export const MUTES_FETCH_REQUEST = 'MUTES_FETCH_REQUEST';
-export const MUTES_FETCH_SUCCESS = 'MUTES_FETCH_SUCCESS';
-export const MUTES_FETCH_FAIL    = 'MUTES_FETCH_FAIL';
-
-export const MUTES_EXPAND_REQUEST = 'MUTES_EXPAND_REQUEST';
-export const MUTES_EXPAND_SUCCESS = 'MUTES_EXPAND_SUCCESS';
-export const MUTES_EXPAND_FAIL    = 'MUTES_EXPAND_FAIL';
-
-export const MUTES_INIT_MODAL = 'MUTES_INIT_MODAL';
-export const MUTES_TOGGLE_HIDE_NOTIFICATIONS = 'MUTES_TOGGLE_HIDE_NOTIFICATIONS';
-
-export function fetchMutes() {
-  return (dispatch, getState) => {
-    dispatch(fetchMutesRequest());
-
-    api(getState).get('/api/v1/mutes').then(response => {
-      const next = getLinks(response).refs.find(link => link.rel === 'next');
-      dispatch(fetchMutesSuccess(response.data, next ? next.uri : null));
-      dispatch(fetchRelationships(response.data.map(item => item.id)));
-    }).catch(error => dispatch(fetchMutesFail(error)));
-  };
-};
-
-export function fetchMutesRequest() {
-  return {
-    type: MUTES_FETCH_REQUEST,
-  };
-};
-
-export function fetchMutesSuccess(accounts, next) {
-  return {
-    type: MUTES_FETCH_SUCCESS,
-    accounts,
-    next,
-  };
-};
-
-export function fetchMutesFail(error) {
-  return {
-    type: MUTES_FETCH_FAIL,
-    error,
-  };
-};
-
-export function expandMutes() {
-  return (dispatch, getState) => {
-    const url = getState().getIn(['user_lists', 'mutes', 'next']);
-
-    if (url === null) {
-      return;
-    }
-
-    dispatch(expandMutesRequest());
-
-    api(getState).get(url).then(response => {
-      const next = getLinks(response).refs.find(link => link.rel === 'next');
-      dispatch(expandMutesSuccess(response.data, next ? next.uri : null));
-      dispatch(fetchRelationships(response.data.map(item => item.id)));
-    }).catch(error => dispatch(expandMutesFail(error)));
-  };
-};
-
-export function expandMutesRequest() {
-  return {
-    type: MUTES_EXPAND_REQUEST,
-  };
-};
-
-export function expandMutesSuccess(accounts, next) {
-  return {
-    type: MUTES_EXPAND_SUCCESS,
-    accounts,
-    next,
-  };
-};
-
-export function expandMutesFail(error) {
-  return {
-    type: MUTES_EXPAND_FAIL,
-    error,
-  };
-};
-
-export function initMuteModal(account) {
-  return dispatch => {
-    dispatch({
-      type: MUTES_INIT_MODAL,
-      account,
-    });
-
-    dispatch(openModal('MUTE'));
-  };
-}
-
-export function toggleHideNotifications() {
-  return dispatch => {
-    dispatch({ type: MUTES_TOGGLE_HIDE_NOTIFICATIONS });
-  };
-}
diff --git a/app/javascript/themes/glitch/actions/notifications.js b/app/javascript/themes/glitch/actions/notifications.js
deleted file mode 100644
index fbf06f7c4..000000000
--- a/app/javascript/themes/glitch/actions/notifications.js
+++ /dev/null
@@ -1,265 +0,0 @@
-import api, { getLinks } from 'themes/glitch/util/api';
-import { List as ImmutableList } from 'immutable';
-import IntlMessageFormat from 'intl-messageformat';
-import { fetchRelationships } from './accounts';
-import { defineMessages } from 'react-intl';
-
-export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE';
-
-// tracking the notif cleaning request
-export const NOTIFICATIONS_DELETE_MARKED_REQUEST = 'NOTIFICATIONS_DELETE_MARKED_REQUEST';
-export const NOTIFICATIONS_DELETE_MARKED_SUCCESS = 'NOTIFICATIONS_DELETE_MARKED_SUCCESS';
-export const NOTIFICATIONS_DELETE_MARKED_FAIL = 'NOTIFICATIONS_DELETE_MARKED_FAIL';
-export const NOTIFICATIONS_MARK_ALL_FOR_DELETE = 'NOTIFICATIONS_MARK_ALL_FOR_DELETE';
-export const NOTIFICATIONS_ENTER_CLEARING_MODE = 'NOTIFICATIONS_ENTER_CLEARING_MODE'; // arg: yes
-// Unmark notifications (when the cleaning mode is left)
-export const NOTIFICATIONS_UNMARK_ALL_FOR_DELETE = 'NOTIFICATIONS_UNMARK_ALL_FOR_DELETE';
-// Mark one for delete
-export const NOTIFICATION_MARK_FOR_DELETE = 'NOTIFICATION_MARK_FOR_DELETE';
-
-export const NOTIFICATIONS_REFRESH_REQUEST = 'NOTIFICATIONS_REFRESH_REQUEST';
-export const NOTIFICATIONS_REFRESH_SUCCESS = 'NOTIFICATIONS_REFRESH_SUCCESS';
-export const NOTIFICATIONS_REFRESH_FAIL    = 'NOTIFICATIONS_REFRESH_FAIL';
-
-export const NOTIFICATIONS_EXPAND_REQUEST = 'NOTIFICATIONS_EXPAND_REQUEST';
-export const NOTIFICATIONS_EXPAND_SUCCESS = 'NOTIFICATIONS_EXPAND_SUCCESS';
-export const NOTIFICATIONS_EXPAND_FAIL    = 'NOTIFICATIONS_EXPAND_FAIL';
-
-export const NOTIFICATIONS_CLEAR      = 'NOTIFICATIONS_CLEAR';
-export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP';
-
-defineMessages({
-  mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' },
-});
-
-const fetchRelatedRelationships = (dispatch, notifications) => {
-  const accountIds = notifications.filter(item => item.type === 'follow').map(item => item.account.id);
-
-  if (accountIds > 0) {
-    dispatch(fetchRelationships(accountIds));
-  }
-};
-
-const unescapeHTML = (html) => {
-  const wrapper = document.createElement('div');
-  html = html.replace(/<br \/>|<br>|\n/, ' ');
-  wrapper.innerHTML = html;
-  return wrapper.textContent;
-};
-
-export function updateNotifications(notification, intlMessages, intlLocale) {
-  return (dispatch, getState) => {
-    const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true);
-    const playSound = getState().getIn(['settings', 'notifications', 'sounds', notification.type], true);
-
-    dispatch({
-      type: NOTIFICATIONS_UPDATE,
-      notification,
-      account: notification.account,
-      status: notification.status,
-      meta: playSound ? { sound: 'boop' } : undefined,
-    });
-
-    fetchRelatedRelationships(dispatch, [notification]);
-
-    // Desktop notifications
-    if (typeof window.Notification !== 'undefined' && showAlert) {
-      const title = new IntlMessageFormat(intlMessages[`notification.${notification.type}`], intlLocale).format({ name: notification.account.display_name.length > 0 ? notification.account.display_name : notification.account.username });
-      const body  = (notification.status && notification.status.spoiler_text.length > 0) ? notification.status.spoiler_text : unescapeHTML(notification.status ? notification.status.content : '');
-
-      const notify = new Notification(title, { body, icon: notification.account.avatar, tag: notification.id });
-      notify.addEventListener('click', () => {
-        window.focus();
-        notify.close();
-      });
-    }
-  };
-};
-
-const excludeTypesFromSettings = state => state.getIn(['settings', 'notifications', 'shows']).filter(enabled => !enabled).keySeq().toJS();
-
-export function refreshNotifications() {
-  return (dispatch, getState) => {
-    const params = {};
-    const ids    = getState().getIn(['notifications', 'items']);
-
-    let skipLoading = false;
-
-    if (ids.size > 0) {
-      params.since_id = ids.first().get('id');
-    }
-
-    if (getState().getIn(['notifications', 'loaded'])) {
-      skipLoading = true;
-    }
-
-    params.exclude_types = excludeTypesFromSettings(getState());
-
-    dispatch(refreshNotificationsRequest(skipLoading));
-
-    api(getState).get('/api/v1/notifications', { params }).then(response => {
-      const next = getLinks(response).refs.find(link => link.rel === 'next');
-
-      dispatch(refreshNotificationsSuccess(response.data, skipLoading, next ? next.uri : null));
-      fetchRelatedRelationships(dispatch, response.data);
-    }).catch(error => {
-      dispatch(refreshNotificationsFail(error, skipLoading));
-    });
-  };
-};
-
-export function refreshNotificationsRequest(skipLoading) {
-  return {
-    type: NOTIFICATIONS_REFRESH_REQUEST,
-    skipLoading,
-  };
-};
-
-export function refreshNotificationsSuccess(notifications, skipLoading, next) {
-  return {
-    type: NOTIFICATIONS_REFRESH_SUCCESS,
-    notifications,
-    accounts: notifications.map(item => item.account),
-    statuses: notifications.map(item => item.status).filter(status => !!status),
-    skipLoading,
-    next,
-  };
-};
-
-export function refreshNotificationsFail(error, skipLoading) {
-  return {
-    type: NOTIFICATIONS_REFRESH_FAIL,
-    error,
-    skipLoading,
-  };
-};
-
-export function expandNotifications() {
-  return (dispatch, getState) => {
-    const items  = getState().getIn(['notifications', 'items'], ImmutableList());
-
-    if (getState().getIn(['notifications', 'isLoading']) || items.size === 0) {
-      return;
-    }
-
-    const params = {
-      max_id: items.last().get('id'),
-      limit: 20,
-      exclude_types: excludeTypesFromSettings(getState()),
-    };
-
-    dispatch(expandNotificationsRequest());
-
-    api(getState).get('/api/v1/notifications', { params }).then(response => {
-      const next = getLinks(response).refs.find(link => link.rel === 'next');
-      dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null));
-      fetchRelatedRelationships(dispatch, response.data);
-    }).catch(error => {
-      dispatch(expandNotificationsFail(error));
-    });
-  };
-};
-
-export function expandNotificationsRequest() {
-  return {
-    type: NOTIFICATIONS_EXPAND_REQUEST,
-  };
-};
-
-export function expandNotificationsSuccess(notifications, next) {
-  return {
-    type: NOTIFICATIONS_EXPAND_SUCCESS,
-    notifications,
-    accounts: notifications.map(item => item.account),
-    statuses: notifications.map(item => item.status).filter(status => !!status),
-    next,
-  };
-};
-
-export function expandNotificationsFail(error) {
-  return {
-    type: NOTIFICATIONS_EXPAND_FAIL,
-    error,
-  };
-};
-
-export function clearNotifications() {
-  return (dispatch, getState) => {
-    dispatch({
-      type: NOTIFICATIONS_CLEAR,
-    });
-
-    api(getState).post('/api/v1/notifications/clear');
-  };
-};
-
-export function scrollTopNotifications(top) {
-  return {
-    type: NOTIFICATIONS_SCROLL_TOP,
-    top,
-  };
-};
-
-export function deleteMarkedNotifications() {
-  return (dispatch, getState) => {
-    dispatch(deleteMarkedNotificationsRequest());
-
-    let ids = [];
-    getState().getIn(['notifications', 'items']).forEach((n) => {
-      if (n.get('markedForDelete')) {
-        ids.push(n.get('id'));
-      }
-    });
-
-    if (ids.length === 0) {
-      return;
-    }
-
-    api(getState).delete(`/api/v1/notifications/destroy_multiple?ids[]=${ids.join('&ids[]=')}`).then(() => {
-      dispatch(deleteMarkedNotificationsSuccess());
-    }).catch(error => {
-      console.error(error);
-      dispatch(deleteMarkedNotificationsFail(error));
-    });
-  };
-};
-
-export function enterNotificationClearingMode(yes) {
-  return {
-    type: NOTIFICATIONS_ENTER_CLEARING_MODE,
-    yes: yes,
-  };
-};
-
-export function markAllNotifications(yes) {
-  return {
-    type: NOTIFICATIONS_MARK_ALL_FOR_DELETE,
-    yes: yes, // true, false or null. null = invert
-  };
-};
-
-export function deleteMarkedNotificationsRequest() {
-  return {
-    type: NOTIFICATIONS_DELETE_MARKED_REQUEST,
-  };
-};
-
-export function deleteMarkedNotificationsFail() {
-  return {
-    type: NOTIFICATIONS_DELETE_MARKED_FAIL,
-  };
-};
-
-export function markNotificationForDelete(id, yes) {
-  return {
-    type: NOTIFICATION_MARK_FOR_DELETE,
-    id: id,
-    yes: yes,
-  };
-};
-
-export function deleteMarkedNotificationsSuccess() {
-  return {
-    type: NOTIFICATIONS_DELETE_MARKED_SUCCESS,
-  };
-};
diff --git a/app/javascript/themes/glitch/actions/onboarding.js b/app/javascript/themes/glitch/actions/onboarding.js
deleted file mode 100644
index a161c50ef..000000000
--- a/app/javascript/themes/glitch/actions/onboarding.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { openModal } from './modal';
-import { changeSetting, saveSettings } from './settings';
-
-export function showOnboardingOnce() {
-  return (dispatch, getState) => {
-    const alreadySeen = getState().getIn(['settings', 'onboarded']);
-
-    if (!alreadySeen) {
-      dispatch(openModal('ONBOARDING'));
-      dispatch(changeSetting(['onboarded'], true));
-      dispatch(saveSettings());
-    }
-  };
-};
diff --git a/app/javascript/themes/glitch/actions/pin_statuses.js b/app/javascript/themes/glitch/actions/pin_statuses.js
deleted file mode 100644
index b3e064e58..000000000
--- a/app/javascript/themes/glitch/actions/pin_statuses.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import api from 'themes/glitch/util/api';
-
-export const PINNED_STATUSES_FETCH_REQUEST = 'PINNED_STATUSES_FETCH_REQUEST';
-export const PINNED_STATUSES_FETCH_SUCCESS = 'PINNED_STATUSES_FETCH_SUCCESS';
-export const PINNED_STATUSES_FETCH_FAIL = 'PINNED_STATUSES_FETCH_FAIL';
-
-import { me } from 'themes/glitch/util/initial_state';
-
-export function fetchPinnedStatuses() {
-  return (dispatch, getState) => {
-    dispatch(fetchPinnedStatusesRequest());
-
-    api(getState).get(`/api/v1/accounts/${me}/statuses`, { params: { pinned: true } }).then(response => {
-      dispatch(fetchPinnedStatusesSuccess(response.data, null));
-    }).catch(error => {
-      dispatch(fetchPinnedStatusesFail(error));
-    });
-  };
-};
-
-export function fetchPinnedStatusesRequest() {
-  return {
-    type: PINNED_STATUSES_FETCH_REQUEST,
-  };
-};
-
-export function fetchPinnedStatusesSuccess(statuses, next) {
-  return {
-    type: PINNED_STATUSES_FETCH_SUCCESS,
-    statuses,
-    next,
-  };
-};
-
-export function fetchPinnedStatusesFail(error) {
-  return {
-    type: PINNED_STATUSES_FETCH_FAIL,
-    error,
-  };
-};
diff --git a/app/javascript/themes/glitch/actions/push_notifications.js b/app/javascript/themes/glitch/actions/push_notifications.js
deleted file mode 100644
index 55661d2b0..000000000
--- a/app/javascript/themes/glitch/actions/push_notifications.js
+++ /dev/null
@@ -1,52 +0,0 @@
-import axios from 'axios';
-
-export const SET_BROWSER_SUPPORT = 'PUSH_NOTIFICATIONS_SET_BROWSER_SUPPORT';
-export const SET_SUBSCRIPTION = 'PUSH_NOTIFICATIONS_SET_SUBSCRIPTION';
-export const CLEAR_SUBSCRIPTION = 'PUSH_NOTIFICATIONS_CLEAR_SUBSCRIPTION';
-export const ALERTS_CHANGE = 'PUSH_NOTIFICATIONS_ALERTS_CHANGE';
-
-export function setBrowserSupport (value) {
-  return {
-    type: SET_BROWSER_SUPPORT,
-    value,
-  };
-}
-
-export function setSubscription (subscription) {
-  return {
-    type: SET_SUBSCRIPTION,
-    subscription,
-  };
-}
-
-export function clearSubscription () {
-  return {
-    type: CLEAR_SUBSCRIPTION,
-  };
-}
-
-export function changeAlerts(key, value) {
-  return dispatch => {
-    dispatch({
-      type: ALERTS_CHANGE,
-      key,
-      value,
-    });
-
-    dispatch(saveSettings());
-  };
-}
-
-export function saveSettings() {
-  return (_, getState) => {
-    const state = getState().get('push_notifications');
-    const subscription = state.get('subscription');
-    const alerts = state.get('alerts');
-
-    axios.put(`/api/web/push_subscriptions/${subscription.get('id')}`, {
-      data: {
-        alerts,
-      },
-    });
-  };
-}
diff --git a/app/javascript/themes/glitch/actions/reports.js b/app/javascript/themes/glitch/actions/reports.js
deleted file mode 100644
index 93f9085b2..000000000
--- a/app/javascript/themes/glitch/actions/reports.js
+++ /dev/null
@@ -1,80 +0,0 @@
-import api from 'themes/glitch/util/api';
-import { openModal, closeModal } from './modal';
-
-export const REPORT_INIT   = 'REPORT_INIT';
-export const REPORT_CANCEL = 'REPORT_CANCEL';
-
-export const REPORT_SUBMIT_REQUEST = 'REPORT_SUBMIT_REQUEST';
-export const REPORT_SUBMIT_SUCCESS = 'REPORT_SUBMIT_SUCCESS';
-export const REPORT_SUBMIT_FAIL    = 'REPORT_SUBMIT_FAIL';
-
-export const REPORT_STATUS_TOGGLE  = 'REPORT_STATUS_TOGGLE';
-export const REPORT_COMMENT_CHANGE = 'REPORT_COMMENT_CHANGE';
-
-export function initReport(account, status) {
-  return dispatch => {
-    dispatch({
-      type: REPORT_INIT,
-      account,
-      status,
-    });
-
-    dispatch(openModal('REPORT'));
-  };
-};
-
-export function cancelReport() {
-  return {
-    type: REPORT_CANCEL,
-  };
-};
-
-export function toggleStatusReport(statusId, checked) {
-  return {
-    type: REPORT_STATUS_TOGGLE,
-    statusId,
-    checked,
-  };
-};
-
-export function submitReport() {
-  return (dispatch, getState) => {
-    dispatch(submitReportRequest());
-
-    api(getState).post('/api/v1/reports', {
-      account_id: getState().getIn(['reports', 'new', 'account_id']),
-      status_ids: getState().getIn(['reports', 'new', 'status_ids']),
-      comment: getState().getIn(['reports', 'new', 'comment']),
-    }).then(response => {
-      dispatch(closeModal());
-      dispatch(submitReportSuccess(response.data));
-    }).catch(error => dispatch(submitReportFail(error)));
-  };
-};
-
-export function submitReportRequest() {
-  return {
-    type: REPORT_SUBMIT_REQUEST,
-  };
-};
-
-export function submitReportSuccess(report) {
-  return {
-    type: REPORT_SUBMIT_SUCCESS,
-    report,
-  };
-};
-
-export function submitReportFail(error) {
-  return {
-    type: REPORT_SUBMIT_FAIL,
-    error,
-  };
-};
-
-export function changeReportComment(comment) {
-  return {
-    type: REPORT_COMMENT_CHANGE,
-    comment,
-  };
-};
diff --git a/app/javascript/themes/glitch/actions/search.js b/app/javascript/themes/glitch/actions/search.js
deleted file mode 100644
index 414e4755e..000000000
--- a/app/javascript/themes/glitch/actions/search.js
+++ /dev/null
@@ -1,73 +0,0 @@
-import api from 'themes/glitch/util/api';
-
-export const SEARCH_CHANGE = 'SEARCH_CHANGE';
-export const SEARCH_CLEAR  = 'SEARCH_CLEAR';
-export const SEARCH_SHOW   = 'SEARCH_SHOW';
-
-export const SEARCH_FETCH_REQUEST = 'SEARCH_FETCH_REQUEST';
-export const SEARCH_FETCH_SUCCESS = 'SEARCH_FETCH_SUCCESS';
-export const SEARCH_FETCH_FAIL    = 'SEARCH_FETCH_FAIL';
-
-export function changeSearch(value) {
-  return {
-    type: SEARCH_CHANGE,
-    value,
-  };
-};
-
-export function clearSearch() {
-  return {
-    type: SEARCH_CLEAR,
-  };
-};
-
-export function submitSearch() {
-  return (dispatch, getState) => {
-    const value = getState().getIn(['search', 'value']);
-
-    if (value.length === 0) {
-      return;
-    }
-
-    dispatch(fetchSearchRequest());
-
-    api(getState).get('/api/v1/search', {
-      params: {
-        q: value,
-        resolve: true,
-      },
-    }).then(response => {
-      dispatch(fetchSearchSuccess(response.data));
-    }).catch(error => {
-      dispatch(fetchSearchFail(error));
-    });
-  };
-};
-
-export function fetchSearchRequest() {
-  return {
-    type: SEARCH_FETCH_REQUEST,
-  };
-};
-
-export function fetchSearchSuccess(results) {
-  return {
-    type: SEARCH_FETCH_SUCCESS,
-    results,
-    accounts: results.accounts,
-    statuses: results.statuses,
-  };
-};
-
-export function fetchSearchFail(error) {
-  return {
-    type: SEARCH_FETCH_FAIL,
-    error,
-  };
-};
-
-export function showSearch() {
-  return {
-    type: SEARCH_SHOW,
-  };
-};
diff --git a/app/javascript/themes/glitch/actions/settings.js b/app/javascript/themes/glitch/actions/settings.js
deleted file mode 100644
index 79adca18c..000000000
--- a/app/javascript/themes/glitch/actions/settings.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import axios from 'axios';
-import { debounce } from 'lodash';
-
-export const SETTING_CHANGE = 'SETTING_CHANGE';
-export const SETTING_SAVE   = 'SETTING_SAVE';
-
-export function changeSetting(key, value) {
-  return dispatch => {
-    dispatch({
-      type: SETTING_CHANGE,
-      key,
-      value,
-    });
-
-    dispatch(saveSettings());
-  };
-};
-
-const debouncedSave = debounce((dispatch, getState) => {
-  if (getState().getIn(['settings', 'saved'])) {
-    return;
-  }
-
-  const data = getState().get('settings').filter((_, key) => key !== 'saved').toJS();
-
-  axios.put('/api/web/settings', { data }).then(() => dispatch({ type: SETTING_SAVE }));
-}, 5000, { trailing: true });
-
-export function saveSettings() {
-  return (dispatch, getState) => debouncedSave(dispatch, getState);
-};
diff --git a/app/javascript/themes/glitch/actions/statuses.js b/app/javascript/themes/glitch/actions/statuses.js
deleted file mode 100644
index 702f4e9b6..000000000
--- a/app/javascript/themes/glitch/actions/statuses.js
+++ /dev/null
@@ -1,217 +0,0 @@
-import api from 'themes/glitch/util/api';
-
-import { deleteFromTimelines } from './timelines';
-import { fetchStatusCard } from './cards';
-
-export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST';
-export const STATUS_FETCH_SUCCESS = 'STATUS_FETCH_SUCCESS';
-export const STATUS_FETCH_FAIL    = 'STATUS_FETCH_FAIL';
-
-export const STATUS_DELETE_REQUEST = 'STATUS_DELETE_REQUEST';
-export const STATUS_DELETE_SUCCESS = 'STATUS_DELETE_SUCCESS';
-export const STATUS_DELETE_FAIL    = 'STATUS_DELETE_FAIL';
-
-export const CONTEXT_FETCH_REQUEST = 'CONTEXT_FETCH_REQUEST';
-export const CONTEXT_FETCH_SUCCESS = 'CONTEXT_FETCH_SUCCESS';
-export const CONTEXT_FETCH_FAIL    = 'CONTEXT_FETCH_FAIL';
-
-export const STATUS_MUTE_REQUEST = 'STATUS_MUTE_REQUEST';
-export const STATUS_MUTE_SUCCESS = 'STATUS_MUTE_SUCCESS';
-export const STATUS_MUTE_FAIL    = 'STATUS_MUTE_FAIL';
-
-export const STATUS_UNMUTE_REQUEST = 'STATUS_UNMUTE_REQUEST';
-export const STATUS_UNMUTE_SUCCESS = 'STATUS_UNMUTE_SUCCESS';
-export const STATUS_UNMUTE_FAIL    = 'STATUS_UNMUTE_FAIL';
-
-export function fetchStatusRequest(id, skipLoading) {
-  return {
-    type: STATUS_FETCH_REQUEST,
-    id,
-    skipLoading,
-  };
-};
-
-export function fetchStatus(id) {
-  return (dispatch, getState) => {
-    const skipLoading = getState().getIn(['statuses', id], null) !== null;
-
-    dispatch(fetchContext(id));
-    dispatch(fetchStatusCard(id));
-
-    if (skipLoading) {
-      return;
-    }
-
-    dispatch(fetchStatusRequest(id, skipLoading));
-
-    api(getState).get(`/api/v1/statuses/${id}`).then(response => {
-      dispatch(fetchStatusSuccess(response.data, skipLoading));
-    }).catch(error => {
-      dispatch(fetchStatusFail(id, error, skipLoading));
-    });
-  };
-};
-
-export function fetchStatusSuccess(status, skipLoading) {
-  return {
-    type: STATUS_FETCH_SUCCESS,
-    status,
-    skipLoading,
-  };
-};
-
-export function fetchStatusFail(id, error, skipLoading) {
-  return {
-    type: STATUS_FETCH_FAIL,
-    id,
-    error,
-    skipLoading,
-    skipAlert: true,
-  };
-};
-
-export function deleteStatus(id) {
-  return (dispatch, getState) => {
-    dispatch(deleteStatusRequest(id));
-
-    api(getState).delete(`/api/v1/statuses/${id}`).then(() => {
-      dispatch(deleteStatusSuccess(id));
-      dispatch(deleteFromTimelines(id));
-    }).catch(error => {
-      dispatch(deleteStatusFail(id, error));
-    });
-  };
-};
-
-export function deleteStatusRequest(id) {
-  return {
-    type: STATUS_DELETE_REQUEST,
-    id: id,
-  };
-};
-
-export function deleteStatusSuccess(id) {
-  return {
-    type: STATUS_DELETE_SUCCESS,
-    id: id,
-  };
-};
-
-export function deleteStatusFail(id, error) {
-  return {
-    type: STATUS_DELETE_FAIL,
-    id: id,
-    error: error,
-  };
-};
-
-export function fetchContext(id) {
-  return (dispatch, getState) => {
-    dispatch(fetchContextRequest(id));
-
-    api(getState).get(`/api/v1/statuses/${id}/context`).then(response => {
-      dispatch(fetchContextSuccess(id, response.data.ancestors, response.data.descendants));
-
-    }).catch(error => {
-      if (error.response && error.response.status === 404) {
-        dispatch(deleteFromTimelines(id));
-      }
-
-      dispatch(fetchContextFail(id, error));
-    });
-  };
-};
-
-export function fetchContextRequest(id) {
-  return {
-    type: CONTEXT_FETCH_REQUEST,
-    id,
-  };
-};
-
-export function fetchContextSuccess(id, ancestors, descendants) {
-  return {
-    type: CONTEXT_FETCH_SUCCESS,
-    id,
-    ancestors,
-    descendants,
-    statuses: ancestors.concat(descendants),
-  };
-};
-
-export function fetchContextFail(id, error) {
-  return {
-    type: CONTEXT_FETCH_FAIL,
-    id,
-    error,
-    skipAlert: true,
-  };
-};
-
-export function muteStatus(id) {
-  return (dispatch, getState) => {
-    dispatch(muteStatusRequest(id));
-
-    api(getState).post(`/api/v1/statuses/${id}/mute`).then(() => {
-      dispatch(muteStatusSuccess(id));
-    }).catch(error => {
-      dispatch(muteStatusFail(id, error));
-    });
-  };
-};
-
-export function muteStatusRequest(id) {
-  return {
-    type: STATUS_MUTE_REQUEST,
-    id,
-  };
-};
-
-export function muteStatusSuccess(id) {
-  return {
-    type: STATUS_MUTE_SUCCESS,
-    id,
-  };
-};
-
-export function muteStatusFail(id, error) {
-  return {
-    type: STATUS_MUTE_FAIL,
-    id,
-    error,
-  };
-};
-
-export function unmuteStatus(id) {
-  return (dispatch, getState) => {
-    dispatch(unmuteStatusRequest(id));
-
-    api(getState).post(`/api/v1/statuses/${id}/unmute`).then(() => {
-      dispatch(unmuteStatusSuccess(id));
-    }).catch(error => {
-      dispatch(unmuteStatusFail(id, error));
-    });
-  };
-};
-
-export function unmuteStatusRequest(id) {
-  return {
-    type: STATUS_UNMUTE_REQUEST,
-    id,
-  };
-};
-
-export function unmuteStatusSuccess(id) {
-  return {
-    type: STATUS_UNMUTE_SUCCESS,
-    id,
-  };
-};
-
-export function unmuteStatusFail(id, error) {
-  return {
-    type: STATUS_UNMUTE_FAIL,
-    id,
-    error,
-  };
-};
diff --git a/app/javascript/themes/glitch/actions/store.js b/app/javascript/themes/glitch/actions/store.js
deleted file mode 100644
index a1db0fdd5..000000000
--- a/app/javascript/themes/glitch/actions/store.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Iterable, fromJS } from 'immutable';
-
-export const STORE_HYDRATE = 'STORE_HYDRATE';
-export const STORE_HYDRATE_LAZY = 'STORE_HYDRATE_LAZY';
-
-const convertState = rawState =>
-  fromJS(rawState, (k, v) =>
-    Iterable.isIndexed(v) ? v.toList() : v.toMap());
-
-export function hydrateStore(rawState) {
-  const state = convertState(rawState);
-
-  return {
-    type: STORE_HYDRATE,
-    state,
-  };
-};
diff --git a/app/javascript/themes/glitch/actions/streaming.js b/app/javascript/themes/glitch/actions/streaming.js
deleted file mode 100644
index ccf6c27d8..000000000
--- a/app/javascript/themes/glitch/actions/streaming.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import { connectStream } from 'themes/glitch/util/stream';
-import {
-  updateTimeline,
-  deleteFromTimelines,
-  refreshHomeTimeline,
-  connectTimeline,
-  disconnectTimeline,
-} from './timelines';
-import { updateNotifications, refreshNotifications } from './notifications';
-import { getLocale } from 'mastodon/locales';
-
-const { messages } = getLocale();
-
-export function connectTimelineStream (timelineId, path, pollingRefresh = null) {
-
-  return connectStream (path, pollingRefresh, (dispatch, getState) => {
-    const locale = getState().getIn(['meta', 'locale']);
-    return {
-      onConnect() {
-        dispatch(connectTimeline(timelineId));
-      },
-
-      onDisconnect() {
-        dispatch(disconnectTimeline(timelineId));
-      },
-
-      onReceive (data) {
-        switch(data.event) {
-        case 'update':
-          dispatch(updateTimeline(timelineId, JSON.parse(data.payload)));
-          break;
-        case 'delete':
-          dispatch(deleteFromTimelines(data.payload));
-          break;
-        case 'notification':
-          dispatch(updateNotifications(JSON.parse(data.payload), messages, locale));
-          break;
-        }
-      },
-    };
-  });
-}
-
-function refreshHomeTimelineAndNotification (dispatch) {
-  dispatch(refreshHomeTimeline());
-  dispatch(refreshNotifications());
-}
-
-export const connectUserStream = () => connectTimelineStream('home', 'user', refreshHomeTimelineAndNotification);
-export const connectCommunityStream = () => connectTimelineStream('community', 'public:local');
-export const connectMediaStream = () => connectTimelineStream('community', 'public:local');
-export const connectPublicStream = () => connectTimelineStream('public', 'public');
-export const connectHashtagStream = (tag) => connectTimelineStream(`hashtag:${tag}`, `hashtag&tag=${tag}`);
-export const connectDirectStream = () => connectTimelineStream('direct', 'direct');
diff --git a/app/javascript/themes/glitch/actions/timelines.js b/app/javascript/themes/glitch/actions/timelines.js
deleted file mode 100644
index 5ce14fbe9..000000000
--- a/app/javascript/themes/glitch/actions/timelines.js
+++ /dev/null
@@ -1,208 +0,0 @@
-import api, { getLinks } from 'themes/glitch/util/api';
-import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
-
-export const TIMELINE_UPDATE  = 'TIMELINE_UPDATE';
-export const TIMELINE_DELETE  = 'TIMELINE_DELETE';
-
-export const TIMELINE_REFRESH_REQUEST = 'TIMELINE_REFRESH_REQUEST';
-export const TIMELINE_REFRESH_SUCCESS = 'TIMELINE_REFRESH_SUCCESS';
-export const TIMELINE_REFRESH_FAIL    = 'TIMELINE_REFRESH_FAIL';
-
-export const TIMELINE_EXPAND_REQUEST = 'TIMELINE_EXPAND_REQUEST';
-export const TIMELINE_EXPAND_SUCCESS = 'TIMELINE_EXPAND_SUCCESS';
-export const TIMELINE_EXPAND_FAIL    = 'TIMELINE_EXPAND_FAIL';
-
-export const TIMELINE_SCROLL_TOP = 'TIMELINE_SCROLL_TOP';
-
-export const TIMELINE_CONNECT    = 'TIMELINE_CONNECT';
-export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT';
-
-export const TIMELINE_CONTEXT_UPDATE = 'CONTEXT_UPDATE';
-
-export function refreshTimelineSuccess(timeline, statuses, skipLoading, next) {
-  return {
-    type: TIMELINE_REFRESH_SUCCESS,
-    timeline,
-    statuses,
-    skipLoading,
-    next,
-  };
-};
-
-export function updateTimeline(timeline, status) {
-  return (dispatch, getState) => {
-    const references = status.reblog ? getState().get('statuses').filter((item, itemId) => (itemId === status.reblog.id || item.get('reblog') === status.reblog.id)).map((_, itemId) => itemId) : [];
-    const parents = [];
-
-    if (status.in_reply_to_id) {
-      let parent = getState().getIn(['statuses', status.in_reply_to_id]);
-
-      while (parent && parent.get('in_reply_to_id')) {
-        parents.push(parent.get('id'));
-        parent = getState().getIn(['statuses', parent.get('in_reply_to_id')]);
-      }
-    }
-
-    dispatch({
-      type: TIMELINE_UPDATE,
-      timeline,
-      status,
-      references,
-    });
-
-    if (parents.length > 0) {
-      dispatch({
-        type: TIMELINE_CONTEXT_UPDATE,
-        status,
-        references: parents,
-      });
-    }
-  };
-};
-
-export function deleteFromTimelines(id) {
-  return (dispatch, getState) => {
-    const accountId  = getState().getIn(['statuses', id, 'account']);
-    const references = getState().get('statuses').filter(status => status.get('reblog') === id).map(status => [status.get('id'), status.get('account')]);
-    const reblogOf   = getState().getIn(['statuses', id, 'reblog'], null);
-
-    dispatch({
-      type: TIMELINE_DELETE,
-      id,
-      accountId,
-      references,
-      reblogOf,
-    });
-  };
-};
-
-export function refreshTimelineRequest(timeline, skipLoading) {
-  return {
-    type: TIMELINE_REFRESH_REQUEST,
-    timeline,
-    skipLoading,
-  };
-};
-
-export function refreshTimeline(timelineId, path, params = {}) {
-  return function (dispatch, getState) {
-    const timeline = getState().getIn(['timelines', timelineId], ImmutableMap());
-
-    if (timeline.get('isLoading') || timeline.get('online')) {
-      return;
-    }
-
-    const ids      = timeline.get('items', ImmutableList());
-    const newestId = ids.size > 0 ? ids.first() : null;
-
-    let skipLoading = timeline.get('loaded');
-
-    if (newestId !== null) {
-      params.since_id = newestId;
-    }
-
-    dispatch(refreshTimelineRequest(timelineId, skipLoading));
-
-    api(getState).get(path, { params }).then(response => {
-      const next = getLinks(response).refs.find(link => link.rel === 'next');
-      dispatch(refreshTimelineSuccess(timelineId, response.data, skipLoading, next ? next.uri : null));
-    }).catch(error => {
-      dispatch(refreshTimelineFail(timelineId, error, skipLoading));
-    });
-  };
-};
-
-export const refreshHomeTimeline         = () => refreshTimeline('home', '/api/v1/timelines/home');
-export const refreshPublicTimeline       = () => refreshTimeline('public', '/api/v1/timelines/public');
-export const refreshCommunityTimeline    = () => refreshTimeline('community', '/api/v1/timelines/public', { local: true });
-export const refreshDirectTimeline       = () => refreshTimeline('direct', '/api/v1/timelines/direct');
-export const refreshAccountTimeline      = accountId => refreshTimeline(`account:${accountId}`, `/api/v1/accounts/${accountId}/statuses`);
-export const refreshAccountMediaTimeline = accountId => refreshTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { only_media: true });
-export const refreshHashtagTimeline      = hashtag => refreshTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`);
-
-export function refreshTimelineFail(timeline, error, skipLoading) {
-  return {
-    type: TIMELINE_REFRESH_FAIL,
-    timeline,
-    error,
-    skipLoading,
-    skipAlert: error.response && error.response.status === 404,
-  };
-};
-
-export function expandTimeline(timelineId, path, params = {}) {
-  return (dispatch, getState) => {
-    const timeline = getState().getIn(['timelines', timelineId], ImmutableMap());
-    const ids      = timeline.get('items', ImmutableList());
-
-    if (timeline.get('isLoading') || ids.size === 0) {
-      return;
-    }
-
-    params.max_id = ids.last();
-    params.limit  = 10;
-
-    dispatch(expandTimelineRequest(timelineId));
-
-    api(getState).get(path, { params }).then(response => {
-      const next = getLinks(response).refs.find(link => link.rel === 'next');
-      dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null));
-    }).catch(error => {
-      dispatch(expandTimelineFail(timelineId, error));
-    });
-  };
-};
-
-export const expandHomeTimeline         = () => expandTimeline('home', '/api/v1/timelines/home');
-export const expandPublicTimeline       = () => expandTimeline('public', '/api/v1/timelines/public');
-export const expandCommunityTimeline    = () => expandTimeline('community', '/api/v1/timelines/public', { local: true });
-export const expandDirectTimeline       = () => expandTimeline('direct', '/api/v1/timelines/direct');
-export const expandAccountTimeline      = accountId => expandTimeline(`account:${accountId}`, `/api/v1/accounts/${accountId}/statuses`);
-export const expandAccountMediaTimeline = accountId => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { only_media: true });
-export const expandHashtagTimeline      = hashtag => expandTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`);
-
-export function expandTimelineRequest(timeline) {
-  return {
-    type: TIMELINE_EXPAND_REQUEST,
-    timeline,
-  };
-};
-
-export function expandTimelineSuccess(timeline, statuses, next) {
-  return {
-    type: TIMELINE_EXPAND_SUCCESS,
-    timeline,
-    statuses,
-    next,
-  };
-};
-
-export function expandTimelineFail(timeline, error) {
-  return {
-    type: TIMELINE_EXPAND_FAIL,
-    timeline,
-    error,
-  };
-};
-
-export function scrollTopTimeline(timeline, top) {
-  return {
-    type: TIMELINE_SCROLL_TOP,
-    timeline,
-    top,
-  };
-};
-
-export function connectTimeline(timeline) {
-  return {
-    type: TIMELINE_CONNECT,
-    timeline,
-  };
-};
-
-export function disconnectTimeline(timeline) {
-  return {
-    type: TIMELINE_DISCONNECT,
-    timeline,
-  };
-};
diff --git a/app/javascript/themes/glitch/components/account.js b/app/javascript/themes/glitch/components/account.js
deleted file mode 100644
index d0ff77050..000000000
--- a/app/javascript/themes/glitch/components/account.js
+++ /dev/null
@@ -1,116 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import Avatar from './avatar';
-import DisplayName from './display_name';
-import Permalink from './permalink';
-import IconButton from './icon_button';
-import { defineMessages, injectIntl } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { me } from 'themes/glitch/util/initial_state';
-
-const messages = defineMessages({
-  follow: { id: 'account.follow', defaultMessage: 'Follow' },
-  unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
-  requested: { id: 'account.requested', defaultMessage: 'Awaiting approval' },
-  unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
-  unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
-  mute_notifications: { id: 'account.mute_notifications', defaultMessage: 'You are not currently muting notifications from @{name}. Click to mute notifications' },
-  unmute_notifications: { id: 'account.unmute_notifications', defaultMessage: 'You are currently muting notifications from @{name}. Click to unmute notifications' },
-});
-
-@injectIntl
-export default class Account extends ImmutablePureComponent {
-
-  static propTypes = {
-    account: ImmutablePropTypes.map.isRequired,
-    onFollow: PropTypes.func.isRequired,
-    onBlock: PropTypes.func.isRequired,
-    onMute: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-    hidden: PropTypes.bool,
-  };
-
-  handleFollow = () => {
-    this.props.onFollow(this.props.account);
-  }
-
-  handleBlock = () => {
-    this.props.onBlock(this.props.account);
-  }
-
-  handleMute = () => {
-    this.props.onMute(this.props.account);
-  }
-
-  handleMuteNotifications = () => {
-    this.props.onMuteNotifications(this.props.account, true);
-  }
-
-  handleUnmuteNotifications = () => {
-    this.props.onMuteNotifications(this.props.account, false);
-  }
-
-  render () {
-    const { account, intl, hidden } = this.props;
-
-    if (!account) {
-      return <div />;
-    }
-
-    if (hidden) {
-      return (
-        <div>
-          {account.get('display_name')}
-          {account.get('username')}
-        </div>
-      );
-    }
-
-    let buttons;
-
-    if (account.get('id') !== me && account.get('relationship', null) !== null) {
-      const following = account.getIn(['relationship', 'following']);
-      const requested = account.getIn(['relationship', 'requested']);
-      const blocking  = account.getIn(['relationship', 'blocking']);
-      const muting  = account.getIn(['relationship', 'muting']);
-
-      if (requested) {
-        buttons = <IconButton disabled icon='hourglass' title={intl.formatMessage(messages.requested)} />;
-      } else if (blocking) {
-        buttons = <IconButton active icon='unlock-alt' title={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.handleBlock} />;
-      } else if (muting) {
-        let hidingNotificationsButton;
-        if (muting.get('notifications')) {
-          hidingNotificationsButton = <IconButton active icon='bell' title={intl.formatMessage(messages.unmute_notifications, { name: account.get('username') })} onClick={this.handleUnmuteNotifications} />;
-        } else {
-          hidingNotificationsButton = <IconButton active icon='bell-slash' title={intl.formatMessage(messages.mute_notifications, { name: account.get('username')  })} onClick={this.handleMuteNotifications} />;
-        }
-        buttons = (
-          <div>
-            <IconButton active icon='volume-up' title={intl.formatMessage(messages.unmute, { name: account.get('username') })} onClick={this.handleMute} />
-            {hidingNotificationsButton}
-          </div>
-        );
-      } else {
-        buttons = <IconButton icon={following ? 'user-times' : 'user-plus'} title={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={this.handleFollow} active={following ? true : false} />;
-      }
-    }
-
-    return (
-      <div className='account'>
-        <div className='account__wrapper'>
-          <Permalink key={account.get('id')} className='account__display-name' href={account.get('url')} to={`/accounts/${account.get('id')}`}>
-            <div className='account__avatar-wrapper'><Avatar account={account} size={36} /></div>
-            <DisplayName account={account} />
-          </Permalink>
-
-          <div className='account__relationship'>
-            {buttons}
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/attachment_list.js b/app/javascript/themes/glitch/components/attachment_list.js
deleted file mode 100644
index b3d00b335..000000000
--- a/app/javascript/themes/glitch/components/attachment_list.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const filename = url => url.split('/').pop().split('#')[0].split('?')[0];
-
-export default class AttachmentList extends ImmutablePureComponent {
-
-  static propTypes = {
-    media: ImmutablePropTypes.list.isRequired,
-  };
-
-  render () {
-    const { media } = this.props;
-
-    return (
-      <div className='attachment-list'>
-        <div className='attachment-list__icon'>
-          <i className='fa fa-link' />
-        </div>
-
-        <ul className='attachment-list__list'>
-          {media.map(attachment =>
-            <li key={attachment.get('id')}>
-              <a href={attachment.get('remote_url')} target='_blank' rel='noopener'>{filename(attachment.get('remote_url'))}</a>
-            </li>
-          )}
-        </ul>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/autosuggest_emoji.js b/app/javascript/themes/glitch/components/autosuggest_emoji.js
deleted file mode 100644
index 3c6f915e4..000000000
--- a/app/javascript/themes/glitch/components/autosuggest_emoji.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import unicodeMapping from 'themes/glitch/util/emoji/emoji_unicode_mapping_light';
-
-const assetHost = process.env.CDN_HOST || '';
-
-export default class AutosuggestEmoji extends React.PureComponent {
-
-  static propTypes = {
-    emoji: PropTypes.object.isRequired,
-  };
-
-  render () {
-    const { emoji } = this.props;
-    let url;
-
-    if (emoji.custom) {
-      url = emoji.imageUrl;
-    } else {
-      const mapping = unicodeMapping[emoji.native] || unicodeMapping[emoji.native.replace(/\uFE0F$/, '')];
-
-      if (!mapping) {
-        return null;
-      }
-
-      url = `${assetHost}/emoji/${mapping.filename}.svg`;
-    }
-
-    return (
-      <div className='autosuggest-emoji'>
-        <img
-          className='emojione'
-          src={url}
-          alt={emoji.native || emoji.colons}
-        />
-
-        {emoji.colons}
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/autosuggest_textarea.js b/app/javascript/themes/glitch/components/autosuggest_textarea.js
deleted file mode 100644
index fa93847a2..000000000
--- a/app/javascript/themes/glitch/components/autosuggest_textarea.js
+++ /dev/null
@@ -1,222 +0,0 @@
-import React from 'react';
-import AutosuggestAccountContainer from 'themes/glitch/features/compose/containers/autosuggest_account_container';
-import AutosuggestEmoji from './autosuggest_emoji';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import { isRtl } from 'themes/glitch/util/rtl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import Textarea from 'react-textarea-autosize';
-import classNames from 'classnames';
-
-const textAtCursorMatchesToken = (str, caretPosition) => {
-  let word;
-
-  let left  = str.slice(0, caretPosition).search(/[^\s\u200B]+$/);
-  let right = str.slice(caretPosition).search(/[\s\u200B]/);
-
-  if (right < 0) {
-    word = str.slice(left);
-  } else {
-    word = str.slice(left, right + caretPosition);
-  }
-
-  if (!word || word.trim().length < 3 || ['@', ':'].indexOf(word[0]) === -1) {
-    return [null, null];
-  }
-
-  word = word.trim().toLowerCase();
-
-  if (word.length > 0) {
-    return [left + 1, word];
-  } else {
-    return [null, null];
-  }
-};
-
-export default class AutosuggestTextarea extends ImmutablePureComponent {
-
-  static propTypes = {
-    value: PropTypes.string,
-    suggestions: ImmutablePropTypes.list,
-    disabled: PropTypes.bool,
-    placeholder: PropTypes.string,
-    onSuggestionSelected: PropTypes.func.isRequired,
-    onSuggestionsClearRequested: PropTypes.func.isRequired,
-    onSuggestionsFetchRequested: PropTypes.func.isRequired,
-    onChange: PropTypes.func.isRequired,
-    onKeyUp: PropTypes.func,
-    onKeyDown: PropTypes.func,
-    onPaste: PropTypes.func.isRequired,
-    autoFocus: PropTypes.bool,
-  };
-
-  static defaultProps = {
-    autoFocus: true,
-  };
-
-  state = {
-    suggestionsHidden: false,
-    selectedSuggestion: 0,
-    lastToken: null,
-    tokenStart: 0,
-  };
-
-  onChange = (e) => {
-    const [ tokenStart, token ] = textAtCursorMatchesToken(e.target.value, e.target.selectionStart);
-
-    if (token !== null && this.state.lastToken !== token) {
-      this.setState({ lastToken: token, selectedSuggestion: 0, tokenStart });
-      this.props.onSuggestionsFetchRequested(token);
-    } else if (token === null) {
-      this.setState({ lastToken: null });
-      this.props.onSuggestionsClearRequested();
-    }
-
-    this.props.onChange(e);
-  }
-
-  onKeyDown = (e) => {
-    const { suggestions, disabled } = this.props;
-    const { selectedSuggestion, suggestionsHidden } = this.state;
-
-    if (disabled) {
-      e.preventDefault();
-      return;
-    }
-
-    switch(e.key) {
-    case 'Escape':
-      if (!suggestionsHidden) {
-        e.preventDefault();
-        this.setState({ suggestionsHidden: true });
-      }
-
-      break;
-    case 'ArrowDown':
-      if (suggestions.size > 0 && !suggestionsHidden) {
-        e.preventDefault();
-        this.setState({ selectedSuggestion: Math.min(selectedSuggestion + 1, suggestions.size - 1) });
-      }
-
-      break;
-    case 'ArrowUp':
-      if (suggestions.size > 0 && !suggestionsHidden) {
-        e.preventDefault();
-        this.setState({ selectedSuggestion: Math.max(selectedSuggestion - 1, 0) });
-      }
-
-      break;
-    case 'Enter':
-    case 'Tab':
-      // Select suggestion
-      if (this.state.lastToken !== null && suggestions.size > 0 && !suggestionsHidden) {
-        e.preventDefault();
-        e.stopPropagation();
-        this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestions.get(selectedSuggestion));
-      }
-
-      break;
-    }
-
-    if (e.defaultPrevented || !this.props.onKeyDown) {
-      return;
-    }
-
-    this.props.onKeyDown(e);
-  }
-
-  onKeyUp = e => {
-    if (e.key === 'Escape' && this.state.suggestionsHidden) {
-      document.querySelector('.ui').parentElement.focus();
-    }
-
-    if (this.props.onKeyUp) {
-      this.props.onKeyUp(e);
-    }
-  }
-
-  onBlur = () => {
-    this.setState({ suggestionsHidden: true });
-  }
-
-  onSuggestionClick = (e) => {
-    const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute('data-index'));
-    e.preventDefault();
-    this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion);
-    this.textarea.focus();
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden) {
-      this.setState({ suggestionsHidden: false });
-    }
-  }
-
-  setTextarea = (c) => {
-    this.textarea = c;
-  }
-
-  onPaste = (e) => {
-    if (e.clipboardData && e.clipboardData.files.length === 1) {
-      this.props.onPaste(e.clipboardData.files);
-      e.preventDefault();
-    }
-  }
-
-  renderSuggestion = (suggestion, i) => {
-    const { selectedSuggestion } = this.state;
-    let inner, key;
-
-    if (typeof suggestion === 'object') {
-      inner = <AutosuggestEmoji emoji={suggestion} />;
-      key   = suggestion.id;
-    } else {
-      inner = <AutosuggestAccountContainer id={suggestion} />;
-      key   = suggestion;
-    }
-
-    return (
-      <div role='button' tabIndex='0' key={key} data-index={i} className={classNames('autosuggest-textarea__suggestions__item', { selected: i === selectedSuggestion })} onMouseDown={this.onSuggestionClick}>
-        {inner}
-      </div>
-    );
-  }
-
-  render () {
-    const { value, suggestions, disabled, placeholder, autoFocus } = this.props;
-    const { suggestionsHidden } = this.state;
-    const style = { direction: 'ltr' };
-
-    if (isRtl(value)) {
-      style.direction = 'rtl';
-    }
-
-    return (
-      <div className='autosuggest-textarea'>
-        <label>
-          <span style={{ display: 'none' }}>{placeholder}</span>
-
-          <Textarea
-            inputRef={this.setTextarea}
-            className='autosuggest-textarea__textarea'
-            disabled={disabled}
-            placeholder={placeholder}
-            autoFocus={autoFocus}
-            value={value}
-            onChange={this.onChange}
-            onKeyDown={this.onKeyDown}
-            onKeyUp={this.onKeyUp}
-            onBlur={this.onBlur}
-            onPaste={this.onPaste}
-            style={style}
-          />
-        </label>
-
-        <div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}>
-          {suggestions.map(this.renderSuggestion)}
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/avatar.js b/app/javascript/themes/glitch/components/avatar.js
deleted file mode 100644
index dd155f059..000000000
--- a/app/javascript/themes/glitch/components/avatar.js
+++ /dev/null
@@ -1,72 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-
-export default class Avatar extends React.PureComponent {
-
-  static propTypes = {
-    account: ImmutablePropTypes.map.isRequired,
-    size: PropTypes.number.isRequired,
-    style: PropTypes.object,
-    animate: PropTypes.bool,
-    inline: PropTypes.bool,
-  };
-
-  static defaultProps = {
-    animate: false,
-    size: 20,
-    inline: false,
-  };
-
-  state = {
-    hovering: false,
-  };
-
-  handleMouseEnter = () => {
-    if (this.props.animate) return;
-    this.setState({ hovering: true });
-  }
-
-  handleMouseLeave = () => {
-    if (this.props.animate) return;
-    this.setState({ hovering: false });
-  }
-
-  render () {
-    const { account, size, animate, inline } = this.props;
-    const { hovering } = this.state;
-
-    const src = account.get('avatar');
-    const staticSrc = account.get('avatar_static');
-
-    let className = 'account__avatar';
-
-    if (inline) {
-      className = className + ' account__avatar-inline';
-    }
-
-    const style = {
-      ...this.props.style,
-      width: `${size}px`,
-      height: `${size}px`,
-      backgroundSize: `${size}px ${size}px`,
-    };
-
-    if (hovering || animate) {
-      style.backgroundImage = `url(${src})`;
-    } else {
-      style.backgroundImage = `url(${staticSrc})`;
-    }
-
-    return (
-      <div
-        className={className}
-        onMouseEnter={this.handleMouseEnter}
-        onMouseLeave={this.handleMouseLeave}
-        style={style}
-        data-avatar-of={`@${account.get('acct')}`}
-      />
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/avatar_overlay.js b/app/javascript/themes/glitch/components/avatar_overlay.js
deleted file mode 100644
index 2ecf9fa44..000000000
--- a/app/javascript/themes/glitch/components/avatar_overlay.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-
-export default class AvatarOverlay extends React.PureComponent {
-
-  static propTypes = {
-    account: ImmutablePropTypes.map.isRequired,
-    friend: ImmutablePropTypes.map.isRequired,
-  };
-
-  render() {
-    const { account, friend } = this.props;
-
-    const baseStyle = {
-      backgroundImage: `url(${account.get('avatar_static')})`,
-    };
-
-    const overlayStyle = {
-      backgroundImage: `url(${friend.get('avatar_static')})`,
-    };
-
-    return (
-      <div className='account__avatar-overlay'>
-        <div className='account__avatar-overlay-base' style={baseStyle} data-avatar-of={`@${account.get('acct')}`} />
-        <div className='account__avatar-overlay-overlay' style={overlayStyle} data-avatar-of={`@${friend.get('acct')}`} />
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/button.js b/app/javascript/themes/glitch/components/button.js
deleted file mode 100644
index 16868010c..000000000
--- a/app/javascript/themes/glitch/components/button.js
+++ /dev/null
@@ -1,64 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
-
-export default class Button extends React.PureComponent {
-
-  static propTypes = {
-    text: PropTypes.node,
-    onClick: PropTypes.func,
-    disabled: PropTypes.bool,
-    block: PropTypes.bool,
-    secondary: PropTypes.bool,
-    size: PropTypes.number,
-    className: PropTypes.string,
-    style: PropTypes.object,
-    children: PropTypes.node,
-    title: PropTypes.string,
-  };
-
-  static defaultProps = {
-    size: 36,
-  };
-
-  handleClick = (e) => {
-    if (!this.props.disabled) {
-      this.props.onClick(e);
-    }
-  }
-
-  setRef = (c) => {
-    this.node = c;
-  }
-
-  focus() {
-    this.node.focus();
-  }
-
-  render () {
-    let attrs = {
-      className: classNames('button', this.props.className, {
-        'button-secondary': this.props.secondary,
-        'button--block': this.props.block,
-      }),
-      disabled: this.props.disabled,
-      onClick: this.handleClick,
-      ref: this.setRef,
-      style: {
-        padding: `0 ${this.props.size / 2.25}px`,
-        height: `${this.props.size}px`,
-        lineHeight: `${this.props.size}px`,
-        ...this.props.style,
-      },
-    };
-
-    if (this.props.title) attrs.title = this.props.title;
-
-    return (
-      <button {...attrs}>
-        {this.props.text || this.props.children}
-      </button>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/collapsable.js b/app/javascript/themes/glitch/components/collapsable.js
deleted file mode 100644
index 8bc0a54f4..000000000
--- a/app/javascript/themes/glitch/components/collapsable.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import React from 'react';
-import Motion from 'themes/glitch/util/optional_motion';
-import spring from 'react-motion/lib/spring';
-import PropTypes from 'prop-types';
-
-const Collapsable = ({ fullHeight, isVisible, children }) => (
-  <Motion defaultStyle={{ opacity: !isVisible ? 0 : 100, height: isVisible ? fullHeight : 0 }} style={{ opacity: spring(!isVisible ? 0 : 100), height: spring(!isVisible ? 0 : fullHeight) }}>
-    {({ opacity, height }) =>
-      <div style={{ height: `${height}px`, overflow: 'hidden', opacity: opacity / 100, display: Math.floor(opacity) === 0 ? 'none' : 'block' }}>
-        {children}
-      </div>
-    }
-  </Motion>
-);
-
-Collapsable.propTypes = {
-  fullHeight: PropTypes.number.isRequired,
-  isVisible: PropTypes.bool.isRequired,
-  children: PropTypes.node.isRequired,
-};
-
-export default Collapsable;
diff --git a/app/javascript/themes/glitch/components/column.js b/app/javascript/themes/glitch/components/column.js
deleted file mode 100644
index adeba9cc1..000000000
--- a/app/javascript/themes/glitch/components/column.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import detectPassiveEvents from 'detect-passive-events';
-import { scrollTop } from 'themes/glitch/util/scroll';
-
-export default class Column extends React.PureComponent {
-
-  static propTypes = {
-    children: PropTypes.node,
-    extraClasses: PropTypes.string,
-    name: PropTypes.string,
-  };
-
-  scrollTop () {
-    const scrollable = this.node.querySelector('.scrollable');
-
-    if (!scrollable) {
-      return;
-    }
-
-    this._interruptScrollAnimation = scrollTop(scrollable);
-  }
-
-  handleWheel = () => {
-    if (typeof this._interruptScrollAnimation !== 'function') {
-      return;
-    }
-
-    this._interruptScrollAnimation();
-  }
-
-  setRef = c => {
-    this.node = c;
-  }
-
-  componentDidMount () {
-    this.node.addEventListener('wheel', this.handleWheel,  detectPassiveEvents.hasSupport ? { passive: true } : false);
-  }
-
-  componentWillUnmount () {
-    this.node.removeEventListener('wheel', this.handleWheel);
-  }
-
-  render () {
-    const { children, extraClasses, name } = this.props;
-
-    return (
-      <div role='region' data-column={name} className={`column ${extraClasses || ''}`} ref={this.setRef}>
-        {children}
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/column_back_button.js b/app/javascript/themes/glitch/components/column_back_button.js
deleted file mode 100644
index 50c3bf11f..000000000
--- a/app/javascript/themes/glitch/components/column_back_button.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import React from 'react';
-import { FormattedMessage } from 'react-intl';
-import PropTypes from 'prop-types';
-
-export default class ColumnBackButton extends React.PureComponent {
-
-  static contextTypes = {
-    router: PropTypes.object,
-  };
-
-  handleClick = () => {
-    // if history is exhausted, or we would leave mastodon, just go to root.
-    if (window.history && (window.history.length === 1 || window.history.length === window._mastoInitialHistoryLen)) {
-      this.context.router.history.push('/');
-    } else {
-      this.context.router.history.goBack();
-    }
-  }
-
-  render () {
-    return (
-      <button onClick={this.handleClick} className='column-back-button'>
-        <i className='fa fa-fw fa-chevron-left column-back-button__icon' />
-        <FormattedMessage id='column_back_button.label' defaultMessage='Back' />
-      </button>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/column_back_button_slim.js b/app/javascript/themes/glitch/components/column_back_button_slim.js
deleted file mode 100644
index 2cdf1b25b..000000000
--- a/app/javascript/themes/glitch/components/column_back_button_slim.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import React from 'react';
-import { FormattedMessage } from 'react-intl';
-import PropTypes from 'prop-types';
-
-export default class ColumnBackButtonSlim extends React.PureComponent {
-
-  static contextTypes = {
-    router: PropTypes.object,
-  };
-
-  handleClick = () => {
-    // if history is exhausted, or we would leave mastodon, just go to root.
-    if (window.history && (window.history.length === 1 || window.history.length === window._mastoInitialHistoryLen)) {
-      this.context.router.history.push('/');
-    } else {
-      this.context.router.history.goBack();
-    }
-  }
-
-  render () {
-    return (
-      <div className='column-back-button--slim'>
-        <div role='button' tabIndex='0' onClick={this.handleClick} className='column-back-button column-back-button--slim-button'>
-          <i className='fa fa-fw fa-chevron-left column-back-button__icon' />
-          <FormattedMessage id='column_back_button.label' defaultMessage='Back' />
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/column_header.js b/app/javascript/themes/glitch/components/column_header.js
deleted file mode 100644
index e601082c8..000000000
--- a/app/javascript/themes/glitch/components/column_header.js
+++ /dev/null
@@ -1,214 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
-import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-
-// Glitch imports
-import NotificationPurgeButtonsContainer from 'themes/glitch/containers/notification_purge_buttons_container';
-
-const messages = defineMessages({
-  show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
-  hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' },
-  moveLeft: { id: 'column_header.moveLeft_settings', defaultMessage: 'Move column to the left' },
-  moveRight: { id: 'column_header.moveRight_settings', defaultMessage: 'Move column to the right' },
-  enterNotifCleaning : { id: 'notification_purge.start', defaultMessage: 'Enter notification cleaning mode' },
-});
-
-@injectIntl
-export default class ColumnHeader extends React.PureComponent {
-
-  static contextTypes = {
-    router: PropTypes.object,
-  };
-
-  static propTypes = {
-    intl: PropTypes.object.isRequired,
-    title: PropTypes.node.isRequired,
-    icon: PropTypes.string.isRequired,
-    active: PropTypes.bool,
-    localSettings : ImmutablePropTypes.map,
-    multiColumn: PropTypes.bool,
-    focusable: PropTypes.bool,
-    showBackButton: PropTypes.bool,
-    notifCleaning: PropTypes.bool, // true only for the notification column
-    notifCleaningActive: PropTypes.bool,
-    onEnterCleaningMode: PropTypes.func,
-    children: PropTypes.node,
-    pinned: PropTypes.bool,
-    onPin: PropTypes.func,
-    onMove: PropTypes.func,
-    onClick: PropTypes.func,
-    intl: PropTypes.object.isRequired,
-  };
-
-  static defaultProps = {
-    focusable: true,
-  }
-
-  state = {
-    collapsed: true,
-    animating: false,
-    animatingNCD: false,
-  };
-
-  handleToggleClick = (e) => {
-    e.stopPropagation();
-    this.setState({ collapsed: !this.state.collapsed, animating: true });
-  }
-
-  handleTitleClick = () => {
-    this.props.onClick();
-  }
-
-  handleMoveLeft = () => {
-    this.props.onMove(-1);
-  }
-
-  handleMoveRight = () => {
-    this.props.onMove(1);
-  }
-
-  handleBackClick = () => {
-    // if history is exhausted, or we would leave mastodon, just go to root.
-    if (window.history && (window.history.length === 1 || window.history.length === window._mastoInitialHistoryLen)) {
-      this.context.router.history.push('/');
-    } else {
-      this.context.router.history.goBack();
-    }
-  }
-
-  handleTransitionEnd = () => {
-    this.setState({ animating: false });
-  }
-
-  handleTransitionEndNCD = () => {
-    this.setState({ animatingNCD: false });
-  }
-
-  onEnterCleaningMode = () => {
-    this.setState({ animatingNCD: true });
-    this.props.onEnterCleaningMode(!this.props.notifCleaningActive);
-  }
-
-  render () {
-    const { intl, icon, active, children, pinned, onPin, multiColumn, focusable, showBackButton, intl: { formatMessage }, notifCleaning, notifCleaningActive } = this.props;
-    const { collapsed, animating, animatingNCD } = this.state;
-
-    let title = this.props.title;
-
-    const wrapperClassName = classNames('column-header__wrapper', {
-      'active': active,
-    });
-
-    const buttonClassName = classNames('column-header', {
-      'active': active,
-    });
-
-    const collapsibleClassName = classNames('column-header__collapsible', {
-      'collapsed': collapsed,
-      'animating': animating,
-    });
-
-    const collapsibleButtonClassName = classNames('column-header__button', {
-      'active': !collapsed,
-    });
-
-    const notifCleaningButtonClassName = classNames('column-header__button', {
-      'active': notifCleaningActive,
-    });
-
-    const notifCleaningDrawerClassName = classNames('ncd column-header__collapsible', {
-      'collapsed': !notifCleaningActive,
-      'animating': animatingNCD,
-    });
-
-    let extraContent, pinButton, moveButtons, backButton, collapseButton;
-
-    //*glitch
-    const msgEnterNotifCleaning = intl.formatMessage(messages.enterNotifCleaning);
-
-    if (children) {
-      extraContent = (
-        <div key='extra-content' className='column-header__collapsible__extra'>
-          {children}
-        </div>
-      );
-    }
-
-    if (multiColumn && pinned) {
-      pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={onPin}><i className='fa fa fa-times' /> <FormattedMessage id='column_header.unpin' defaultMessage='Unpin' /></button>;
-
-      moveButtons = (
-        <div key='move-buttons' className='column-header__setting-arrows'>
-          <button title={formatMessage(messages.moveLeft)} aria-label={formatMessage(messages.moveLeft)} className='text-btn column-header__setting-btn' onClick={this.handleMoveLeft}><i className='fa fa-chevron-left' /></button>
-          <button title={formatMessage(messages.moveRight)} aria-label={formatMessage(messages.moveRight)} className='text-btn column-header__setting-btn' onClick={this.handleMoveRight}><i className='fa fa-chevron-right' /></button>
-        </div>
-      );
-    } else if (multiColumn) {
-      pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={onPin}><i className='fa fa fa-plus' /> <FormattedMessage id='column_header.pin' defaultMessage='Pin' /></button>;
-    }
-
-    if (!pinned && (multiColumn || showBackButton)) {
-      backButton = (
-        <button onClick={this.handleBackClick} className='column-header__back-button'>
-          <i className='fa fa-fw fa-chevron-left column-back-button__icon' />
-          <FormattedMessage id='column_back_button.label' defaultMessage='Back' />
-        </button>
-      );
-    }
-
-    const collapsedContent = [
-      extraContent,
-    ];
-
-    if (multiColumn) {
-      collapsedContent.push(moveButtons);
-      collapsedContent.push(pinButton);
-    }
-
-    if (children || multiColumn) {
-      collapseButton = <button className={collapsibleButtonClassName} aria-label={formatMessage(collapsed ? messages.show : messages.hide)} aria-pressed={collapsed ? 'false' : 'true'} onClick={this.handleToggleClick}><i className='fa fa-sliders' /></button>;
-    }
-
-    return (
-      <div className={wrapperClassName}>
-        <h1 tabIndex={focusable ? 0 : null} role='button' className={buttonClassName} aria-label={title} onClick={this.handleTitleClick}>
-          <i className={`fa fa-fw fa-${icon} column-header__icon`} />
-          <span className='column-header__title'>
-            {title}
-          </span>
-          <div className='column-header__buttons'>
-            {backButton}
-            { notifCleaning ? (
-              <button
-                aria-label={msgEnterNotifCleaning}
-                title={msgEnterNotifCleaning}
-                onClick={this.onEnterCleaningMode}
-                className={notifCleaningButtonClassName}
-              >
-                <i className='fa fa-eraser' />
-              </button>
-            ) : null}
-            {collapseButton}
-          </div>
-        </h1>
-
-        { notifCleaning ? (
-          <div className={notifCleaningDrawerClassName} onTransitionEnd={this.handleTransitionEndNCD}>
-            <div className='column-header__collapsible-inner nopad-drawer'>
-              {(notifCleaningActive || animatingNCD) ? (<NotificationPurgeButtonsContainer />) : null }
-            </div>
-          </div>
-        ) : null}
-
-        <div className={collapsibleClassName} tabIndex={collapsed ? -1 : null} onTransitionEnd={this.handleTransitionEnd}>
-          <div className='column-header__collapsible-inner'>
-            {(!collapsed || animating) && collapsedContent}
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/display_name.js b/app/javascript/themes/glitch/components/display_name.js
deleted file mode 100644
index 2cf84f8f4..000000000
--- a/app/javascript/themes/glitch/components/display_name.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-
-export default class DisplayName extends React.PureComponent {
-
-  static propTypes = {
-    account: ImmutablePropTypes.map.isRequired,
-  };
-
-  render () {
-    const displayNameHtml = { __html: this.props.account.get('display_name_html') };
-
-    return (
-      <span className='display-name'>
-        <strong className='display-name__html' dangerouslySetInnerHTML={displayNameHtml} /> <span className='display-name__account'>@{this.props.account.get('acct')}</span>
-      </span>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/dropdown_menu.js b/app/javascript/themes/glitch/components/dropdown_menu.js
deleted file mode 100644
index d30dc2aaf..000000000
--- a/app/javascript/themes/glitch/components/dropdown_menu.js
+++ /dev/null
@@ -1,211 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import IconButton from './icon_button';
-import Overlay from 'react-overlays/lib/Overlay';
-import Motion from 'themes/glitch/util/optional_motion';
-import spring from 'react-motion/lib/spring';
-import detectPassiveEvents from 'detect-passive-events';
-
-const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false;
-
-class DropdownMenu extends React.PureComponent {
-
-  static contextTypes = {
-    router: PropTypes.object,
-  };
-
-  static propTypes = {
-    items: PropTypes.array.isRequired,
-    onClose: PropTypes.func.isRequired,
-    style: PropTypes.object,
-    placement: PropTypes.string,
-    arrowOffsetLeft: PropTypes.string,
-    arrowOffsetTop: PropTypes.string,
-  };
-
-  static defaultProps = {
-    style: {},
-    placement: 'bottom',
-  };
-
-  handleDocumentClick = e => {
-    if (this.node && !this.node.contains(e.target)) {
-      this.props.onClose();
-    }
-  }
-
-  componentDidMount () {
-    document.addEventListener('click', this.handleDocumentClick, false);
-    document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
-  }
-
-  componentWillUnmount () {
-    document.removeEventListener('click', this.handleDocumentClick, false);
-    document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
-  }
-
-  setRef = c => {
-    this.node = c;
-  }
-
-  handleClick = e => {
-    const i = Number(e.currentTarget.getAttribute('data-index'));
-    const { action, to } = this.props.items[i];
-
-    this.props.onClose();
-
-    if (typeof action === 'function') {
-      e.preventDefault();
-      action();
-    } else if (to) {
-      e.preventDefault();
-      this.context.router.history.push(to);
-    }
-  }
-
-  renderItem (option, i) {
-    if (option === null) {
-      return <li key={`sep-${i}`} className='dropdown-menu__separator' />;
-    }
-
-    const { text, href = '#' } = option;
-
-    return (
-      <li className='dropdown-menu__item' key={`${text}-${i}`}>
-        <a href={href} target='_blank' rel='noopener' role='button' tabIndex='0' autoFocus={i === 0} onClick={this.handleClick} data-index={i}>
-          {text}
-        </a>
-      </li>
-    );
-  }
-
-  render () {
-    const { items, style, placement, arrowOffsetLeft, arrowOffsetTop } = this.props;
-
-    return (
-      <Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
-        {({ opacity, scaleX, scaleY }) => (
-          <div className='dropdown-menu' style={{ ...style, opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }} ref={this.setRef}>
-            <div className={`dropdown-menu__arrow ${placement}`} style={{ left: arrowOffsetLeft, top: arrowOffsetTop }} />
-
-            <ul>
-              {items.map((option, i) => this.renderItem(option, i))}
-            </ul>
-          </div>
-        )}
-      </Motion>
-    );
-  }
-
-}
-
-export default class Dropdown extends React.PureComponent {
-
-  static contextTypes = {
-    router: PropTypes.object,
-  };
-
-  static propTypes = {
-    icon: PropTypes.string.isRequired,
-    items: PropTypes.array.isRequired,
-    size: PropTypes.number.isRequired,
-    ariaLabel: PropTypes.string,
-    disabled: PropTypes.bool,
-    status: ImmutablePropTypes.map,
-    isUserTouching: PropTypes.func,
-    isModalOpen: PropTypes.bool.isRequired,
-    onModalOpen: PropTypes.func,
-    onModalClose: PropTypes.func,
-  };
-
-  static defaultProps = {
-    ariaLabel: 'Menu',
-  };
-
-  state = {
-    expanded: false,
-  };
-
-  handleClick = () => {
-    if (!this.state.expanded && this.props.isUserTouching() && this.props.onModalOpen) {
-      const { status, items } = this.props;
-
-      this.props.onModalOpen({
-        status,
-        actions: items,
-        onClick: this.handleItemClick,
-      });
-
-      return;
-    }
-
-    this.setState({ expanded: !this.state.expanded });
-  }
-
-  handleClose = () => {
-    if (this.props.onModalClose) {
-      this.props.onModalClose();
-    }
-
-    this.setState({ expanded: false });
-  }
-
-  handleKeyDown = e => {
-    switch(e.key) {
-    case 'Enter':
-      this.handleClick();
-      break;
-    case 'Escape':
-      this.handleClose();
-      break;
-    }
-  }
-
-  handleItemClick = e => {
-    const i = Number(e.currentTarget.getAttribute('data-index'));
-    const { action, to } = this.props.items[i];
-
-    this.handleClose();
-
-    if (typeof action === 'function') {
-      e.preventDefault();
-      action();
-    } else if (to) {
-      e.preventDefault();
-      this.context.router.history.push(to);
-    }
-  }
-
-  setTargetRef = c => {
-    this.target = c;
-  }
-
-  findTarget = () => {
-    return this.target;
-  }
-
-  render () {
-    const { icon, items, size, ariaLabel, disabled } = this.props;
-    const { expanded } = this.state;
-
-    return (
-      <div onKeyDown={this.handleKeyDown}>
-        <IconButton
-          icon={icon}
-          title={ariaLabel}
-          active={expanded}
-          disabled={disabled}
-          size={size}
-          ref={this.setTargetRef}
-          onClick={this.handleClick}
-        />
-
-        <Overlay show={expanded} placement='bottom' target={this.findTarget}>
-          <DropdownMenu items={items} onClose={this.handleClose} />
-        </Overlay>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/extended_video_player.js b/app/javascript/themes/glitch/components/extended_video_player.js
deleted file mode 100644
index f8bd067e8..000000000
--- a/app/javascript/themes/glitch/components/extended_video_player.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-export default class ExtendedVideoPlayer extends React.PureComponent {
-
-  static propTypes = {
-    src: PropTypes.string.isRequired,
-    alt: PropTypes.string,
-    width: PropTypes.number,
-    height: PropTypes.number,
-    time: PropTypes.number,
-    controls: PropTypes.bool.isRequired,
-    muted: PropTypes.bool.isRequired,
-  };
-
-  handleLoadedData = () => {
-    if (this.props.time) {
-      this.video.currentTime = this.props.time;
-    }
-  }
-
-  componentDidMount () {
-    this.video.addEventListener('loadeddata', this.handleLoadedData);
-  }
-
-  componentWillUnmount () {
-    this.video.removeEventListener('loadeddata', this.handleLoadedData);
-  }
-
-  setRef = (c) => {
-    this.video = c;
-  }
-
-  render () {
-    const { src, muted, controls, alt } = this.props;
-
-    return (
-      <div className='extended-video-player'>
-        <video
-          ref={this.setRef}
-          src={src}
-          autoPlay
-          role='button'
-          tabIndex='0'
-          aria-label={alt}
-          muted={muted}
-          controls={controls}
-          loop={!controls}
-        />
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/icon_button.js b/app/javascript/themes/glitch/components/icon_button.js
deleted file mode 100644
index 31cdf4703..000000000
--- a/app/javascript/themes/glitch/components/icon_button.js
+++ /dev/null
@@ -1,137 +0,0 @@
-import React from 'react';
-import Motion from 'themes/glitch/util/optional_motion';
-import spring from 'react-motion/lib/spring';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
-
-export default class IconButton extends React.PureComponent {
-
-  static propTypes = {
-    className: PropTypes.string,
-    title: PropTypes.string.isRequired,
-    icon: PropTypes.string.isRequired,
-    onClick: PropTypes.func,
-    size: PropTypes.number,
-    active: PropTypes.bool,
-    pressed: PropTypes.bool,
-    expanded: PropTypes.bool,
-    style: PropTypes.object,
-    activeStyle: PropTypes.object,
-    disabled: PropTypes.bool,
-    inverted: PropTypes.bool,
-    animate: PropTypes.bool,
-    flip: PropTypes.bool,
-    overlay: PropTypes.bool,
-    tabIndex: PropTypes.string,
-    label: PropTypes.string,
-  };
-
-  static defaultProps = {
-    size: 18,
-    active: false,
-    disabled: false,
-    animate: false,
-    overlay: false,
-    tabIndex: '0',
-  };
-
-  handleClick = (e) =>  {
-    e.preventDefault();
-
-    if (!this.props.disabled) {
-      this.props.onClick(e);
-    }
-  }
-
-  render () {
-    let style = {
-      fontSize: `${this.props.size}px`,
-      height: `${this.props.size * 1.28571429}px`,
-      lineHeight: `${this.props.size}px`,
-      ...this.props.style,
-      ...(this.props.active ? this.props.activeStyle : {}),
-    };
-    if (!this.props.label) {
-      style.width = `${this.props.size * 1.28571429}px`;
-    } else {
-      style.textAlign = 'left';
-    }
-
-    const {
-      active,
-      animate,
-      className,
-      disabled,
-      expanded,
-      icon,
-      inverted,
-      flip,
-      overlay,
-      pressed,
-      tabIndex,
-      title,
-    } = this.props;
-
-    const classes = classNames(className, 'icon-button', {
-      active,
-      disabled,
-      inverted,
-      overlayed: overlay,
-    });
-
-    const flipDeg = flip ? -180 : -360;
-    const rotateDeg = active ? flipDeg : 0;
-
-    const motionDefaultStyle = {
-      rotate: rotateDeg,
-    };
-
-    const springOpts = {
-      stiffness: this.props.flip ? 60 : 120,
-      damping: 7,
-    };
-    const motionStyle = {
-      rotate: animate ? spring(rotateDeg, springOpts) : 0,
-    };
-
-    if (!animate) {
-      // Perf optimization: avoid unnecessary <Motion> components unless
-      // we actually need to animate.
-      return (
-        <button
-          aria-label={title}
-          aria-pressed={pressed}
-          aria-expanded={expanded}
-          title={title}
-          className={classes}
-          onClick={this.handleClick}
-          style={style}
-          tabIndex={tabIndex}
-        >
-          <i className={`fa fa-fw fa-${icon}`} aria-hidden='true' />
-        </button>
-      );
-    }
-
-    return (
-      <Motion defaultStyle={motionDefaultStyle} style={motionStyle}>
-        {({ rotate }) =>
-          <button
-            aria-label={title}
-            aria-pressed={pressed}
-            aria-expanded={expanded}
-            title={title}
-            className={classes}
-            onClick={this.handleClick}
-            style={style}
-            tabIndex={tabIndex}
-          >
-            <i style={{ transform: `rotate(${rotate}deg)` }} className={`fa fa-fw fa-${icon}`} aria-hidden='true' />
-            {this.props.label}
-          </button>
-        }
-      </Motion>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/intersection_observer_article.js b/app/javascript/themes/glitch/components/intersection_observer_article.js
deleted file mode 100644
index f0139ac75..000000000
--- a/app/javascript/themes/glitch/components/intersection_observer_article.js
+++ /dev/null
@@ -1,130 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import scheduleIdleTask from 'themes/glitch/util/schedule_idle_task';
-import getRectFromEntry from 'themes/glitch/util/get_rect_from_entry';
-import { is } from 'immutable';
-
-// Diff these props in the "rendered" state
-const updateOnPropsForRendered = ['id', 'index', 'listLength'];
-// Diff these props in the "unrendered" state
-const updateOnPropsForUnrendered = ['id', 'index', 'listLength', 'cachedHeight'];
-
-export default class IntersectionObserverArticle extends React.Component {
-
-  static propTypes = {
-    intersectionObserverWrapper: PropTypes.object.isRequired,
-    id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
-    index: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
-    listLength: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
-    saveHeightKey: PropTypes.string,
-    cachedHeight: PropTypes.number,
-    onHeightChange: PropTypes.func,
-    children: PropTypes.node,
-  };
-
-  state = {
-    isHidden: false, // set to true in requestIdleCallback to trigger un-render
-  }
-
-  shouldComponentUpdate (nextProps, nextState) {
-    const isUnrendered = !this.state.isIntersecting && (this.state.isHidden || this.props.cachedHeight);
-    const willBeUnrendered = !nextState.isIntersecting && (nextState.isHidden || nextProps.cachedHeight);
-    if (!!isUnrendered !== !!willBeUnrendered) {
-      // If we're going from rendered to unrendered (or vice versa) then update
-      return true;
-    }
-    // Otherwise, diff based on props
-    const propsToDiff = isUnrendered ? updateOnPropsForUnrendered : updateOnPropsForRendered;
-    return !propsToDiff.every(prop => is(nextProps[prop], this.props[prop]));
-  }
-
-  componentDidMount () {
-    const { intersectionObserverWrapper, id } = this.props;
-
-    intersectionObserverWrapper.observe(
-      id,
-      this.node,
-      this.handleIntersection
-    );
-
-    this.componentMounted = true;
-  }
-
-  componentWillUnmount () {
-    const { intersectionObserverWrapper, id } = this.props;
-    intersectionObserverWrapper.unobserve(id, this.node);
-
-    this.componentMounted = false;
-  }
-
-  handleIntersection = (entry) => {
-    this.entry = entry;
-
-    scheduleIdleTask(this.calculateHeight);
-    this.setState(this.updateStateAfterIntersection);
-  }
-
-  updateStateAfterIntersection = (prevState) => {
-    if (prevState.isIntersecting && !this.entry.isIntersecting) {
-      scheduleIdleTask(this.hideIfNotIntersecting);
-    }
-    return {
-      isIntersecting: this.entry.isIntersecting,
-      isHidden: false,
-    };
-  }
-
-  calculateHeight = () => {
-    const { onHeightChange, saveHeightKey, id } = this.props;
-    // save the height of the fully-rendered element (this is expensive
-    // on Chrome, where we need to fall back to getBoundingClientRect)
-    this.height = getRectFromEntry(this.entry).height;
-
-    if (onHeightChange && saveHeightKey) {
-      onHeightChange(saveHeightKey, id, this.height);
-    }
-  }
-
-  hideIfNotIntersecting = () => {
-    if (!this.componentMounted) {
-      return;
-    }
-
-    // When the browser gets a chance, test if we're still not intersecting,
-    // and if so, set our isHidden to true to trigger an unrender. The point of
-    // this is to save DOM nodes and avoid using up too much memory.
-    // See: https://github.com/tootsuite/mastodon/issues/2900
-    this.setState((prevState) => ({ isHidden: !prevState.isIntersecting }));
-  }
-
-  handleRef = (node) => {
-    this.node = node;
-  }
-
-  render () {
-    const { children, id, index, listLength, cachedHeight } = this.props;
-    const { isIntersecting, isHidden } = this.state;
-
-    if (!isIntersecting && (isHidden || cachedHeight)) {
-      return (
-        <article
-          ref={this.handleRef}
-          aria-posinset={index}
-          aria-setsize={listLength}
-          style={{ height: `${this.height || cachedHeight}px`, opacity: 0, overflow: 'hidden' }}
-          data-id={id}
-          tabIndex='0'
-        >
-          {children && React.cloneElement(children, { hidden: true })}
-        </article>
-      );
-    }
-
-    return (
-      <article ref={this.handleRef} aria-posinset={index} aria-setsize={listLength} data-id={id} tabIndex='0'>
-        {children && React.cloneElement(children, { hidden: false })}
-      </article>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/load_more.js b/app/javascript/themes/glitch/components/load_more.js
deleted file mode 100644
index c4c8c94a2..000000000
--- a/app/javascript/themes/glitch/components/load_more.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import React from 'react';
-import { FormattedMessage } from 'react-intl';
-import PropTypes from 'prop-types';
-
-export default class LoadMore extends React.PureComponent {
-
-  static propTypes = {
-    onClick: PropTypes.func,
-    visible: PropTypes.bool,
-  }
-
-  static defaultProps = {
-    visible: true,
-  }
-
-  render() {
-    const { visible } = this.props;
-
-    return (
-      <button className='load-more' disabled={!visible} style={{ visibility: visible ? 'visible' : 'hidden' }} onClick={this.props.onClick}>
-        <FormattedMessage id='status.load_more' defaultMessage='Load more' />
-      </button>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/loading_indicator.js b/app/javascript/themes/glitch/components/loading_indicator.js
deleted file mode 100644
index d6a5adb6f..000000000
--- a/app/javascript/themes/glitch/components/loading_indicator.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import React from 'react';
-import { FormattedMessage } from 'react-intl';
-
-const LoadingIndicator = () => (
-  <div className='loading-indicator'>
-    <div className='loading-indicator__figure' />
-    <FormattedMessage id='loading_indicator.label' defaultMessage='Loading...' />
-  </div>
-);
-
-export default LoadingIndicator;
diff --git a/app/javascript/themes/glitch/components/media_gallery.js b/app/javascript/themes/glitch/components/media_gallery.js
deleted file mode 100644
index b6b40c585..000000000
--- a/app/javascript/themes/glitch/components/media_gallery.js
+++ /dev/null
@@ -1,255 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import { is } from 'immutable';
-import IconButton from './icon_button';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import { isIOS } from 'themes/glitch/util/is_mobile';
-import classNames from 'classnames';
-import { autoPlayGif } from 'themes/glitch/util/initial_state';
-
-const messages = defineMessages({
-  toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Toggle visibility' },
-});
-
-class Item extends React.PureComponent {
-
-  static contextTypes = {
-    router: PropTypes.object,
-  };
-
-  static propTypes = {
-    attachment: ImmutablePropTypes.map.isRequired,
-    standalone: PropTypes.bool,
-    index: PropTypes.number.isRequired,
-    size: PropTypes.number.isRequired,
-    letterbox: PropTypes.bool,
-    onClick: PropTypes.func.isRequired,
-  };
-
-  static defaultProps = {
-    standalone: false,
-    index: 0,
-    size: 1,
-  };
-
-  handleMouseEnter = (e) => {
-    if (this.hoverToPlay()) {
-      e.target.play();
-    }
-  }
-
-  handleMouseLeave = (e) => {
-    if (this.hoverToPlay()) {
-      e.target.pause();
-      e.target.currentTime = 0;
-    }
-  }
-
-  hoverToPlay () {
-    const { attachment } = this.props;
-    return !autoPlayGif && attachment.get('type') === 'gifv';
-  }
-
-  handleClick = (e) => {
-    const { index, onClick } = this.props;
-
-    if (this.context.router && e.button === 0) {
-      e.preventDefault();
-      onClick(index);
-    }
-
-    e.stopPropagation();
-  }
-
-  render () {
-    const { attachment, index, size, standalone, letterbox } = this.props;
-
-    let width  = 50;
-    let height = 100;
-    let top    = 'auto';
-    let left   = 'auto';
-    let bottom = 'auto';
-    let right  = 'auto';
-
-    if (size === 1) {
-      width = 100;
-    }
-
-    if (size === 4 || (size === 3 && index > 0)) {
-      height = 50;
-    }
-
-    if (size === 2) {
-      if (index === 0) {
-        right = '2px';
-      } else {
-        left = '2px';
-      }
-    } else if (size === 3) {
-      if (index === 0) {
-        right = '2px';
-      } else if (index > 0) {
-        left = '2px';
-      }
-
-      if (index === 1) {
-        bottom = '2px';
-      } else if (index > 1) {
-        top = '2px';
-      }
-    } else if (size === 4) {
-      if (index === 0 || index === 2) {
-        right = '2px';
-      }
-
-      if (index === 1 || index === 3) {
-        left = '2px';
-      }
-
-      if (index < 2) {
-        bottom = '2px';
-      } else {
-        top = '2px';
-      }
-    }
-
-    let thumbnail = '';
-
-    if (attachment.get('type') === 'image') {
-      const previewUrl = attachment.get('preview_url');
-      const previewWidth = attachment.getIn(['meta', 'small', 'width']);
-
-      const originalUrl = attachment.get('url');
-      const originalWidth = attachment.getIn(['meta', 'original', 'width']);
-
-      const hasSize = typeof originalWidth === 'number' && typeof previewWidth === 'number';
-
-      const srcSet = hasSize ? `${originalUrl} ${originalWidth}w, ${previewUrl} ${previewWidth}w` : null;
-      const sizes = hasSize ? `(min-width: 1025px) ${320 * (width / 100)}px, ${width}vw` : null;
-
-      thumbnail = (
-        <a
-          className='media-gallery__item-thumbnail'
-          href={attachment.get('remote_url') || originalUrl}
-          onClick={this.handleClick}
-          target='_blank'
-        >
-          <img className={letterbox ? 'letterbox' : null} src={previewUrl} srcSet={srcSet} sizes={sizes} alt={attachment.get('description')} title={attachment.get('description')} />
-        </a>
-      );
-    } else if (attachment.get('type') === 'gifv') {
-      const autoPlay = !isIOS() && autoPlayGif;
-
-      thumbnail = (
-        <div className={classNames('media-gallery__gifv', { autoplay: autoPlay })}>
-          <video
-            className={`media-gallery__item-gifv-thumbnail${letterbox ? ' letterbox' : ''}`}
-            aria-label={attachment.get('description')}
-            role='application'
-            src={attachment.get('url')}
-            onClick={this.handleClick}
-            onMouseEnter={this.handleMouseEnter}
-            onMouseLeave={this.handleMouseLeave}
-            autoPlay={autoPlay}
-            loop
-            muted
-          />
-
-          <span className='media-gallery__gifv__label'>GIF</span>
-        </div>
-      );
-    }
-
-    return (
-      <div className={classNames('media-gallery__item', { standalone })} key={attachment.get('id')} style={{ left: left, top: top, right: right, bottom: bottom, width: `${width}%`, height: `${height}%` }}>
-        {thumbnail}
-      </div>
-    );
-  }
-
-}
-
-@injectIntl
-export default class MediaGallery extends React.PureComponent {
-
-  static propTypes = {
-    sensitive: PropTypes.bool,
-    standalone: PropTypes.bool,
-    letterbox: PropTypes.bool,
-    fullwidth: PropTypes.bool,
-    media: ImmutablePropTypes.list.isRequired,
-    size: PropTypes.object,
-    onOpenMedia: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  static defaultProps = {
-    standalone: false,
-  };
-
-  state = {
-    visible: !this.props.sensitive,
-  };
-
-  componentWillReceiveProps (nextProps) {
-    if (!is(nextProps.media, this.props.media)) {
-      this.setState({ visible: !nextProps.sensitive });
-    }
-  }
-
-  handleOpen = () => {
-    this.setState({ visible: !this.state.visible });
-  }
-
-  handleClick = (index) => {
-    this.props.onOpenMedia(this.props.media, index);
-  }
-
-  isStandaloneEligible() {
-    const { media, standalone } = this.props;
-    return standalone && media.size === 1 && media.getIn([0, 'meta', 'small', 'aspect']);
-  }
-
-  render () {
-    const { media, intl, sensitive, letterbox, fullwidth } = this.props;
-    const { visible } = this.state;
-    const size = media.take(4).size;
-
-    let children;
-
-    if (!visible) {
-      let warning;
-
-      if (sensitive) {
-        warning = <FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' />;
-      } else {
-        warning = <FormattedMessage id='status.media_hidden' defaultMessage='Media hidden' />;
-      }
-
-      children = (
-        <button className='media-spoiler' onClick={this.handleOpen}>
-          <span className='media-spoiler__warning'>{warning}</span>
-          <span className='media-spoiler__trigger'><FormattedMessage id='status.sensitive_toggle' defaultMessage='Click to view' /></span>
-        </button>
-      );
-    } else {
-      if (this.isStandaloneEligible()) {
-        children = <Item standalone onClick={this.handleClick} attachment={media.get(0)} />;
-      } else {
-        children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} onClick={this.handleClick} attachment={attachment} index={i} size={size} letterbox={letterbox} />);
-      }
-    }
-
-    return (
-      <div className={`media-gallery size-${size} ${fullwidth ? 'full-width' : ''}`}>
-        <div className={classNames('spoiler-button', { 'spoiler-button--visible': visible })}>
-          <IconButton title={intl.formatMessage(messages.toggle_visible)} icon={visible ? 'eye' : 'eye-slash'} overlay onClick={this.handleOpen} />
-        </div>
-
-        {children}
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/missing_indicator.js b/app/javascript/themes/glitch/components/missing_indicator.js
deleted file mode 100644
index 87df7f61c..000000000
--- a/app/javascript/themes/glitch/components/missing_indicator.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import React from 'react';
-import { FormattedMessage } from 'react-intl';
-
-const MissingIndicator = () => (
-  <div className='missing-indicator'>
-    <div>
-      <FormattedMessage id='missing_indicator.label' defaultMessage='Not found' />
-    </div>
-  </div>
-);
-
-export default MissingIndicator;
diff --git a/app/javascript/themes/glitch/components/notification_purge_buttons.js b/app/javascript/themes/glitch/components/notification_purge_buttons.js
deleted file mode 100644
index e0c1543b0..000000000
--- a/app/javascript/themes/glitch/components/notification_purge_buttons.js
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * Buttons widget for controlling the notification clearing mode.
- * In idle state, the cleaning mode button is shown. When the mode is active,
- * a Confirm and Abort buttons are shown in its place.
- */
-
-
-//  Package imports  //
-import React from 'react';
-import PropTypes from 'prop-types';
-import { defineMessages, injectIntl } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const messages = defineMessages({
-  btnAll : { id: 'notification_purge.btn_all', defaultMessage: 'Select\nall' },
-  btnNone : { id: 'notification_purge.btn_none', defaultMessage: 'Select\nnone' },
-  btnInvert : { id: 'notification_purge.btn_invert', defaultMessage: 'Invert\nselection' },
-  btnApply : { id: 'notification_purge.btn_apply', defaultMessage: 'Clear\nselected' },
-});
-
-@injectIntl
-export default class NotificationPurgeButtons extends ImmutablePureComponent {
-
-  static propTypes = {
-    onDeleteMarked : PropTypes.func.isRequired,
-    onMarkAll : PropTypes.func.isRequired,
-    onMarkNone : PropTypes.func.isRequired,
-    onInvert : PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-    markNewForDelete: PropTypes.bool,
-  };
-
-  render () {
-    const { intl, markNewForDelete } = this.props;
-
-    //className='active'
-    return (
-      <div className='column-header__notif-cleaning-buttons'>
-        <button onClick={this.props.onMarkAll} className={markNewForDelete ? 'active' : ''}>
-          <b>∀</b><br />{intl.formatMessage(messages.btnAll)}
-        </button>
-
-        <button onClick={this.props.onMarkNone} className={!markNewForDelete ? 'active' : ''}>
-          <b>∅</b><br />{intl.formatMessage(messages.btnNone)}
-        </button>
-
-        <button onClick={this.props.onInvert}>
-          <b>¬</b><br />{intl.formatMessage(messages.btnInvert)}
-        </button>
-
-        <button onClick={this.props.onDeleteMarked}>
-          <i className='fa fa-trash' /><br />{intl.formatMessage(messages.btnApply)}
-        </button>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/permalink.js b/app/javascript/themes/glitch/components/permalink.js
deleted file mode 100644
index d726d37a2..000000000
--- a/app/javascript/themes/glitch/components/permalink.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-export default class Permalink extends React.PureComponent {
-
-  static contextTypes = {
-    router: PropTypes.object,
-  };
-
-  static propTypes = {
-    className: PropTypes.string,
-    href: PropTypes.string.isRequired,
-    to: PropTypes.string.isRequired,
-    children: PropTypes.node,
-  };
-
-  handleClick = (e) => {
-    if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
-      e.preventDefault();
-      this.context.router.history.push(this.props.to);
-    }
-  }
-
-  render () {
-    const { href, children, className, ...other } = this.props;
-
-    return (
-      <a target='_blank' href={href} onClick={this.handleClick} {...other} className={`permalink${className ? ' ' + className : ''}`}>
-        {children}
-      </a>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/relative_timestamp.js b/app/javascript/themes/glitch/components/relative_timestamp.js
deleted file mode 100644
index 51588e78c..000000000
--- a/app/javascript/themes/glitch/components/relative_timestamp.js
+++ /dev/null
@@ -1,147 +0,0 @@
-import React from 'react';
-import { injectIntl, defineMessages } from 'react-intl';
-import PropTypes from 'prop-types';
-
-const messages = defineMessages({
-  just_now: { id: 'relative_time.just_now', defaultMessage: 'now' },
-  seconds: { id: 'relative_time.seconds', defaultMessage: '{number}s' },
-  minutes: { id: 'relative_time.minutes', defaultMessage: '{number}m' },
-  hours: { id: 'relative_time.hours', defaultMessage: '{number}h' },
-  days: { id: 'relative_time.days', defaultMessage: '{number}d' },
-});
-
-const dateFormatOptions = {
-  hour12: false,
-  year: 'numeric',
-  month: 'short',
-  day: '2-digit',
-  hour: '2-digit',
-  minute: '2-digit',
-};
-
-const shortDateFormatOptions = {
-  month: 'numeric',
-  day: 'numeric',
-};
-
-const SECOND = 1000;
-const MINUTE = 1000 * 60;
-const HOUR   = 1000 * 60 * 60;
-const DAY    = 1000 * 60 * 60 * 24;
-
-const MAX_DELAY = 2147483647;
-
-const selectUnits = delta => {
-  const absDelta = Math.abs(delta);
-
-  if (absDelta < MINUTE) {
-    return 'second';
-  } else if (absDelta < HOUR) {
-    return 'minute';
-  } else if (absDelta < DAY) {
-    return 'hour';
-  }
-
-  return 'day';
-};
-
-const getUnitDelay = units => {
-  switch (units) {
-  case 'second':
-    return SECOND;
-  case 'minute':
-    return MINUTE;
-  case 'hour':
-    return HOUR;
-  case 'day':
-    return DAY;
-  default:
-    return MAX_DELAY;
-  }
-};
-
-@injectIntl
-export default class RelativeTimestamp extends React.Component {
-
-  static propTypes = {
-    intl: PropTypes.object.isRequired,
-    timestamp: PropTypes.string.isRequired,
-  };
-
-  state = {
-    now: this.props.intl.now(),
-  };
-
-  shouldComponentUpdate (nextProps, nextState) {
-    // As of right now the locale doesn't change without a new page load,
-    // but we might as well check in case that ever changes.
-    return this.props.timestamp !== nextProps.timestamp ||
-      this.props.intl.locale !== nextProps.intl.locale ||
-      this.state.now !== nextState.now;
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (this.props.timestamp !== nextProps.timestamp) {
-      this.setState({ now: this.props.intl.now() });
-    }
-  }
-
-  componentDidMount () {
-    this._scheduleNextUpdate(this.props, this.state);
-  }
-
-  componentWillUpdate (nextProps, nextState) {
-    this._scheduleNextUpdate(nextProps, nextState);
-  }
-
-  componentWillUnmount () {
-    clearTimeout(this._timer);
-  }
-
-  _scheduleNextUpdate (props, state) {
-    clearTimeout(this._timer);
-
-    const { timestamp }  = props;
-    const delta          = (new Date(timestamp)).getTime() - state.now;
-    const unitDelay      = getUnitDelay(selectUnits(delta));
-    const unitRemainder  = Math.abs(delta % unitDelay);
-    const updateInterval = 1000 * 10;
-    const delay          = delta < 0 ? Math.max(updateInterval, unitDelay - unitRemainder) : Math.max(updateInterval, unitRemainder);
-
-    this._timer = setTimeout(() => {
-      this.setState({ now: this.props.intl.now() });
-    }, delay);
-  }
-
-  render () {
-    const { timestamp, intl } = this.props;
-
-    const date  = new Date(timestamp);
-    const delta = this.state.now - date.getTime();
-
-    let relativeTime;
-
-    if (delta < 10 * SECOND) {
-      relativeTime = intl.formatMessage(messages.just_now);
-    } else if (delta < 3 * DAY) {
-      if (delta < MINUTE) {
-        relativeTime = intl.formatMessage(messages.seconds, { number: Math.floor(delta / SECOND) });
-      } else if (delta < HOUR) {
-        relativeTime = intl.formatMessage(messages.minutes, { number: Math.floor(delta / MINUTE) });
-      } else if (delta < DAY) {
-        relativeTime = intl.formatMessage(messages.hours, { number: Math.floor(delta / HOUR) });
-      } else {
-        relativeTime = intl.formatMessage(messages.days, { number: Math.floor(delta / DAY) });
-      }
-    } else {
-      relativeTime = intl.formatDate(date, shortDateFormatOptions);
-    }
-
-    return (
-      <time dateTime={timestamp} title={intl.formatDate(date, dateFormatOptions)}>
-        {relativeTime}
-      </time>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/scrollable_list.js b/app/javascript/themes/glitch/components/scrollable_list.js
deleted file mode 100644
index ccdcd7c85..000000000
--- a/app/javascript/themes/glitch/components/scrollable_list.js
+++ /dev/null
@@ -1,198 +0,0 @@
-import React, { PureComponent } from 'react';
-import { ScrollContainer } from 'react-router-scroll-4';
-import PropTypes from 'prop-types';
-import IntersectionObserverArticleContainer from 'themes/glitch/containers/intersection_observer_article_container';
-import LoadMore from './load_more';
-import IntersectionObserverWrapper from 'themes/glitch/util/intersection_observer_wrapper';
-import { throttle } from 'lodash';
-import { List as ImmutableList } from 'immutable';
-import classNames from 'classnames';
-import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from 'themes/glitch/util/fullscreen';
-
-export default class ScrollableList extends PureComponent {
-
-  static contextTypes = {
-    router: PropTypes.object,
-  };
-
-  static propTypes = {
-    scrollKey: PropTypes.string.isRequired,
-    onScrollToBottom: PropTypes.func,
-    onScrollToTop: PropTypes.func,
-    onScroll: PropTypes.func,
-    trackScroll: PropTypes.bool,
-    shouldUpdateScroll: PropTypes.func,
-    isLoading: PropTypes.bool,
-    hasMore: PropTypes.bool,
-    prepend: PropTypes.node,
-    emptyMessage: PropTypes.node,
-    children: PropTypes.node,
-  };
-
-  static defaultProps = {
-    trackScroll: true,
-  };
-
-  state = {
-    lastMouseMove: null,
-  };
-
-  intersectionObserverWrapper = new IntersectionObserverWrapper();
-
-  handleScroll = throttle(() => {
-    if (this.node) {
-      const { scrollTop, scrollHeight, clientHeight } = this.node;
-      const offset = scrollHeight - scrollTop - clientHeight;
-      this._oldScrollPosition = scrollHeight - scrollTop;
-
-      if (400 > offset && this.props.onScrollToBottom && !this.props.isLoading) {
-        this.props.onScrollToBottom();
-      } else if (scrollTop < 100 && this.props.onScrollToTop) {
-        this.props.onScrollToTop();
-      } else if (this.props.onScroll) {
-        this.props.onScroll();
-      }
-    }
-  }, 150, {
-    trailing: true,
-  });
-
-  handleMouseMove = throttle(() => {
-    this._lastMouseMove = new Date();
-  }, 300);
-
-  handleMouseLeave = () => {
-    this._lastMouseMove = null;
-  }
-
-  componentDidMount () {
-    this.attachScrollListener();
-    this.attachIntersectionObserver();
-    attachFullscreenListener(this.onFullScreenChange);
-
-    // Handle initial scroll posiiton
-    this.handleScroll();
-  }
-
-  componentDidUpdate (prevProps) {
-    const someItemInserted = React.Children.count(prevProps.children) > 0 &&
-      React.Children.count(prevProps.children) < React.Children.count(this.props.children) &&
-      this.getFirstChildKey(prevProps) !== this.getFirstChildKey(this.props);
-
-    // Reset the scroll position when a new child comes in in order not to
-    // jerk the scrollbar around if you're already scrolled down the page.
-    if (someItemInserted && this._oldScrollPosition && this.node.scrollTop > 0) {
-      const newScrollTop = this.node.scrollHeight - this._oldScrollPosition;
-
-      if (this.node.scrollTop !== newScrollTop) {
-        this.node.scrollTop = newScrollTop;
-      }
-    } else {
-      this._oldScrollPosition = this.node.scrollHeight - this.node.scrollTop;
-    }
-  }
-
-  componentWillUnmount () {
-    this.detachScrollListener();
-    this.detachIntersectionObserver();
-    detachFullscreenListener(this.onFullScreenChange);
-  }
-
-  onFullScreenChange = () => {
-    this.setState({ fullscreen: isFullscreen() });
-  }
-
-  attachIntersectionObserver () {
-    this.intersectionObserverWrapper.connect({
-      root: this.node,
-      rootMargin: '300% 0px',
-    });
-  }
-
-  detachIntersectionObserver () {
-    this.intersectionObserverWrapper.disconnect();
-  }
-
-  attachScrollListener () {
-    this.node.addEventListener('scroll', this.handleScroll);
-  }
-
-  detachScrollListener () {
-    this.node.removeEventListener('scroll', this.handleScroll);
-  }
-
-  getFirstChildKey (props) {
-    const { children } = props;
-    let firstChild = children;
-    if (children instanceof ImmutableList) {
-      firstChild = children.get(0);
-    } else if (Array.isArray(children)) {
-      firstChild = children[0];
-    }
-    return firstChild && firstChild.key;
-  }
-
-  setRef = (c) => {
-    this.node = c;
-  }
-
-  handleLoadMore = (e) => {
-    e.preventDefault();
-    this.props.onScrollToBottom();
-  }
-
-  _recentlyMoved () {
-    return this._lastMouseMove !== null && ((new Date()) - this._lastMouseMove < 600);
-  }
-
-  render () {
-    const { children, scrollKey, trackScroll, shouldUpdateScroll, isLoading, hasMore, prepend, emptyMessage } = this.props;
-    const { fullscreen } = this.state;
-    const childrenCount = React.Children.count(children);
-
-    const loadMore     = (hasMore && childrenCount > 0) ? <LoadMore visible={!isLoading} onClick={this.handleLoadMore} /> : null;
-    let scrollableArea = null;
-
-    if (isLoading || childrenCount > 0 || !emptyMessage) {
-      scrollableArea = (
-        <div className={classNames('scrollable', { fullscreen })} ref={this.setRef} onMouseMove={this.handleMouseMove} onMouseLeave={this.handleMouseLeave}>
-          <div role='feed' className='item-list'>
-            {prepend}
-
-            {React.Children.map(this.props.children, (child, index) => (
-              <IntersectionObserverArticleContainer
-                key={child.key}
-                id={child.key}
-                index={index}
-                listLength={childrenCount}
-                intersectionObserverWrapper={this.intersectionObserverWrapper}
-                saveHeightKey={trackScroll ? `${this.context.router.route.location.key}:${scrollKey}` : null}
-              >
-                {child}
-              </IntersectionObserverArticleContainer>
-            ))}
-
-            {loadMore}
-          </div>
-        </div>
-      );
-    } else {
-      scrollableArea = (
-        <div className='empty-column-indicator' ref={this.setRef}>
-          {emptyMessage}
-        </div>
-      );
-    }
-
-    if (trackScroll) {
-      return (
-        <ScrollContainer scrollKey={scrollKey} shouldUpdateScroll={shouldUpdateScroll}>
-          {scrollableArea}
-        </ScrollContainer>
-      );
-    } else {
-      return scrollableArea;
-    }
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/setting_text.js b/app/javascript/themes/glitch/components/setting_text.js
deleted file mode 100644
index a6dde4c0f..000000000
--- a/app/javascript/themes/glitch/components/setting_text.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-
-export default class SettingText extends React.PureComponent {
-
-  static propTypes = {
-    settings: ImmutablePropTypes.map.isRequired,
-    settingKey: PropTypes.array.isRequired,
-    label: PropTypes.string.isRequired,
-    onChange: PropTypes.func.isRequired,
-  };
-
-  handleChange = (e) => {
-    this.props.onChange(this.props.settingKey, e.target.value);
-  }
-
-  render () {
-    const { settings, settingKey, label } = this.props;
-
-    return (
-      <label>
-        <span style={{ display: 'none' }}>{label}</span>
-        <input
-          className='setting-text'
-          value={settings.getIn(settingKey)}
-          onChange={this.handleChange}
-          placeholder={label}
-        />
-      </label>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/status.js b/app/javascript/themes/glitch/components/status.js
deleted file mode 100644
index 9288bcafa..000000000
--- a/app/javascript/themes/glitch/components/status.js
+++ /dev/null
@@ -1,440 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import StatusPrepend from './status_prepend';
-import StatusHeader from './status_header';
-import StatusContent from './status_content';
-import StatusActionBar from './status_action_bar';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { MediaGallery, Video } from 'themes/glitch/util/async-components';
-import { HotKeys } from 'react-hotkeys';
-import NotificationOverlayContainer from 'themes/glitch/features/notifications/containers/overlay_container';
-import classNames from 'classnames';
-
-// We use the component (and not the container) since we do not want
-// to use the progress bar to show download progress
-import Bundle from '../features/ui/components/bundle';
-
-export default class Status extends ImmutablePureComponent {
-
-  static contextTypes = {
-    router: PropTypes.object,
-  };
-
-  static propTypes = {
-    containerId: PropTypes.string,
-    id: PropTypes.string,
-    status: ImmutablePropTypes.map,
-    account: ImmutablePropTypes.map,
-    onReply: PropTypes.func,
-    onFavourite: PropTypes.func,
-    onReblog: PropTypes.func,
-    onDelete: PropTypes.func,
-    onPin: PropTypes.func,
-    onOpenMedia: PropTypes.func,
-    onOpenVideo: PropTypes.func,
-    onBlock: PropTypes.func,
-    onEmbed: PropTypes.func,
-    onHeightChange: PropTypes.func,
-    muted: PropTypes.bool,
-    collapse: PropTypes.bool,
-    hidden: PropTypes.bool,
-    prepend: PropTypes.string,
-    withDismiss: PropTypes.bool,
-    onMoveUp: PropTypes.func,
-    onMoveDown: PropTypes.func,
-  };
-
-  state = {
-    isExpanded: null,
-    markedForDelete: false,
-  }
-
-  // Avoid checking props that are functions (and whose equality will always
-  // evaluate to false. See react-immutable-pure-component for usage.
-  updateOnProps = [
-    'status',
-    'account',
-    'settings',
-    'prepend',
-    'boostModal',
-    'muted',
-    'collapse',
-    'notification',
-    'hidden',
-  ]
-
-  updateOnStates = [
-    'isExpanded',
-    'markedForDelete',
-  ]
-
-  //  If our settings have changed to disable collapsed statuses, then we
-  //  need to make sure that we uncollapse every one. We do that by watching
-  //  for changes to `settings.collapsed.enabled` in
-  //  `componentWillReceiveProps()`.
-
-  //  We also need to watch for changes on the `collapse` prop---if this
-  //  changes to anything other than `undefined`, then we need to collapse or
-  //  uncollapse our status accordingly.
-  componentWillReceiveProps (nextProps) {
-    if (!nextProps.settings.getIn(['collapsed', 'enabled'])) {
-      if (this.state.isExpanded === false) {
-        this.setExpansion(null);
-      }
-    } else if (
-      nextProps.collapse !== this.props.collapse &&
-      nextProps.collapse !== undefined
-    ) this.setExpansion(nextProps.collapse ? false : null);
-  }
-
-  //  When mounting, we just check to see if our status should be collapsed,
-  //  and collapse it if so. We don't need to worry about whether collapsing
-  //  is enabled here, because `setExpansion()` already takes that into
-  //  account.
-
-  //  The cases where a status should be collapsed are:
-  //
-  //   -  The `collapse` prop has been set to `true`
-  //   -  The user has decided in local settings to collapse all statuses.
-  //   -  The user has decided to collapse all notifications ('muted'
-  //      statuses).
-  //   -  The user has decided to collapse long statuses and the status is
-  //      over 400px (without media, or 650px with).
-  //   -  The status is a reply and the user has decided to collapse all
-  //      replies.
-  //   -  The status contains media and the user has decided to collapse all
-  //      statuses with media.
-  //   -  The status is a reblog the user has decided to collapse all
-  //      statuses which are reblogs.
-  componentDidMount () {
-    const { node } = this;
-    const {
-      status,
-      settings,
-      collapse,
-      muted,
-      prepend,
-    } = this.props;
-    const autoCollapseSettings = settings.getIn(['collapsed', 'auto']);
-
-    if (function () {
-      switch (true) {
-      case collapse:
-      case autoCollapseSettings.get('all'):
-      case autoCollapseSettings.get('notifications') && muted:
-      case autoCollapseSettings.get('lengthy') && node.clientHeight > (
-        status.get('media_attachments').size && !muted ? 650 : 400
-      ):
-      case autoCollapseSettings.get('reblogs') && prepend === 'reblogged_by':
-      case autoCollapseSettings.get('replies') && status.get('in_reply_to_id', null) !== null:
-      case autoCollapseSettings.get('media') && !(status.get('spoiler_text').length) && status.get('media_attachments').size:
-        return true;
-      default:
-        return false;
-      }
-    }()) this.setExpansion(false);
-  }
-
-  //  `setExpansion()` sets the value of `isExpanded` in our state. It takes
-  //  one argument, `value`, which gives the desired value for `isExpanded`.
-  //  The default for this argument is `null`.
-
-  //  `setExpansion()` automatically checks for us whether toot collapsing
-  //  is enabled, so we don't have to.
-  setExpansion = (value) => {
-    switch (true) {
-    case value === undefined || value === null:
-      this.setState({ isExpanded: null });
-      break;
-    case !value && this.props.settings.getIn(['collapsed', 'enabled']):
-      this.setState({ isExpanded: false });
-      break;
-    case !!value:
-      this.setState({ isExpanded: true });
-      break;
-    }
-  }
-
-  //  `parseClick()` takes a click event and responds appropriately.
-  //  If our status is collapsed, then clicking on it should uncollapse it.
-  //  If `Shift` is held, then clicking on it should collapse it.
-  //  Otherwise, we open the url handed to us in `destination`, if
-  //  applicable.
-  parseClick = (e, destination) => {
-    const { router } = this.context;
-    const { status } = this.props;
-    const { isExpanded } = this.state;
-    if (!router) return;
-    if (destination === undefined) {
-      destination = `/statuses/${
-        status.getIn(['reblog', 'id'], status.get('id'))
-      }`;
-    }
-    if (e.button === 0) {
-      if (isExpanded === false) this.setExpansion(null);
-      else if (e.shiftKey) {
-        this.setExpansion(false);
-        document.getSelection().removeAllRanges();
-      } else router.history.push(destination);
-      e.preventDefault();
-    }
-  }
-
-  handleAccountClick = (e) => {
-    if (this.context.router && e.button === 0) {
-      const id = e.currentTarget.getAttribute('data-id');
-      e.preventDefault();
-      this.context.router.history.push(`/accounts/${id}`);
-    }
-  }
-
-  handleExpandedToggle = () => {
-    if (this.props.status.get('spoiler_text')) {
-      this.setExpansion(this.state.isExpanded ? null : true);
-    }
-  };
-
-  handleOpenVideo = startTime => {
-    this.props.onOpenVideo(this.props.status.getIn(['media_attachments', 0]), startTime);
-  }
-
-  handleHotkeyReply = e => {
-    e.preventDefault();
-    this.props.onReply(this.props.status, this.context.router.history);
-  }
-
-  handleHotkeyFavourite = () => {
-    this.props.onFavourite(this.props.status);
-  }
-
-  handleHotkeyBoost = e => {
-    this.props.onReblog(this.props.status, e);
-  }
-
-  handleHotkeyMention = e => {
-    e.preventDefault();
-    this.props.onMention(this.props.status.get('account'), this.context.router.history);
-  }
-
-  handleHotkeyOpen = () => {
-    this.context.router.history.push(`/statuses/${this.props.status.get('id')}`);
-  }
-
-  handleHotkeyOpenProfile = () => {
-    this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
-  }
-
-  handleHotkeyMoveUp = () => {
-    this.props.onMoveUp(this.props.containerId || this.props.id);
-  }
-
-  handleHotkeyMoveDown = () => {
-    this.props.onMoveDown(this.props.containerId || this.props.id);
-  }
-
-  handleRef = c => {
-    this.node = c;
-  }
-
-  renderLoadingMediaGallery () {
-    return <div className='media_gallery' style={{ height: '110px' }} />;
-  }
-
-  renderLoadingVideoPlayer () {
-    return <div className='media-spoiler-video' style={{ height: '110px' }} />;
-  }
-
-  render () {
-    const {
-      handleRef,
-      parseClick,
-      setExpansion,
-    } = this;
-    const { router } = this.context;
-    const {
-      status,
-      account,
-      settings,
-      collapsed,
-      muted,
-      prepend,
-      intersectionObserverWrapper,
-      onOpenVideo,
-      onOpenMedia,
-      notification,
-      hidden,
-      ...other
-    } = this.props;
-    const { isExpanded } = this.state;
-    let background = null;
-    let attachments = null;
-    let media = null;
-    let mediaIcon = null;
-
-    if (status === null) {
-      return null;
-    }
-
-    if (hidden) {
-      return (
-        <div
-          ref={this.handleRef}
-          data-id={status.get('id')}
-          style={{
-            height: `${this.height}px`,
-            opacity: 0,
-            overflow: 'hidden',
-          }}
-        >
-          {status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])}
-          {' '}
-          {status.get('content')}
-        </div>
-      );
-    }
-
-    //  If user backgrounds for collapsed statuses are enabled, then we
-    //  initialize our background accordingly. This will only be rendered if
-    //  the status is collapsed.
-    if (settings.getIn(['collapsed', 'backgrounds', 'user_backgrounds'])) {
-      background = status.getIn(['account', 'header']);
-    }
-
-    //  This handles our media attachments. Note that we don't show media on
-    //  muted (notification) statuses. If the media type is unknown, then we
-    //  simply ignore it.
-
-    //  After we have generated our appropriate media element and stored it in
-    //  `media`, we snatch the thumbnail to use as our `background` if media
-    //  backgrounds for collapsed statuses are enabled.
-    attachments = status.get('media_attachments');
-    if (attachments.size > 0 && !muted) {
-      if (attachments.some(item => item.get('type') === 'unknown')) {  //  Media type is 'unknown'
-        /*  Do nothing  */
-      } else if (attachments.getIn([0, 'type']) === 'video') {  //  Media type is 'video'
-        const video = status.getIn(['media_attachments', 0]);
-
-        media = (
-          <Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} >
-            {Component => <Component
-              preview={video.get('preview_url')}
-              src={video.get('url')}
-              sensitive={status.get('sensitive')}
-              letterbox={settings.getIn(['media', 'letterbox'])}
-              fullwidth={settings.getIn(['media', 'fullwidth'])}
-              onOpenVideo={this.handleOpenVideo}
-            />}
-          </Bundle>
-        );
-        mediaIcon = 'video-camera';
-      } else {  //  Media type is 'image' or 'gifv'
-        media = (
-          <Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery} >
-            {Component => (
-              <Component
-                media={attachments}
-                sensitive={status.get('sensitive')}
-                letterbox={settings.getIn(['media', 'letterbox'])}
-                fullwidth={settings.getIn(['media', 'fullwidth'])}
-                onOpenMedia={this.props.onOpenMedia}
-              />
-            )}
-          </Bundle>
-        );
-        mediaIcon = 'picture-o';
-      }
-
-      if (!status.get('sensitive') && !(status.get('spoiler_text').length > 0) && settings.getIn(['collapsed', 'backgrounds', 'preview_images'])) {
-        background = attachments.getIn([0, 'preview_url']);
-      }
-    }
-
-    //  Here we prepare extra data-* attributes for CSS selectors.
-    //  Users can use those for theming, hiding avatars etc via UserStyle
-    const selectorAttribs = {
-      'data-status-by': `@${status.getIn(['account', 'acct'])}`,
-    };
-
-    if (prepend && account) {
-      const notifKind = {
-        favourite: 'favourited',
-        reblog: 'boosted',
-        reblogged_by: 'boosted',
-      }[prepend];
-
-      selectorAttribs[`data-${notifKind}-by`] = `@${account.get('acct')}`;
-    }
-
-    const handlers = {
-      reply: this.handleHotkeyReply,
-      favourite: this.handleHotkeyFavourite,
-      boost: this.handleHotkeyBoost,
-      mention: this.handleHotkeyMention,
-      open: this.handleHotkeyOpen,
-      openProfile: this.handleHotkeyOpenProfile,
-      moveUp: this.handleHotkeyMoveUp,
-      moveDown: this.handleHotkeyMoveDown,
-      toggleSpoiler: this.handleExpandedToggle,
-    };
-
-    const computedClass = classNames('status', `status-${status.get('visibility')}`, {
-      collapsed: isExpanded === false,
-      'has-background': isExpanded === false && background,
-      'marked-for-delete': this.state.markedForDelete,
-      muted,
-    }, 'focusable');
-
-    return (
-      <HotKeys handlers={handlers}>
-        <div
-          className={computedClass}
-          style={isExpanded === false && background ? { backgroundImage: `url(${background})` } : null}
-          {...selectorAttribs}
-          ref={handleRef}
-          tabIndex='0'
-        >
-          {prepend && account ? (
-            <StatusPrepend
-              type={prepend}
-              account={account}
-              parseClick={parseClick}
-              notificationId={this.props.notificationId}
-            />
-          ) : null}
-          <StatusHeader
-            status={status}
-            friend={account}
-            mediaIcon={mediaIcon}
-            collapsible={settings.getIn(['collapsed', 'enabled'])}
-            collapsed={isExpanded === false}
-            parseClick={parseClick}
-            setExpansion={setExpansion}
-          />
-          <StatusContent
-            status={status}
-            media={media}
-            mediaIcon={mediaIcon}
-            expanded={isExpanded}
-            setExpansion={setExpansion}
-            parseClick={parseClick}
-            disabled={!router}
-          />
-          {isExpanded !== false ? (
-            <StatusActionBar
-              {...other}
-              status={status}
-              account={status.get('account')}
-            />
-          ) : null}
-          {notification ? (
-            <NotificationOverlayContainer
-              notification={notification}
-            />
-          ) : null}
-        </div>
-      </HotKeys>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/status_action_bar.js b/app/javascript/themes/glitch/components/status_action_bar.js
deleted file mode 100644
index 9d615ed7c..000000000
--- a/app/javascript/themes/glitch/components/status_action_bar.js
+++ /dev/null
@@ -1,188 +0,0 @@
-//  THIS FILE EXISTS FOR UPSTREAM COMPATIBILITY & SHOULDN'T BE USED !!
-//  SEE INSTEAD : glitch/components/status/action_bar
-
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import IconButton from './icon_button';
-import DropdownMenuContainer from 'themes/glitch/containers/dropdown_menu_container';
-import { defineMessages, injectIntl } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { me } from 'themes/glitch/util/initial_state';
-import RelativeTimestamp from './relative_timestamp';
-
-const messages = defineMessages({
-  delete: { id: 'status.delete', defaultMessage: 'Delete' },
-  mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
-  mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
-  block: { id: 'account.block', defaultMessage: 'Block @{name}' },
-  reply: { id: 'status.reply', defaultMessage: 'Reply' },
-  share: { id: 'status.share', defaultMessage: 'Share' },
-  more: { id: 'status.more', defaultMessage: 'More' },
-  replyAll: { id: 'status.replyAll', defaultMessage: 'Reply to thread' },
-  reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
-  cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
-  favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
-  open: { id: 'status.open', defaultMessage: 'Expand this status' },
-  report: { id: 'status.report', defaultMessage: 'Report @{name}' },
-  muteConversation: { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' },
-  unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' },
-  pin: { id: 'status.pin', defaultMessage: 'Pin on profile' },
-  unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
-  embed: { id: 'status.embed', defaultMessage: 'Embed' },
-});
-
-@injectIntl
-export default class StatusActionBar extends ImmutablePureComponent {
-
-  static contextTypes = {
-    router: PropTypes.object,
-  };
-
-  static propTypes = {
-    status: ImmutablePropTypes.map.isRequired,
-    onReply: PropTypes.func,
-    onFavourite: PropTypes.func,
-    onReblog: PropTypes.func,
-    onDelete: PropTypes.func,
-    onMention: PropTypes.func,
-    onMute: PropTypes.func,
-    onBlock: PropTypes.func,
-    onReport: PropTypes.func,
-    onEmbed: PropTypes.func,
-    onMuteConversation: PropTypes.func,
-    onPin: PropTypes.func,
-    withDismiss: PropTypes.bool,
-    intl: PropTypes.object.isRequired,
-  };
-
-  // Avoid checking props that are functions (and whose equality will always
-  // evaluate to false. See react-immutable-pure-component for usage.
-  updateOnProps = [
-    'status',
-    'withDismiss',
-  ]
-
-  handleReplyClick = () => {
-    this.props.onReply(this.props.status, this.context.router.history);
-  }
-
-  handleShareClick = () => {
-    navigator.share({
-      text: this.props.status.get('search_index'),
-      url: this.props.status.get('url'),
-    });
-  }
-
-  handleFavouriteClick = () => {
-    this.props.onFavourite(this.props.status);
-  }
-
-  handleReblogClick = (e) => {
-    this.props.onReblog(this.props.status, e);
-  }
-
-  handleDeleteClick = () => {
-    this.props.onDelete(this.props.status);
-  }
-
-  handlePinClick = () => {
-    this.props.onPin(this.props.status);
-  }
-
-  handleMentionClick = () => {
-    this.props.onMention(this.props.status.get('account'), this.context.router.history);
-  }
-
-  handleMuteClick = () => {
-    this.props.onMute(this.props.status.get('account'));
-  }
-
-  handleBlockClick = () => {
-    this.props.onBlock(this.props.status.get('account'));
-  }
-
-  handleOpen = () => {
-    this.context.router.history.push(`/statuses/${this.props.status.get('id')}`);
-  }
-
-  handleEmbed = () => {
-    this.props.onEmbed(this.props.status);
-  }
-
-  handleReport = () => {
-    this.props.onReport(this.props.status);
-  }
-
-  handleConversationMuteClick = () => {
-    this.props.onMuteConversation(this.props.status);
-  }
-
-  render () {
-    const { status, intl, withDismiss } = this.props;
-
-    const mutingConversation = status.get('muted');
-    const anonymousAccess    = !me;
-    const publicStatus       = ['public', 'unlisted'].includes(status.get('visibility'));
-
-    let menu = [];
-    let reblogIcon = 'retweet';
-    let replyIcon;
-    let replyTitle;
-
-    menu.push({ text: intl.formatMessage(messages.open), action: this.handleOpen });
-
-    if (publicStatus) {
-      menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed });
-    }
-
-    menu.push(null);
-
-    if (status.getIn(['account', 'id']) === me || withDismiss) {
-      menu.push({ text: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick });
-      menu.push(null);
-    }
-
-    if (status.getIn(['account', 'id']) === me) {
-      if (publicStatus) {
-        menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
-      }
-
-      menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
-    } else {
-      menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
-      menu.push(null);
-      menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick });
-      menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick });
-      menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
-    }
-
-    if (status.get('in_reply_to_id', null) === null) {
-      replyIcon = 'reply';
-      replyTitle = intl.formatMessage(messages.reply);
-    } else {
-      replyIcon = 'reply-all';
-      replyTitle = intl.formatMessage(messages.replyAll);
-    }
-
-    const shareButton = ('share' in navigator) && status.get('visibility') === 'public' && (
-      <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.share)} icon='share-alt' onClick={this.handleShareClick} />
-    );
-
-    return (
-      <div className='status__action-bar'>
-        <IconButton className='status__action-bar-button' disabled={anonymousAccess} title={replyTitle} icon={replyIcon} onClick={this.handleReplyClick} />
-        <IconButton className='status__action-bar-button' disabled={anonymousAccess || !publicStatus} active={status.get('reblogged')} pressed={status.get('reblogged')} title={!publicStatus ? intl.formatMessage(messages.cannot_reblog) : intl.formatMessage(messages.reblog)} icon={reblogIcon} onClick={this.handleReblogClick} />
-        <IconButton className='status__action-bar-button star-icon' disabled={anonymousAccess} animate active={status.get('favourited')} pressed={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} />
-        {shareButton}
-
-        <div className='status__action-bar-dropdown'>
-          <DropdownMenuContainer disabled={anonymousAccess} status={status} items={menu} icon='ellipsis-h' size={18} direction='right' ariaLabel={intl.formatMessage(messages.more)} />
-        </div>
-
-        <a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener'><RelativeTimestamp timestamp={status.get('created_at')} /></a>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/status_content.js b/app/javascript/themes/glitch/components/status_content.js
deleted file mode 100644
index 3eba6eaa0..000000000
--- a/app/javascript/themes/glitch/components/status_content.js
+++ /dev/null
@@ -1,245 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import { isRtl } from 'themes/glitch/util/rtl';
-import { FormattedMessage } from 'react-intl';
-import Permalink from './permalink';
-import classnames from 'classnames';
-
-export default class StatusContent extends React.PureComponent {
-
-  static propTypes = {
-    status: ImmutablePropTypes.map.isRequired,
-    expanded: PropTypes.bool,
-    setExpansion: PropTypes.func,
-    media: PropTypes.element,
-    mediaIcon: PropTypes.string,
-    parseClick: PropTypes.func,
-    disabled: PropTypes.bool,
-  };
-
-  state = {
-    hidden: true,
-  };
-
-  _updateStatusLinks () {
-    const node  = this.node;
-    const links = node.querySelectorAll('a');
-
-    for (var i = 0; i < links.length; ++i) {
-      let link = links[i];
-      if (link.classList.contains('status-link')) {
-        continue;
-      }
-      link.classList.add('status-link');
-
-      let mention = this.props.status.get('mentions').find(item => link.href === item.get('url'));
-
-      if (mention) {
-        link.addEventListener('click', this.onMentionClick.bind(this, mention), false);
-        link.setAttribute('title', mention.get('acct'));
-      } else if (link.textContent[0] === '#' || (link.previousSibling && link.previousSibling.textContent && link.previousSibling.textContent[link.previousSibling.textContent.length - 1] === '#')) {
-        link.addEventListener('click', this.onHashtagClick.bind(this, link.text), false);
-      } else {
-        link.addEventListener('click', this.onLinkClick.bind(this), false);
-        link.setAttribute('title', link.href);
-      }
-
-      link.setAttribute('target', '_blank');
-      link.setAttribute('rel', 'noopener');
-    }
-  }
-
-  componentDidMount () {
-    this._updateStatusLinks();
-  }
-
-  componentDidUpdate () {
-    this._updateStatusLinks();
-  }
-
-  onLinkClick = (e) => {
-    if (this.props.expanded === false) {
-      if (this.props.parseClick) this.props.parseClick(e);
-    }
-  }
-
-  onMentionClick = (mention, e) => {
-    if (this.props.parseClick) {
-      this.props.parseClick(e, `/accounts/${mention.get('id')}`);
-    }
-  }
-
-  onHashtagClick = (hashtag, e) => {
-    hashtag = hashtag.replace(/^#/, '').toLowerCase();
-
-    if (this.props.parseClick) {
-      this.props.parseClick(e, `/timelines/tag/${hashtag}`);
-    }
-  }
-
-  handleMouseDown = (e) => {
-    this.startXY = [e.clientX, e.clientY];
-  }
-
-  handleMouseUp = (e) => {
-    const { parseClick } = this.props;
-
-    if (!this.startXY) {
-      return;
-    }
-
-    const [ startX, startY ] = this.startXY;
-    const [ deltaX, deltaY ] = [Math.abs(e.clientX - startX), Math.abs(e.clientY - startY)];
-
-    if (e.target.localName === 'button' || e.target.localName === 'a' || (e.target.parentNode && (e.target.parentNode.localName === 'button' || e.target.parentNode.localName === 'a'))) {
-      return;
-    }
-
-    if (deltaX + deltaY < 5 && e.button === 0 && parseClick) {
-      parseClick(e);
-    }
-
-    this.startXY = null;
-  }
-
-  handleSpoilerClick = (e) => {
-    e.preventDefault();
-
-    if (this.props.setExpansion) {
-      this.props.setExpansion(this.props.expanded ? null : true);
-    } else {
-      this.setState({ hidden: !this.state.hidden });
-    }
-  }
-
-  setRef = (c) => {
-    this.node = c;
-  }
-
-  render () {
-    const {
-      status,
-      media,
-      mediaIcon,
-      parseClick,
-      disabled,
-    } = this.props;
-
-    const hidden = this.props.setExpansion ? !this.props.expanded : this.state.hidden;
-
-    const content = { __html: status.get('contentHtml') };
-    const spoilerContent = { __html: status.get('spoilerHtml') };
-    const directionStyle = { direction: 'ltr' };
-    const classNames = classnames('status__content', {
-      'status__content--with-action': parseClick && !disabled,
-      'status__content--with-spoiler': status.get('spoiler_text').length > 0,
-    });
-
-    if (isRtl(status.get('search_index'))) {
-      directionStyle.direction = 'rtl';
-    }
-
-    if (status.get('spoiler_text').length > 0) {
-      let mentionsPlaceholder = '';
-
-      const mentionLinks = status.get('mentions').map(item => (
-        <Permalink
-          to={`/accounts/${item.get('id')}`}
-          href={item.get('url')}
-          key={item.get('id')}
-          className='mention'
-        >
-          @<span>{item.get('username')}</span>
-        </Permalink>
-      )).reduce((aggregate, item) => [...aggregate, item, ' '], []);
-
-      const toggleText = hidden ? [
-        <FormattedMessage
-          id='status.show_more'
-          defaultMessage='Show more'
-          key='0'
-        />,
-        mediaIcon ? (
-          <i
-            className={
-              `fa fa-fw fa-${mediaIcon} status__content__spoiler-icon`
-            }
-            aria-hidden='true'
-            key='1'
-          />
-        ) : null,
-      ] : [
-        <FormattedMessage
-          id='status.show_less'
-          defaultMessage='Show less'
-          key='0'
-        />,
-      ];
-
-      if (hidden) {
-        mentionsPlaceholder = <div>{mentionLinks}</div>;
-      }
-
-      return (
-        <div className={classNames} tabIndex='0'>
-          <p
-            style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}
-            onMouseDown={this.handleMouseDown}
-            onMouseUp={this.handleMouseUp}
-          >
-            <span dangerouslySetInnerHTML={spoilerContent} />
-            {' '}
-            <button tabIndex='0' className='status__content__spoiler-link' onClick={this.handleSpoilerClick}>
-              {toggleText}
-            </button>
-          </p>
-
-          {mentionsPlaceholder}
-
-          <div className={`status__content__spoiler ${!hidden ? 'status__content__spoiler--visible' : ''}`}>
-            <div
-              ref={this.setRef}
-              style={directionStyle}
-              tabIndex={!hidden ? 0 : null}
-              onMouseDown={this.handleMouseDown}
-              onMouseUp={this.handleMouseUp}
-              dangerouslySetInnerHTML={content}
-            />
-            {media}
-          </div>
-
-        </div>
-      );
-    } else if (parseClick) {
-      return (
-        <div
-          className={classNames}
-          style={directionStyle}
-          tabIndex='0'
-        >
-          <div
-            ref={this.setRef}
-            onMouseDown={this.handleMouseDown}
-            onMouseUp={this.handleMouseUp}
-            dangerouslySetInnerHTML={content}
-            tabIndex='0'
-          />
-          {media}
-        </div>
-      );
-    } else {
-      return (
-        <div
-          className='status__content'
-          style={directionStyle}
-          tabIndex='0'
-        >
-          <div ref={this.setRef} dangerouslySetInnerHTML={content} tabIndex='0' />
-          {media}
-        </div>
-      );
-    }
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/status_header.js b/app/javascript/themes/glitch/components/status_header.js
deleted file mode 100644
index bfa996cd5..000000000
--- a/app/javascript/themes/glitch/components/status_header.js
+++ /dev/null
@@ -1,120 +0,0 @@
-//  Package imports.
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { defineMessages, injectIntl } from 'react-intl';
-
-//  Mastodon imports.
-import Avatar from './avatar';
-import AvatarOverlay from './avatar_overlay';
-import DisplayName from './display_name';
-import IconButton from './icon_button';
-import VisibilityIcon from './status_visibility_icon';
-
-//  Messages for use with internationalization stuff.
-const messages = defineMessages({
-  collapse: { id: 'status.collapse', defaultMessage: 'Collapse' },
-  uncollapse: { id: 'status.uncollapse', defaultMessage: 'Uncollapse' },
-  public: { id: 'privacy.public.short', defaultMessage: 'Public' },
-  unlisted: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
-  private: { id: 'privacy.private.short', defaultMessage: 'Followers-only' },
-  direct: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
-});
-
-@injectIntl
-export default class StatusHeader extends React.PureComponent {
-
-  static propTypes = {
-    status: ImmutablePropTypes.map.isRequired,
-    friend: ImmutablePropTypes.map,
-    mediaIcon: PropTypes.string,
-    collapsible: PropTypes.bool,
-    collapsed: PropTypes.bool,
-    parseClick: PropTypes.func.isRequired,
-    setExpansion: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  //  Handles clicks on collapsed button
-  handleCollapsedClick = (e) => {
-    const { collapsed, setExpansion } = this.props;
-    if (e.button === 0) {
-      setExpansion(collapsed ? null : false);
-      e.preventDefault();
-    }
-  }
-
-  //  Handles clicks on account name/image
-  handleAccountClick = (e) => {
-    const { status, parseClick } = this.props;
-    parseClick(e, `/accounts/${+status.getIn(['account', 'id'])}`);
-  }
-
-  //  Rendering.
-  render () {
-    const {
-      status,
-      friend,
-      mediaIcon,
-      collapsible,
-      collapsed,
-      intl,
-    } = this.props;
-
-    const account = status.get('account');
-
-    return (
-      <header className='status__info'>
-        <a
-          href={account.get('url')}
-          target='_blank'
-          className='status__avatar'
-          onClick={this.handleAccountClick}
-        >
-          {
-            friend ? (
-              <AvatarOverlay account={account} friend={friend} />
-            ) : (
-              <Avatar account={account} size={48} />
-            )
-          }
-        </a>
-        <a
-          href={account.get('url')}
-          target='_blank'
-          className='status__display-name'
-          onClick={this.handleAccountClick}
-        >
-          <DisplayName account={account} />
-        </a>
-        <div className='status__info__icons'>
-          {mediaIcon ? (
-            <i
-              className={`fa fa-fw fa-${mediaIcon}`}
-              aria-hidden='true'
-            />
-          ) : null}
-          {(
-            <VisibilityIcon visibility={status.get('visibility')} />
-          )}
-          {collapsible ? (
-            <IconButton
-              className='status__collapse-button'
-              animate flip
-              active={collapsed}
-              title={
-                collapsed ?
-                intl.formatMessage(messages.uncollapse) :
-                intl.formatMessage(messages.collapse)
-              }
-              icon='angle-double-up'
-              onClick={this.handleCollapsedClick}
-            />
-          ) : null}
-        </div>
-
-      </header>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/status_list.js b/app/javascript/themes/glitch/components/status_list.js
deleted file mode 100644
index ddb1354c6..000000000
--- a/app/javascript/themes/glitch/components/status_list.js
+++ /dev/null
@@ -1,72 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import StatusContainer from 'themes/glitch/containers/status_container';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import ScrollableList from './scrollable_list';
-
-export default class StatusList extends ImmutablePureComponent {
-
-  static propTypes = {
-    scrollKey: PropTypes.string.isRequired,
-    statusIds: ImmutablePropTypes.list.isRequired,
-    onScrollToBottom: PropTypes.func,
-    onScrollToTop: PropTypes.func,
-    onScroll: PropTypes.func,
-    trackScroll: PropTypes.bool,
-    shouldUpdateScroll: PropTypes.func,
-    isLoading: PropTypes.bool,
-    hasMore: PropTypes.bool,
-    prepend: PropTypes.node,
-    emptyMessage: PropTypes.node,
-  };
-
-  static defaultProps = {
-    trackScroll: true,
-  };
-
-  handleMoveUp = id => {
-    const elementIndex = this.props.statusIds.indexOf(id) - 1;
-    this._selectChild(elementIndex);
-  }
-
-  handleMoveDown = id => {
-    const elementIndex = this.props.statusIds.indexOf(id) + 1;
-    this._selectChild(elementIndex);
-  }
-
-  _selectChild (index) {
-    const element = this.node.node.querySelector(`article:nth-of-type(${index + 1}) .focusable`);
-
-    if (element) {
-      element.focus();
-    }
-  }
-
-  setRef = c => {
-    this.node = c;
-  }
-
-  render () {
-    const { statusIds, ...other } = this.props;
-    const { isLoading } = other;
-
-    const scrollableContent = (isLoading || statusIds.size > 0) ? (
-      statusIds.map((statusId) => (
-        <StatusContainer
-          key={statusId}
-          id={statusId}
-          onMoveUp={this.handleMoveUp}
-          onMoveDown={this.handleMoveDown}
-        />
-      ))
-    ) : null;
-
-    return (
-      <ScrollableList {...other} ref={this.setRef}>
-        {scrollableContent}
-      </ScrollableList>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/status_prepend.js b/app/javascript/themes/glitch/components/status_prepend.js
deleted file mode 100644
index bd2559e46..000000000
--- a/app/javascript/themes/glitch/components/status_prepend.js
+++ /dev/null
@@ -1,83 +0,0 @@
-//  Package imports  //
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { FormattedMessage } from 'react-intl';
-
-export default class StatusPrepend extends React.PureComponent {
-
-  static propTypes = {
-    type: PropTypes.string.isRequired,
-    account: ImmutablePropTypes.map.isRequired,
-    parseClick: PropTypes.func.isRequired,
-    notificationId: PropTypes.number,
-  };
-
-  handleClick = (e) => {
-    const { account, parseClick } = this.props;
-    parseClick(e, `/accounts/${+account.get('id')}`);
-  }
-
-  Message = () => {
-    const { type, account } = this.props;
-    let link = (
-      <a
-        onClick={this.handleClick}
-        href={account.get('url')}
-        className='status__display-name'
-      >
-        <b
-          dangerouslySetInnerHTML={{
-            __html : account.get('display_name_html') || account.get('username'),
-          }}
-        />
-      </a>
-    );
-    switch (type) {
-    case 'reblogged_by':
-      return (
-        <FormattedMessage
-          id='status.reblogged_by'
-          defaultMessage='{name} boosted'
-          values={{ name : link }}
-        />
-      );
-    case 'favourite':
-      return (
-        <FormattedMessage
-          id='notification.favourite'
-          defaultMessage='{name} favourited your status'
-          values={{ name : link }}
-        />
-      );
-    case 'reblog':
-      return (
-        <FormattedMessage
-          id='notification.reblog'
-          defaultMessage='{name} boosted your status'
-          values={{ name : link }}
-        />
-      );
-    }
-    return null;
-  }
-
-  render () {
-    const { Message } = this;
-    const { type } = this.props;
-
-    return !type ? null : (
-      <aside className={type === 'reblogged_by' ? 'status__prepend' : 'notification__message'}>
-        <div className={type === 'reblogged_by' ? 'status__prepend-icon-wrapper' : 'notification__favourite-icon-wrapper'}>
-          <i
-            className={`fa fa-fw fa-${
-              type === 'favourite' ? 'star star-icon' : 'retweet'
-            } status__prepend-icon`}
-          />
-        </div>
-        <Message />
-      </aside>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/components/status_visibility_icon.js b/app/javascript/themes/glitch/components/status_visibility_icon.js
deleted file mode 100644
index 017b69cbb..000000000
--- a/app/javascript/themes/glitch/components/status_visibility_icon.js
+++ /dev/null
@@ -1,48 +0,0 @@
-//  Package imports  //
-import React from 'react';
-import PropTypes from 'prop-types';
-import { defineMessages, injectIntl } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const messages = defineMessages({
-  public: { id: 'privacy.public.short', defaultMessage: 'Public' },
-  unlisted: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
-  private: { id: 'privacy.private.short', defaultMessage: 'Followers-only' },
-  direct: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
-});
-
-@injectIntl
-export default class VisibilityIcon extends ImmutablePureComponent {
-
-  static propTypes = {
-    visibility: PropTypes.string,
-    intl: PropTypes.object.isRequired,
-    withLabel: PropTypes.bool,
-  };
-
-  render() {
-    const { withLabel, visibility, intl } = this.props;
-
-    const visibilityClass = {
-      public: 'globe',
-      unlisted: 'unlock-alt',
-      private: 'lock',
-      direct: 'envelope',
-    }[visibility];
-
-    const label = intl.formatMessage(messages[visibility]);
-
-    const icon = (<i
-      className={`status__visibility-icon fa fa-fw fa-${visibilityClass}`}
-      title={label}
-      aria-hidden='true'
-    />);
-
-    if (withLabel) {
-      return (<span style={{ whiteSpace: 'nowrap' }}>{icon} {label}</span>);
-    } else {
-      return icon;
-    }
-  }
-
-}
diff --git a/app/javascript/themes/glitch/containers/account_container.js b/app/javascript/themes/glitch/containers/account_container.js
deleted file mode 100644
index c1ce49987..000000000
--- a/app/javascript/themes/glitch/containers/account_container.js
+++ /dev/null
@@ -1,72 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import { makeGetAccount } from 'themes/glitch/selectors';
-import Account from 'themes/glitch/components/account';
-import {
-  followAccount,
-  unfollowAccount,
-  blockAccount,
-  unblockAccount,
-  muteAccount,
-  unmuteAccount,
-} from 'themes/glitch/actions/accounts';
-import { openModal } from 'themes/glitch/actions/modal';
-import { initMuteModal } from 'themes/glitch/actions/mutes';
-import { unfollowModal } from 'themes/glitch/util/initial_state';
-
-const messages = defineMessages({
-  unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' },
-});
-
-const makeMapStateToProps = () => {
-  const getAccount = makeGetAccount();
-
-  const mapStateToProps = (state, props) => ({
-    account: getAccount(state, props.id),
-  });
-
-  return mapStateToProps;
-};
-
-const mapDispatchToProps = (dispatch, { intl }) => ({
-
-  onFollow (account) {
-    if (account.getIn(['relationship', 'following']) || account.getIn(['relationship', 'requested'])) {
-      if (unfollowModal) {
-        dispatch(openModal('CONFIRM', {
-          message: <FormattedMessage id='confirmations.unfollow.message' defaultMessage='Are you sure you want to unfollow {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
-          confirm: intl.formatMessage(messages.unfollowConfirm),
-          onConfirm: () => dispatch(unfollowAccount(account.get('id'))),
-        }));
-      } else {
-        dispatch(unfollowAccount(account.get('id')));
-      }
-    } else {
-      dispatch(followAccount(account.get('id')));
-    }
-  },
-
-  onBlock (account) {
-    if (account.getIn(['relationship', 'blocking'])) {
-      dispatch(unblockAccount(account.get('id')));
-    } else {
-      dispatch(blockAccount(account.get('id')));
-    }
-  },
-
-  onMute (account) {
-    if (account.getIn(['relationship', 'muting'])) {
-      dispatch(unmuteAccount(account.get('id')));
-    } else {
-      dispatch(initMuteModal(account));
-    }
-  },
-
-
-  onMuteNotifications (account, notifications) {
-    dispatch(muteAccount(account.get('id'), notifications));
-  },
-});
-
-export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Account));
diff --git a/app/javascript/themes/glitch/containers/card_container.js b/app/javascript/themes/glitch/containers/card_container.js
deleted file mode 100644
index 8285437bb..000000000
--- a/app/javascript/themes/glitch/containers/card_container.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Card from 'themes/glitch/features/status/components/card';
-import { fromJS } from 'immutable';
-
-export default class CardContainer extends React.PureComponent {
-
-  static propTypes = {
-    locale: PropTypes.string,
-    card: PropTypes.array.isRequired,
-  };
-
-  render () {
-    const { card, ...props } = this.props;
-    return <Card card={fromJS(card)} {...props} />;
-  }
-
-}
diff --git a/app/javascript/themes/glitch/containers/compose_container.js b/app/javascript/themes/glitch/containers/compose_container.js
deleted file mode 100644
index 82980ee36..000000000
--- a/app/javascript/themes/glitch/containers/compose_container.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import React from 'react';
-import { Provider } from 'react-redux';
-import PropTypes from 'prop-types';
-import configureStore from 'themes/glitch/store/configureStore';
-import { hydrateStore } from 'themes/glitch/actions/store';
-import { IntlProvider, addLocaleData } from 'react-intl';
-import { getLocale } from 'mastodon/locales';
-import Compose from 'themes/glitch/features/standalone/compose';
-import initialState from 'themes/glitch/util/initial_state';
-
-const { localeData, messages } = getLocale();
-addLocaleData(localeData);
-
-const store = configureStore();
-
-if (initialState) {
-  store.dispatch(hydrateStore(initialState));
-}
-
-export default class TimelineContainer extends React.PureComponent {
-
-  static propTypes = {
-    locale: PropTypes.string.isRequired,
-  };
-
-  render () {
-    const { locale } = this.props;
-
-    return (
-      <IntlProvider locale={locale} messages={messages}>
-        <Provider store={store}>
-          <Compose />
-        </Provider>
-      </IntlProvider>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/containers/dropdown_menu_container.js b/app/javascript/themes/glitch/containers/dropdown_menu_container.js
deleted file mode 100644
index 15e8da2e3..000000000
--- a/app/javascript/themes/glitch/containers/dropdown_menu_container.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { openModal, closeModal } from 'themes/glitch/actions/modal';
-import { connect } from 'react-redux';
-import DropdownMenu from 'themes/glitch/components/dropdown_menu';
-import { isUserTouching } from 'themes/glitch/util/is_mobile';
-
-const mapStateToProps = state => ({
-  isModalOpen: state.get('modal').modalType === 'ACTIONS',
-});
-
-const mapDispatchToProps = dispatch => ({
-  isUserTouching,
-  onModalOpen: props => dispatch(openModal('ACTIONS', props)),
-  onModalClose: () => dispatch(closeModal()),
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(DropdownMenu);
diff --git a/app/javascript/themes/glitch/containers/intersection_observer_article_container.js b/app/javascript/themes/glitch/containers/intersection_observer_article_container.js
deleted file mode 100644
index 6ede64738..000000000
--- a/app/javascript/themes/glitch/containers/intersection_observer_article_container.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import { connect } from 'react-redux';
-import IntersectionObserverArticle from 'themes/glitch/components/intersection_observer_article';
-import { setHeight } from 'themes/glitch/actions/height_cache';
-
-const makeMapStateToProps = (state, props) => ({
-  cachedHeight: state.getIn(['height_cache', props.saveHeightKey, props.id]),
-});
-
-const mapDispatchToProps = (dispatch) => ({
-
-  onHeightChange (key, id, height) {
-    dispatch(setHeight(key, id, height));
-  },
-
-});
-
-export default connect(makeMapStateToProps, mapDispatchToProps)(IntersectionObserverArticle);
diff --git a/app/javascript/themes/glitch/containers/mastodon.js b/app/javascript/themes/glitch/containers/mastodon.js
deleted file mode 100644
index 348470637..000000000
--- a/app/javascript/themes/glitch/containers/mastodon.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import React from 'react';
-import { Provider } from 'react-redux';
-import PropTypes from 'prop-types';
-import configureStore from 'themes/glitch/store/configureStore';
-import { showOnboardingOnce } from 'themes/glitch/actions/onboarding';
-import { BrowserRouter, Route } from 'react-router-dom';
-import { ScrollContext } from 'react-router-scroll-4';
-import UI from 'themes/glitch/features/ui';
-import { hydrateStore } from 'themes/glitch/actions/store';
-import { connectUserStream } from 'themes/glitch/actions/streaming';
-import { IntlProvider, addLocaleData } from 'react-intl';
-import { getLocale } from 'mastodon/locales';
-import initialState from 'themes/glitch/util/initial_state';
-
-const { localeData, messages } = getLocale();
-addLocaleData(localeData);
-
-export const store = configureStore();
-const hydrateAction = hydrateStore(initialState);
-store.dispatch(hydrateAction);
-
-export default class Mastodon extends React.PureComponent {
-
-  static propTypes = {
-    locale: PropTypes.string.isRequired,
-  };
-
-  componentDidMount() {
-    this.disconnect = store.dispatch(connectUserStream());
-
-    // Desktop notifications
-    // Ask after 1 minute
-    if (typeof window.Notification !== 'undefined' && Notification.permission === 'default') {
-      window.setTimeout(() => Notification.requestPermission(), 60 * 1000);
-    }
-
-    // Protocol handler
-    // Ask after 5 minutes
-    if (typeof navigator.registerProtocolHandler !== 'undefined') {
-      const handlerUrl = window.location.protocol + '//' + window.location.host + '/intent?uri=%s';
-      window.setTimeout(() => navigator.registerProtocolHandler('web+mastodon', handlerUrl, 'Mastodon'), 5 * 60 * 1000);
-    }
-
-    store.dispatch(showOnboardingOnce());
-  }
-
-  componentWillUnmount () {
-    if (this.disconnect) {
-      this.disconnect();
-      this.disconnect = null;
-    }
-  }
-
-  render () {
-    const { locale } = this.props;
-
-    return (
-      <IntlProvider locale={locale} messages={messages}>
-        <Provider store={store}>
-          <BrowserRouter basename='/web'>
-            <ScrollContext>
-              <Route path='/' component={UI} />
-            </ScrollContext>
-          </BrowserRouter>
-        </Provider>
-      </IntlProvider>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/containers/media_gallery_container.js b/app/javascript/themes/glitch/containers/media_gallery_container.js
deleted file mode 100644
index 86965f73b..000000000
--- a/app/javascript/themes/glitch/containers/media_gallery_container.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { IntlProvider, addLocaleData } from 'react-intl';
-import { getLocale } from 'mastodon/locales';
-import MediaGallery from 'themes/glitch/components/media_gallery';
-import { fromJS } from 'immutable';
-
-const { localeData, messages } = getLocale();
-addLocaleData(localeData);
-
-export default class MediaGalleryContainer extends React.PureComponent {
-
-  static propTypes = {
-    locale: PropTypes.string.isRequired,
-    media: PropTypes.array.isRequired,
-  };
-
-  handleOpenMedia = () => {}
-
-  render () {
-    const { locale, media, ...props } = this.props;
-
-    return (
-      <IntlProvider locale={locale} messages={messages}>
-        <MediaGallery
-          {...props}
-          media={fromJS(media)}
-          onOpenMedia={this.handleOpenMedia}
-        />
-      </IntlProvider>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/containers/notification_purge_buttons_container.js b/app/javascript/themes/glitch/containers/notification_purge_buttons_container.js
deleted file mode 100644
index ee4cb84cd..000000000
--- a/app/javascript/themes/glitch/containers/notification_purge_buttons_container.js
+++ /dev/null
@@ -1,49 +0,0 @@
-//  Package imports.
-import { connect } from 'react-redux';
-import { defineMessages, injectIntl } from 'react-intl';
-
-//  Our imports.
-import NotificationPurgeButtons from 'themes/glitch/components/notification_purge_buttons';
-import {
-  deleteMarkedNotifications,
-  enterNotificationClearingMode,
-  markAllNotifications,
-} from 'themes/glitch/actions/notifications';
-import { openModal } from 'themes/glitch/actions/modal';
-
-const messages = defineMessages({
-  clearMessage: { id: 'notifications.marked_clear_confirmation', defaultMessage: 'Are you sure you want to permanently clear all selected notifications?' },
-  clearConfirm: { id: 'notifications.marked_clear', defaultMessage: 'Clear selected notifications' },
-});
-
-const mapDispatchToProps = (dispatch, { intl }) => ({
-  onEnterCleaningMode(yes) {
-    dispatch(enterNotificationClearingMode(yes));
-  },
-
-  onDeleteMarked() {
-    dispatch(openModal('CONFIRM', {
-      message: intl.formatMessage(messages.clearMessage),
-      confirm: intl.formatMessage(messages.clearConfirm),
-      onConfirm: () => dispatch(deleteMarkedNotifications()),
-    }));
-  },
-
-  onMarkAll() {
-    dispatch(markAllNotifications(true));
-  },
-
-  onMarkNone() {
-    dispatch(markAllNotifications(false));
-  },
-
-  onInvert() {
-    dispatch(markAllNotifications(null));
-  },
-});
-
-const mapStateToProps = state => ({
-  markNewForDelete: state.getIn(['notifications', 'markNewForDelete']),
-});
-
-export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(NotificationPurgeButtons));
diff --git a/app/javascript/themes/glitch/containers/status_container.js b/app/javascript/themes/glitch/containers/status_container.js
deleted file mode 100644
index 0cee73b9a..000000000
--- a/app/javascript/themes/glitch/containers/status_container.js
+++ /dev/null
@@ -1,151 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import Status from 'themes/glitch/components/status';
-import { makeGetStatus } from 'themes/glitch/selectors';
-import {
-  replyCompose,
-  mentionCompose,
-} from 'themes/glitch/actions/compose';
-import {
-  reblog,
-  favourite,
-  unreblog,
-  unfavourite,
-  pin,
-  unpin,
-} from 'themes/glitch/actions/interactions';
-import { blockAccount } from 'themes/glitch/actions/accounts';
-import { muteStatus, unmuteStatus, deleteStatus } from 'themes/glitch/actions/statuses';
-import { initMuteModal } from 'themes/glitch/actions/mutes';
-import { initReport } from 'themes/glitch/actions/reports';
-import { openModal } from 'themes/glitch/actions/modal';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import { boostModal, deleteModal } from 'themes/glitch/util/initial_state';
-
-const messages = defineMessages({
-  deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
-  deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
-  blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' },
-});
-
-const makeMapStateToProps = () => {
-  const getStatus = makeGetStatus();
-
-  const mapStateToProps = (state, props) => {
-
-    let status = getStatus(state, props.id);
-    let reblogStatus = status ? status.get('reblog', null) : null;
-    let account = undefined;
-    let prepend = undefined;
-
-    if (reblogStatus !== null && typeof reblogStatus === 'object') {
-      account = status.get('account');
-      status = reblogStatus;
-      prepend = 'reblogged_by';
-    }
-
-    return {
-      containerId : props.containerId || props.id,  //  Should match reblogStatus's id for reblogs
-      status      : status,
-      account     : account || props.account,
-      settings    : state.get('local_settings'),
-      prepend     : prepend || props.prepend,
-    };
-  };
-
-  return mapStateToProps;
-};
-
-const mapDispatchToProps = (dispatch, { intl }) => ({
-
-  onReply (status, router) {
-    dispatch(replyCompose(status, router));
-  },
-
-  onModalReblog (status) {
-    dispatch(reblog(status));
-  },
-
-  onReblog (status, e) {
-    if (status.get('reblogged')) {
-      dispatch(unreblog(status));
-    } else {
-      if (e.shiftKey || !boostModal) {
-        this.onModalReblog(status);
-      } else {
-        dispatch(openModal('BOOST', { status, onReblog: this.onModalReblog }));
-      }
-    }
-  },
-
-  onFavourite (status) {
-    if (status.get('favourited')) {
-      dispatch(unfavourite(status));
-    } else {
-      dispatch(favourite(status));
-    }
-  },
-
-  onPin (status) {
-    if (status.get('pinned')) {
-      dispatch(unpin(status));
-    } else {
-      dispatch(pin(status));
-    }
-  },
-
-  onEmbed (status) {
-    dispatch(openModal('EMBED', { url: status.get('url') }));
-  },
-
-  onDelete (status) {
-    if (!deleteModal) {
-      dispatch(deleteStatus(status.get('id')));
-    } else {
-      dispatch(openModal('CONFIRM', {
-        message: intl.formatMessage(messages.deleteMessage),
-        confirm: intl.formatMessage(messages.deleteConfirm),
-        onConfirm: () => dispatch(deleteStatus(status.get('id'))),
-      }));
-    }
-  },
-
-  onMention (account, router) {
-    dispatch(mentionCompose(account, router));
-  },
-
-  onOpenMedia (media, index) {
-    dispatch(openModal('MEDIA', { media, index }));
-  },
-
-  onOpenVideo (media, time) {
-    dispatch(openModal('VIDEO', { media, time }));
-  },
-
-  onBlock (account) {
-    dispatch(openModal('CONFIRM', {
-      message: <FormattedMessage id='confirmations.block.message' defaultMessage='Are you sure you want to block {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
-      confirm: intl.formatMessage(messages.blockConfirm),
-      onConfirm: () => dispatch(blockAccount(account.get('id'))),
-    }));
-  },
-
-  onReport (status) {
-    dispatch(initReport(status.get('account'), status));
-  },
-
-  onMute (account) {
-    dispatch(initMuteModal(account));
-  },
-
-  onMuteConversation (status) {
-    if (status.get('muted')) {
-      dispatch(unmuteStatus(status.get('id')));
-    } else {
-      dispatch(muteStatus(status.get('id')));
-    }
-  },
-
-});
-
-export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Status));
diff --git a/app/javascript/themes/glitch/containers/timeline_container.js b/app/javascript/themes/glitch/containers/timeline_container.js
deleted file mode 100644
index a75f8808d..000000000
--- a/app/javascript/themes/glitch/containers/timeline_container.js
+++ /dev/null
@@ -1,48 +0,0 @@
-import React from 'react';
-import { Provider } from 'react-redux';
-import PropTypes from 'prop-types';
-import configureStore from 'themes/glitch/store/configureStore';
-import { hydrateStore } from 'themes/glitch/actions/store';
-import { IntlProvider, addLocaleData } from 'react-intl';
-import { getLocale } from 'mastodon/locales';
-import PublicTimeline from 'themes/glitch/features/standalone/public_timeline';
-import HashtagTimeline from 'themes/glitch/features/standalone/hashtag_timeline';
-import initialState from 'themes/glitch/util/initial_state';
-
-const { localeData, messages } = getLocale();
-addLocaleData(localeData);
-
-const store = configureStore();
-
-if (initialState) {
-  store.dispatch(hydrateStore(initialState));
-}
-
-export default class TimelineContainer extends React.PureComponent {
-
-  static propTypes = {
-    locale: PropTypes.string.isRequired,
-    hashtag: PropTypes.string,
-  };
-
-  render () {
-    const { locale, hashtag } = this.props;
-
-    let timeline;
-
-    if (hashtag) {
-      timeline = <HashtagTimeline hashtag={hashtag} />;
-    } else {
-      timeline = <PublicTimeline />;
-    }
-
-    return (
-      <IntlProvider locale={locale} messages={messages}>
-        <Provider store={store}>
-          {timeline}
-        </Provider>
-      </IntlProvider>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/containers/video_container.js b/app/javascript/themes/glitch/containers/video_container.js
deleted file mode 100644
index 2b0e98666..000000000
--- a/app/javascript/themes/glitch/containers/video_container.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { IntlProvider, addLocaleData } from 'react-intl';
-import { getLocale } from 'mastodon/locales';
-import Video from 'themes/glitch/features/video';
-
-const { localeData, messages } = getLocale();
-addLocaleData(localeData);
-
-export default class VideoContainer extends React.PureComponent {
-
-  static propTypes = {
-    locale: PropTypes.string.isRequired,
-  };
-
-  render () {
-    const { locale, ...props } = this.props;
-
-    return (
-      <IntlProvider locale={locale} messages={messages}>
-        <Video {...props} />
-      </IntlProvider>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/account/components/action_bar.js b/app/javascript/themes/glitch/features/account/components/action_bar.js
deleted file mode 100644
index 0edd5c848..000000000
--- a/app/javascript/themes/glitch/features/account/components/action_bar.js
+++ /dev/null
@@ -1,145 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import DropdownMenuContainer from 'themes/glitch/containers/dropdown_menu_container';
-import { Link } from 'react-router-dom';
-import { defineMessages, injectIntl, FormattedMessage, FormattedNumber } from 'react-intl';
-import { me } from 'themes/glitch/util/initial_state';
-
-const messages = defineMessages({
-  mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' },
-  edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
-  unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
-  unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
-  unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
-  block: { id: 'account.block', defaultMessage: 'Block @{name}' },
-  mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
-  follow: { id: 'account.follow', defaultMessage: 'Follow' },
-  report: { id: 'account.report', defaultMessage: 'Report @{name}' },
-  share: { id: 'account.share', defaultMessage: 'Share @{name}\'s profile' },
-  media: { id: 'account.media', defaultMessage: 'Media' },
-  blockDomain: { id: 'account.block_domain', defaultMessage: 'Hide everything from {domain}' },
-  unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
-  hideReblogs: { id: 'account.hide_reblogs', defaultMessage: 'Hide boosts from @{name}' },
-  showReblogs: { id: 'account.show_reblogs', defaultMessage: 'Show boosts from @{name}' },
-});
-
-@injectIntl
-export default class ActionBar extends React.PureComponent {
-
-  static propTypes = {
-    account: ImmutablePropTypes.map.isRequired,
-    onFollow: PropTypes.func,
-    onBlock: PropTypes.func.isRequired,
-    onMention: PropTypes.func.isRequired,
-    onReblogToggle: PropTypes.func.isRequired,
-    onReport: PropTypes.func.isRequired,
-    onMute: PropTypes.func.isRequired,
-    onBlockDomain: PropTypes.func.isRequired,
-    onUnblockDomain: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  handleShare = () => {
-    navigator.share({
-      url: this.props.account.get('url'),
-    });
-  }
-
-  render () {
-    const { account, intl } = this.props;
-
-    let menu = [];
-    let extraInfo = '';
-
-    menu.push({ text: intl.formatMessage(messages.mention, { name: account.get('username') }), action: this.props.onMention });
-    if ('share' in navigator) {
-      menu.push({ text: intl.formatMessage(messages.share, { name: account.get('username') }), action: this.handleShare });
-    }
-    menu.push(null);
-    menu.push({ text: intl.formatMessage(messages.media), to: `/accounts/${account.get('id')}/media` });
-    menu.push(null);
-
-    if (account.get('id') === me) {
-      menu.push({ text: intl.formatMessage(messages.edit_profile), href: '/settings/profile' });
-    } else {
-      const following = account.getIn(['relationship', 'following']);
-      if (following) {
-        if (following.get('reblogs')) {
-          menu.push({ text: intl.formatMessage(messages.hideReblogs, { name: account.get('username') }), action: this.props.onReblogToggle });
-        } else {
-          menu.push({ text: intl.formatMessage(messages.showReblogs, { name: account.get('username') }), action: this.props.onReblogToggle });
-        }
-      }
-
-      if (account.getIn(['relationship', 'muting'])) {
-        menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get('username') }), action: this.props.onMute });
-      } else {
-        menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.props.onMute });
-      }
-
-      if (account.getIn(['relationship', 'blocking'])) {
-        menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get('username') }), action: this.props.onBlock });
-      } else {
-        menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.props.onBlock });
-      }
-
-      menu.push({ text: intl.formatMessage(messages.report, { name: account.get('username') }), action: this.props.onReport });
-    }
-
-    if (account.get('acct') !== account.get('username')) {
-      const domain = account.get('acct').split('@')[1];
-
-      extraInfo = (
-        <div className='account__disclaimer'>
-          <FormattedMessage
-            id='account.disclaimer_full'
-            defaultMessage="Information below may reflect the user's profile incompletely."
-          />
-          {' '}
-          <a target='_blank' rel='noopener' href={account.get('url')}>
-            <FormattedMessage id='account.view_full_profile' defaultMessage='View full profile' />
-          </a>
-        </div>
-      );
-
-      menu.push(null);
-
-      if (account.getIn(['relationship', 'domain_blocking'])) {
-        menu.push({ text: intl.formatMessage(messages.unblockDomain, { domain }), action: this.props.onUnblockDomain });
-      } else {
-        menu.push({ text: intl.formatMessage(messages.blockDomain, { domain }), action: this.props.onBlockDomain });
-      }
-    }
-
-    return (
-      <div>
-        {extraInfo}
-
-        <div className='account__action-bar'>
-          <div className='account__action-bar-dropdown'>
-            <DropdownMenuContainer items={menu} icon='bars' size={24} direction='right' />
-          </div>
-
-          <div className='account__action-bar-links'>
-            <Link className='account__action-bar__tab' to={`/accounts/${account.get('id')}`}>
-              <span><FormattedMessage id='account.posts' defaultMessage='Posts' /></span>
-              <strong><FormattedNumber value={account.get('statuses_count')} /></strong>
-            </Link>
-
-            <Link className='account__action-bar__tab' to={`/accounts/${account.get('id')}/following`}>
-              <span><FormattedMessage id='account.follows' defaultMessage='Follows' /></span>
-              <strong><FormattedNumber value={account.get('following_count')} /></strong>
-            </Link>
-
-            <Link className='account__action-bar__tab' to={`/accounts/${account.get('id')}/followers`}>
-              <span><FormattedMessage id='account.followers' defaultMessage='Followers' /></span>
-              <strong><FormattedNumber value={account.get('followers_count')} /></strong>
-            </Link>
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/account/components/header.js b/app/javascript/themes/glitch/features/account/components/header.js
deleted file mode 100644
index 696bb1991..000000000
--- a/app/javascript/themes/glitch/features/account/components/header.js
+++ /dev/null
@@ -1,99 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-import Avatar from 'themes/glitch/components/avatar';
-import IconButton from 'themes/glitch/components/icon_button';
-
-import emojify from 'themes/glitch/util/emoji';
-import { me } from 'themes/glitch/util/initial_state';
-import { processBio } from 'themes/glitch/util/bio_metadata';
-
-const messages = defineMessages({
-  unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
-  follow: { id: 'account.follow', defaultMessage: 'Follow' },
-  requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' },
-});
-
-@injectIntl
-export default class Header extends ImmutablePureComponent {
-
-  static propTypes = {
-    account: ImmutablePropTypes.map,
-    onFollow: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  render () {
-    const { account, intl } = this.props;
-
-    if (!account) {
-      return null;
-    }
-
-    let displayName = account.get('display_name_html');
-    let info        = '';
-    let actionBtn   = '';
-
-    if (me !== account.get('id') && account.getIn(['relationship', 'followed_by'])) {
-      info = <span className='account--follows-info'><FormattedMessage id='account.follows_you' defaultMessage='Follows you' /></span>;
-    }
-
-    if (me !== account.get('id')) {
-      if (account.getIn(['relationship', 'requested'])) {
-        actionBtn = (
-          <div className='account--action-button'>
-            <IconButton size={26} active icon='hourglass' title={intl.formatMessage(messages.requested)} onClick={this.props.onFollow} />
-          </div>
-        );
-      } else if (!account.getIn(['relationship', 'blocking'])) {
-        actionBtn = (
-          <div className='account--action-button'>
-            <IconButton size={26} icon={account.getIn(['relationship', 'following']) ? 'user-times' : 'user-plus'} active={account.getIn(['relationship', 'following'])} title={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={this.props.onFollow} />
-          </div>
-        );
-      }
-    }
-
-    const { text, metadata } = processBio(account.get('note'));
-
-    return (
-      <div className='account__header__wrapper'>
-        <div className='account__header' style={{ backgroundImage: `url(${account.get('header')})` }}>
-          <div>
-            <Avatar account={account} size={90} />
-
-            <span className='account__header__display-name' dangerouslySetInnerHTML={{ __html: displayName }} />
-            <span className='account__header__username'>@{account.get('acct')} {account.get('locked') ? <i className='fa fa-lock' /> : null}</span>
-            <div className='account__header__content' dangerouslySetInnerHTML={{ __html: emojify(text) }} />
-
-            {info}
-            {actionBtn}
-          </div>
-        </div>
-
-        {metadata.length && (
-          <table className='account__metadata'>
-            <tbody>
-              {(() => {
-                let data = [];
-                for (let i = 0; i < metadata.length; i++) {
-                  data.push(
-                    <tr key={i}>
-                      <th scope='row'><div dangerouslySetInnerHTML={{ __html: emojify(metadata[i][0]) }} /></th>
-                      <td><div dangerouslySetInnerHTML={{ __html: emojify(metadata[i][1]) }} /></td>
-                    </tr>
-                  );
-                }
-                return data;
-              })()}
-            </tbody>
-          </table>
-        ) || null}
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/account_gallery/components/media_item.js b/app/javascript/themes/glitch/features/account_gallery/components/media_item.js
deleted file mode 100644
index 88c9156b5..000000000
--- a/app/javascript/themes/glitch/features/account_gallery/components/media_item.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import Permalink from 'themes/glitch/components/permalink';
-
-export default class MediaItem extends ImmutablePureComponent {
-
-  static propTypes = {
-    media: ImmutablePropTypes.map.isRequired,
-  };
-
-  render () {
-    const { media } = this.props;
-    const status = media.get('status');
-
-    let content, style;
-
-    if (media.get('type') === 'gifv') {
-      content = <span className='media-gallery__gifv__label'>GIF</span>;
-    }
-
-    if (!status.get('sensitive')) {
-      style = { backgroundImage: `url(${media.get('preview_url')})` };
-    }
-
-    return (
-      <div className='account-gallery__item'>
-        <Permalink
-          to={`/statuses/${status.get('id')}`}
-          href={status.get('url')}
-          style={style}
-        >
-          {content}
-        </Permalink>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/account_gallery/index.js b/app/javascript/themes/glitch/features/account_gallery/index.js
deleted file mode 100644
index a21c089da..000000000
--- a/app/javascript/themes/glitch/features/account_gallery/index.js
+++ /dev/null
@@ -1,111 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import { fetchAccount } from 'themes/glitch/actions/accounts';
-import { refreshAccountMediaTimeline, expandAccountMediaTimeline } from 'themes/glitch/actions/timelines';
-import LoadingIndicator from 'themes/glitch/components/loading_indicator';
-import Column from 'themes/glitch/features/ui/components/column';
-import ColumnBackButton from 'themes/glitch/components/column_back_button';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { getAccountGallery } from 'themes/glitch/selectors';
-import MediaItem from './components/media_item';
-import HeaderContainer from 'themes/glitch/features/account_timeline/containers/header_container';
-import { FormattedMessage } from 'react-intl';
-import { ScrollContainer } from 'react-router-scroll-4';
-import LoadMore from 'themes/glitch/components/load_more';
-
-const mapStateToProps = (state, props) => ({
-  medias: getAccountGallery(state, props.params.accountId),
-  isLoading: state.getIn(['timelines', `account:${props.params.accountId}:media`, 'isLoading']),
-  hasMore: !!state.getIn(['timelines', `account:${props.params.accountId}:media`, 'next']),
-});
-
-@connect(mapStateToProps)
-export default class AccountGallery extends ImmutablePureComponent {
-
-  static propTypes = {
-    params: PropTypes.object.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    medias: ImmutablePropTypes.list.isRequired,
-    isLoading: PropTypes.bool,
-    hasMore: PropTypes.bool,
-  };
-
-  componentDidMount () {
-    this.props.dispatch(fetchAccount(this.props.params.accountId));
-    this.props.dispatch(refreshAccountMediaTimeline(this.props.params.accountId));
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) {
-      this.props.dispatch(fetchAccount(nextProps.params.accountId));
-      this.props.dispatch(refreshAccountMediaTimeline(this.props.params.accountId));
-    }
-  }
-
-  handleScrollToBottom = () => {
-    if (this.props.hasMore) {
-      this.props.dispatch(expandAccountMediaTimeline(this.props.params.accountId));
-    }
-  }
-
-  handleScroll = (e) => {
-    const { scrollTop, scrollHeight, clientHeight } = e.target;
-    const offset = scrollHeight - scrollTop - clientHeight;
-
-    if (150 > offset && !this.props.isLoading) {
-      this.handleScrollToBottom();
-    }
-  }
-
-  handleLoadMore = (e) => {
-    e.preventDefault();
-    this.handleScrollToBottom();
-  }
-
-  render () {
-    const { medias, isLoading, hasMore } = this.props;
-
-    let loadMore = null;
-
-    if (!medias && isLoading) {
-      return (
-        <Column>
-          <LoadingIndicator />
-        </Column>
-      );
-    }
-
-    if (!isLoading && medias.size > 0 && hasMore) {
-      loadMore = <LoadMore onClick={this.handleLoadMore} />;
-    }
-
-    return (
-      <Column>
-        <ColumnBackButton />
-
-        <ScrollContainer scrollKey='account_gallery'>
-          <div className='scrollable' onScroll={this.handleScroll}>
-            <HeaderContainer accountId={this.props.params.accountId} />
-
-            <div className='account-section-headline'>
-              <FormattedMessage id='account.media' defaultMessage='Media' />
-            </div>
-
-            <div className='account-gallery__container'>
-              {medias.map(media =>
-                <MediaItem
-                  key={media.get('id')}
-                  media={media}
-                />
-              )}
-              {loadMore}
-            </div>
-          </div>
-        </ScrollContainer>
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/account_timeline/components/header.js b/app/javascript/themes/glitch/features/account_timeline/components/header.js
deleted file mode 100644
index c719a7bcb..000000000
--- a/app/javascript/themes/glitch/features/account_timeline/components/header.js
+++ /dev/null
@@ -1,95 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import InnerHeader from 'themes/glitch/features/account/components/header';
-import ActionBar from 'themes/glitch/features/account/components/action_bar';
-import MissingIndicator from 'themes/glitch/components/missing_indicator';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-export default class Header extends ImmutablePureComponent {
-
-  static propTypes = {
-    account: ImmutablePropTypes.map,
-    onFollow: PropTypes.func.isRequired,
-    onBlock: PropTypes.func.isRequired,
-    onMention: PropTypes.func.isRequired,
-    onReblogToggle: PropTypes.func.isRequired,
-    onReport: PropTypes.func.isRequired,
-    onMute: PropTypes.func.isRequired,
-    onBlockDomain: PropTypes.func.isRequired,
-    onUnblockDomain: PropTypes.func.isRequired,
-  };
-
-  static contextTypes = {
-    router: PropTypes.object,
-  };
-
-  handleFollow = () => {
-    this.props.onFollow(this.props.account);
-  }
-
-  handleBlock = () => {
-    this.props.onBlock(this.props.account);
-  }
-
-  handleMention = () => {
-    this.props.onMention(this.props.account, this.context.router.history);
-  }
-
-  handleReport = () => {
-    this.props.onReport(this.props.account);
-  }
-
-  handleReblogToggle = () => {
-    this.props.onReblogToggle(this.props.account);
-  }
-
-  handleMute = () => {
-    this.props.onMute(this.props.account);
-  }
-
-  handleBlockDomain = () => {
-    const domain = this.props.account.get('acct').split('@')[1];
-
-    if (!domain) return;
-
-    this.props.onBlockDomain(domain, this.props.account.get('id'));
-  }
-
-  handleUnblockDomain = () => {
-    const domain = this.props.account.get('acct').split('@')[1];
-
-    if (!domain) return;
-
-    this.props.onUnblockDomain(domain, this.props.account.get('id'));
-  }
-
-  render () {
-    const { account } = this.props;
-
-    if (account === null) {
-      return <MissingIndicator />;
-    }
-
-    return (
-      <div className='account-timeline__header'>
-        <InnerHeader
-          account={account}
-          onFollow={this.handleFollow}
-        />
-
-        <ActionBar
-          account={account}
-          onBlock={this.handleBlock}
-          onMention={this.handleMention}
-          onReblogToggle={this.handleReblogToggle}
-          onReport={this.handleReport}
-          onMute={this.handleMute}
-          onBlockDomain={this.handleBlockDomain}
-          onUnblockDomain={this.handleUnblockDomain}
-        />
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/account_timeline/containers/header_container.js b/app/javascript/themes/glitch/features/account_timeline/containers/header_container.js
deleted file mode 100644
index 766b57b56..000000000
--- a/app/javascript/themes/glitch/features/account_timeline/containers/header_container.js
+++ /dev/null
@@ -1,104 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import { makeGetAccount } from 'themes/glitch/selectors';
-import Header from '../components/header';
-import {
-  followAccount,
-  unfollowAccount,
-  blockAccount,
-  unblockAccount,
-  unmuteAccount,
-} from 'themes/glitch/actions/accounts';
-import { mentionCompose } from 'themes/glitch/actions/compose';
-import { initMuteModal } from 'themes/glitch/actions/mutes';
-import { initReport } from 'themes/glitch/actions/reports';
-import { openModal } from 'themes/glitch/actions/modal';
-import { blockDomain, unblockDomain } from 'themes/glitch/actions/domain_blocks';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import { unfollowModal } from 'themes/glitch/util/initial_state';
-
-const messages = defineMessages({
-  unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' },
-  blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' },
-  blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
-});
-
-const makeMapStateToProps = () => {
-  const getAccount = makeGetAccount();
-
-  const mapStateToProps = (state, { accountId }) => ({
-    account: getAccount(state, accountId),
-  });
-
-  return mapStateToProps;
-};
-
-const mapDispatchToProps = (dispatch, { intl }) => ({
-
-  onFollow (account) {
-    if (account.getIn(['relationship', 'following']) || account.getIn(['relationship', 'requested'])) {
-      if (unfollowModal) {
-        dispatch(openModal('CONFIRM', {
-          message: <FormattedMessage id='confirmations.unfollow.message' defaultMessage='Are you sure you want to unfollow {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
-          confirm: intl.formatMessage(messages.unfollowConfirm),
-          onConfirm: () => dispatch(unfollowAccount(account.get('id'))),
-        }));
-      } else {
-        dispatch(unfollowAccount(account.get('id')));
-      }
-    } else {
-      dispatch(followAccount(account.get('id')));
-    }
-  },
-
-  onBlock (account) {
-    if (account.getIn(['relationship', 'blocking'])) {
-      dispatch(unblockAccount(account.get('id')));
-    } else {
-      dispatch(openModal('CONFIRM', {
-        message: <FormattedMessage id='confirmations.block.message' defaultMessage='Are you sure you want to block {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
-        confirm: intl.formatMessage(messages.blockConfirm),
-        onConfirm: () => dispatch(blockAccount(account.get('id'))),
-      }));
-    }
-  },
-
-  onMention (account, router) {
-    dispatch(mentionCompose(account, router));
-  },
-
-  onReblogToggle (account) {
-    if (account.getIn(['relationship', 'following', 'reblogs'])) {
-      dispatch(followAccount(account.get('id'), false));
-    } else {
-      dispatch(followAccount(account.get('id'), true));
-    }
-  },
-
-  onReport (account) {
-    dispatch(initReport(account));
-  },
-
-  onMute (account) {
-    if (account.getIn(['relationship', 'muting'])) {
-      dispatch(unmuteAccount(account.get('id')));
-    } else {
-      dispatch(initMuteModal(account));
-    }
-  },
-
-  onBlockDomain (domain, accountId) {
-    dispatch(openModal('CONFIRM', {
-      message: <FormattedMessage id='confirmations.domain_block.message' defaultMessage='Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.' values={{ domain: <strong>{domain}</strong> }} />,
-      confirm: intl.formatMessage(messages.blockDomainConfirm),
-      onConfirm: () => dispatch(blockDomain(domain, accountId)),
-    }));
-  },
-
-  onUnblockDomain (domain, accountId) {
-    dispatch(unblockDomain(domain, accountId));
-  },
-
-});
-
-export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Header));
diff --git a/app/javascript/themes/glitch/features/account_timeline/index.js b/app/javascript/themes/glitch/features/account_timeline/index.js
deleted file mode 100644
index 81336ef3a..000000000
--- a/app/javascript/themes/glitch/features/account_timeline/index.js
+++ /dev/null
@@ -1,77 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import { fetchAccount } from 'themes/glitch/actions/accounts';
-import { refreshAccountTimeline, expandAccountTimeline } from 'themes/glitch/actions/timelines';
-import StatusList from '../../components/status_list';
-import LoadingIndicator from '../../components/loading_indicator';
-import Column from '../ui/components/column';
-import HeaderContainer from './containers/header_container';
-import ColumnBackButton from '../../components/column_back_button';
-import { List as ImmutableList } from 'immutable';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const mapStateToProps = (state, props) => ({
-  statusIds: state.getIn(['timelines', `account:${props.params.accountId}`, 'items'], ImmutableList()),
-  isLoading: state.getIn(['timelines', `account:${props.params.accountId}`, 'isLoading']),
-  hasMore: !!state.getIn(['timelines', `account:${props.params.accountId}`, 'next']),
-});
-
-@connect(mapStateToProps)
-export default class AccountTimeline extends ImmutablePureComponent {
-
-  static propTypes = {
-    params: PropTypes.object.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    statusIds: ImmutablePropTypes.list,
-    isLoading: PropTypes.bool,
-    hasMore: PropTypes.bool,
-  };
-
-  componentWillMount () {
-    this.props.dispatch(fetchAccount(this.props.params.accountId));
-    this.props.dispatch(refreshAccountTimeline(this.props.params.accountId));
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) {
-      this.props.dispatch(fetchAccount(nextProps.params.accountId));
-      this.props.dispatch(refreshAccountTimeline(nextProps.params.accountId));
-    }
-  }
-
-  handleScrollToBottom = () => {
-    if (!this.props.isLoading && this.props.hasMore) {
-      this.props.dispatch(expandAccountTimeline(this.props.params.accountId));
-    }
-  }
-
-  render () {
-    const { statusIds, isLoading, hasMore } = this.props;
-
-    if (!statusIds && isLoading) {
-      return (
-        <Column>
-          <LoadingIndicator />
-        </Column>
-      );
-    }
-
-    return (
-      <Column name='account'>
-        <ColumnBackButton />
-
-        <StatusList
-          prepend={<HeaderContainer accountId={this.props.params.accountId} />}
-          scrollKey='account_timeline'
-          statusIds={statusIds}
-          isLoading={isLoading}
-          hasMore={hasMore}
-          onScrollToBottom={this.handleScrollToBottom}
-        />
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/blocks/index.js b/app/javascript/themes/glitch/features/blocks/index.js
deleted file mode 100644
index 70630818c..000000000
--- a/app/javascript/themes/glitch/features/blocks/index.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import LoadingIndicator from 'themes/glitch/components/loading_indicator';
-import { ScrollContainer } from 'react-router-scroll-4';
-import Column from 'themes/glitch/features/ui/components/column';
-import ColumnBackButtonSlim from 'themes/glitch/components/column_back_button_slim';
-import AccountContainer from 'themes/glitch/containers/account_container';
-import { fetchBlocks, expandBlocks } from 'themes/glitch/actions/blocks';
-import { defineMessages, injectIntl } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const messages = defineMessages({
-  heading: { id: 'column.blocks', defaultMessage: 'Blocked users' },
-});
-
-const mapStateToProps = state => ({
-  accountIds: state.getIn(['user_lists', 'blocks', 'items']),
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class Blocks extends ImmutablePureComponent {
-
-  static propTypes = {
-    params: PropTypes.object.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    accountIds: ImmutablePropTypes.list,
-    intl: PropTypes.object.isRequired,
-  };
-
-  componentWillMount () {
-    this.props.dispatch(fetchBlocks());
-  }
-
-  handleScroll = (e) => {
-    const { scrollTop, scrollHeight, clientHeight } = e.target;
-
-    if (scrollTop === scrollHeight - clientHeight) {
-      this.props.dispatch(expandBlocks());
-    }
-  }
-
-  render () {
-    const { intl, accountIds } = this.props;
-
-    if (!accountIds) {
-      return (
-        <Column>
-          <LoadingIndicator />
-        </Column>
-      );
-    }
-
-    return (
-      <Column name='blocks' icon='ban' heading={intl.formatMessage(messages.heading)}>
-        <ColumnBackButtonSlim />
-        <ScrollContainer scrollKey='blocks'>
-          <div className='scrollable' onScroll={this.handleScroll}>
-            {accountIds.map(id =>
-              <AccountContainer key={id} id={id} />
-            )}
-          </div>
-        </ScrollContainer>
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/community_timeline/components/column_settings.js b/app/javascript/themes/glitch/features/community_timeline/components/column_settings.js
deleted file mode 100644
index 988e36308..000000000
--- a/app/javascript/themes/glitch/features/community_timeline/components/column_settings.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import SettingText from 'themes/glitch/components/setting_text';
-
-const messages = defineMessages({
-  filter_regex: { id: 'home.column_settings.filter_regex', defaultMessage: 'Filter out by regular expressions' },
-  settings: { id: 'home.settings', defaultMessage: 'Column settings' },
-});
-
-@injectIntl
-export default class ColumnSettings extends React.PureComponent {
-
-  static propTypes = {
-    settings: ImmutablePropTypes.map.isRequired,
-    onChange: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  render () {
-    const { settings, onChange, intl } = this.props;
-
-    return (
-      <div>
-        <span className='column-settings__section'><FormattedMessage id='home.column_settings.advanced' defaultMessage='Advanced' /></span>
-
-        <div className='column-settings__row'>
-          <SettingText settings={settings} settingKey={['regex', 'body']} onChange={onChange} label={intl.formatMessage(messages.filter_regex)} />
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/community_timeline/containers/column_settings_container.js b/app/javascript/themes/glitch/features/community_timeline/containers/column_settings_container.js
deleted file mode 100644
index cd9c34365..000000000
--- a/app/javascript/themes/glitch/features/community_timeline/containers/column_settings_container.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import { connect } from 'react-redux';
-import ColumnSettings from '../components/column_settings';
-import { changeSetting } from 'themes/glitch/actions/settings';
-
-const mapStateToProps = state => ({
-  settings: state.getIn(['settings', 'community']),
-});
-
-const mapDispatchToProps = dispatch => ({
-
-  onChange (key, checked) {
-    dispatch(changeSetting(['community', ...key], checked));
-  },
-
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings);
diff --git a/app/javascript/themes/glitch/features/community_timeline/index.js b/app/javascript/themes/glitch/features/community_timeline/index.js
deleted file mode 100644
index 9d255bd01..000000000
--- a/app/javascript/themes/glitch/features/community_timeline/index.js
+++ /dev/null
@@ -1,107 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import StatusListContainer from 'themes/glitch/features/ui/containers/status_list_container';
-import Column from 'themes/glitch/components/column';
-import ColumnHeader from 'themes/glitch/components/column_header';
-import {
-  refreshCommunityTimeline,
-  expandCommunityTimeline,
-} from 'themes/glitch/actions/timelines';
-import { addColumn, removeColumn, moveColumn } from 'themes/glitch/actions/columns';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import ColumnSettingsContainer from './containers/column_settings_container';
-import { connectCommunityStream } from 'themes/glitch/actions/streaming';
-
-const messages = defineMessages({
-  title: { id: 'column.community', defaultMessage: 'Local timeline' },
-});
-
-const mapStateToProps = state => ({
-  hasUnread: state.getIn(['timelines', 'community', 'unread']) > 0,
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class CommunityTimeline extends React.PureComponent {
-
-  static propTypes = {
-    dispatch: PropTypes.func.isRequired,
-    columnId: PropTypes.string,
-    intl: PropTypes.object.isRequired,
-    hasUnread: PropTypes.bool,
-    multiColumn: PropTypes.bool,
-  };
-
-  handlePin = () => {
-    const { columnId, dispatch } = this.props;
-
-    if (columnId) {
-      dispatch(removeColumn(columnId));
-    } else {
-      dispatch(addColumn('COMMUNITY', {}));
-    }
-  }
-
-  handleMove = (dir) => {
-    const { columnId, dispatch } = this.props;
-    dispatch(moveColumn(columnId, dir));
-  }
-
-  handleHeaderClick = () => {
-    this.column.scrollTop();
-  }
-
-  componentDidMount () {
-    const { dispatch } = this.props;
-
-    dispatch(refreshCommunityTimeline());
-    this.disconnect = dispatch(connectCommunityStream());
-  }
-
-  componentWillUnmount () {
-    if (this.disconnect) {
-      this.disconnect();
-      this.disconnect = null;
-    }
-  }
-
-  setRef = c => {
-    this.column = c;
-  }
-
-  handleLoadMore = () => {
-    this.props.dispatch(expandCommunityTimeline());
-  }
-
-  render () {
-    const { intl, hasUnread, columnId, multiColumn } = this.props;
-    const pinned = !!columnId;
-
-    return (
-      <Column ref={this.setRef} name='local'>
-        <ColumnHeader
-          icon='users'
-          active={hasUnread}
-          title={intl.formatMessage(messages.title)}
-          onPin={this.handlePin}
-          onMove={this.handleMove}
-          onClick={this.handleHeaderClick}
-          pinned={pinned}
-          multiColumn={multiColumn}
-        >
-          <ColumnSettingsContainer />
-        </ColumnHeader>
-
-        <StatusListContainer
-          trackScroll={!pinned}
-          scrollKey={`community_timeline-${columnId}`}
-          timelineId='community'
-          loadMore={this.handleLoadMore}
-          emptyMessage={<FormattedMessage id='empty_column.community' defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' />}
-        />
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/advanced_options.js b/app/javascript/themes/glitch/features/compose/components/advanced_options.js
deleted file mode 100644
index 045bad2e5..000000000
--- a/app/javascript/themes/glitch/features/compose/components/advanced_options.js
+++ /dev/null
@@ -1,62 +0,0 @@
-//  Package imports.
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { injectIntl, defineMessages } from 'react-intl';
-
-//  Our imports.
-import ComposeAdvancedOptionsToggle from './advanced_options_toggle';
-import ComposeDropdown from './dropdown';
-
-const messages = defineMessages({
-  local_only_short            :
-    { id: 'advanced-options.local-only.short', defaultMessage: 'Local-only' },
-  local_only_long             :
-    { id: 'advanced-options.local-only.long', defaultMessage: 'Do not post to other instances' },
-  advanced_options_icon_title :
-    { id: 'advanced_options.icon_title', defaultMessage: 'Advanced options' },
-});
-
-@injectIntl
-export default class ComposeAdvancedOptions extends React.PureComponent {
-
-  static propTypes = {
-    values   : ImmutablePropTypes.contains({
-      do_not_federate : PropTypes.bool.isRequired,
-    }).isRequired,
-    onChange : PropTypes.func.isRequired,
-    intl     : PropTypes.object.isRequired,
-  };
-
-  render () {
-    const { intl, values } = this.props;
-    const options = [
-      { icon: 'wifi', shortText: messages.local_only_short, longText: messages.local_only_long, name: 'do_not_federate' },
-    ];
-    const anyEnabled = values.some((enabled) => enabled);
-
-    const optionElems = options.map((option) => {
-      return (
-        <ComposeAdvancedOptionsToggle
-          onChange={this.props.onChange}
-          active={values.get(option.name)}
-          key={option.name}
-          name={option.name}
-          shortText={intl.formatMessage(option.shortText)}
-          longText={intl.formatMessage(option.longText)}
-        />
-      );
-    });
-
-    return (
-      <ComposeDropdown
-        title={intl.formatMessage(messages.advanced_options_icon_title)}
-        icon='home'
-        highlight={anyEnabled}
-      >
-        {optionElems}
-      </ComposeDropdown>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/advanced_options_toggle.js b/app/javascript/themes/glitch/features/compose/components/advanced_options_toggle.js
deleted file mode 100644
index 98b3b6a44..000000000
--- a/app/javascript/themes/glitch/features/compose/components/advanced_options_toggle.js
+++ /dev/null
@@ -1,35 +0,0 @@
-//  Package imports.
-import React from 'react';
-import PropTypes from 'prop-types';
-import Toggle from 'react-toggle';
-
-export default class ComposeAdvancedOptionsToggle extends React.PureComponent {
-
-  static propTypes = {
-    onChange: PropTypes.func.isRequired,
-    active: PropTypes.bool.isRequired,
-    name: PropTypes.string.isRequired,
-    shortText: PropTypes.string.isRequired,
-    longText: PropTypes.string.isRequired,
-  }
-
-  onToggle = () => {
-    this.props.onChange(this.props.name);
-  }
-
-  render() {
-    const { active, shortText, longText } = this.props;
-    return (
-      <div role='button' tabIndex='0' className='advanced-options-dropdown__option' onClick={this.onToggle}>
-        <div className='advanced-options-dropdown__option__toggle'>
-          <Toggle checked={active} onChange={this.onToggle} />
-        </div>
-        <div className='advanced-options-dropdown__option__content'>
-          <strong>{shortText}</strong>
-          {longText}
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/attach_options.js b/app/javascript/themes/glitch/features/compose/components/attach_options.js
deleted file mode 100644
index c396714f3..000000000
--- a/app/javascript/themes/glitch/features/compose/components/attach_options.js
+++ /dev/null
@@ -1,131 +0,0 @@
-//  Package imports  //
-import React from 'react';
-import PropTypes from 'prop-types';
-import { connect } from 'react-redux';
-import { injectIntl, defineMessages } from 'react-intl';
-
-//  Our imports  //
-import ComposeDropdown from './dropdown';
-import { uploadCompose } from 'themes/glitch/actions/compose';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { openModal } from 'themes/glitch/actions/modal';
-
-const messages = defineMessages({
-  upload :
-    { id: 'compose.attach.upload', defaultMessage: 'Upload a file' },
-  doodle :
-    { id: 'compose.attach.doodle', defaultMessage: 'Draw something' },
-  attach :
-    { id: 'compose.attach', defaultMessage: 'Attach...' },
-});
-
-const mapStateToProps = state => ({
-  // This horrible expression is copied from vanilla upload_button_container
-  disabled: state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size > 3 || state.getIn(['compose', 'media_attachments']).some(m => m.get('type') === 'video')),
-  resetFileKey: state.getIn(['compose', 'resetFileKey']),
-  acceptContentTypes: state.getIn(['media_attachments', 'accept_content_types']),
-});
-
-const mapDispatchToProps = dispatch => ({
-  onSelectFile (files) {
-    dispatch(uploadCompose(files));
-  },
-  onOpenDoodle () {
-    dispatch(openModal('DOODLE', { noEsc: true }));
-  },
-});
-
-@injectIntl
-@connect(mapStateToProps, mapDispatchToProps)
-export default class ComposeAttachOptions extends ImmutablePureComponent {
-
-  static propTypes = {
-    intl     : PropTypes.object.isRequired,
-    resetFileKey: PropTypes.number,
-    acceptContentTypes: ImmutablePropTypes.listOf(PropTypes.string).isRequired,
-    disabled: PropTypes.bool,
-    onSelectFile: PropTypes.func.isRequired,
-    onOpenDoodle: PropTypes.func.isRequired,
-  };
-
-  handleItemClick = bt => {
-    if (bt === 'upload') {
-      this.fileElement.click();
-    }
-
-    if (bt === 'doodle') {
-      this.props.onOpenDoodle();
-    }
-
-    this.dropdown.setState({ open: false });
-  };
-
-  handleFileChange = (e) => {
-    if (e.target.files.length > 0) {
-      this.props.onSelectFile(e.target.files);
-    }
-  }
-
-  setFileRef = (c) => {
-    this.fileElement = c;
-  }
-
-  setDropdownRef = (c) => {
-    this.dropdown = c;
-  }
-
-  render () {
-    const { intl, resetFileKey, disabled, acceptContentTypes } = this.props;
-
-    const options = [
-      { icon: 'cloud-upload', text: messages.upload, name: 'upload' },
-      { icon: 'paint-brush', text: messages.doodle, name: 'doodle' },
-    ];
-
-    const optionElems = options.map((item) => {
-      const hdl = () => this.handleItemClick(item.name);
-      return (
-        <div
-          role='button'
-          tabIndex='0'
-          key={item.name}
-          onClick={hdl}
-          className='privacy-dropdown__option'
-        >
-          <div className='privacy-dropdown__option__icon'>
-            <i className={`fa fa-fw fa-${item.icon}`} />
-          </div>
-
-          <div className='privacy-dropdown__option__content'>
-            <strong>{intl.formatMessage(item.text)}</strong>
-          </div>
-        </div>
-      );
-    });
-
-    return (
-      <div>
-        <ComposeDropdown
-          title={intl.formatMessage(messages.attach)}
-          icon='paperclip'
-          disabled={disabled}
-          ref={this.setDropdownRef}
-        >
-          {optionElems}
-        </ComposeDropdown>
-        <input
-          key={resetFileKey}
-          ref={this.setFileRef}
-          type='file'
-          multiple={false}
-          accept={acceptContentTypes.toArray().join(',')}
-          onChange={this.handleFileChange}
-          disabled={disabled}
-          style={{ display: 'none' }}
-        />
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/autosuggest_account.js b/app/javascript/themes/glitch/features/compose/components/autosuggest_account.js
deleted file mode 100644
index 4a98d89fe..000000000
--- a/app/javascript/themes/glitch/features/compose/components/autosuggest_account.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import React from 'react';
-import Avatar from 'themes/glitch/components/avatar';
-import DisplayName from 'themes/glitch/components/display_name';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-export default class AutosuggestAccount extends ImmutablePureComponent {
-
-  static propTypes = {
-    account: ImmutablePropTypes.map.isRequired,
-  };
-
-  render () {
-    const { account } = this.props;
-
-    return (
-      <div className='autosuggest-account'>
-        <div className='autosuggest-account-icon'><Avatar account={account} size={18} /></div>
-        <DisplayName account={account} />
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/character_counter.js b/app/javascript/themes/glitch/features/compose/components/character_counter.js
deleted file mode 100644
index 0ecfc9141..000000000
--- a/app/javascript/themes/glitch/features/compose/components/character_counter.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { length } from 'stringz';
-
-export default class CharacterCounter extends React.PureComponent {
-
-  static propTypes = {
-    text: PropTypes.string.isRequired,
-    max: PropTypes.number.isRequired,
-  };
-
-  checkRemainingText (diff) {
-    if (diff < 0) {
-      return <span className='character-counter character-counter--over'>{diff}</span>;
-    }
-
-    return <span className='character-counter'>{diff}</span>;
-  }
-
-  render () {
-    const diff = this.props.max - length(this.props.text);
-    return this.checkRemainingText(diff);
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/compose_form.js b/app/javascript/themes/glitch/features/compose/components/compose_form.js
deleted file mode 100644
index 54b1944a4..000000000
--- a/app/javascript/themes/glitch/features/compose/components/compose_form.js
+++ /dev/null
@@ -1,286 +0,0 @@
-import React from 'react';
-import CharacterCounter from './character_counter';
-import Button from 'themes/glitch/components/button';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import ReplyIndicatorContainer from '../containers/reply_indicator_container';
-import AutosuggestTextarea from 'themes/glitch/components/autosuggest_textarea';
-import { defineMessages, injectIntl } from 'react-intl';
-import Collapsable from 'themes/glitch/components/collapsable';
-import SpoilerButtonContainer from '../containers/spoiler_button_container';
-import PrivacyDropdownContainer from '../containers/privacy_dropdown_container';
-import ComposeAdvancedOptionsContainer from '../containers/advanced_options_container';
-import SensitiveButtonContainer from '../containers/sensitive_button_container';
-import EmojiPickerDropdown from '../containers/emoji_picker_dropdown_container';
-import UploadFormContainer from '../containers/upload_form_container';
-import WarningContainer from '../containers/warning_container';
-import { isMobile } from 'themes/glitch/util/is_mobile';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { length } from 'stringz';
-import { countableText } from 'themes/glitch/util/counter';
-import ComposeAttachOptions from './attach_options';
-import initialState from 'themes/glitch/util/initial_state';
-
-const maxChars = initialState.max_toot_chars;
-
-const messages = defineMessages({
-  placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' },
-  spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Write your warning here' },
-  publish: { id: 'compose_form.publish', defaultMessage: 'Toot' },
-  publishLoud: { id: 'compose_form.publish_loud', defaultMessage: '{publish}!' },
-});
-
-@injectIntl
-export default class ComposeForm extends ImmutablePureComponent {
-
-  static propTypes = {
-    intl: PropTypes.object.isRequired,
-    text: PropTypes.string.isRequired,
-    suggestion_token: PropTypes.string,
-    suggestions: ImmutablePropTypes.list,
-    spoiler: PropTypes.bool,
-    privacy: PropTypes.string,
-    advanced_options: ImmutablePropTypes.contains({
-      do_not_federate: PropTypes.bool,
-    }),
-    spoiler_text: PropTypes.string,
-    focusDate: PropTypes.instanceOf(Date),
-    preselectDate: PropTypes.instanceOf(Date),
-    is_submitting: PropTypes.bool,
-    is_uploading: PropTypes.bool,
-    onChange: PropTypes.func.isRequired,
-    onSubmit: PropTypes.func.isRequired,
-    onClearSuggestions: PropTypes.func.isRequired,
-    onFetchSuggestions: PropTypes.func.isRequired,
-    onPrivacyChange: PropTypes.func.isRequired,
-    onSuggestionSelected: PropTypes.func.isRequired,
-    onChangeSpoilerText: PropTypes.func.isRequired,
-    onPaste: PropTypes.func.isRequired,
-    onPickEmoji: PropTypes.func.isRequired,
-    showSearch: PropTypes.bool,
-    settings : ImmutablePropTypes.map.isRequired,
-  };
-
-  static defaultProps = {
-    showSearch: false,
-  };
-
-  handleChange = (e) => {
-    this.props.onChange(e.target.value);
-  }
-
-  handleKeyDown = (e) => {
-    if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
-      this.handleSubmit();
-    }
-  }
-
-  handleSubmit2 = () => {
-    this.props.onPrivacyChange(this.props.settings.get('side_arm'));
-    this.handleSubmit();
-  }
-
-  handleSubmit = () => {
-    if (this.props.text !== this.autosuggestTextarea.textarea.value) {
-      // Something changed the text inside the textarea (e.g. browser extensions like Grammarly)
-      // Update the state to match the current text
-      this.props.onChange(this.autosuggestTextarea.textarea.value);
-    }
-
-    this.props.onSubmit();
-  }
-
-  onSuggestionsClearRequested = () => {
-    this.props.onClearSuggestions();
-  }
-
-  onSuggestionsFetchRequested = (token) => {
-    this.props.onFetchSuggestions(token);
-  }
-
-  onSuggestionSelected = (tokenStart, token, value) => {
-    this._restoreCaret = null;
-    this.props.onSuggestionSelected(tokenStart, token, value);
-  }
-
-  handleChangeSpoilerText = (e) => {
-    this.props.onChangeSpoilerText(e.target.value);
-  }
-
-  componentWillReceiveProps (nextProps) {
-    // If this is the update where we've finished uploading,
-    // save the last caret position so we can restore it below!
-    if (!nextProps.is_uploading && this.props.is_uploading) {
-      this._restoreCaret = this.autosuggestTextarea.textarea.selectionStart;
-    }
-  }
-
-  componentDidUpdate (prevProps) {
-    // This statement does several things:
-    // - If we're beginning a reply, and,
-    //     - Replying to zero or one users, places the cursor at the end of the textbox.
-    //     - Replying to more than one user, selects any usernames past the first;
-    //       this provides a convenient shortcut to drop everyone else from the conversation.
-    // - If we've just finished uploading an image, and have a saved caret position,
-    //   restores the cursor to that position after the text changes!
-    if (this.props.focusDate !== prevProps.focusDate || (prevProps.is_uploading && !this.props.is_uploading && typeof this._restoreCaret === 'number')) {
-      let selectionEnd, selectionStart;
-
-      if (this.props.preselectDate !== prevProps.preselectDate) {
-        selectionEnd   = this.props.text.length;
-        selectionStart = this.props.text.search(/\s/) + 1;
-      } else if (typeof this._restoreCaret === 'number') {
-        selectionStart = this._restoreCaret;
-        selectionEnd   = this._restoreCaret;
-      } else {
-        selectionEnd   = this.props.text.length;
-        selectionStart = selectionEnd;
-      }
-
-      this.autosuggestTextarea.textarea.setSelectionRange(selectionStart, selectionEnd);
-      this.autosuggestTextarea.textarea.focus();
-    } else if(prevProps.is_submitting && !this.props.is_submitting) {
-      this.autosuggestTextarea.textarea.focus();
-    }
-  }
-
-  setAutosuggestTextarea = (c) => {
-    this.autosuggestTextarea = c;
-  }
-
-  handleEmojiPick = (data) => {
-    const position     = this.autosuggestTextarea.textarea.selectionStart;
-    const emojiChar    = data.native;
-    this._restoreCaret = position + emojiChar.length + 1;
-    this.props.onPickEmoji(position, data);
-  }
-
-  render () {
-    const { intl, onPaste, showSearch } = this.props;
-    const disabled = this.props.is_submitting;
-    const maybeEye = (this.props.advanced_options && this.props.advanced_options.do_not_federate) ? ' 👁️' : '';
-    const text     = [this.props.spoiler_text, countableText(this.props.text), maybeEye].join('');
-
-    const secondaryVisibility = this.props.settings.get('side_arm');
-    let showSideArm = secondaryVisibility !== 'none';
-
-    let publishText = '';
-    let publishText2 = '';
-    let title = '';
-    let title2 = '';
-
-    const privacyIcons = {
-      none: '',
-      public: 'globe',
-      unlisted: 'unlock-alt',
-      private: 'lock',
-      direct: 'envelope',
-    };
-
-    title = `${intl.formatMessage(messages.publish)}: ${intl.formatMessage({ id: `privacy.${this.props.privacy}.short` })}`;
-
-    if (showSideArm) {
-      // Enhanced behavior with dual toot buttons
-      publishText = (
-        <span>
-          {
-            <i
-              className={`fa fa-${privacyIcons[this.props.privacy]}`}
-              style={{ paddingRight: '5px' }}
-            />
-          }{intl.formatMessage(messages.publish)}
-        </span>
-      );
-
-      title2 = `${intl.formatMessage(messages.publish)}: ${intl.formatMessage({ id: `privacy.${secondaryVisibility}.short` })}`;
-      publishText2 = (
-        <i
-          className={`fa fa-${privacyIcons[secondaryVisibility]}`}
-          aria-label={title2}
-        />
-      );
-    } else {
-      // Original vanilla behavior - no icon if public or unlisted
-      if (this.props.privacy === 'private' || this.props.privacy === 'direct') {
-        publishText = <span className='compose-form__publish-private'><i className='fa fa-lock' /> {intl.formatMessage(messages.publish)}</span>;
-      } else {
-        publishText = this.props.privacy !== 'unlisted' ? intl.formatMessage(messages.publishLoud, { publish: intl.formatMessage(messages.publish) }) : intl.formatMessage(messages.publish);
-      }
-    }
-
-    const submitDisabled = disabled || this.props.is_uploading || length(text) > maxChars || (text.length !== 0 && text.trim().length === 0);
-
-    return (
-      <div className='compose-form'>
-        <Collapsable isVisible={this.props.spoiler} fullHeight={50}>
-          <div className='spoiler-input'>
-            <label>
-              <span style={{ display: 'none' }}>{intl.formatMessage(messages.spoiler_placeholder)}</span>
-              <input placeholder={intl.formatMessage(messages.spoiler_placeholder)} value={this.props.spoiler_text} onChange={this.handleChangeSpoilerText} onKeyDown={this.handleKeyDown} type='text' className='spoiler-input__input'  id='cw-spoiler-input' />
-            </label>
-          </div>
-        </Collapsable>
-
-        <WarningContainer />
-
-        <ReplyIndicatorContainer />
-
-        <div className='compose-form__autosuggest-wrapper'>
-          <AutosuggestTextarea
-            ref={this.setAutosuggestTextarea}
-            placeholder={intl.formatMessage(messages.placeholder)}
-            disabled={disabled}
-            value={this.props.text}
-            onChange={this.handleChange}
-            suggestions={this.props.suggestions}
-            onKeyDown={this.handleKeyDown}
-            onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
-            onSuggestionsClearRequested={this.onSuggestionsClearRequested}
-            onSuggestionSelected={this.onSuggestionSelected}
-            onPaste={onPaste}
-            autoFocus={!showSearch && !isMobile(window.innerWidth)}
-          />
-
-          <EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} />
-        </div>
-
-        <div className='compose-form__modifiers'>
-          <UploadFormContainer />
-        </div>
-
-        <div className='compose-form__buttons'>
-          <ComposeAttachOptions />
-          <SensitiveButtonContainer />
-          <div className='compose-form__buttons-separator' />
-          <PrivacyDropdownContainer />
-          <SpoilerButtonContainer />
-          <ComposeAdvancedOptionsContainer />
-        </div>
-
-        <div className='compose-form__publish'>
-          <div className='character-counter__wrapper'><CharacterCounter max={maxChars} text={text} /></div>
-          <div className='compose-form__publish-button-wrapper'>
-            {
-              showSideArm ?
-                <Button
-                  className='compose-form__publish__side-arm'
-                  text={publishText2}
-                  title={title2}
-                  onClick={this.handleSubmit2}
-                  disabled={submitDisabled}
-                /> : ''
-            }
-            <Button
-              className='compose-form__publish__primary'
-              text={publishText}
-              title={title}
-              onClick={this.handleSubmit}
-              disabled={submitDisabled}
-            />
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/dropdown.js b/app/javascript/themes/glitch/features/compose/components/dropdown.js
deleted file mode 100644
index f3d9f094e..000000000
--- a/app/javascript/themes/glitch/features/compose/components/dropdown.js
+++ /dev/null
@@ -1,77 +0,0 @@
-//  Package imports.
-import React from 'react';
-import PropTypes from 'prop-types';
-
-//  Our imports.
-import IconButton from 'themes/glitch/components/icon_button';
-
-const iconStyle = {
-  height     : null,
-  lineHeight : '27px',
-};
-
-export default class ComposeDropdown extends React.PureComponent {
-
-  static propTypes = {
-    title: PropTypes.string.isRequired,
-    icon: PropTypes.string,
-    highlight: PropTypes.bool,
-    disabled: PropTypes.bool,
-    children: PropTypes.arrayOf(PropTypes.node).isRequired,
-  };
-
-  state = {
-    open: false,
-  };
-
-  onGlobalClick = (e) => {
-    if (e.target !== this.node && !this.node.contains(e.target) && this.state.open) {
-      this.setState({ open: false });
-    }
-  };
-
-  componentDidMount () {
-    window.addEventListener('click', this.onGlobalClick);
-    window.addEventListener('touchstart', this.onGlobalClick);
-  }
-  componentWillUnmount () {
-    window.removeEventListener('click', this.onGlobalClick);
-    window.removeEventListener('touchstart', this.onGlobalClick);
-  }
-
-  onToggleDropdown = () => {
-    if (this.props.disabled) return;
-    this.setState({ open: !this.state.open });
-  };
-
-  setRef = (c) => {
-    this.node = c;
-  };
-
-  render () {
-    const { open } = this.state;
-    let { highlight, title, icon, disabled } = this.props;
-
-    if (!icon) icon = 'ellipsis-h';
-
-    return (
-      <div ref={this.setRef} className={`advanced-options-dropdown ${open ?  'open' : ''} ${highlight ? 'active' : ''} `}>
-        <div className='advanced-options-dropdown__value'>
-          <IconButton
-            className={'inverted'}
-            title={title}
-            icon={icon} active={open || highlight}
-            size={18}
-            style={iconStyle}
-            disabled={disabled}
-            onClick={this.onToggleDropdown}
-          />
-        </div>
-        <div className='advanced-options-dropdown__dropdown'>
-          {this.props.children}
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/emoji_picker_dropdown.js b/app/javascript/themes/glitch/features/compose/components/emoji_picker_dropdown.js
deleted file mode 100644
index fd59efb85..000000000
--- a/app/javascript/themes/glitch/features/compose/components/emoji_picker_dropdown.js
+++ /dev/null
@@ -1,376 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { defineMessages, injectIntl } from 'react-intl';
-import { EmojiPicker as EmojiPickerAsync } from 'themes/glitch/util/async-components';
-import Overlay from 'react-overlays/lib/Overlay';
-import classNames from 'classnames';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import detectPassiveEvents from 'detect-passive-events';
-import { buildCustomEmojis } from 'themes/glitch/util/emoji';
-
-const messages = defineMessages({
-  emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' },
-  emoji_search: { id: 'emoji_button.search', defaultMessage: 'Search...' },
-  emoji_not_found: { id: 'emoji_button.not_found', defaultMessage: 'No emojos!! (╯°□°)╯︵ ┻━┻' },
-  custom: { id: 'emoji_button.custom', defaultMessage: 'Custom' },
-  recent: { id: 'emoji_button.recent', defaultMessage: 'Frequently used' },
-  search_results: { id: 'emoji_button.search_results', defaultMessage: 'Search results' },
-  people: { id: 'emoji_button.people', defaultMessage: 'People' },
-  nature: { id: 'emoji_button.nature', defaultMessage: 'Nature' },
-  food: { id: 'emoji_button.food', defaultMessage: 'Food & Drink' },
-  activity: { id: 'emoji_button.activity', defaultMessage: 'Activity' },
-  travel: { id: 'emoji_button.travel', defaultMessage: 'Travel & Places' },
-  objects: { id: 'emoji_button.objects', defaultMessage: 'Objects' },
-  symbols: { id: 'emoji_button.symbols', defaultMessage: 'Symbols' },
-  flags: { id: 'emoji_button.flags', defaultMessage: 'Flags' },
-});
-
-const assetHost = process.env.CDN_HOST || '';
-let EmojiPicker, Emoji; // load asynchronously
-
-const backgroundImageFn = () => `${assetHost}/emoji/sheet.png`;
-const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false;
-
-const categoriesSort = [
-  'recent',
-  'custom',
-  'people',
-  'nature',
-  'foods',
-  'activity',
-  'places',
-  'objects',
-  'symbols',
-  'flags',
-];
-
-class ModifierPickerMenu extends React.PureComponent {
-
-  static propTypes = {
-    active: PropTypes.bool,
-    onSelect: PropTypes.func.isRequired,
-    onClose: PropTypes.func.isRequired,
-  };
-
-  handleClick = e => {
-    this.props.onSelect(e.currentTarget.getAttribute('data-index') * 1);
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (nextProps.active) {
-      this.attachListeners();
-    } else {
-      this.removeListeners();
-    }
-  }
-
-  componentWillUnmount () {
-    this.removeListeners();
-  }
-
-  handleDocumentClick = e => {
-    if (this.node && !this.node.contains(e.target)) {
-      this.props.onClose();
-    }
-  }
-
-  attachListeners () {
-    document.addEventListener('click', this.handleDocumentClick, false);
-    document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
-  }
-
-  removeListeners () {
-    document.removeEventListener('click', this.handleDocumentClick, false);
-    document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
-  }
-
-  setRef = c => {
-    this.node = c;
-  }
-
-  render () {
-    const { active } = this.props;
-
-    return (
-      <div className='emoji-picker-dropdown__modifiers__menu' style={{ display: active ? 'block' : 'none' }} ref={this.setRef}>
-        <button onClick={this.handleClick} data-index={1}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={1} backgroundImageFn={backgroundImageFn} /></button>
-        <button onClick={this.handleClick} data-index={2}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={2} backgroundImageFn={backgroundImageFn} /></button>
-        <button onClick={this.handleClick} data-index={3}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={3} backgroundImageFn={backgroundImageFn} /></button>
-        <button onClick={this.handleClick} data-index={4}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={4} backgroundImageFn={backgroundImageFn} /></button>
-        <button onClick={this.handleClick} data-index={5}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={5} backgroundImageFn={backgroundImageFn} /></button>
-        <button onClick={this.handleClick} data-index={6}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={6} backgroundImageFn={backgroundImageFn} /></button>
-      </div>
-    );
-  }
-
-}
-
-class ModifierPicker extends React.PureComponent {
-
-  static propTypes = {
-    active: PropTypes.bool,
-    modifier: PropTypes.number,
-    onChange: PropTypes.func,
-    onClose: PropTypes.func,
-    onOpen: PropTypes.func,
-  };
-
-  handleClick = () => {
-    if (this.props.active) {
-      this.props.onClose();
-    } else {
-      this.props.onOpen();
-    }
-  }
-
-  handleSelect = modifier => {
-    this.props.onChange(modifier);
-    this.props.onClose();
-  }
-
-  render () {
-    const { active, modifier } = this.props;
-
-    return (
-      <div className='emoji-picker-dropdown__modifiers'>
-        <Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={modifier} onClick={this.handleClick} backgroundImageFn={backgroundImageFn} />
-        <ModifierPickerMenu active={active} onSelect={this.handleSelect} onClose={this.props.onClose} />
-      </div>
-    );
-  }
-
-}
-
-@injectIntl
-class EmojiPickerMenu extends React.PureComponent {
-
-  static propTypes = {
-    custom_emojis: ImmutablePropTypes.list,
-    frequentlyUsedEmojis: PropTypes.arrayOf(PropTypes.string),
-    loading: PropTypes.bool,
-    onClose: PropTypes.func.isRequired,
-    onPick: PropTypes.func.isRequired,
-    style: PropTypes.object,
-    placement: PropTypes.string,
-    arrowOffsetLeft: PropTypes.string,
-    arrowOffsetTop: PropTypes.string,
-    intl: PropTypes.object.isRequired,
-    skinTone: PropTypes.number.isRequired,
-    onSkinTone: PropTypes.func.isRequired,
-  };
-
-  static defaultProps = {
-    style: {},
-    loading: true,
-    placement: 'bottom',
-    frequentlyUsedEmojis: [],
-  };
-
-  state = {
-    modifierOpen: false,
-  };
-
-  handleDocumentClick = e => {
-    if (this.node && !this.node.contains(e.target)) {
-      this.props.onClose();
-    }
-  }
-
-  componentDidMount () {
-    document.addEventListener('click', this.handleDocumentClick, false);
-    document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
-  }
-
-  componentWillUnmount () {
-    document.removeEventListener('click', this.handleDocumentClick, false);
-    document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
-  }
-
-  setRef = c => {
-    this.node = c;
-  }
-
-  getI18n = () => {
-    const { intl } = this.props;
-
-    return {
-      search: intl.formatMessage(messages.emoji_search),
-      notfound: intl.formatMessage(messages.emoji_not_found),
-      categories: {
-        search: intl.formatMessage(messages.search_results),
-        recent: intl.formatMessage(messages.recent),
-        people: intl.formatMessage(messages.people),
-        nature: intl.formatMessage(messages.nature),
-        foods: intl.formatMessage(messages.food),
-        activity: intl.formatMessage(messages.activity),
-        places: intl.formatMessage(messages.travel),
-        objects: intl.formatMessage(messages.objects),
-        symbols: intl.formatMessage(messages.symbols),
-        flags: intl.formatMessage(messages.flags),
-        custom: intl.formatMessage(messages.custom),
-      },
-    };
-  }
-
-  handleClick = emoji => {
-    if (!emoji.native) {
-      emoji.native = emoji.colons;
-    }
-
-    this.props.onClose();
-    this.props.onPick(emoji);
-  }
-
-  handleModifierOpen = () => {
-    this.setState({ modifierOpen: true });
-  }
-
-  handleModifierClose = () => {
-    this.setState({ modifierOpen: false });
-  }
-
-  handleModifierChange = modifier => {
-    this.props.onSkinTone(modifier);
-  }
-
-  render () {
-    const { loading, style, intl, custom_emojis, skinTone, frequentlyUsedEmojis } = this.props;
-
-    if (loading) {
-      return <div style={{ width: 299 }} />;
-    }
-
-    const title = intl.formatMessage(messages.emoji);
-    const { modifierOpen } = this.state;
-
-    return (
-      <div className={classNames('emoji-picker-dropdown__menu', { selecting: modifierOpen })} style={style} ref={this.setRef}>
-        <EmojiPicker
-          perLine={8}
-          emojiSize={22}
-          sheetSize={32}
-          custom={buildCustomEmojis(custom_emojis)}
-          color=''
-          emoji=''
-          set='twitter'
-          title={title}
-          i18n={this.getI18n()}
-          onClick={this.handleClick}
-          include={categoriesSort}
-          recent={frequentlyUsedEmojis}
-          skin={skinTone}
-          showPreview={false}
-          backgroundImageFn={backgroundImageFn}
-          emojiTooltip
-        />
-
-        <ModifierPicker
-          active={modifierOpen}
-          modifier={skinTone}
-          onOpen={this.handleModifierOpen}
-          onClose={this.handleModifierClose}
-          onChange={this.handleModifierChange}
-        />
-      </div>
-    );
-  }
-
-}
-
-@injectIntl
-export default class EmojiPickerDropdown extends React.PureComponent {
-
-  static propTypes = {
-    custom_emojis: ImmutablePropTypes.list,
-    frequentlyUsedEmojis: PropTypes.arrayOf(PropTypes.string),
-    intl: PropTypes.object.isRequired,
-    onPickEmoji: PropTypes.func.isRequired,
-    onSkinTone: PropTypes.func.isRequired,
-    skinTone: PropTypes.number.isRequired,
-  };
-
-  state = {
-    active: false,
-    loading: false,
-  };
-
-  setRef = (c) => {
-    this.dropdown = c;
-  }
-
-  onShowDropdown = () => {
-    this.setState({ active: true });
-
-    if (!EmojiPicker) {
-      this.setState({ loading: true });
-
-      EmojiPickerAsync().then(EmojiMart => {
-        EmojiPicker = EmojiMart.Picker;
-        Emoji       = EmojiMart.Emoji;
-
-        this.setState({ loading: false });
-      }).catch(() => {
-        this.setState({ loading: false });
-      });
-    }
-  }
-
-  onHideDropdown = () => {
-    this.setState({ active: false });
-  }
-
-  onToggle = (e) => {
-    if (!this.state.loading && (!e.key || e.key === 'Enter')) {
-      if (this.state.active) {
-        this.onHideDropdown();
-      } else {
-        this.onShowDropdown();
-      }
-    }
-  }
-
-  handleKeyDown = e => {
-    if (e.key === 'Escape') {
-      this.onHideDropdown();
-    }
-  }
-
-  setTargetRef = c => {
-    this.target = c;
-  }
-
-  findTarget = () => {
-    return this.target;
-  }
-
-  render () {
-    const { intl, onPickEmoji, onSkinTone, skinTone, frequentlyUsedEmojis } = this.props;
-    const title = intl.formatMessage(messages.emoji);
-    const { active, loading } = this.state;
-
-    return (
-      <div className='emoji-picker-dropdown' onKeyDown={this.handleKeyDown}>
-        <div ref={this.setTargetRef} className='emoji-button' title={title} aria-label={title} aria-expanded={active} role='button' onClick={this.onToggle} onKeyDown={this.onToggle} tabIndex={0}>
-          <img
-            className={classNames('emojione', { 'pulse-loading': active && loading })}
-            alt='🙂'
-            src={`${assetHost}/emoji/1f602.svg`}
-          />
-        </div>
-
-        <Overlay show={active} placement='bottom' target={this.findTarget}>
-          <EmojiPickerMenu
-            custom_emojis={this.props.custom_emojis}
-            loading={loading}
-            onClose={this.onHideDropdown}
-            onPick={onPickEmoji}
-            onSkinTone={onSkinTone}
-            skinTone={skinTone}
-            frequentlyUsedEmojis={frequentlyUsedEmojis}
-          />
-        </Overlay>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/navigation_bar.js b/app/javascript/themes/glitch/features/compose/components/navigation_bar.js
deleted file mode 100644
index 24a70949b..000000000
--- a/app/javascript/themes/glitch/features/compose/components/navigation_bar.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import Avatar from 'themes/glitch/components/avatar';
-import IconButton from 'themes/glitch/components/icon_button';
-import Permalink from 'themes/glitch/components/permalink';
-import { FormattedMessage } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-export default class NavigationBar extends ImmutablePureComponent {
-
-  static propTypes = {
-    account: ImmutablePropTypes.map.isRequired,
-    onClose: PropTypes.func.isRequired,
-  };
-
-  render () {
-    return (
-      <div className='navigation-bar'>
-        <Permalink href={this.props.account.get('url')} to={`/accounts/${this.props.account.get('id')}`}>
-          <span style={{ display: 'none' }}>{this.props.account.get('acct')}</span>
-          <Avatar account={this.props.account} size={40} />
-        </Permalink>
-
-        <div className='navigation-bar__profile'>
-          <Permalink href={this.props.account.get('url')} to={`/accounts/${this.props.account.get('id')}`}>
-            <strong className='navigation-bar__profile-account'>@{this.props.account.get('acct')}</strong>
-          </Permalink>
-
-          <a href='/settings/profile' className='navigation-bar__profile-edit'><FormattedMessage id='navigation_bar.edit_profile' defaultMessage='Edit profile' /></a>
-        </div>
-
-        <IconButton title='' icon='close' onClick={this.props.onClose} />
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/privacy_dropdown.js b/app/javascript/themes/glitch/features/compose/components/privacy_dropdown.js
deleted file mode 100644
index 0cd92d174..000000000
--- a/app/javascript/themes/glitch/features/compose/components/privacy_dropdown.js
+++ /dev/null
@@ -1,200 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { injectIntl, defineMessages } from 'react-intl';
-import IconButton from 'themes/glitch/components/icon_button';
-import Overlay from 'react-overlays/lib/Overlay';
-import Motion from 'themes/glitch/util/optional_motion';
-import spring from 'react-motion/lib/spring';
-import detectPassiveEvents from 'detect-passive-events';
-import classNames from 'classnames';
-
-const messages = defineMessages({
-  public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
-  public_long: { id: 'privacy.public.long', defaultMessage: 'Post to public timelines' },
-  unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
-  unlisted_long: { id: 'privacy.unlisted.long', defaultMessage: 'Do not show in public timelines' },
-  private_short: { id: 'privacy.private.short', defaultMessage: 'Followers-only' },
-  private_long: { id: 'privacy.private.long', defaultMessage: 'Post to followers only' },
-  direct_short: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
-  direct_long: { id: 'privacy.direct.long', defaultMessage: 'Post to mentioned users only' },
-  change_privacy: { id: 'privacy.change', defaultMessage: 'Adjust status privacy' },
-});
-
-const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false;
-
-class PrivacyDropdownMenu extends React.PureComponent {
-
-  static propTypes = {
-    style: PropTypes.object,
-    items: PropTypes.array.isRequired,
-    value: PropTypes.string.isRequired,
-    onClose: PropTypes.func.isRequired,
-    onChange: PropTypes.func.isRequired,
-  };
-
-  handleDocumentClick = e => {
-    if (this.node && !this.node.contains(e.target)) {
-      this.props.onClose();
-    }
-  }
-
-  handleClick = e => {
-    if (e.key === 'Escape') {
-      this.props.onClose();
-    } else if (!e.key || e.key === 'Enter') {
-      const value = e.currentTarget.getAttribute('data-index');
-
-      e.preventDefault();
-
-      this.props.onClose();
-      this.props.onChange(value);
-    }
-  }
-
-  componentDidMount () {
-    document.addEventListener('click', this.handleDocumentClick, false);
-    document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
-  }
-
-  componentWillUnmount () {
-    document.removeEventListener('click', this.handleDocumentClick, false);
-    document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
-  }
-
-  setRef = c => {
-    this.node = c;
-  }
-
-  render () {
-    const { style, items, value } = this.props;
-
-    return (
-      <Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
-        {({ opacity, scaleX, scaleY }) => (
-          <div className='privacy-dropdown__dropdown' style={{ ...style, opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }} ref={this.setRef}>
-            {items.map(item =>
-              <div role='button' tabIndex='0' key={item.value} data-index={item.value} onKeyDown={this.handleClick} onClick={this.handleClick} className={classNames('privacy-dropdown__option', { active: item.value === value })}>
-                <div className='privacy-dropdown__option__icon'>
-                  <i className={`fa fa-fw fa-${item.icon}`} />
-                </div>
-
-                <div className='privacy-dropdown__option__content'>
-                  <strong>{item.text}</strong>
-                  {item.meta}
-                </div>
-              </div>
-            )}
-          </div>
-        )}
-      </Motion>
-    );
-  }
-
-}
-
-@injectIntl
-export default class PrivacyDropdown extends React.PureComponent {
-
-  static propTypes = {
-    isUserTouching: PropTypes.func,
-    isModalOpen: PropTypes.bool.isRequired,
-    onModalOpen: PropTypes.func,
-    onModalClose: PropTypes.func,
-    value: PropTypes.string.isRequired,
-    onChange: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  state = {
-    open: false,
-  };
-
-  handleToggle = () => {
-    if (this.props.isUserTouching()) {
-      if (this.state.open) {
-        this.props.onModalClose();
-      } else {
-        this.props.onModalOpen({
-          actions: this.options.map(option => ({ ...option, active: option.value === this.props.value })),
-          onClick: this.handleModalActionClick,
-        });
-      }
-    } else {
-      this.setState({ open: !this.state.open });
-    }
-  }
-
-  handleModalActionClick = (e) => {
-    e.preventDefault();
-
-    const { value } = this.options[e.currentTarget.getAttribute('data-index')];
-
-    this.props.onModalClose();
-    this.props.onChange(value);
-  }
-
-  handleKeyDown = e => {
-    switch(e.key) {
-    case 'Enter':
-      this.handleToggle();
-      break;
-    case 'Escape':
-      this.handleClose();
-      break;
-    }
-  }
-
-  handleClose = () => {
-    this.setState({ open: false });
-  }
-
-  handleChange = value => {
-    this.props.onChange(value);
-  }
-
-  componentWillMount () {
-    const { intl: { formatMessage } } = this.props;
-
-    this.options = [
-      { icon: 'globe', value: 'public', text: formatMessage(messages.public_short), meta: formatMessage(messages.public_long) },
-      { icon: 'unlock-alt', value: 'unlisted', text: formatMessage(messages.unlisted_short), meta: formatMessage(messages.unlisted_long) },
-      { icon: 'lock', value: 'private', text: formatMessage(messages.private_short), meta: formatMessage(messages.private_long) },
-      { icon: 'envelope', value: 'direct', text: formatMessage(messages.direct_short), meta: formatMessage(messages.direct_long) },
-    ];
-  }
-
-  render () {
-    const { value, intl } = this.props;
-    const { open } = this.state;
-
-    const valueOption = this.options.find(item => item.value === value);
-
-    return (
-      <div className={classNames('privacy-dropdown', { active: open })} onKeyDown={this.handleKeyDown}>
-        <div className={classNames('privacy-dropdown__value', { active: this.options.indexOf(valueOption) === 0 })}>
-          <IconButton
-            className='privacy-dropdown__value-icon'
-            icon={valueOption.icon}
-            title={intl.formatMessage(messages.change_privacy)}
-            size={18}
-            expanded={open}
-            active={open}
-            inverted
-            onClick={this.handleToggle}
-            style={{ height: null, lineHeight: '27px' }}
-          />
-        </div>
-
-        <Overlay show={open} placement='bottom' target={this}>
-          <PrivacyDropdownMenu
-            items={this.options}
-            value={value}
-            onClose={this.handleClose}
-            onChange={this.handleChange}
-          />
-        </Overlay>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/reply_indicator.js b/app/javascript/themes/glitch/features/compose/components/reply_indicator.js
deleted file mode 100644
index 9a8d10ceb..000000000
--- a/app/javascript/themes/glitch/features/compose/components/reply_indicator.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import Avatar from 'themes/glitch/components/avatar';
-import IconButton from 'themes/glitch/components/icon_button';
-import DisplayName from 'themes/glitch/components/display_name';
-import { defineMessages, injectIntl } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const messages = defineMessages({
-  cancel: { id: 'reply_indicator.cancel', defaultMessage: 'Cancel' },
-});
-
-@injectIntl
-export default class ReplyIndicator extends ImmutablePureComponent {
-
-  static contextTypes = {
-    router: PropTypes.object,
-  };
-
-  static propTypes = {
-    status: ImmutablePropTypes.map,
-    onCancel: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  handleClick = () => {
-    this.props.onCancel();
-  }
-
-  handleAccountClick = (e) => {
-    if (e.button === 0) {
-      e.preventDefault();
-      this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
-    }
-  }
-
-  render () {
-    const { status, intl } = this.props;
-
-    if (!status) {
-      return null;
-    }
-
-    const content  = { __html: status.get('contentHtml') };
-
-    return (
-      <div className='reply-indicator'>
-        <div className='reply-indicator__header'>
-          <div className='reply-indicator__cancel'><IconButton title={intl.formatMessage(messages.cancel)} icon='times' onClick={this.handleClick} /></div>
-
-          <a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='reply-indicator__display-name'>
-            <div className='reply-indicator__display-avatar'><Avatar account={status.get('account')} size={24} /></div>
-            <DisplayName account={status.get('account')} />
-          </a>
-        </div>
-
-        <div className='reply-indicator__content' dangerouslySetInnerHTML={content} />
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/search.js b/app/javascript/themes/glitch/features/compose/components/search.js
deleted file mode 100644
index c3218137f..000000000
--- a/app/javascript/themes/glitch/features/compose/components/search.js
+++ /dev/null
@@ -1,129 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import Overlay from 'react-overlays/lib/Overlay';
-import Motion from 'themes/glitch/util/optional_motion';
-import spring from 'react-motion/lib/spring';
-
-const messages = defineMessages({
-  placeholder: { id: 'search.placeholder', defaultMessage: 'Search' },
-});
-
-class SearchPopout extends React.PureComponent {
-
-  static propTypes = {
-    style: PropTypes.object,
-  };
-
-  render () {
-    const { style } = this.props;
-
-    return (
-      <div style={{ ...style, position: 'absolute', width: 285 }}>
-        <Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
-          {({ opacity, scaleX, scaleY }) => (
-            <div className='search-popout' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}>
-              <h4><FormattedMessage id='search_popout.search_format' defaultMessage='Advanced search format' /></h4>
-
-              <ul>
-                <li><em>#example</em> <FormattedMessage id='search_popout.tips.hashtag' defaultMessage='hashtag' /></li>
-                <li><em>@username@domain</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li>
-                <li><em>URL</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li>
-                <li><em>URL</em> <FormattedMessage id='search_popout.tips.status' defaultMessage='status' /></li>
-              </ul>
-
-              <FormattedMessage id='search_popout.tips.text' defaultMessage='Simple text returns matching display names, usernames and hashtags' />
-            </div>
-          )}
-        </Motion>
-      </div>
-    );
-  }
-
-}
-
-@injectIntl
-export default class Search extends React.PureComponent {
-
-  static propTypes = {
-    value: PropTypes.string.isRequired,
-    submitted: PropTypes.bool,
-    onChange: PropTypes.func.isRequired,
-    onSubmit: PropTypes.func.isRequired,
-    onClear: PropTypes.func.isRequired,
-    onShow: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  state = {
-    expanded: false,
-  };
-
-  handleChange = (e) => {
-    this.props.onChange(e.target.value);
-  }
-
-  handleClear = (e) => {
-    e.preventDefault();
-
-    if (this.props.value.length > 0 || this.props.submitted) {
-      this.props.onClear();
-    }
-  }
-
-  handleKeyDown = (e) => {
-    if (e.key === 'Enter') {
-      e.preventDefault();
-      this.props.onSubmit();
-    } else if (e.key === 'Escape') {
-      document.querySelector('.ui').parentElement.focus();
-    }
-  }
-
-  noop () {
-
-  }
-
-  handleFocus = () => {
-    this.setState({ expanded: true });
-    this.props.onShow();
-  }
-
-  handleBlur = () => {
-    this.setState({ expanded: false });
-  }
-
-  render () {
-    const { intl, value, submitted } = this.props;
-    const { expanded } = this.state;
-    const hasValue = value.length > 0 || submitted;
-
-    return (
-      <div className='search'>
-        <label>
-          <span style={{ display: 'none' }}>{intl.formatMessage(messages.placeholder)}</span>
-          <input
-            className='search__input'
-            type='text'
-            placeholder={intl.formatMessage(messages.placeholder)}
-            value={value}
-            onChange={this.handleChange}
-            onKeyUp={this.handleKeyDown}
-            onFocus={this.handleFocus}
-            onBlur={this.handleBlur}
-          />
-        </label>
-
-        <div role='button' tabIndex='0' className='search__icon' onClick={this.handleClear}>
-          <i className={`fa fa-search ${hasValue ? '' : 'active'}`} />
-          <i aria-label={intl.formatMessage(messages.placeholder)} className={`fa fa-times-circle ${hasValue ? 'active' : ''}`} />
-        </div>
-
-        <Overlay show={expanded && !hasValue} placement='bottom' target={this}>
-          <SearchPopout />
-        </Overlay>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/search_results.js b/app/javascript/themes/glitch/features/compose/components/search_results.js
deleted file mode 100644
index 3fdafa5f3..000000000
--- a/app/javascript/themes/glitch/features/compose/components/search_results.js
+++ /dev/null
@@ -1,65 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { FormattedMessage } from 'react-intl';
-import AccountContainer from 'themes/glitch/containers/account_container';
-import StatusContainer from 'themes/glitch/containers/status_container';
-import { Link } from 'react-router-dom';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-export default class SearchResults extends ImmutablePureComponent {
-
-  static propTypes = {
-    results: ImmutablePropTypes.map.isRequired,
-  };
-
-  render () {
-    const { results } = this.props;
-
-    let accounts, statuses, hashtags;
-    let count = 0;
-
-    if (results.get('accounts') && results.get('accounts').size > 0) {
-      count   += results.get('accounts').size;
-      accounts = (
-        <div className='search-results__section'>
-          {results.get('accounts').map(accountId => <AccountContainer key={accountId} id={accountId} />)}
-        </div>
-      );
-    }
-
-    if (results.get('statuses') && results.get('statuses').size > 0) {
-      count   += results.get('statuses').size;
-      statuses = (
-        <div className='search-results__section'>
-          {results.get('statuses').map(statusId => <StatusContainer key={statusId} id={statusId} />)}
-        </div>
-      );
-    }
-
-    if (results.get('hashtags') && results.get('hashtags').size > 0) {
-      count += results.get('hashtags').size;
-      hashtags = (
-        <div className='search-results__section'>
-          {results.get('hashtags').map(hashtag =>
-            <Link key={hashtag} className='search-results__hashtag' to={`/timelines/tag/${hashtag}`}>
-              #{hashtag}
-            </Link>
-          )}
-        </div>
-      );
-    }
-
-    return (
-      <div className='search-results'>
-        <div className='search-results__header'>
-          <FormattedMessage id='search_results.total' defaultMessage='{count, number} {count, plural, one {result} other {results}}' values={{ count }} />
-        </div>
-
-        {accounts}
-        {statuses}
-        {hashtags}
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/text_icon_button.js b/app/javascript/themes/glitch/features/compose/components/text_icon_button.js
deleted file mode 100644
index 9c8ffab1f..000000000
--- a/app/javascript/themes/glitch/features/compose/components/text_icon_button.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-export default class TextIconButton extends React.PureComponent {
-
-  static propTypes = {
-    label: PropTypes.string.isRequired,
-    title: PropTypes.string,
-    active: PropTypes.bool,
-    onClick: PropTypes.func.isRequired,
-    ariaControls: PropTypes.string,
-  };
-
-  handleClick = (e) => {
-    e.preventDefault();
-    this.props.onClick();
-  }
-
-  render () {
-    const { label, title, active, ariaControls } = this.props;
-
-    return (
-      <button title={title} aria-label={title} className={`text-icon-button ${active ? 'active' : ''}`} aria-expanded={active} onClick={this.handleClick} aria-controls={ariaControls}>
-        {label}
-      </button>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/upload.js b/app/javascript/themes/glitch/features/compose/components/upload.js
deleted file mode 100644
index ded376ada..000000000
--- a/app/javascript/themes/glitch/features/compose/components/upload.js
+++ /dev/null
@@ -1,96 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import IconButton from 'themes/glitch/components/icon_button';
-import Motion from 'themes/glitch/util/optional_motion';
-import spring from 'react-motion/lib/spring';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { defineMessages, injectIntl } from 'react-intl';
-import classNames from 'classnames';
-
-const messages = defineMessages({
-  undo: { id: 'upload_form.undo', defaultMessage: 'Undo' },
-  description: { id: 'upload_form.description', defaultMessage: 'Describe for the visually impaired' },
-});
-
-@injectIntl
-export default class Upload extends ImmutablePureComponent {
-
-  static propTypes = {
-    media: ImmutablePropTypes.map.isRequired,
-    intl: PropTypes.object.isRequired,
-    onUndo: PropTypes.func.isRequired,
-    onDescriptionChange: PropTypes.func.isRequired,
-  };
-
-  state = {
-    hovered: false,
-    focused: false,
-    dirtyDescription: null,
-  };
-
-  handleUndoClick = () => {
-    this.props.onUndo(this.props.media.get('id'));
-  }
-
-  handleInputChange = e => {
-    this.setState({ dirtyDescription: e.target.value });
-  }
-
-  handleMouseEnter = () => {
-    this.setState({ hovered: true });
-  }
-
-  handleMouseLeave = () => {
-    this.setState({ hovered: false });
-  }
-
-  handleInputFocus = () => {
-    this.setState({ focused: true });
-  }
-
-  handleInputBlur = () => {
-    const { dirtyDescription } = this.state;
-
-    this.setState({ focused: false, dirtyDescription: null });
-
-    if (dirtyDescription !== null) {
-      this.props.onDescriptionChange(this.props.media.get('id'), dirtyDescription);
-    }
-  }
-
-  render () {
-    const { intl, media } = this.props;
-    const active          = this.state.hovered || this.state.focused;
-    const description     = this.state.dirtyDescription || media.get('description') || '';
-
-    return (
-      <div className='compose-form__upload' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
-        <Motion defaultStyle={{ scale: 0.8 }} style={{ scale: spring(1, { stiffness: 180, damping: 12 }) }}>
-          {({ scale }) => (
-            <div className='compose-form__upload-thumbnail' style={{ transform: `scale(${scale})`, backgroundImage: `url(${media.get('preview_url')})` }}>
-              <IconButton icon='times' title={intl.formatMessage(messages.undo)} size={36} onClick={this.handleUndoClick} />
-
-              <div className={classNames('compose-form__upload-description', { active })}>
-                <label>
-                  <span style={{ display: 'none' }}>{intl.formatMessage(messages.description)}</span>
-
-                  <input
-                    placeholder={intl.formatMessage(messages.description)}
-                    type='text'
-                    value={description}
-                    maxLength={420}
-                    onFocus={this.handleInputFocus}
-                    onChange={this.handleInputChange}
-                    onBlur={this.handleInputBlur}
-                  />
-                </label>
-              </div>
-            </div>
-          )}
-        </Motion>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/upload_button.js b/app/javascript/themes/glitch/features/compose/components/upload_button.js
deleted file mode 100644
index d7742adfe..000000000
--- a/app/javascript/themes/glitch/features/compose/components/upload_button.js
+++ /dev/null
@@ -1,77 +0,0 @@
-import React from 'react';
-import IconButton from 'themes/glitch/components/icon_button';
-import PropTypes from 'prop-types';
-import { defineMessages, injectIntl } from 'react-intl';
-import { connect } from 'react-redux';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-
-const messages = defineMessages({
-  upload: { id: 'upload_button.label', defaultMessage: 'Add media' },
-});
-
-const makeMapStateToProps = () => {
-  const mapStateToProps = state => ({
-    acceptContentTypes: state.getIn(['media_attachments', 'accept_content_types']),
-  });
-
-  return mapStateToProps;
-};
-
-const iconStyle = {
-  height: null,
-  lineHeight: '27px',
-};
-
-@connect(makeMapStateToProps)
-@injectIntl
-export default class UploadButton extends ImmutablePureComponent {
-
-  static propTypes = {
-    disabled: PropTypes.bool,
-    onSelectFile: PropTypes.func.isRequired,
-    style: PropTypes.object,
-    resetFileKey: PropTypes.number,
-    acceptContentTypes: ImmutablePropTypes.listOf(PropTypes.string).isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  handleChange = (e) => {
-    if (e.target.files.length > 0) {
-      this.props.onSelectFile(e.target.files);
-    }
-  }
-
-  handleClick = () => {
-    this.fileElement.click();
-  }
-
-  setRef = (c) => {
-    this.fileElement = c;
-  }
-
-  render () {
-
-    const { intl, resetFileKey, disabled, acceptContentTypes } = this.props;
-
-    return (
-      <div className='compose-form__upload-button'>
-        <IconButton icon='camera' title={intl.formatMessage(messages.upload)} disabled={disabled} onClick={this.handleClick} className='compose-form__upload-button-icon' size={18} inverted style={iconStyle} />
-        <label>
-          <span style={{ display: 'none' }}>{intl.formatMessage(messages.upload)}</span>
-          <input
-            key={resetFileKey}
-            ref={this.setRef}
-            type='file'
-            multiple={false}
-            accept={acceptContentTypes.toArray().join(',')}
-            onChange={this.handleChange}
-            disabled={disabled}
-            style={{ display: 'none' }}
-          />
-        </label>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/upload_form.js b/app/javascript/themes/glitch/features/compose/components/upload_form.js
deleted file mode 100644
index b7f112205..000000000
--- a/app/javascript/themes/glitch/features/compose/components/upload_form.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import UploadProgressContainer from '../containers/upload_progress_container';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import UploadContainer from '../containers/upload_container';
-
-export default class UploadForm extends ImmutablePureComponent {
-
-  static propTypes = {
-    mediaIds: ImmutablePropTypes.list.isRequired,
-  };
-
-  render () {
-    const { mediaIds } = this.props;
-
-    return (
-      <div className='compose-form__upload-wrapper'>
-        <UploadProgressContainer />
-
-        <div className='compose-form__uploads-wrapper'>
-          {mediaIds.map(id => (
-            <UploadContainer id={id} key={id} />
-          ))}
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/upload_progress.js b/app/javascript/themes/glitch/features/compose/components/upload_progress.js
deleted file mode 100644
index b923d0a22..000000000
--- a/app/javascript/themes/glitch/features/compose/components/upload_progress.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Motion from 'themes/glitch/util/optional_motion';
-import spring from 'react-motion/lib/spring';
-import { FormattedMessage } from 'react-intl';
-
-export default class UploadProgress extends React.PureComponent {
-
-  static propTypes = {
-    active: PropTypes.bool,
-    progress: PropTypes.number,
-  };
-
-  render () {
-    const { active, progress } = this.props;
-
-    if (!active) {
-      return null;
-    }
-
-    return (
-      <div className='upload-progress'>
-        <div className='upload-progress__icon'>
-          <i className='fa fa-upload' />
-        </div>
-
-        <div className='upload-progress__message'>
-          <FormattedMessage id='upload_progress.label' defaultMessage='Uploading...' />
-
-          <div className='upload-progress__backdrop'>
-            <Motion defaultStyle={{ width: 0 }} style={{ width: spring(progress) }}>
-              {({ width }) =>
-                <div className='upload-progress__tracker' style={{ width: `${width}%` }} />
-              }
-            </Motion>
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/warning.js b/app/javascript/themes/glitch/features/compose/components/warning.js
deleted file mode 100644
index 82df55a31..000000000
--- a/app/javascript/themes/glitch/features/compose/components/warning.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Motion from 'themes/glitch/util/optional_motion';
-import spring from 'react-motion/lib/spring';
-
-export default class Warning extends React.PureComponent {
-
-  static propTypes = {
-    message: PropTypes.node.isRequired,
-  };
-
-  render () {
-    const { message } = this.props;
-
-    return (
-      <Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
-        {({ opacity, scaleX, scaleY }) => (
-          <div className='compose-form__warning' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}>
-            {message}
-          </div>
-        )}
-      </Motion>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/containers/advanced_options_container.js b/app/javascript/themes/glitch/features/compose/containers/advanced_options_container.js
deleted file mode 100644
index 9f168942a..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/advanced_options_container.js
+++ /dev/null
@@ -1,20 +0,0 @@
-//  Package imports.
-import { connect } from 'react-redux';
-
-//  Our imports.
-import { toggleComposeAdvancedOption } from 'themes/glitch/actions/compose';
-import ComposeAdvancedOptions from '../components/advanced_options';
-
-const mapStateToProps = state => ({
-  values: state.getIn(['compose', 'advanced_options']),
-});
-
-const mapDispatchToProps = dispatch => ({
-
-  onChange (option) {
-    dispatch(toggleComposeAdvancedOption(option));
-  },
-
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(ComposeAdvancedOptions);
diff --git a/app/javascript/themes/glitch/features/compose/containers/autosuggest_account_container.js b/app/javascript/themes/glitch/features/compose/containers/autosuggest_account_container.js
deleted file mode 100644
index 96eb70c18..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/autosuggest_account_container.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import { connect } from 'react-redux';
-import AutosuggestAccount from '../components/autosuggest_account';
-import { makeGetAccount } from 'themes/glitch/selectors';
-
-const makeMapStateToProps = () => {
-  const getAccount = makeGetAccount();
-
-  const mapStateToProps = (state, { id }) => ({
-    account: getAccount(state, id),
-  });
-
-  return mapStateToProps;
-};
-
-export default connect(makeMapStateToProps)(AutosuggestAccount);
diff --git a/app/javascript/themes/glitch/features/compose/containers/compose_form_container.js b/app/javascript/themes/glitch/features/compose/containers/compose_form_container.js
deleted file mode 100644
index 7afa988f1..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/compose_form_container.js
+++ /dev/null
@@ -1,71 +0,0 @@
-import { connect } from 'react-redux';
-import ComposeForm from '../components/compose_form';
-import { changeComposeVisibility, uploadCompose } from 'themes/glitch/actions/compose';
-import {
-  changeCompose,
-  submitCompose,
-  clearComposeSuggestions,
-  fetchComposeSuggestions,
-  selectComposeSuggestion,
-  changeComposeSpoilerText,
-  insertEmojiCompose,
-} from 'themes/glitch/actions/compose';
-
-const mapStateToProps = state => ({
-  text: state.getIn(['compose', 'text']),
-  suggestion_token: state.getIn(['compose', 'suggestion_token']),
-  suggestions: state.getIn(['compose', 'suggestions']),
-  advanced_options: state.getIn(['compose', 'advanced_options']),
-  spoiler: state.getIn(['compose', 'spoiler']),
-  spoiler_text: state.getIn(['compose', 'spoiler_text']),
-  privacy: state.getIn(['compose', 'privacy']),
-  focusDate: state.getIn(['compose', 'focusDate']),
-  preselectDate: state.getIn(['compose', 'preselectDate']),
-  is_submitting: state.getIn(['compose', 'is_submitting']),
-  is_uploading: state.getIn(['compose', 'is_uploading']),
-  showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
-  settings: state.get('local_settings'),
-  filesAttached: state.getIn(['compose', 'media_attachments']).size > 0,
-});
-
-const mapDispatchToProps = (dispatch) => ({
-
-  onChange (text) {
-    dispatch(changeCompose(text));
-  },
-
-  onPrivacyChange (value) {
-    dispatch(changeComposeVisibility(value));
-  },
-
-  onSubmit () {
-    dispatch(submitCompose());
-  },
-
-  onClearSuggestions () {
-    dispatch(clearComposeSuggestions());
-  },
-
-  onFetchSuggestions (token) {
-    dispatch(fetchComposeSuggestions(token));
-  },
-
-  onSuggestionSelected (position, token, accountId) {
-    dispatch(selectComposeSuggestion(position, token, accountId));
-  },
-
-  onChangeSpoilerText (checked) {
-    dispatch(changeComposeSpoilerText(checked));
-  },
-
-  onPaste (files) {
-    dispatch(uploadCompose(files));
-  },
-
-  onPickEmoji (position, data) {
-    dispatch(insertEmojiCompose(position, data));
-  },
-
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(ComposeForm);
diff --git a/app/javascript/themes/glitch/features/compose/containers/emoji_picker_dropdown_container.js b/app/javascript/themes/glitch/features/compose/containers/emoji_picker_dropdown_container.js
deleted file mode 100644
index 55a13bd65..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/emoji_picker_dropdown_container.js
+++ /dev/null
@@ -1,82 +0,0 @@
-import { connect } from 'react-redux';
-import EmojiPickerDropdown from '../components/emoji_picker_dropdown';
-import { changeSetting } from 'themes/glitch/actions/settings';
-import { createSelector } from 'reselect';
-import { Map as ImmutableMap } from 'immutable';
-import { useEmoji } from 'themes/glitch/actions/emojis';
-
-const perLine = 8;
-const lines   = 2;
-
-const DEFAULTS = [
-  '+1',
-  'grinning',
-  'kissing_heart',
-  'heart_eyes',
-  'laughing',
-  'stuck_out_tongue_winking_eye',
-  'sweat_smile',
-  'joy',
-  'yum',
-  'disappointed',
-  'thinking_face',
-  'weary',
-  'sob',
-  'sunglasses',
-  'heart',
-  'ok_hand',
-];
-
-const getFrequentlyUsedEmojis = createSelector([
-  state => state.getIn(['settings', 'frequentlyUsedEmojis'], ImmutableMap()),
-], emojiCounters => {
-  let emojis = emojiCounters
-    .keySeq()
-    .sort((a, b) => emojiCounters.get(a) - emojiCounters.get(b))
-    .reverse()
-    .slice(0, perLine * lines)
-    .toArray();
-
-  if (emojis.length < DEFAULTS.length) {
-    emojis = emojis.concat(DEFAULTS.slice(0, DEFAULTS.length - emojis.length));
-  }
-
-  return emojis;
-});
-
-const getCustomEmojis = createSelector([
-  state => state.get('custom_emojis'),
-], emojis => emojis.filter(e => e.get('visible_in_picker')).sort((a, b) => {
-  const aShort = a.get('shortcode').toLowerCase();
-  const bShort = b.get('shortcode').toLowerCase();
-
-  if (aShort < bShort) {
-    return -1;
-  } else if (aShort > bShort ) {
-    return 1;
-  } else {
-    return 0;
-  }
-}));
-
-const mapStateToProps = state => ({
-  custom_emojis: getCustomEmojis(state),
-  skinTone: state.getIn(['settings', 'skinTone']),
-  frequentlyUsedEmojis: getFrequentlyUsedEmojis(state),
-});
-
-const mapDispatchToProps = (dispatch, { onPickEmoji }) => ({
-  onSkinTone: skinTone => {
-    dispatch(changeSetting(['skinTone'], skinTone));
-  },
-
-  onPickEmoji: emoji => {
-    dispatch(useEmoji(emoji));
-
-    if (onPickEmoji) {
-      onPickEmoji(emoji);
-    }
-  },
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(EmojiPickerDropdown);
diff --git a/app/javascript/themes/glitch/features/compose/containers/navigation_container.js b/app/javascript/themes/glitch/features/compose/containers/navigation_container.js
deleted file mode 100644
index b6d737b46..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/navigation_container.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import { connect }   from 'react-redux';
-import NavigationBar from '../components/navigation_bar';
-import { me } from 'themes/glitch/util/initial_state';
-
-const mapStateToProps = state => {
-  return {
-    account: state.getIn(['accounts', me]),
-  };
-};
-
-export default connect(mapStateToProps)(NavigationBar);
diff --git a/app/javascript/themes/glitch/features/compose/containers/privacy_dropdown_container.js b/app/javascript/themes/glitch/features/compose/containers/privacy_dropdown_container.js
deleted file mode 100644
index 9636ceab2..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/privacy_dropdown_container.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import { connect } from 'react-redux';
-import PrivacyDropdown from '../components/privacy_dropdown';
-import { changeComposeVisibility } from 'themes/glitch/actions/compose';
-import { openModal, closeModal } from 'themes/glitch/actions/modal';
-import { isUserTouching } from 'themes/glitch/util/is_mobile';
-
-const mapStateToProps = state => ({
-  isModalOpen: state.get('modal').modalType === 'ACTIONS',
-  value: state.getIn(['compose', 'privacy']),
-});
-
-const mapDispatchToProps = dispatch => ({
-
-  onChange (value) {
-    dispatch(changeComposeVisibility(value));
-  },
-
-  isUserTouching,
-  onModalOpen: props => dispatch(openModal('ACTIONS', props)),
-  onModalClose: () => dispatch(closeModal()),
-
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(PrivacyDropdown);
diff --git a/app/javascript/themes/glitch/features/compose/containers/reply_indicator_container.js b/app/javascript/themes/glitch/features/compose/containers/reply_indicator_container.js
deleted file mode 100644
index 6dcabb3cd..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/reply_indicator_container.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import { connect } from 'react-redux';
-import { cancelReplyCompose } from 'themes/glitch/actions/compose';
-import { makeGetStatus } from 'themes/glitch/selectors';
-import ReplyIndicator from '../components/reply_indicator';
-
-const makeMapStateToProps = () => {
-  const getStatus = makeGetStatus();
-
-  const mapStateToProps = state => ({
-    status: getStatus(state, state.getIn(['compose', 'in_reply_to'])),
-  });
-
-  return mapStateToProps;
-};
-
-const mapDispatchToProps = dispatch => ({
-
-  onCancel () {
-    dispatch(cancelReplyCompose());
-  },
-
-});
-
-export default connect(makeMapStateToProps, mapDispatchToProps)(ReplyIndicator);
diff --git a/app/javascript/themes/glitch/features/compose/containers/search_container.js b/app/javascript/themes/glitch/features/compose/containers/search_container.js
deleted file mode 100644
index a450d27e7..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/search_container.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import { connect } from 'react-redux';
-import {
-  changeSearch,
-  clearSearch,
-  submitSearch,
-  showSearch,
-} from 'themes/glitch/actions/search';
-import Search from '../components/search';
-
-const mapStateToProps = state => ({
-  value: state.getIn(['search', 'value']),
-  submitted: state.getIn(['search', 'submitted']),
-});
-
-const mapDispatchToProps = dispatch => ({
-
-  onChange (value) {
-    dispatch(changeSearch(value));
-  },
-
-  onClear () {
-    dispatch(clearSearch());
-  },
-
-  onSubmit () {
-    dispatch(submitSearch());
-  },
-
-  onShow () {
-    dispatch(showSearch());
-  },
-
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(Search);
diff --git a/app/javascript/themes/glitch/features/compose/containers/search_results_container.js b/app/javascript/themes/glitch/features/compose/containers/search_results_container.js
deleted file mode 100644
index 16d95d417..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/search_results_container.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import { connect } from 'react-redux';
-import SearchResults from '../components/search_results';
-
-const mapStateToProps = state => ({
-  results: state.getIn(['search', 'results']),
-});
-
-export default connect(mapStateToProps)(SearchResults);
diff --git a/app/javascript/themes/glitch/features/compose/containers/sensitive_button_container.js b/app/javascript/themes/glitch/features/compose/containers/sensitive_button_container.js
deleted file mode 100644
index a710dd104..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/sensitive_button_container.js
+++ /dev/null
@@ -1,71 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
-import IconButton from 'themes/glitch/components/icon_button';
-import { changeComposeSensitivity } from 'themes/glitch/actions/compose';
-import Motion from 'themes/glitch/util/optional_motion';
-import spring from 'react-motion/lib/spring';
-import { injectIntl, defineMessages } from 'react-intl';
-
-const messages = defineMessages({
-  title: { id: 'compose_form.sensitive', defaultMessage: 'Mark media as sensitive' },
-});
-
-const mapStateToProps = state => ({
-  visible: state.getIn(['compose', 'media_attachments']).size > 0,
-  active: state.getIn(['compose', 'sensitive']),
-  disabled: state.getIn(['compose', 'spoiler']),
-});
-
-const mapDispatchToProps = dispatch => ({
-
-  onClick () {
-    dispatch(changeComposeSensitivity());
-  },
-
-});
-
-class SensitiveButton extends React.PureComponent {
-
-  static propTypes = {
-    visible: PropTypes.bool,
-    active: PropTypes.bool,
-    disabled: PropTypes.bool,
-    onClick: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  render () {
-    const { visible, active, disabled, onClick, intl } = this.props;
-
-    return (
-      <Motion defaultStyle={{ scale: 0.87 }} style={{ scale: spring(visible ? 1 : 0.87, { stiffness: 200, damping: 3 }) }}>
-        {({ scale }) => {
-          const icon = active ? 'eye-slash' : 'eye';
-          const className = classNames('compose-form__sensitive-button', {
-            'compose-form__sensitive-button--visible': visible,
-          });
-          return (
-            <div className={className} style={{ transform: `scale(${scale})` }}>
-              <IconButton
-                className='compose-form__sensitive-button__icon'
-                title={intl.formatMessage(messages.title)}
-                icon={icon}
-                onClick={onClick}
-                size={18}
-                active={active}
-                disabled={disabled}
-                style={{ lineHeight: null, height: null }}
-                inverted
-              />
-            </div>
-          );
-        }}
-      </Motion>
-    );
-  }
-
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(SensitiveButton));
diff --git a/app/javascript/themes/glitch/features/compose/containers/spoiler_button_container.js b/app/javascript/themes/glitch/features/compose/containers/spoiler_button_container.js
deleted file mode 100644
index 160e71ba9..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/spoiler_button_container.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import { connect } from 'react-redux';
-import TextIconButton from '../components/text_icon_button';
-import { changeComposeSpoilerness } from 'themes/glitch/actions/compose';
-import { injectIntl, defineMessages } from 'react-intl';
-
-const messages = defineMessages({
-  title: { id: 'compose_form.spoiler', defaultMessage: 'Hide text behind warning' },
-});
-
-const mapStateToProps = (state, { intl }) => ({
-  label: 'CW',
-  title: intl.formatMessage(messages.title),
-  active: state.getIn(['compose', 'spoiler']),
-  ariaControls: 'cw-spoiler-input',
-});
-
-const mapDispatchToProps = dispatch => ({
-
-  onClick () {
-    dispatch(changeComposeSpoilerness());
-  },
-
-});
-
-export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(TextIconButton));
diff --git a/app/javascript/themes/glitch/features/compose/containers/upload_button_container.js b/app/javascript/themes/glitch/features/compose/containers/upload_button_container.js
deleted file mode 100644
index f332eae1a..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/upload_button_container.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { connect } from 'react-redux';
-import UploadButton from '../components/upload_button';
-import { uploadCompose } from 'themes/glitch/actions/compose';
-
-const mapStateToProps = state => ({
-  disabled: state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size > 3 || state.getIn(['compose', 'media_attachments']).some(m => m.get('type') === 'video')),
-  resetFileKey: state.getIn(['compose', 'resetFileKey']),
-});
-
-const mapDispatchToProps = dispatch => ({
-
-  onSelectFile (files) {
-    dispatch(uploadCompose(files));
-  },
-
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(UploadButton);
diff --git a/app/javascript/themes/glitch/features/compose/containers/upload_container.js b/app/javascript/themes/glitch/features/compose/containers/upload_container.js
deleted file mode 100644
index eea514bf5..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/upload_container.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import { connect } from 'react-redux';
-import Upload from '../components/upload';
-import { undoUploadCompose, changeUploadCompose } from 'themes/glitch/actions/compose';
-
-const mapStateToProps = (state, { id }) => ({
-  media: state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id),
-});
-
-const mapDispatchToProps = dispatch => ({
-
-  onUndo: id => {
-    dispatch(undoUploadCompose(id));
-  },
-
-  onDescriptionChange: (id, description) => {
-    dispatch(changeUploadCompose(id, description));
-  },
-
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(Upload);
diff --git a/app/javascript/themes/glitch/features/compose/containers/upload_form_container.js b/app/javascript/themes/glitch/features/compose/containers/upload_form_container.js
deleted file mode 100644
index a6798bf51..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/upload_form_container.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import { connect } from 'react-redux';
-import UploadForm from '../components/upload_form';
-
-const mapStateToProps = state => ({
-  mediaIds: state.getIn(['compose', 'media_attachments']).map(item => item.get('id')),
-});
-
-export default connect(mapStateToProps)(UploadForm);
diff --git a/app/javascript/themes/glitch/features/compose/containers/upload_progress_container.js b/app/javascript/themes/glitch/features/compose/containers/upload_progress_container.js
deleted file mode 100644
index 0cfee96da..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/upload_progress_container.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import { connect } from 'react-redux';
-import UploadProgress from '../components/upload_progress';
-
-const mapStateToProps = state => ({
-  active: state.getIn(['compose', 'is_uploading']),
-  progress: state.getIn(['compose', 'progress']),
-});
-
-export default connect(mapStateToProps)(UploadProgress);
diff --git a/app/javascript/themes/glitch/features/compose/containers/warning_container.js b/app/javascript/themes/glitch/features/compose/containers/warning_container.js
deleted file mode 100644
index 225d6a1dd..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/warning_container.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import Warning from '../components/warning';
-import PropTypes from 'prop-types';
-import { FormattedMessage } from 'react-intl';
-import { me } from 'themes/glitch/util/initial_state';
-
-const mapStateToProps = state => ({
-  needsLockWarning: state.getIn(['compose', 'privacy']) === 'private' && !state.getIn(['accounts', me, 'locked']),
-});
-
-const WarningWrapper = ({ needsLockWarning }) => {
-  if (needsLockWarning) {
-    return <Warning message={<FormattedMessage id='compose_form.lock_disclaimer' defaultMessage='Your account is not {locked}. Anyone can follow you to view your follower-only posts.' values={{ locked: <a href='/settings/profile'><FormattedMessage id='compose_form.lock_disclaimer.lock' defaultMessage='locked' /></a> }} />} />;
-  }
-
-  return null;
-};
-
-WarningWrapper.propTypes = {
-  needsLockWarning: PropTypes.bool,
-};
-
-export default connect(mapStateToProps)(WarningWrapper);
diff --git a/app/javascript/themes/glitch/features/compose/index.js b/app/javascript/themes/glitch/features/compose/index.js
deleted file mode 100644
index 3fcaf416f..000000000
--- a/app/javascript/themes/glitch/features/compose/index.js
+++ /dev/null
@@ -1,126 +0,0 @@
-import React from 'react';
-import ComposeFormContainer from './containers/compose_form_container';
-import NavigationContainer from './containers/navigation_container';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { connect } from 'react-redux';
-import { mountCompose, unmountCompose } from 'themes/glitch/actions/compose';
-import { openModal } from 'themes/glitch/actions/modal';
-import { changeLocalSetting } from 'themes/glitch/actions/local_settings';
-import { Link } from 'react-router-dom';
-import { injectIntl, defineMessages } from 'react-intl';
-import SearchContainer from './containers/search_container';
-import Motion from 'themes/glitch/util/optional_motion';
-import spring from 'react-motion/lib/spring';
-import SearchResultsContainer from './containers/search_results_container';
-import { changeComposing } from 'themes/glitch/actions/compose';
-
-const messages = defineMessages({
-  start: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
-  home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' },
-  notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' },
-  public: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' },
-  community: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
-  settings: { id: 'navigation_bar.app_settings', defaultMessage: 'App settings' },
-  logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
-});
-
-const mapStateToProps = state => ({
-  columns: state.getIn(['settings', 'columns']),
-  showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class Compose extends React.PureComponent {
-
-  static propTypes = {
-    dispatch: PropTypes.func.isRequired,
-    columns: ImmutablePropTypes.list.isRequired,
-    multiColumn: PropTypes.bool,
-    showSearch: PropTypes.bool,
-    intl: PropTypes.object.isRequired,
-  };
-
-  componentDidMount () {
-    this.props.dispatch(mountCompose());
-  }
-
-  componentWillUnmount () {
-    this.props.dispatch(unmountCompose());
-  }
-
-  onLayoutClick = (e) => {
-    const layout = e.currentTarget.getAttribute('data-mastodon-layout');
-    this.props.dispatch(changeLocalSetting(['layout'], layout));
-    e.preventDefault();
-  }
-
-  openSettings = () => {
-    this.props.dispatch(openModal('SETTINGS', {}));
-  }
-
-  onFocus = () => {
-    this.props.dispatch(changeComposing(true));
-  }
-
-  onBlur = () => {
-    this.props.dispatch(changeComposing(false));
-  }
-
-  render () {
-    const { multiColumn, showSearch, intl } = this.props;
-
-    let header = '';
-
-    if (multiColumn) {
-      const { columns } = this.props;
-      header = (
-        <nav className='drawer__header'>
-          <Link to='/getting-started' className='drawer__tab' title={intl.formatMessage(messages.start)} aria-label={intl.formatMessage(messages.start)}><i role='img' className='fa fa-fw fa-asterisk' /></Link>
-          {!columns.some(column => column.get('id') === 'HOME') && (
-            <Link to='/timelines/home' className='drawer__tab' title={intl.formatMessage(messages.home_timeline)} aria-label={intl.formatMessage(messages.home_timeline)}><i role='img' className='fa fa-fw fa-home' /></Link>
-          )}
-          {!columns.some(column => column.get('id') === 'NOTIFICATIONS') && (
-            <Link to='/notifications' className='drawer__tab' title={intl.formatMessage(messages.notifications)} aria-label={intl.formatMessage(messages.notifications)}><i role='img' className='fa fa-fw fa-bell' /></Link>
-          )}
-          {!columns.some(column => column.get('id') === 'COMMUNITY') && (
-            <Link to='/timelines/public/local' className='drawer__tab' title={intl.formatMessage(messages.community)} aria-label={intl.formatMessage(messages.community)}><i role='img' className='fa fa-fw fa-users' /></Link>
-          )}
-          {!columns.some(column => column.get('id') === 'PUBLIC') && (
-            <Link to='/timelines/public' className='drawer__tab' title={intl.formatMessage(messages.public)} aria-label={intl.formatMessage(messages.public)}><i role='img' className='fa fa-fw fa-globe' /></Link>
-          )}
-          <a onClick={this.openSettings} role='button' tabIndex='0' className='drawer__tab' title={intl.formatMessage(messages.settings)} aria-label={intl.formatMessage(messages.settings)}><i role='img' className='fa fa-fw fa-cogs' /></a>
-          <a href='/auth/sign_out' className='drawer__tab' data-method='delete' title={intl.formatMessage(messages.logout)} aria-label={intl.formatMessage(messages.logout)}><i role='img' className='fa fa-fw fa-sign-out' /></a>
-        </nav>
-      );
-    }
-
-
-
-    return (
-      <div className='drawer'>
-        {header}
-
-        <SearchContainer />
-
-        <div className='drawer__pager'>
-          <div className='drawer__inner scrollable optionally-scrollable' onFocus={this.onFocus}>
-            <NavigationContainer onClose={this.onBlur} />
-            <ComposeFormContainer />
-          </div>
-
-          <Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}>
-            {({ x }) =>
-              <div className='drawer__inner darker' style={{ transform: `translateX(${x}%)`, visibility: x === -100 ? 'hidden' : 'visible' }}>
-                <SearchResultsContainer />
-              </div>
-            }
-          </Motion>
-        </div>
-
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/direct_timeline/containers/column_settings_container.js b/app/javascript/themes/glitch/features/direct_timeline/containers/column_settings_container.js
deleted file mode 100644
index 2a40c65a5..000000000
--- a/app/javascript/themes/glitch/features/direct_timeline/containers/column_settings_container.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import { connect } from 'react-redux';
-import ColumnSettings from 'themes/glitch/features/community_timeline/components/column_settings';
-import { changeSetting } from 'themes/glitch/actions/settings';
-
-const mapStateToProps = state => ({
-  settings: state.getIn(['settings', 'direct']),
-});
-
-const mapDispatchToProps = dispatch => ({
-
-  onChange (key, checked) {
-    dispatch(changeSetting(['direct', ...key], checked));
-  },
-
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings);
diff --git a/app/javascript/themes/glitch/features/direct_timeline/index.js b/app/javascript/themes/glitch/features/direct_timeline/index.js
deleted file mode 100644
index 6b29cf94d..000000000
--- a/app/javascript/themes/glitch/features/direct_timeline/index.js
+++ /dev/null
@@ -1,107 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import StatusListContainer from 'themes/glitch/features/ui/containers/status_list_container';
-import Column from 'themes/glitch/components/column';
-import ColumnHeader from 'themes/glitch/components/column_header';
-import {
-  refreshDirectTimeline,
-  expandDirectTimeline,
-} from 'themes/glitch/actions/timelines';
-import { addColumn, removeColumn, moveColumn } from 'themes/glitch/actions/columns';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import ColumnSettingsContainer from './containers/column_settings_container';
-import { connectDirectStream } from 'themes/glitch/actions/streaming';
-
-const messages = defineMessages({
-  title: { id: 'column.direct', defaultMessage: 'Direct messages' },
-});
-
-const mapStateToProps = state => ({
-  hasUnread: state.getIn(['timelines', 'direct', 'unread']) > 0,
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class DirectTimeline extends React.PureComponent {
-
-  static propTypes = {
-    dispatch: PropTypes.func.isRequired,
-    columnId: PropTypes.string,
-    intl: PropTypes.object.isRequired,
-    hasUnread: PropTypes.bool,
-    multiColumn: PropTypes.bool,
-  };
-
-  handlePin = () => {
-    const { columnId, dispatch } = this.props;
-
-    if (columnId) {
-      dispatch(removeColumn(columnId));
-    } else {
-      dispatch(addColumn('DIRECT', {}));
-    }
-  }
-
-  handleMove = (dir) => {
-    const { columnId, dispatch } = this.props;
-    dispatch(moveColumn(columnId, dir));
-  }
-
-  handleHeaderClick = () => {
-    this.column.scrollTop();
-  }
-
-  componentDidMount () {
-    const { dispatch } = this.props;
-
-    dispatch(refreshDirectTimeline());
-    this.disconnect = dispatch(connectDirectStream());
-  }
-
-  componentWillUnmount () {
-    if (this.disconnect) {
-      this.disconnect();
-      this.disconnect = null;
-    }
-  }
-
-  setRef = c => {
-    this.column = c;
-  }
-
-  handleLoadMore = () => {
-    this.props.dispatch(expandDirectTimeline());
-  }
-
-  render () {
-    const { intl, hasUnread, columnId, multiColumn } = this.props;
-    const pinned = !!columnId;
-
-    return (
-      <Column ref={this.setRef}>
-        <ColumnHeader
-          icon='envelope'
-          active={hasUnread}
-          title={intl.formatMessage(messages.title)}
-          onPin={this.handlePin}
-          onMove={this.handleMove}
-          onClick={this.handleHeaderClick}
-          pinned={pinned}
-          multiColumn={multiColumn}
-        >
-          <ColumnSettingsContainer />
-        </ColumnHeader>
-
-        <StatusListContainer
-          trackScroll={!pinned}
-          scrollKey={`direct_timeline-${columnId}`}
-          timelineId='direct'
-          loadMore={this.handleLoadMore}
-          emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any direct messages yet. When you send or receive one, it will show up here." />}
-        />
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/favourited_statuses/index.js b/app/javascript/themes/glitch/features/favourited_statuses/index.js
deleted file mode 100644
index 80345e0e2..000000000
--- a/app/javascript/themes/glitch/features/favourited_statuses/index.js
+++ /dev/null
@@ -1,94 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { fetchFavouritedStatuses, expandFavouritedStatuses } from 'themes/glitch/actions/favourites';
-import Column from 'themes/glitch/features/ui/components/column';
-import ColumnHeader from 'themes/glitch/components/column_header';
-import { addColumn, removeColumn, moveColumn } from 'themes/glitch/actions/columns';
-import StatusList from 'themes/glitch/components/status_list';
-import { defineMessages, injectIntl } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const messages = defineMessages({
-  heading: { id: 'column.favourites', defaultMessage: 'Favourites' },
-});
-
-const mapStateToProps = state => ({
-  statusIds: state.getIn(['status_lists', 'favourites', 'items']),
-  hasMore: !!state.getIn(['status_lists', 'favourites', 'next']),
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class Favourites extends ImmutablePureComponent {
-
-  static propTypes = {
-    dispatch: PropTypes.func.isRequired,
-    statusIds: ImmutablePropTypes.list.isRequired,
-    intl: PropTypes.object.isRequired,
-    columnId: PropTypes.string,
-    multiColumn: PropTypes.bool,
-    hasMore: PropTypes.bool,
-  };
-
-  componentWillMount () {
-    this.props.dispatch(fetchFavouritedStatuses());
-  }
-
-  handlePin = () => {
-    const { columnId, dispatch } = this.props;
-
-    if (columnId) {
-      dispatch(removeColumn(columnId));
-    } else {
-      dispatch(addColumn('FAVOURITES', {}));
-    }
-  }
-
-  handleMove = (dir) => {
-    const { columnId, dispatch } = this.props;
-    dispatch(moveColumn(columnId, dir));
-  }
-
-  handleHeaderClick = () => {
-    this.column.scrollTop();
-  }
-
-  setRef = c => {
-    this.column = c;
-  }
-
-  handleScrollToBottom = () => {
-    this.props.dispatch(expandFavouritedStatuses());
-  }
-
-  render () {
-    const { intl, statusIds, columnId, multiColumn, hasMore } = this.props;
-    const pinned = !!columnId;
-
-    return (
-      <Column ref={this.setRef} name='favourites'>
-        <ColumnHeader
-          icon='star'
-          title={intl.formatMessage(messages.heading)}
-          onPin={this.handlePin}
-          onMove={this.handleMove}
-          onClick={this.handleHeaderClick}
-          pinned={pinned}
-          multiColumn={multiColumn}
-          showBackButton
-        />
-
-        <StatusList
-          trackScroll={!pinned}
-          statusIds={statusIds}
-          scrollKey={`favourited_statuses-${columnId}`}
-          hasMore={hasMore}
-          onScrollToBottom={this.handleScrollToBottom}
-        />
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/favourites/index.js b/app/javascript/themes/glitch/features/favourites/index.js
deleted file mode 100644
index d7b8ac3b1..000000000
--- a/app/javascript/themes/glitch/features/favourites/index.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import LoadingIndicator from 'themes/glitch/components/loading_indicator';
-import { fetchFavourites } from 'themes/glitch/actions/interactions';
-import { ScrollContainer } from 'react-router-scroll-4';
-import AccountContainer from 'themes/glitch/containers/account_container';
-import Column from 'themes/glitch/features/ui/components/column';
-import ColumnBackButton from 'themes/glitch/components/column_back_button';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const mapStateToProps = (state, props) => ({
-  accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId]),
-});
-
-@connect(mapStateToProps)
-export default class Favourites extends ImmutablePureComponent {
-
-  static propTypes = {
-    params: PropTypes.object.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    accountIds: ImmutablePropTypes.list,
-  };
-
-  componentWillMount () {
-    this.props.dispatch(fetchFavourites(this.props.params.statusId));
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
-      this.props.dispatch(fetchFavourites(nextProps.params.statusId));
-    }
-  }
-
-  render () {
-    const { accountIds } = this.props;
-
-    if (!accountIds) {
-      return (
-        <Column>
-          <LoadingIndicator />
-        </Column>
-      );
-    }
-
-    return (
-      <Column>
-        <ColumnBackButton />
-
-        <ScrollContainer scrollKey='favourites'>
-          <div className='scrollable'>
-            {accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} />)}
-          </div>
-        </ScrollContainer>
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/follow_requests/components/account_authorize.js b/app/javascript/themes/glitch/features/follow_requests/components/account_authorize.js
deleted file mode 100644
index ce386d888..000000000
--- a/app/javascript/themes/glitch/features/follow_requests/components/account_authorize.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import Permalink from 'themes/glitch/components/permalink';
-import Avatar from 'themes/glitch/components/avatar';
-import DisplayName from 'themes/glitch/components/display_name';
-import IconButton from 'themes/glitch/components/icon_button';
-import { defineMessages, injectIntl } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const messages = defineMessages({
-  authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' },
-  reject: { id: 'follow_request.reject', defaultMessage: 'Reject' },
-});
-
-@injectIntl
-export default class AccountAuthorize extends ImmutablePureComponent {
-
-  static propTypes = {
-    account: ImmutablePropTypes.map.isRequired,
-    onAuthorize: PropTypes.func.isRequired,
-    onReject: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  render () {
-    const { intl, account, onAuthorize, onReject } = this.props;
-    const content = { __html: account.get('note_emojified') };
-
-    return (
-      <div className='account-authorize__wrapper'>
-        <div className='account-authorize'>
-          <Permalink href={account.get('url')} to={`/accounts/${account.get('id')}`} className='detailed-status__display-name'>
-            <div className='account-authorize__avatar'><Avatar account={account} size={48} /></div>
-            <DisplayName account={account} />
-          </Permalink>
-
-          <div className='account__header__content' dangerouslySetInnerHTML={content} />
-        </div>
-
-        <div className='account--panel'>
-          <div className='account--panel__button'><IconButton title={intl.formatMessage(messages.authorize)} icon='check' onClick={onAuthorize} /></div>
-          <div className='account--panel__button'><IconButton title={intl.formatMessage(messages.reject)} icon='times' onClick={onReject} /></div>
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/follow_requests/containers/account_authorize_container.js b/app/javascript/themes/glitch/features/follow_requests/containers/account_authorize_container.js
deleted file mode 100644
index 78ae77eee..000000000
--- a/app/javascript/themes/glitch/features/follow_requests/containers/account_authorize_container.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import { connect } from 'react-redux';
-import { makeGetAccount } from 'themes/glitch/selectors';
-import AccountAuthorize from '../components/account_authorize';
-import { authorizeFollowRequest, rejectFollowRequest } from 'themes/glitch/actions/accounts';
-
-const makeMapStateToProps = () => {
-  const getAccount = makeGetAccount();
-
-  const mapStateToProps = (state, props) => ({
-    account: getAccount(state, props.id),
-  });
-
-  return mapStateToProps;
-};
-
-const mapDispatchToProps = (dispatch, { id }) => ({
-  onAuthorize () {
-    dispatch(authorizeFollowRequest(id));
-  },
-
-  onReject () {
-    dispatch(rejectFollowRequest(id));
-  },
-});
-
-export default connect(makeMapStateToProps, mapDispatchToProps)(AccountAuthorize);
diff --git a/app/javascript/themes/glitch/features/follow_requests/index.js b/app/javascript/themes/glitch/features/follow_requests/index.js
deleted file mode 100644
index 3f44f518a..000000000
--- a/app/javascript/themes/glitch/features/follow_requests/index.js
+++ /dev/null
@@ -1,71 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import LoadingIndicator from 'themes/glitch/components/loading_indicator';
-import { ScrollContainer } from 'react-router-scroll-4';
-import Column from 'themes/glitch/features/ui/components/column';
-import ColumnBackButtonSlim from 'themes/glitch/components/column_back_button_slim';
-import AccountAuthorizeContainer from './containers/account_authorize_container';
-import { fetchFollowRequests, expandFollowRequests } from 'themes/glitch/actions/accounts';
-import { defineMessages, injectIntl } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const messages = defineMessages({
-  heading: { id: 'column.follow_requests', defaultMessage: 'Follow requests' },
-});
-
-const mapStateToProps = state => ({
-  accountIds: state.getIn(['user_lists', 'follow_requests', 'items']),
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class FollowRequests extends ImmutablePureComponent {
-
-  static propTypes = {
-    params: PropTypes.object.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    accountIds: ImmutablePropTypes.list,
-    intl: PropTypes.object.isRequired,
-  };
-
-  componentWillMount () {
-    this.props.dispatch(fetchFollowRequests());
-  }
-
-  handleScroll = (e) => {
-    const { scrollTop, scrollHeight, clientHeight } = e.target;
-
-    if (scrollTop === scrollHeight - clientHeight) {
-      this.props.dispatch(expandFollowRequests());
-    }
-  }
-
-  render () {
-    const { intl, accountIds } = this.props;
-
-    if (!accountIds) {
-      return (
-        <Column name='follow-requests'>
-          <LoadingIndicator />
-        </Column>
-      );
-    }
-
-    return (
-      <Column name='follow-requests' icon='users' heading={intl.formatMessage(messages.heading)}>
-        <ColumnBackButtonSlim />
-
-        <ScrollContainer scrollKey='follow_requests'>
-          <div className='scrollable' onScroll={this.handleScroll}>
-            {accountIds.map(id =>
-              <AccountAuthorizeContainer key={id} id={id} />
-            )}
-          </div>
-        </ScrollContainer>
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/followers/index.js b/app/javascript/themes/glitch/features/followers/index.js
deleted file mode 100644
index d586bf41d..000000000
--- a/app/javascript/themes/glitch/features/followers/index.js
+++ /dev/null
@@ -1,93 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import LoadingIndicator from 'themes/glitch/components/loading_indicator';
-import {
-  fetchAccount,
-  fetchFollowers,
-  expandFollowers,
-} from 'themes/glitch/actions/accounts';
-import { ScrollContainer } from 'react-router-scroll-4';
-import AccountContainer from 'themes/glitch/containers/account_container';
-import Column from 'themes/glitch/features/ui/components/column';
-import HeaderContainer from 'themes/glitch/features/account_timeline/containers/header_container';
-import LoadMore from 'themes/glitch/components/load_more';
-import ColumnBackButton from 'themes/glitch/components/column_back_button';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const mapStateToProps = (state, props) => ({
-  accountIds: state.getIn(['user_lists', 'followers', props.params.accountId, 'items']),
-  hasMore: !!state.getIn(['user_lists', 'followers', props.params.accountId, 'next']),
-});
-
-@connect(mapStateToProps)
-export default class Followers extends ImmutablePureComponent {
-
-  static propTypes = {
-    params: PropTypes.object.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    accountIds: ImmutablePropTypes.list,
-    hasMore: PropTypes.bool,
-  };
-
-  componentWillMount () {
-    this.props.dispatch(fetchAccount(this.props.params.accountId));
-    this.props.dispatch(fetchFollowers(this.props.params.accountId));
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) {
-      this.props.dispatch(fetchAccount(nextProps.params.accountId));
-      this.props.dispatch(fetchFollowers(nextProps.params.accountId));
-    }
-  }
-
-  handleScroll = (e) => {
-    const { scrollTop, scrollHeight, clientHeight } = e.target;
-
-    if (scrollTop === scrollHeight - clientHeight && this.props.hasMore) {
-      this.props.dispatch(expandFollowers(this.props.params.accountId));
-    }
-  }
-
-  handleLoadMore = (e) => {
-    e.preventDefault();
-    this.props.dispatch(expandFollowers(this.props.params.accountId));
-  }
-
-  render () {
-    const { accountIds, hasMore } = this.props;
-
-    let loadMore = null;
-
-    if (!accountIds) {
-      return (
-        <Column>
-          <LoadingIndicator />
-        </Column>
-      );
-    }
-
-    if (hasMore) {
-      loadMore = <LoadMore onClick={this.handleLoadMore} />;
-    }
-
-    return (
-      <Column>
-        <ColumnBackButton />
-
-        <ScrollContainer scrollKey='followers'>
-          <div className='scrollable' onScroll={this.handleScroll}>
-            <div className='followers'>
-              <HeaderContainer accountId={this.props.params.accountId} />
-              {accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} />)}
-              {loadMore}
-            </div>
-          </div>
-        </ScrollContainer>
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/following/index.js b/app/javascript/themes/glitch/features/following/index.js
deleted file mode 100644
index c306faf21..000000000
--- a/app/javascript/themes/glitch/features/following/index.js
+++ /dev/null
@@ -1,93 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import LoadingIndicator from 'themes/glitch/components/loading_indicator';
-import {
-  fetchAccount,
-  fetchFollowing,
-  expandFollowing,
-} from 'themes/glitch/actions/accounts';
-import { ScrollContainer } from 'react-router-scroll-4';
-import AccountContainer from 'themes/glitch/containers/account_container';
-import Column from 'themes/glitch/features/ui/components/column';
-import HeaderContainer from 'themes/glitch/features/account_timeline/containers/header_container';
-import LoadMore from 'themes/glitch/components/load_more';
-import ColumnBackButton from 'themes/glitch/components/column_back_button';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const mapStateToProps = (state, props) => ({
-  accountIds: state.getIn(['user_lists', 'following', props.params.accountId, 'items']),
-  hasMore: !!state.getIn(['user_lists', 'following', props.params.accountId, 'next']),
-});
-
-@connect(mapStateToProps)
-export default class Following extends ImmutablePureComponent {
-
-  static propTypes = {
-    params: PropTypes.object.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    accountIds: ImmutablePropTypes.list,
-    hasMore: PropTypes.bool,
-  };
-
-  componentWillMount () {
-    this.props.dispatch(fetchAccount(this.props.params.accountId));
-    this.props.dispatch(fetchFollowing(this.props.params.accountId));
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) {
-      this.props.dispatch(fetchAccount(nextProps.params.accountId));
-      this.props.dispatch(fetchFollowing(nextProps.params.accountId));
-    }
-  }
-
-  handleScroll = (e) => {
-    const { scrollTop, scrollHeight, clientHeight } = e.target;
-
-    if (scrollTop === scrollHeight - clientHeight && this.props.hasMore) {
-      this.props.dispatch(expandFollowing(this.props.params.accountId));
-    }
-  }
-
-  handleLoadMore = (e) => {
-    e.preventDefault();
-    this.props.dispatch(expandFollowing(this.props.params.accountId));
-  }
-
-  render () {
-    const { accountIds, hasMore } = this.props;
-
-    let loadMore = null;
-
-    if (!accountIds) {
-      return (
-        <Column>
-          <LoadingIndicator />
-        </Column>
-      );
-    }
-
-    if (hasMore) {
-      loadMore = <LoadMore onClick={this.handleLoadMore} />;
-    }
-
-    return (
-      <Column>
-        <ColumnBackButton />
-
-        <ScrollContainer scrollKey='following'>
-          <div className='scrollable' onScroll={this.handleScroll}>
-            <div className='following'>
-              <HeaderContainer accountId={this.props.params.accountId} />
-              {accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} />)}
-              {loadMore}
-            </div>
-          </div>
-        </ScrollContainer>
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/generic_not_found/index.js b/app/javascript/themes/glitch/features/generic_not_found/index.js
deleted file mode 100644
index ccd2b87b2..000000000
--- a/app/javascript/themes/glitch/features/generic_not_found/index.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import React from 'react';
-import Column from 'themes/glitch/features/ui/components/column';
-import MissingIndicator from 'themes/glitch/components/missing_indicator';
-
-const GenericNotFound = () => (
-  <Column>
-    <MissingIndicator />
-  </Column>
-);
-
-export default GenericNotFound;
diff --git a/app/javascript/themes/glitch/features/getting_started/index.js b/app/javascript/themes/glitch/features/getting_started/index.js
deleted file mode 100644
index 74b019cf1..000000000
--- a/app/javascript/themes/glitch/features/getting_started/index.js
+++ /dev/null
@@ -1,145 +0,0 @@
-import React from 'react';
-import Column from 'themes/glitch/features/ui/components/column';
-import ColumnLink from 'themes/glitch/features/ui/components/column_link';
-import ColumnSubheading from 'themes/glitch/features/ui/components/column_subheading';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import { connect } from 'react-redux';
-import { openModal } from 'themes/glitch/actions/modal';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { me } from 'themes/glitch/util/initial_state';
-
-const messages = defineMessages({
-  heading: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
-  home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' },
-  notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' },
-  public_timeline: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' },
-  navigation_subheading: { id: 'column_subheading.navigation', defaultMessage: 'Navigation' },
-  settings_subheading: { id: 'column_subheading.settings', defaultMessage: 'Settings' },
-  community_timeline: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
-  direct: { id: 'navigation_bar.direct', defaultMessage: 'Direct messages' },
-  preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
-  settings: { id: 'navigation_bar.app_settings', defaultMessage: 'App settings' },
-  follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
-  sign_out: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
-  favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
-  blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
-  mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
-  info: { id: 'navigation_bar.info', defaultMessage: 'Extended information' },
-  show_me_around: { id: 'getting_started.onboarding', defaultMessage: 'Show me around' },
-  pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned toots' },
-});
-
-const mapStateToProps = state => ({
-  myAccount: state.getIn(['accounts', me]),
-  columns: state.getIn(['settings', 'columns']),
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class GettingStarted extends ImmutablePureComponent {
-
-  static propTypes = {
-    intl: PropTypes.object.isRequired,
-    myAccount: ImmutablePropTypes.map.isRequired,
-    columns: ImmutablePropTypes.list,
-    multiColumn: PropTypes.bool,
-    dispatch: PropTypes.func.isRequired,
-  };
-
-  openSettings = () => {
-    this.props.dispatch(openModal('SETTINGS', {}));
-  }
-
-  openOnboardingModal = (e) => {
-    e.preventDefault();
-    this.props.dispatch(openModal('ONBOARDING'));
-  }
-
-  render () {
-    const { intl, myAccount, columns, multiColumn } = this.props;
-
-    let navItems = [];
-
-    if (multiColumn) {
-      if (!columns.find(item => item.get('id') === 'HOME')) {
-        navItems.push(<ColumnLink key='0' icon='home' text={intl.formatMessage(messages.home_timeline)} to='/timelines/home' />);
-      }
-
-      if (!columns.find(item => item.get('id') === 'NOTIFICATIONS')) {
-        navItems.push(<ColumnLink key='1' icon='bell' text={intl.formatMessage(messages.notifications)} to='/notifications' />);
-      }
-
-      if (!columns.find(item => item.get('id') === 'COMMUNITY')) {
-        navItems.push(<ColumnLink key='2' icon='users' text={intl.formatMessage(messages.community_timeline)} to='/timelines/public/local' />);
-      }
-
-      if (!columns.find(item => item.get('id') === 'PUBLIC')) {
-        navItems.push(<ColumnLink key='3' icon='globe' text={intl.formatMessage(messages.public_timeline)} to='/timelines/public' />);
-      }
-    }
-
-    if (!multiColumn || !columns.find(item => item.get('id') === 'DIRECT')) {
-      navItems.push(<ColumnLink key='4' icon='envelope' text={intl.formatMessage(messages.direct)} to='/timelines/direct' />);
-    }
-
-    navItems = navItems.concat([
-      <ColumnLink key='5' icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />,
-      <ColumnLink key='6' icon='thumb-tack' text={intl.formatMessage(messages.pins)} to='/pinned' />,
-    ]);
-
-    if (myAccount.get('locked')) {
-      navItems.push(<ColumnLink key='7' icon='users' text={intl.formatMessage(messages.follow_requests)} to='/follow_requests' />);
-    }
-
-    navItems = navItems.concat([
-      <ColumnLink key='8' icon='volume-off' text={intl.formatMessage(messages.mutes)} to='/mutes' />,
-      <ColumnLink key='9' icon='ban' text={intl.formatMessage(messages.blocks)} to='/blocks' />,
-    ]);
-
-    return (
-      <Column name='getting-started' icon='asterisk' heading={intl.formatMessage(messages.heading)} hideHeadingOnMobile>
-        <div className='scrollable optionally-scrollable'>
-          <div className='getting-started__wrapper'>
-            <ColumnSubheading text={intl.formatMessage(messages.navigation_subheading)} />
-            {navItems}
-            <ColumnSubheading text={intl.formatMessage(messages.settings_subheading)} />
-            <ColumnLink icon='book' text={intl.formatMessage(messages.info)} href='/about/more' />
-            <ColumnLink icon='hand-o-right' text={intl.formatMessage(messages.show_me_around)} onClick={this.openOnboardingModal} />
-            <ColumnLink icon='cog' text={intl.formatMessage(messages.preferences)} href='/settings/preferences' />
-            <ColumnLink icon='cogs' text={intl.formatMessage(messages.settings)} onClick={this.openSettings} />
-            <ColumnLink icon='sign-out' text={intl.formatMessage(messages.sign_out)} href='/auth/sign_out' method='delete' />
-          </div>
-
-          <div className='getting-started__footer'>
-            <div className='static-content getting-started'>
-              <p>
-                <a href='https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/FAQ.md' rel='noopener' target='_blank'>
-                  <FormattedMessage id='getting_started.faq' defaultMessage='FAQ' />
-                </a>&nbsp;•&nbsp;
-                <a href='https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/User-guide.md' rel='noopener' target='_blank'>
-                  <FormattedMessage id='getting_started.userguide' defaultMessage='User Guide' />
-                </a>&nbsp;•&nbsp;
-                <a href='https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md' rel='noopener' target='_blank'>
-                  <FormattedMessage id='getting_started.appsshort' defaultMessage='Apps' />
-                </a>
-              </p>
-              <p>
-                <FormattedMessage
-                  id='getting_started.open_source_notice'
-                  defaultMessage='Glitchsoc is open source software, a friendly fork of {Mastodon}. You can contribute or report issues on GitHub at {github}.'
-                  values={{
-                    github: <a href='https://github.com/glitch-soc/mastodon' rel='noopener' target='_blank'>glitch-soc/mastodon</a>,
-                    Mastodon: <a href='https://github.com/tootsuite/mastodon' rel='noopener' target='_blank'>Mastodon</a>,
-                  }}
-                />
-              </p>
-            </div>
-          </div>
-        </div>
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/hashtag_timeline/index.js b/app/javascript/themes/glitch/features/hashtag_timeline/index.js
deleted file mode 100644
index a878931b3..000000000
--- a/app/javascript/themes/glitch/features/hashtag_timeline/index.js
+++ /dev/null
@@ -1,118 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import StatusListContainer from 'themes/glitch/features/ui/containers/status_list_container';
-import Column from 'themes/glitch/components/column';
-import ColumnHeader from 'themes/glitch/components/column_header';
-import {
-  refreshHashtagTimeline,
-  expandHashtagTimeline,
-} from 'themes/glitch/actions/timelines';
-import { addColumn, removeColumn, moveColumn } from 'themes/glitch/actions/columns';
-import { FormattedMessage } from 'react-intl';
-import { connectHashtagStream } from 'themes/glitch/actions/streaming';
-
-const mapStateToProps = (state, props) => ({
-  hasUnread: state.getIn(['timelines', `hashtag:${props.params.id}`, 'unread']) > 0,
-});
-
-@connect(mapStateToProps)
-export default class HashtagTimeline extends React.PureComponent {
-
-  static propTypes = {
-    params: PropTypes.object.isRequired,
-    columnId: PropTypes.string,
-    dispatch: PropTypes.func.isRequired,
-    hasUnread: PropTypes.bool,
-    multiColumn: PropTypes.bool,
-  };
-
-  handlePin = () => {
-    const { columnId, dispatch } = this.props;
-
-    if (columnId) {
-      dispatch(removeColumn(columnId));
-    } else {
-      dispatch(addColumn('HASHTAG', { id: this.props.params.id }));
-    }
-  }
-
-  handleMove = (dir) => {
-    const { columnId, dispatch } = this.props;
-    dispatch(moveColumn(columnId, dir));
-  }
-
-  handleHeaderClick = () => {
-    this.column.scrollTop();
-  }
-
-  _subscribe (dispatch, id) {
-    this.disconnect = dispatch(connectHashtagStream(id));
-  }
-
-  _unsubscribe () {
-    if (this.disconnect) {
-      this.disconnect();
-      this.disconnect = null;
-    }
-  }
-
-  componentDidMount () {
-    const { dispatch } = this.props;
-    const { id } = this.props.params;
-
-    dispatch(refreshHashtagTimeline(id));
-    this._subscribe(dispatch, id);
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (nextProps.params.id !== this.props.params.id) {
-      this.props.dispatch(refreshHashtagTimeline(nextProps.params.id));
-      this._unsubscribe();
-      this._subscribe(this.props.dispatch, nextProps.params.id);
-    }
-  }
-
-  componentWillUnmount () {
-    this._unsubscribe();
-  }
-
-  setRef = c => {
-    this.column = c;
-  }
-
-  handleLoadMore = () => {
-    this.props.dispatch(expandHashtagTimeline(this.props.params.id));
-  }
-
-  render () {
-    const { hasUnread, columnId, multiColumn } = this.props;
-    const { id } = this.props.params;
-    const pinned = !!columnId;
-
-    return (
-      <Column ref={this.setRef} name='hashtag'>
-        <ColumnHeader
-          icon='hashtag'
-          active={hasUnread}
-          title={id}
-          onPin={this.handlePin}
-          onMove={this.handleMove}
-          onClick={this.handleHeaderClick}
-          pinned={pinned}
-          multiColumn={multiColumn}
-          showBackButton
-        />
-
-        <StatusListContainer
-          trackScroll={!pinned}
-          scrollKey={`hashtag_timeline-${columnId}`}
-          timelineId={`hashtag:${id}`}
-          loadMore={this.handleLoadMore}
-          emptyMessage={<FormattedMessage id='empty_column.hashtag' defaultMessage='There is nothing in this hashtag yet.' />}
-        />
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/home_timeline/components/column_settings.js b/app/javascript/themes/glitch/features/home_timeline/components/column_settings.js
deleted file mode 100644
index 533da6c36..000000000
--- a/app/javascript/themes/glitch/features/home_timeline/components/column_settings.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import SettingToggle from 'themes/glitch/features/notifications/components/setting_toggle';
-import SettingText from 'themes/glitch/components/setting_text';
-
-const messages = defineMessages({
-  filter_regex: { id: 'home.column_settings.filter_regex', defaultMessage: 'Filter out by regular expressions' },
-  settings: { id: 'home.settings', defaultMessage: 'Column settings' },
-});
-
-@injectIntl
-export default class ColumnSettings extends React.PureComponent {
-
-  static propTypes = {
-    settings: ImmutablePropTypes.map.isRequired,
-    onChange: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  render () {
-    const { settings, onChange, intl } = this.props;
-
-    return (
-      <div>
-        <span className='column-settings__section'><FormattedMessage id='home.column_settings.basic' defaultMessage='Basic' /></span>
-
-        <div className='column-settings__row'>
-          <SettingToggle prefix='home_timeline' settings={settings} settingKey={['shows', 'reblog']} onChange={onChange} label={<FormattedMessage id='home.column_settings.show_reblogs' defaultMessage='Show boosts' />} />
-        </div>
-
-        <div className='column-settings__row'>
-          <SettingToggle prefix='home_timeline' settings={settings} settingKey={['shows', 'reply']} onChange={onChange} label={<FormattedMessage id='home.column_settings.show_replies' defaultMessage='Show replies' />} />
-        </div>
-
-        <span className='column-settings__section'><FormattedMessage id='home.column_settings.advanced' defaultMessage='Advanced' /></span>
-
-        <div className='column-settings__row'>
-          <SettingText prefix='home_timeline' settings={settings} settingKey={['regex', 'body']} onChange={onChange} label={intl.formatMessage(messages.filter_regex)} />
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/home_timeline/containers/column_settings_container.js b/app/javascript/themes/glitch/features/home_timeline/containers/column_settings_container.js
deleted file mode 100644
index a0062f564..000000000
--- a/app/javascript/themes/glitch/features/home_timeline/containers/column_settings_container.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import { connect } from 'react-redux';
-import ColumnSettings from '../components/column_settings';
-import { changeSetting, saveSettings } from 'themes/glitch/actions/settings';
-
-const mapStateToProps = state => ({
-  settings: state.getIn(['settings', 'home']),
-});
-
-const mapDispatchToProps = dispatch => ({
-
-  onChange (key, checked) {
-    dispatch(changeSetting(['home', ...key], checked));
-  },
-
-  onSave () {
-    dispatch(saveSettings());
-  },
-
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings);
diff --git a/app/javascript/themes/glitch/features/home_timeline/index.js b/app/javascript/themes/glitch/features/home_timeline/index.js
deleted file mode 100644
index 8a65891cd..000000000
--- a/app/javascript/themes/glitch/features/home_timeline/index.js
+++ /dev/null
@@ -1,90 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import { expandHomeTimeline } from 'themes/glitch/actions/timelines';
-import PropTypes from 'prop-types';
-import StatusListContainer from 'themes/glitch/features/ui/containers/status_list_container';
-import Column from 'themes/glitch/components/column';
-import ColumnHeader from 'themes/glitch/components/column_header';
-import { addColumn, removeColumn, moveColumn } from 'themes/glitch/actions/columns';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import ColumnSettingsContainer from './containers/column_settings_container';
-import { Link } from 'react-router-dom';
-
-const messages = defineMessages({
-  title: { id: 'column.home', defaultMessage: 'Home' },
-});
-
-const mapStateToProps = state => ({
-  hasUnread: state.getIn(['timelines', 'home', 'unread']) > 0,
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class HomeTimeline extends React.PureComponent {
-
-  static propTypes = {
-    dispatch: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-    hasUnread: PropTypes.bool,
-    columnId: PropTypes.string,
-    multiColumn: PropTypes.bool,
-  };
-
-  handlePin = () => {
-    const { columnId, dispatch } = this.props;
-
-    if (columnId) {
-      dispatch(removeColumn(columnId));
-    } else {
-      dispatch(addColumn('HOME', {}));
-    }
-  }
-
-  handleMove = (dir) => {
-    const { columnId, dispatch } = this.props;
-    dispatch(moveColumn(columnId, dir));
-  }
-
-  handleHeaderClick = () => {
-    this.column.scrollTop();
-  }
-
-  setRef = c => {
-    this.column = c;
-  }
-
-  handleLoadMore = () => {
-    this.props.dispatch(expandHomeTimeline());
-  }
-
-  render () {
-    const { intl, hasUnread, columnId, multiColumn } = this.props;
-    const pinned = !!columnId;
-
-    return (
-      <Column ref={this.setRef} name='home'>
-        <ColumnHeader
-          icon='home'
-          active={hasUnread}
-          title={intl.formatMessage(messages.title)}
-          onPin={this.handlePin}
-          onMove={this.handleMove}
-          onClick={this.handleHeaderClick}
-          pinned={pinned}
-          multiColumn={multiColumn}
-        >
-          <ColumnSettingsContainer />
-        </ColumnHeader>
-
-        <StatusListContainer
-          trackScroll={!pinned}
-          scrollKey={`home_timeline-${columnId}`}
-          loadMore={this.handleLoadMore}
-          timelineId='home'
-          emptyMessage={<FormattedMessage id='empty_column.home' defaultMessage='Your home timeline is empty! Visit {public} or use search to get started and meet other users.' values={{ public: <Link to='/timelines/public'><FormattedMessage id='empty_column.home.public_timeline' defaultMessage='the public timeline' /></Link> }} />}
-        />
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/local_settings/index.js b/app/javascript/themes/glitch/features/local_settings/index.js
deleted file mode 100644
index 6c5d51413..000000000
--- a/app/javascript/themes/glitch/features/local_settings/index.js
+++ /dev/null
@@ -1,68 +0,0 @@
-//  Package imports.
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { connect } from 'react-redux';
-
-//  Our imports
-import LocalSettingsPage from './page';
-import LocalSettingsNavigation from './navigation';
-import { closeModal } from 'themes/glitch/actions/modal';
-import { changeLocalSetting } from 'themes/glitch/actions/local_settings';
-
-//  Stylesheet imports
-import './style.scss';
-
-const mapStateToProps = state => ({
-  settings: state.get('local_settings'),
-});
-
-const mapDispatchToProps = dispatch => ({
-  onChange (setting, value) {
-    dispatch(changeLocalSetting(setting, value));
-  },
-  onClose () {
-    dispatch(closeModal());
-  },
-});
-
-class LocalSettings extends React.PureComponent {
-
-  static propTypes = {
-    onChange: PropTypes.func.isRequired,
-    onClose: PropTypes.func.isRequired,
-    settings: ImmutablePropTypes.map.isRequired,
-  };
-
-  state = {
-    currentIndex: 0,
-  };
-
-  navigateTo = (index) =>
-    this.setState({ currentIndex: +index });
-
-  render () {
-
-    const { navigateTo } = this;
-    const { onChange, onClose, settings } = this.props;
-    const { currentIndex } = this.state;
-
-    return (
-      <div className='glitch modal-root__modal local-settings'>
-        <LocalSettingsNavigation
-          index={currentIndex}
-          onClose={onClose}
-          onNavigate={navigateTo}
-        />
-        <LocalSettingsPage
-          index={currentIndex}
-          onChange={onChange}
-          settings={settings}
-        />
-      </div>
-    );
-  }
-
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(LocalSettings);
diff --git a/app/javascript/themes/glitch/features/local_settings/navigation/index.js b/app/javascript/themes/glitch/features/local_settings/navigation/index.js
deleted file mode 100644
index fa35e83c7..000000000
--- a/app/javascript/themes/glitch/features/local_settings/navigation/index.js
+++ /dev/null
@@ -1,74 +0,0 @@
-//  Package imports
-import React from 'react';
-import PropTypes from 'prop-types';
-import { injectIntl, defineMessages } from 'react-intl';
-
-//  Our imports
-import LocalSettingsNavigationItem from './item';
-
-//  Stylesheet imports
-import './style.scss';
-
-//  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-
-const messages = defineMessages({
-  general: {  id: 'settings.general', defaultMessage: 'General' },
-  collapsed: { id: 'settings.collapsed_statuses', defaultMessage: 'Collapsed toots' },
-  media: { id: 'settings.media', defaultMessage: 'Media' },
-  preferences: { id: 'settings.preferences', defaultMessage: 'Preferences' },
-  close: { id: 'settings.close', defaultMessage: 'Close' },
-});
-
-@injectIntl
-export default class LocalSettingsNavigation extends React.PureComponent {
-
-  static propTypes = {
-    index      : PropTypes.number,
-    intl       : PropTypes.object.isRequired,
-    onClose    : PropTypes.func.isRequired,
-    onNavigate : PropTypes.func.isRequired,
-  };
-
-  render () {
-
-    const { index, intl, onClose, onNavigate } = this.props;
-
-    return (
-      <nav className='glitch local-settings__navigation'>
-        <LocalSettingsNavigationItem
-          active={index === 0}
-          index={0}
-          onNavigate={onNavigate}
-          title={intl.formatMessage(messages.general)}
-        />
-        <LocalSettingsNavigationItem
-          active={index === 1}
-          index={1}
-          onNavigate={onNavigate}
-          title={intl.formatMessage(messages.collapsed)}
-        />
-        <LocalSettingsNavigationItem
-          active={index === 2}
-          index={2}
-          onNavigate={onNavigate}
-          title={intl.formatMessage(messages.media)}
-        />
-        <LocalSettingsNavigationItem
-          active={index === 3}
-          href='/settings/preferences'
-          index={3}
-          icon='cog'
-          title={intl.formatMessage(messages.preferences)}
-        />
-        <LocalSettingsNavigationItem
-          active={index === 4}
-          className='close'
-          index={4}
-          onNavigate={onClose}
-          title={intl.formatMessage(messages.close)}
-        />
-      </nav>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/local_settings/navigation/item/index.js b/app/javascript/themes/glitch/features/local_settings/navigation/item/index.js
deleted file mode 100644
index a352d5fb2..000000000
--- a/app/javascript/themes/glitch/features/local_settings/navigation/item/index.js
+++ /dev/null
@@ -1,69 +0,0 @@
-//  Package imports
-import React from 'react';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
-
-//  Stylesheet imports
-import './style.scss';
-
-//  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-
-export default class LocalSettingsPage extends React.PureComponent {
-
-  static propTypes = {
-    active: PropTypes.bool,
-    className: PropTypes.string,
-    href: PropTypes.string,
-    icon: PropTypes.string,
-    index: PropTypes.number.isRequired,
-    onNavigate: PropTypes.func,
-    title: PropTypes.string,
-  };
-
-  handleClick = (e) => {
-    const { index, onNavigate } = this.props;
-    if (onNavigate) {
-      onNavigate(index);
-      e.preventDefault();
-    }
-  }
-
-  render () {
-    const { handleClick } = this;
-    const {
-      active,
-      className,
-      href,
-      icon,
-      onNavigate,
-      title,
-    } = this.props;
-
-    const finalClassName = classNames('glitch', 'local-settings__navigation__item', {
-      active,
-    }, className);
-
-    const iconElem = icon ? <i className={`fa fa-fw fa-${icon}`} /> : null;
-
-    if (href) return (
-      <a
-        href={href}
-        className={finalClassName}
-      >
-        {iconElem} {title}
-      </a>
-    );
-    else if (onNavigate) return (
-      <a
-        onClick={handleClick}
-        role='button'
-        tabIndex='0'
-        className={finalClassName}
-      >
-        {iconElem} {title}
-      </a>
-    );
-    else return null;
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/local_settings/navigation/item/style.scss b/app/javascript/themes/glitch/features/local_settings/navigation/item/style.scss
deleted file mode 100644
index 7f7371993..000000000
--- a/app/javascript/themes/glitch/features/local_settings/navigation/item/style.scss
+++ /dev/null
@@ -1,27 +0,0 @@
-@import 'styles/mastodon/variables';
-
-.glitch.local-settings__navigation__item {
-  display: block;
-  padding: 15px 20px;
-  color: inherit;
-  background: $primary-text-color;
-  border-bottom: 1px $ui-primary-color solid;
-  cursor: pointer;
-  text-decoration: none;
-  outline: none;
-  transition: background .3s;
-
-  &:hover {
-    background: $ui-secondary-color;
-  }
-
-  &.active {
-    background: $ui-highlight-color;
-    color: $primary-text-color;
-  }
-
-  &.close, &.close:hover {
-    background: $error-value-color;
-    color: $primary-text-color;
-  }
-}
diff --git a/app/javascript/themes/glitch/features/local_settings/navigation/style.scss b/app/javascript/themes/glitch/features/local_settings/navigation/style.scss
deleted file mode 100644
index 0336f943b..000000000
--- a/app/javascript/themes/glitch/features/local_settings/navigation/style.scss
+++ /dev/null
@@ -1,10 +0,0 @@
-@import 'styles/mastodon/variables';
-
-.glitch.local-settings__navigation {
-  background: $primary-text-color;
-  color: $ui-base-color;
-  width: 200px;
-  font-size: 15px;
-  line-height: 20px;
-  overflow-y: auto;
-}
diff --git a/app/javascript/themes/glitch/features/local_settings/page/index.js b/app/javascript/themes/glitch/features/local_settings/page/index.js
deleted file mode 100644
index 498230f7b..000000000
--- a/app/javascript/themes/glitch/features/local_settings/page/index.js
+++ /dev/null
@@ -1,212 +0,0 @@
-//  Package imports
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
-
-//  Our imports
-import LocalSettingsPageItem from './item';
-
-//  Stylesheet imports
-import './style.scss';
-
-//  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-
-const messages = defineMessages({
-  layout_auto: {  id: 'layout.auto', defaultMessage: 'Auto' },
-  layout_desktop: { id: 'layout.desktop', defaultMessage: 'Desktop' },
-  layout_mobile: { id: 'layout.single', defaultMessage: 'Mobile' },
-  side_arm_none: { id: 'settings.side_arm.none', defaultMessage: 'None' },
-});
-
-@injectIntl
-export default class LocalSettingsPage extends React.PureComponent {
-
-  static propTypes = {
-    index    : PropTypes.number,
-    intl     : PropTypes.object.isRequired,
-    onChange : PropTypes.func.isRequired,
-    settings : ImmutablePropTypes.map.isRequired,
-  };
-
-  pages = [
-    ({ intl, onChange, settings }) => (
-      <div className='glitch local-settings__page general'>
-        <h1><FormattedMessage id='settings.general' defaultMessage='General' /></h1>
-        <LocalSettingsPageItem
-          settings={settings}
-          item={['layout']}
-          id='mastodon-settings--layout'
-          options={[
-            { value: 'auto', message: intl.formatMessage(messages.layout_auto) },
-            { value: 'multiple', message: intl.formatMessage(messages.layout_desktop) },
-            { value: 'single', message: intl.formatMessage(messages.layout_mobile) },
-          ]}
-          onChange={onChange}
-        >
-          <FormattedMessage id='settings.layout' defaultMessage='Layout:' />
-        </LocalSettingsPageItem>
-        <LocalSettingsPageItem
-          settings={settings}
-          item={['stretch']}
-          id='mastodon-settings--stretch'
-          onChange={onChange}
-        >
-          <FormattedMessage id='settings.wide_view' defaultMessage='Wide view (Desktop mode only)' />
-        </LocalSettingsPageItem>
-        <LocalSettingsPageItem
-          settings={settings}
-          item={['navbar_under']}
-          id='mastodon-settings--navbar_under'
-          onChange={onChange}
-        >
-          <FormattedMessage id='settings.navbar_under' defaultMessage='Navbar at the bottom (Mobile only)' />
-        </LocalSettingsPageItem>
-        <section>
-          <h2><FormattedMessage id='settings.compose_box_opts' defaultMessage='Compose box options' /></h2>
-          <LocalSettingsPageItem
-            settings={settings}
-            item={['side_arm']}
-            id='mastodon-settings--side_arm'
-            options={[
-              { value: 'none', message: intl.formatMessage(messages.side_arm_none) },
-              { value: 'direct', message: intl.formatMessage({ id: 'privacy.direct.short' }) },
-              { value: 'private', message: intl.formatMessage({ id: 'privacy.private.short' }) },
-              { value: 'unlisted', message: intl.formatMessage({ id: 'privacy.unlisted.short' }) },
-              { value: 'public', message: intl.formatMessage({ id: 'privacy.public.short' }) },
-            ]}
-            onChange={onChange}
-          >
-            <FormattedMessage id='settings.side_arm' defaultMessage='Secondary toot button:' />
-          </LocalSettingsPageItem>
-        </section>
-      </div>
-    ),
-    ({ onChange, settings }) => (
-      <div className='glitch local-settings__page collapsed'>
-        <h1><FormattedMessage id='settings.collapsed_statuses' defaultMessage='Collapsed toots' /></h1>
-        <LocalSettingsPageItem
-          settings={settings}
-          item={['collapsed', 'enabled']}
-          id='mastodon-settings--collapsed-enabled'
-          onChange={onChange}
-        >
-          <FormattedMessage id='settings.enable_collapsed' defaultMessage='Enable collapsed toots' />
-        </LocalSettingsPageItem>
-        <section>
-          <h2><FormattedMessage id='settings.auto_collapse' defaultMessage='Automatic collapsing' /></h2>
-          <LocalSettingsPageItem
-            settings={settings}
-            item={['collapsed', 'auto', 'all']}
-            id='mastodon-settings--collapsed-auto-all'
-            onChange={onChange}
-            dependsOn={[['collapsed', 'enabled']]}
-          >
-            <FormattedMessage id='settings.auto_collapse_all' defaultMessage='Everything' />
-          </LocalSettingsPageItem>
-          <LocalSettingsPageItem
-            settings={settings}
-            item={['collapsed', 'auto', 'notifications']}
-            id='mastodon-settings--collapsed-auto-notifications'
-            onChange={onChange}
-            dependsOn={[['collapsed', 'enabled']]}
-            dependsOnNot={[['collapsed', 'auto', 'all']]}
-          >
-            <FormattedMessage id='settings.auto_collapse_notifications' defaultMessage='Notifications' />
-          </LocalSettingsPageItem>
-          <LocalSettingsPageItem
-            settings={settings}
-            item={['collapsed', 'auto', 'lengthy']}
-            id='mastodon-settings--collapsed-auto-lengthy'
-            onChange={onChange}
-            dependsOn={[['collapsed', 'enabled']]}
-            dependsOnNot={[['collapsed', 'auto', 'all']]}
-          >
-            <FormattedMessage id='settings.auto_collapse_lengthy' defaultMessage='Lengthy toots' />
-          </LocalSettingsPageItem>
-          <LocalSettingsPageItem
-            settings={settings}
-            item={['collapsed', 'auto', 'reblogs']}
-            id='mastodon-settings--collapsed-auto-reblogs'
-            onChange={onChange}
-            dependsOn={[['collapsed', 'enabled']]}
-            dependsOnNot={[['collapsed', 'auto', 'all']]}
-          >
-            <FormattedMessage id='settings.auto_collapse_reblogs' defaultMessage='Boosts' />
-          </LocalSettingsPageItem>
-          <LocalSettingsPageItem
-            settings={settings}
-            item={['collapsed', 'auto', 'replies']}
-            id='mastodon-settings--collapsed-auto-replies'
-            onChange={onChange}
-            dependsOn={[['collapsed', 'enabled']]}
-            dependsOnNot={[['collapsed', 'auto', 'all']]}
-          >
-            <FormattedMessage id='settings.auto_collapse_replies' defaultMessage='Replies' />
-          </LocalSettingsPageItem>
-          <LocalSettingsPageItem
-            settings={settings}
-            item={['collapsed', 'auto', 'media']}
-            id='mastodon-settings--collapsed-auto-media'
-            onChange={onChange}
-            dependsOn={[['collapsed', 'enabled']]}
-            dependsOnNot={[['collapsed', 'auto', 'all']]}
-          >
-            <FormattedMessage id='settings.auto_collapse_media' defaultMessage='Toots with media' />
-          </LocalSettingsPageItem>
-        </section>
-        <section>
-          <h2><FormattedMessage id='settings.image_backgrounds' defaultMessage='Image backgrounds' /></h2>
-          <LocalSettingsPageItem
-            settings={settings}
-            item={['collapsed', 'backgrounds', 'user_backgrounds']}
-            id='mastodon-settings--collapsed-user-backgrouns'
-            onChange={onChange}
-            dependsOn={[['collapsed', 'enabled']]}
-          >
-            <FormattedMessage id='settings.image_backgrounds_users' defaultMessage='Give collapsed toots an image background' />
-          </LocalSettingsPageItem>
-          <LocalSettingsPageItem
-            settings={settings}
-            item={['collapsed', 'backgrounds', 'preview_images']}
-            id='mastodon-settings--collapsed-preview-images'
-            onChange={onChange}
-            dependsOn={[['collapsed', 'enabled']]}
-          >
-            <FormattedMessage id='settings.image_backgrounds_media' defaultMessage='Preview collapsed toot media' />
-          </LocalSettingsPageItem>
-        </section>
-      </div>
-    ),
-    ({ onChange, settings }) => (
-      <div className='glitch local-settings__page media'>
-        <h1><FormattedMessage id='settings.media' defaultMessage='Media' /></h1>
-        <LocalSettingsPageItem
-          settings={settings}
-          item={['media', 'letterbox']}
-          id='mastodon-settings--media-letterbox'
-          onChange={onChange}
-        >
-          <FormattedMessage id='settings.media_letterbox' defaultMessage='Letterbox media' />
-        </LocalSettingsPageItem>
-        <LocalSettingsPageItem
-          settings={settings}
-          item={['media', 'fullwidth']}
-          id='mastodon-settings--media-fullwidth'
-          onChange={onChange}
-        >
-          <FormattedMessage id='settings.media_fullwidth' defaultMessage='Full-width media previews' />
-        </LocalSettingsPageItem>
-      </div>
-    ),
-  ];
-
-  render () {
-    const { pages } = this;
-    const { index, intl, onChange, settings } = this.props;
-    const CurrentPage = pages[index] || pages[0];
-
-    return <CurrentPage intl={intl} onChange={onChange} settings={settings} />;
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/local_settings/page/item/index.js b/app/javascript/themes/glitch/features/local_settings/page/item/index.js
deleted file mode 100644
index 37e28c084..000000000
--- a/app/javascript/themes/glitch/features/local_settings/page/item/index.js
+++ /dev/null
@@ -1,90 +0,0 @@
-//  Package imports
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-
-//  Stylesheet imports
-import './style.scss';
-
-//  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-
-export default class LocalSettingsPageItem extends React.PureComponent {
-
-  static propTypes = {
-    children: PropTypes.element.isRequired,
-    dependsOn: PropTypes.array,
-    dependsOnNot: PropTypes.array,
-    id: PropTypes.string.isRequired,
-    item: PropTypes.array.isRequired,
-    onChange: PropTypes.func.isRequired,
-    options: PropTypes.arrayOf(PropTypes.shape({
-      value: PropTypes.string.isRequired,
-      message: PropTypes.string.isRequired,
-    })),
-    settings: ImmutablePropTypes.map.isRequired,
-  };
-
-  handleChange = e => {
-    const { target } = e;
-    const { item, onChange, options } = this.props;
-    if (options && options.length > 0) onChange(item, target.value);
-    else onChange(item, target.checked);
-  }
-
-  render () {
-    const { handleChange } = this;
-    const { settings, item, id, options, children, dependsOn, dependsOnNot } = this.props;
-    let enabled = true;
-
-    if (dependsOn) {
-      for (let i = 0; i < dependsOn.length; i++) {
-        enabled = enabled && settings.getIn(dependsOn[i]);
-      }
-    }
-    if (dependsOnNot) {
-      for (let i = 0; i < dependsOnNot.length; i++) {
-        enabled = enabled && !settings.getIn(dependsOnNot[i]);
-      }
-    }
-
-    if (options && options.length > 0) {
-      const currentValue = settings.getIn(item);
-      const optionElems = options && options.length > 0 && options.map((opt) => (
-        <option
-          key={opt.value}
-          value={opt.value}
-        >
-          {opt.message}
-        </option>
-      ));
-      return (
-        <label className='glitch local-settings__page__item' htmlFor={id}>
-          <p>{children}</p>
-          <p>
-            <select
-              id={id}
-              disabled={!enabled}
-              onBlur={handleChange}
-              onChange={handleChange}
-              value={currentValue}
-            >
-              {optionElems}
-            </select>
-          </p>
-        </label>
-      );
-    } else return (
-      <label className='glitch local-settings__page__item' htmlFor={id}>
-        <input
-          id={id}
-          type='checkbox'
-          checked={settings.getIn(item)}
-          onChange={handleChange}
-          disabled={!enabled}
-        />
-        {children}
-      </label>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/local_settings/page/item/style.scss b/app/javascript/themes/glitch/features/local_settings/page/item/style.scss
deleted file mode 100644
index b2d8f7185..000000000
--- a/app/javascript/themes/glitch/features/local_settings/page/item/style.scss
+++ /dev/null
@@ -1,7 +0,0 @@
-@import 'styles/mastodon/variables';
-
-.glitch.local-settings__page__item {
-  select {
-    margin-bottom: 5px;
-  }
-}
diff --git a/app/javascript/themes/glitch/features/local_settings/page/style.scss b/app/javascript/themes/glitch/features/local_settings/page/style.scss
deleted file mode 100644
index e9eedcad0..000000000
--- a/app/javascript/themes/glitch/features/local_settings/page/style.scss
+++ /dev/null
@@ -1,9 +0,0 @@
-@import 'styles/mastodon/variables';
-
-.glitch.local-settings__page {
-  display: block;
-  flex: auto;
-  padding: 15px 20px 15px 20px;
-  width: 360px;
-  overflow-y: auto;
-}
diff --git a/app/javascript/themes/glitch/features/local_settings/style.scss b/app/javascript/themes/glitch/features/local_settings/style.scss
deleted file mode 100644
index 765294607..000000000
--- a/app/javascript/themes/glitch/features/local_settings/style.scss
+++ /dev/null
@@ -1,34 +0,0 @@
-@import 'styles/mastodon/variables';
-
-.glitch.local-settings {
-  position: relative;
-  display: flex;
-  flex-direction: row;
-  background: $ui-secondary-color;
-  color: $ui-base-color;
-  border-radius: 8px;
-  height: 80vh;
-  width: 80vw;
-  max-width: 740px;
-  max-height: 450px;
-  overflow: hidden;
-
-  label {
-    display: block;
-  }
-
-  h1 {
-    font-size: 18px;
-    font-weight: 500;
-    line-height: 24px;
-    margin-bottom: 20px;
-  }
-
-  h2 {
-    font-size: 15px;
-    font-weight: 500;
-    line-height: 20px;
-    margin-top: 20px;
-    margin-bottom: 10px;
-  }
-}
diff --git a/app/javascript/themes/glitch/features/mutes/index.js b/app/javascript/themes/glitch/features/mutes/index.js
deleted file mode 100644
index 1158b8262..000000000
--- a/app/javascript/themes/glitch/features/mutes/index.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import LoadingIndicator from 'themes/glitch/components/loading_indicator';
-import { ScrollContainer } from 'react-router-scroll-4';
-import Column from 'themes/glitch/features/ui/components/column';
-import ColumnBackButtonSlim from 'themes/glitch/components/column_back_button_slim';
-import AccountContainer from 'themes/glitch/containers/account_container';
-import { fetchMutes, expandMutes } from 'themes/glitch/actions/mutes';
-import { defineMessages, injectIntl } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const messages = defineMessages({
-  heading: { id: 'column.mutes', defaultMessage: 'Muted users' },
-});
-
-const mapStateToProps = state => ({
-  accountIds: state.getIn(['user_lists', 'mutes', 'items']),
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class Mutes extends ImmutablePureComponent {
-
-  static propTypes = {
-    params: PropTypes.object.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    accountIds: ImmutablePropTypes.list,
-    intl: PropTypes.object.isRequired,
-  };
-
-  componentWillMount () {
-    this.props.dispatch(fetchMutes());
-  }
-
-  handleScroll = (e) => {
-    const { scrollTop, scrollHeight, clientHeight } = e.target;
-
-    if (scrollTop === scrollHeight - clientHeight) {
-      this.props.dispatch(expandMutes());
-    }
-  }
-
-  render () {
-    const { intl, accountIds } = this.props;
-
-    if (!accountIds) {
-      return (
-        <Column>
-          <LoadingIndicator />
-        </Column>
-      );
-    }
-
-    return (
-      <Column name='mutes' icon='volume-off' heading={intl.formatMessage(messages.heading)}>
-        <ColumnBackButtonSlim />
-        <ScrollContainer scrollKey='mutes'>
-          <div className='scrollable mutes' onScroll={this.handleScroll}>
-            {accountIds.map(id =>
-              <AccountContainer key={id} id={id} />
-            )}
-          </div>
-        </ScrollContainer>
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/notifications/components/clear_column_button.js b/app/javascript/themes/glitch/features/notifications/components/clear_column_button.js
deleted file mode 100644
index 22a10753f..000000000
--- a/app/javascript/themes/glitch/features/notifications/components/clear_column_button.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { FormattedMessage } from 'react-intl';
-
-export default class ClearColumnButton extends React.Component {
-
-  static propTypes = {
-    onClick: PropTypes.func.isRequired,
-  };
-
-  render () {
-    return (
-      <button className='text-btn column-header__setting-btn' tabIndex='0' onClick={this.props.onClick}><i className='fa fa-eraser' /> <FormattedMessage id='notifications.clear' defaultMessage='Clear notifications' /></button>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/notifications/components/column_settings.js b/app/javascript/themes/glitch/features/notifications/components/column_settings.js
deleted file mode 100644
index 88a29d4d3..000000000
--- a/app/javascript/themes/glitch/features/notifications/components/column_settings.js
+++ /dev/null
@@ -1,86 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { FormattedMessage } from 'react-intl';
-import ClearColumnButton from './clear_column_button';
-import SettingToggle from './setting_toggle';
-
-export default class ColumnSettings extends React.PureComponent {
-
-  static propTypes = {
-    settings: ImmutablePropTypes.map.isRequired,
-    pushSettings: ImmutablePropTypes.map.isRequired,
-    onChange: PropTypes.func.isRequired,
-    onSave: PropTypes.func.isRequired,
-    onClear: PropTypes.func.isRequired,
-  };
-
-  onPushChange = (key, checked) => {
-    this.props.onChange(['push', ...key], checked);
-  }
-
-  render () {
-    const { settings, pushSettings, onChange, onClear } = this.props;
-
-    const alertStr = <FormattedMessage id='notifications.column_settings.alert' defaultMessage='Desktop notifications' />;
-    const showStr  = <FormattedMessage id='notifications.column_settings.show' defaultMessage='Show in column' />;
-    const soundStr = <FormattedMessage id='notifications.column_settings.sound' defaultMessage='Play sound' />;
-
-    const showPushSettings = pushSettings.get('browserSupport') && pushSettings.get('isSubscribed');
-    const pushStr = showPushSettings && <FormattedMessage id='notifications.column_settings.push' defaultMessage='Push notifications' />;
-    const pushMeta = showPushSettings && <FormattedMessage id='notifications.column_settings.push_meta' defaultMessage='This device' />;
-
-    return (
-      <div>
-        <div className='column-settings__row'>
-          <ClearColumnButton onClick={onClear} />
-        </div>
-
-        <div role='group' aria-labelledby='notifications-follow'>
-          <span id='notifications-follow' className='column-settings__section'><FormattedMessage id='notifications.column_settings.follow' defaultMessage='New followers:' /></span>
-
-          <div className='column-settings__row'>
-            <SettingToggle prefix='notifications_desktop' settings={settings} settingKey={['alerts', 'follow']} onChange={onChange} label={alertStr} />
-            {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingKey={['alerts', 'follow']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
-            <SettingToggle prefix='notifications' settings={settings} settingKey={['shows', 'follow']} onChange={onChange} label={showStr} />
-            <SettingToggle prefix='notifications' settings={settings} settingKey={['sounds', 'follow']} onChange={onChange} label={soundStr} />
-          </div>
-        </div>
-
-        <div role='group' aria-labelledby='notifications-favourite'>
-          <span id='notifications-favourite' className='column-settings__section'><FormattedMessage id='notifications.column_settings.favourite' defaultMessage='Favourites:' /></span>
-
-          <div className='column-settings__row'>
-            <SettingToggle prefix='notifications_desktop' settings={settings} settingKey={['alerts', 'favourite']} onChange={onChange} label={alertStr} />
-            {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingKey={['alerts', 'favourite']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
-            <SettingToggle prefix='notifications' settings={settings} settingKey={['shows', 'favourite']} onChange={onChange} label={showStr} />
-            <SettingToggle prefix='notifications' settings={settings} settingKey={['sounds', 'favourite']} onChange={onChange} label={soundStr} />
-          </div>
-        </div>
-
-        <div role='group' aria-labelledby='notifications-mention'>
-          <span id='notifications-mention' className='column-settings__section'><FormattedMessage id='notifications.column_settings.mention' defaultMessage='Mentions:' /></span>
-
-          <div className='column-settings__row'>
-            <SettingToggle prefix='notifications_desktop' settings={settings} settingKey={['alerts', 'mention']} onChange={onChange} label={alertStr} />
-            {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingKey={['alerts', 'mention']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
-            <SettingToggle prefix='notifications' settings={settings} settingKey={['shows', 'mention']} onChange={onChange} label={showStr} />
-            <SettingToggle prefix='notifications' settings={settings} settingKey={['sounds', 'mention']} onChange={onChange} label={soundStr} />
-          </div>
-        </div>
-
-        <div role='group' aria-labelledby='notifications-reblog'>
-          <span id='notifications-reblog' className='column-settings__section'><FormattedMessage id='notifications.column_settings.reblog' defaultMessage='Boosts:' /></span>
-
-          <div className='column-settings__row'>
-            <SettingToggle prefix='notifications_desktop' settings={settings} settingKey={['alerts', 'reblog']} onChange={onChange} label={alertStr} />
-            {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingKey={['alerts', 'reblog']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
-            <SettingToggle prefix='notifications' settings={settings} settingKey={['shows', 'reblog']} onChange={onChange} label={showStr} />
-            <SettingToggle prefix='notifications' settings={settings} settingKey={['sounds', 'reblog']} onChange={onChange} label={soundStr} />
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/notifications/components/follow.js b/app/javascript/themes/glitch/features/notifications/components/follow.js
deleted file mode 100644
index 96cfe83e6..000000000
--- a/app/javascript/themes/glitch/features/notifications/components/follow.js
+++ /dev/null
@@ -1,98 +0,0 @@
-//  Package imports.
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import { FormattedMessage } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { HotKeys } from 'react-hotkeys';
-
-// Our imports.
-import Permalink from 'themes/glitch/components/permalink';
-import AccountContainer from 'themes/glitch/containers/account_container';
-import NotificationOverlayContainer from '../containers/overlay_container';
-
-export default class NotificationFollow extends ImmutablePureComponent {
-
-  static propTypes = {
-    hidden: PropTypes.bool,
-    id: PropTypes.string.isRequired,
-    account: ImmutablePropTypes.map.isRequired,
-    notification: ImmutablePropTypes.map.isRequired,
-  };
-
-  handleMoveUp = () => {
-    const { notification, onMoveUp } = this.props;
-    onMoveUp(notification.get('id'));
-  }
-
-  handleMoveDown = () => {
-    const { notification, onMoveDown } = this.props;
-    onMoveDown(notification.get('id'));
-  }
-
-  handleOpen = () => {
-    this.handleOpenProfile();
-  }
-
-  handleOpenProfile = () => {
-    const { notification } = this.props;
-    this.context.router.history.push(`/accounts/${notification.getIn(['account', 'id'])}`);
-  }
-
-  handleMention = e => {
-    e.preventDefault();
-
-    const { notification, onMention } = this.props;
-    onMention(notification.get('account'), this.context.router.history);
-  }
-
-  getHandlers () {
-    return {
-      moveUp: this.handleMoveUp,
-      moveDown: this.handleMoveDown,
-      open: this.handleOpen,
-      openProfile: this.handleOpenProfile,
-      mention: this.handleMention,
-      reply: this.handleMention,
-    };
-  }
-
-  render () {
-    const { account, notification, hidden } = this.props;
-
-    //  Links to the display name.
-    const displayName = account.get('display_name_html') || account.get('username');
-    const link = (
-      <Permalink
-        className='notification__display-name'
-        href={account.get('url')}
-        title={account.get('acct')}
-        to={`/accounts/${account.get('id')}`}
-        dangerouslySetInnerHTML={{ __html: displayName }}
-      />
-    );
-
-    //  Renders.
-    return (
-      <HotKeys handlers={this.getHandlers()}>
-        <div className='notification notification-follow focusable' tabIndex='0'>
-          <div className='notification__message'>
-            <div className='notification__favourite-icon-wrapper'>
-              <i className='fa fa-fw fa-user-plus' />
-            </div>
-
-            <FormattedMessage
-              id='notification.follow'
-              defaultMessage='{name} followed you'
-              values={{ name: link }}
-            />
-          </div>
-
-          <AccountContainer hidden={hidden} id={account.get('id')} withNote={false} />
-          <NotificationOverlayContainer notification={notification} />
-        </div>
-      </HotKeys>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/notifications/components/notification.js b/app/javascript/themes/glitch/features/notifications/components/notification.js
deleted file mode 100644
index 47f5770bf..000000000
--- a/app/javascript/themes/glitch/features/notifications/components/notification.js
+++ /dev/null
@@ -1,93 +0,0 @@
-//  Package imports.
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-//  Our imports,
-import StatusContainer from 'themes/glitch/containers/status_container';
-import NotificationFollow from './follow';
-
-export default class Notification extends ImmutablePureComponent {
-
-  static propTypes = {
-    notification: ImmutablePropTypes.map.isRequired,
-    hidden: PropTypes.bool,
-    onMoveUp: PropTypes.func.isRequired,
-    onMoveDown: PropTypes.func.isRequired,
-    onMention: PropTypes.func.isRequired,
-  };
-
-  render () {
-    const {
-      hidden,
-      notification,
-      onMoveDown,
-      onMoveUp,
-      onMention,
-    } = this.props;
-
-    switch(notification.get('type')) {
-    case 'follow':
-      return (
-        <NotificationFollow
-          hidden={hidden}
-          id={notification.get('id')}
-          account={notification.get('account')}
-          notification={notification}
-          onMoveDown={onMoveDown}
-          onMoveUp={onMoveUp}
-          onMention={onMention}
-        />
-      );
-    case 'mention':
-      return (
-        <StatusContainer
-          containerId={notification.get('id')}
-          hidden={hidden}
-          id={notification.get('status')}
-          notification={notification}
-          onMoveDown={onMoveDown}
-          onMoveUp={onMoveUp}
-          onMention={onMention}
-          withDismiss
-        />
-      );
-    case 'favourite':
-      return (
-        <StatusContainer
-          containerId={notification.get('id')}
-          hidden={hidden}
-          id={notification.get('status')}
-          account={notification.get('account')}
-          prepend='favourite'
-          muted
-          notification={notification}
-          onMoveDown={onMoveDown}
-          onMoveUp={onMoveUp}
-          onMention={onMention}
-          withDismiss
-        />
-      );
-    case 'reblog':
-      return (
-        <StatusContainer
-          containerId={notification.get('id')}
-          hidden={hidden}
-          id={notification.get('status')}
-          account={notification.get('account')}
-          prepend='reblog'
-          muted
-          notification={notification}
-          onMoveDown={onMoveDown}
-          onMoveUp={onMoveUp}
-          onMention={onMention}
-          withDismiss
-        />
-      );
-    default:
-      return null;
-    }
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/notifications/components/overlay.js b/app/javascript/themes/glitch/features/notifications/components/overlay.js
deleted file mode 100644
index e56f9c628..000000000
--- a/app/javascript/themes/glitch/features/notifications/components/overlay.js
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * Notification overlay
- */
-
-
-//  Package imports.
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { defineMessages, injectIntl } from 'react-intl';
-
-const messages = defineMessages({
-  markForDeletion: { id: 'notification.markForDeletion', defaultMessage: 'Mark for deletion' },
-});
-
-@injectIntl
-export default class NotificationOverlay extends ImmutablePureComponent {
-
-  static propTypes = {
-    notification    : ImmutablePropTypes.map.isRequired,
-    onMarkForDelete : PropTypes.func.isRequired,
-    show            : PropTypes.bool.isRequired,
-    intl            : PropTypes.object.isRequired,
-  };
-
-  onToggleMark = () => {
-    const mark = !this.props.notification.get('markedForDelete');
-    const id = this.props.notification.get('id');
-    this.props.onMarkForDelete(id, mark);
-  }
-
-  render () {
-    const { notification, show, intl } = this.props;
-
-    const active = notification.get('markedForDelete');
-    const label = intl.formatMessage(messages.markForDeletion);
-
-    return show ? (
-      <div
-        aria-label={label}
-        role='checkbox'
-        aria-checked={active}
-        tabIndex={0}
-        className={`notification__dismiss-overlay ${active ? 'active' : ''}`}
-        onClick={this.onToggleMark}
-      >
-        <div className='wrappy'>
-          <div className='ckbox' aria-hidden='true' title={label}>
-            {active ? (<i className='fa fa-check' />) : ''}
-          </div>
-        </div>
-      </div>
-    ) : null;
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/notifications/components/setting_toggle.js b/app/javascript/themes/glitch/features/notifications/components/setting_toggle.js
deleted file mode 100644
index 281359d2a..000000000
--- a/app/javascript/themes/glitch/features/notifications/components/setting_toggle.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import Toggle from 'react-toggle';
-
-export default class SettingToggle extends React.PureComponent {
-
-  static propTypes = {
-    prefix: PropTypes.string,
-    settings: ImmutablePropTypes.map.isRequired,
-    settingKey: PropTypes.array.isRequired,
-    label: PropTypes.node.isRequired,
-    meta: PropTypes.node,
-    onChange: PropTypes.func.isRequired,
-  }
-
-  onChange = ({ target }) => {
-    this.props.onChange(this.props.settingKey, target.checked);
-  }
-
-  render () {
-    const { prefix, settings, settingKey, label, meta } = this.props;
-    const id = ['setting-toggle', prefix, ...settingKey].filter(Boolean).join('-');
-
-    return (
-      <div className='setting-toggle'>
-        <Toggle id={id} checked={settings.getIn(settingKey)} onChange={this.onChange} onKeyDown={this.onKeyDown} />
-        <label htmlFor={id} className='setting-toggle__label'>{label}</label>
-        {meta && <span className='setting-meta__label'>{meta}</span>}
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/notifications/containers/column_settings_container.js b/app/javascript/themes/glitch/features/notifications/containers/column_settings_container.js
deleted file mode 100644
index ddc8495f4..000000000
--- a/app/javascript/themes/glitch/features/notifications/containers/column_settings_container.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import { connect } from 'react-redux';
-import { defineMessages, injectIntl } from 'react-intl';
-import ColumnSettings from '../components/column_settings';
-import { changeSetting, saveSettings } from 'themes/glitch/actions/settings';
-import { clearNotifications } from 'themes/glitch/actions/notifications';
-import { changeAlerts as changePushNotifications, saveSettings as savePushNotificationSettings } from 'themes/glitch/actions/push_notifications';
-import { openModal } from 'themes/glitch/actions/modal';
-
-const messages = defineMessages({
-  clearMessage: { id: 'notifications.clear_confirmation', defaultMessage: 'Are you sure you want to permanently clear all your notifications?' },
-  clearConfirm: { id: 'notifications.clear', defaultMessage: 'Clear notifications' },
-});
-
-const mapStateToProps = state => ({
-  settings: state.getIn(['settings', 'notifications']),
-  pushSettings: state.get('push_notifications'),
-});
-
-const mapDispatchToProps = (dispatch, { intl }) => ({
-
-  onChange (key, checked) {
-    if (key[0] === 'push') {
-      dispatch(changePushNotifications(key.slice(1), checked));
-    } else {
-      dispatch(changeSetting(['notifications', ...key], checked));
-    }
-  },
-
-  onSave () {
-    dispatch(saveSettings());
-    dispatch(savePushNotificationSettings());
-  },
-
-  onClear () {
-    dispatch(openModal('CONFIRM', {
-      message: intl.formatMessage(messages.clearMessage),
-      confirm: intl.formatMessage(messages.clearConfirm),
-      onConfirm: () => dispatch(clearNotifications()),
-    }));
-  },
-
-});
-
-export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(ColumnSettings));
diff --git a/app/javascript/themes/glitch/features/notifications/containers/notification_container.js b/app/javascript/themes/glitch/features/notifications/containers/notification_container.js
deleted file mode 100644
index 033c61e3d..000000000
--- a/app/javascript/themes/glitch/features/notifications/containers/notification_container.js
+++ /dev/null
@@ -1,26 +0,0 @@
-//  Package imports.
-import { connect } from 'react-redux';
-
-//  Our imports.
-import { makeGetNotification } from 'themes/glitch/selectors';
-import Notification from '../components/notification';
-import { mentionCompose } from 'themes/glitch/actions/compose';
-
-const makeMapStateToProps = () => {
-  const getNotification = makeGetNotification();
-
-  const mapStateToProps = (state, props) => ({
-    notification: getNotification(state, props.notification, props.accountId),
-    notifCleaning: state.getIn(['notifications', 'cleaningMode']),
-  });
-
-  return mapStateToProps;
-};
-
-const mapDispatchToProps = dispatch => ({
-  onMention: (account, router) => {
-    dispatch(mentionCompose(account, router));
-  },
-});
-
-export default connect(makeMapStateToProps, mapDispatchToProps)(Notification);
diff --git a/app/javascript/themes/glitch/features/notifications/containers/overlay_container.js b/app/javascript/themes/glitch/features/notifications/containers/overlay_container.js
deleted file mode 100644
index 52649cdd7..000000000
--- a/app/javascript/themes/glitch/features/notifications/containers/overlay_container.js
+++ /dev/null
@@ -1,18 +0,0 @@
-//  Package imports.
-import { connect } from 'react-redux';
-
-//  Our imports.
-import NotificationOverlay from '../components/overlay';
-import { markNotificationForDelete } from 'themes/glitch/actions/notifications';
-
-const mapDispatchToProps = dispatch => ({
-  onMarkForDelete(id, yes) {
-    dispatch(markNotificationForDelete(id, yes));
-  },
-});
-
-const mapStateToProps = state => ({
-  show: state.getIn(['notifications', 'cleaningMode']),
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(NotificationOverlay);
diff --git a/app/javascript/themes/glitch/features/notifications/index.js b/app/javascript/themes/glitch/features/notifications/index.js
deleted file mode 100644
index 1ecde660a..000000000
--- a/app/javascript/themes/glitch/features/notifications/index.js
+++ /dev/null
@@ -1,193 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import Column from 'themes/glitch/components/column';
-import ColumnHeader from 'themes/glitch/components/column_header';
-import {
-  enterNotificationClearingMode,
-  expandNotifications,
-  scrollTopNotifications,
-} from 'themes/glitch/actions/notifications';
-import { addColumn, removeColumn, moveColumn } from 'themes/glitch/actions/columns';
-import NotificationContainer from './containers/notification_container';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import ColumnSettingsContainer from './containers/column_settings_container';
-import { createSelector } from 'reselect';
-import { List as ImmutableList } from 'immutable';
-import { debounce } from 'lodash';
-import ScrollableList from 'themes/glitch/components/scrollable_list';
-
-const messages = defineMessages({
-  title: { id: 'column.notifications', defaultMessage: 'Notifications' },
-});
-
-const getNotifications = createSelector([
-  state => ImmutableList(state.getIn(['settings', 'notifications', 'shows']).filter(item => !item).keys()),
-  state => state.getIn(['notifications', 'items']),
-], (excludedTypes, notifications) => notifications.filterNot(item => excludedTypes.includes(item.get('type'))));
-
-const mapStateToProps = state => ({
-  notifications: getNotifications(state),
-  localSettings:  state.get('local_settings'),
-  isLoading: state.getIn(['notifications', 'isLoading'], true),
-  isUnread: state.getIn(['notifications', 'unread']) > 0,
-  hasMore: !!state.getIn(['notifications', 'next']),
-  notifCleaningActive: state.getIn(['notifications', 'cleaningMode']),
-});
-
-/* glitch */
-const mapDispatchToProps = dispatch => ({
-  onEnterCleaningMode(yes) {
-    dispatch(enterNotificationClearingMode(yes));
-  },
-  dispatch,
-});
-
-@connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
-export default class Notifications extends React.PureComponent {
-
-  static propTypes = {
-    columnId: PropTypes.string,
-    notifications: ImmutablePropTypes.list.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    shouldUpdateScroll: PropTypes.func,
-    intl: PropTypes.object.isRequired,
-    isLoading: PropTypes.bool,
-    isUnread: PropTypes.bool,
-    multiColumn: PropTypes.bool,
-    hasMore: PropTypes.bool,
-    localSettings: ImmutablePropTypes.map,
-    notifCleaningActive: PropTypes.bool,
-    onEnterCleaningMode: PropTypes.func,
-  };
-
-  static defaultProps = {
-    trackScroll: true,
-  };
-
-  handleScrollToBottom = debounce(() => {
-    this.props.dispatch(scrollTopNotifications(false));
-    this.props.dispatch(expandNotifications());
-  }, 300, { leading: true });
-
-  handleScrollToTop = debounce(() => {
-    this.props.dispatch(scrollTopNotifications(true));
-  }, 100);
-
-  handleScroll = debounce(() => {
-    this.props.dispatch(scrollTopNotifications(false));
-  }, 100);
-
-  handlePin = () => {
-    const { columnId, dispatch } = this.props;
-
-    if (columnId) {
-      dispatch(removeColumn(columnId));
-    } else {
-      dispatch(addColumn('NOTIFICATIONS', {}));
-    }
-  }
-
-  handleMove = (dir) => {
-    const { columnId, dispatch } = this.props;
-    dispatch(moveColumn(columnId, dir));
-  }
-
-  handleHeaderClick = () => {
-    this.column.scrollTop();
-  }
-
-  setColumnRef = c => {
-    this.column = c;
-  }
-
-  handleMoveUp = id => {
-    const elementIndex = this.props.notifications.findIndex(item => item.get('id') === id) - 1;
-    this._selectChild(elementIndex);
-  }
-
-  handleMoveDown = id => {
-    const elementIndex = this.props.notifications.findIndex(item => item.get('id') === id) + 1;
-    this._selectChild(elementIndex);
-  }
-
-  _selectChild (index) {
-    const element = this.column.node.querySelector(`article:nth-of-type(${index + 1}) .focusable`);
-
-    if (element) {
-      element.focus();
-    }
-  }
-
-  render () {
-    const { intl, notifications, shouldUpdateScroll, isLoading, isUnread, columnId, multiColumn, hasMore } = this.props;
-    const pinned = !!columnId;
-    const emptyMessage = <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. Interact with others to start the conversation." />;
-
-    let scrollableContent = null;
-
-    if (isLoading && this.scrollableContent) {
-      scrollableContent = this.scrollableContent;
-    } else if (notifications.size > 0 || hasMore) {
-      scrollableContent = notifications.map((item) => (
-        <NotificationContainer
-          key={item.get('id')}
-          notification={item}
-          accountId={item.get('account')}
-          onMoveUp={this.handleMoveUp}
-          onMoveDown={this.handleMoveDown}
-        />
-      ));
-    } else {
-      scrollableContent = null;
-    }
-
-    this.scrollableContent = scrollableContent;
-
-    const scrollContainer = (
-      <ScrollableList
-        scrollKey={`notifications-${columnId}`}
-        trackScroll={!pinned}
-        isLoading={isLoading}
-        hasMore={hasMore}
-        emptyMessage={emptyMessage}
-        onScrollToBottom={this.handleScrollToBottom}
-        onScrollToTop={this.handleScrollToTop}
-        onScroll={this.handleScroll}
-        shouldUpdateScroll={shouldUpdateScroll}
-      >
-        {scrollableContent}
-      </ScrollableList>
-    );
-
-    return (
-      <Column
-        ref={this.setColumnRef}
-        name='notifications'
-        extraClasses={this.props.notifCleaningActive ? 'notif-cleaning' : null}
-      >
-        <ColumnHeader
-          icon='bell'
-          active={isUnread}
-          title={intl.formatMessage(messages.title)}
-          onPin={this.handlePin}
-          onMove={this.handleMove}
-          onClick={this.handleHeaderClick}
-          pinned={pinned}
-          multiColumn={multiColumn}
-          localSettings={this.props.localSettings}
-          notifCleaning
-          notifCleaningActive={this.props.notifCleaningActive} // this is used to toggle the header text
-          onEnterCleaningMode={this.props.onEnterCleaningMode}
-        >
-          <ColumnSettingsContainer />
-        </ColumnHeader>
-
-        {scrollContainer}
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/pinned_statuses/index.js b/app/javascript/themes/glitch/features/pinned_statuses/index.js
deleted file mode 100644
index 0a3997850..000000000
--- a/app/javascript/themes/glitch/features/pinned_statuses/index.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { fetchPinnedStatuses } from 'themes/glitch/actions/pin_statuses';
-import Column from 'themes/glitch/features/ui/components/column';
-import ColumnBackButtonSlim from 'themes/glitch/components/column_back_button_slim';
-import StatusList from 'themes/glitch/components/status_list';
-import { defineMessages, injectIntl } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const messages = defineMessages({
-  heading: { id: 'column.pins', defaultMessage: 'Pinned toot' },
-});
-
-const mapStateToProps = state => ({
-  statusIds: state.getIn(['status_lists', 'pins', 'items']),
-  hasMore: !!state.getIn(['status_lists', 'pins', 'next']),
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class PinnedStatuses extends ImmutablePureComponent {
-
-  static propTypes = {
-    dispatch: PropTypes.func.isRequired,
-    statusIds: ImmutablePropTypes.list.isRequired,
-    intl: PropTypes.object.isRequired,
-    hasMore: PropTypes.bool.isRequired,
-  };
-
-  componentWillMount () {
-    this.props.dispatch(fetchPinnedStatuses());
-  }
-
-  handleHeaderClick = () => {
-    this.column.scrollTop();
-  }
-
-  setRef = c => {
-    this.column = c;
-  }
-
-  render () {
-    const { intl, statusIds, hasMore } = this.props;
-
-    return (
-      <Column icon='thumb-tack' heading={intl.formatMessage(messages.heading)} ref={this.setRef}>
-        <ColumnBackButtonSlim />
-        <StatusList
-          statusIds={statusIds}
-          scrollKey='pinned_statuses'
-          hasMore={hasMore}
-        />
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/public_timeline/containers/column_settings_container.js b/app/javascript/themes/glitch/features/public_timeline/containers/column_settings_container.js
deleted file mode 100644
index 0185a7724..000000000
--- a/app/javascript/themes/glitch/features/public_timeline/containers/column_settings_container.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import { connect } from 'react-redux';
-import ColumnSettings from 'themes/glitch/features/community_timeline/components/column_settings';
-import { changeSetting } from 'themes/glitch/actions/settings';
-
-const mapStateToProps = state => ({
-  settings: state.getIn(['settings', 'public']),
-});
-
-const mapDispatchToProps = dispatch => ({
-
-  onChange (key, checked) {
-    dispatch(changeSetting(['public', ...key], checked));
-  },
-
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings);
diff --git a/app/javascript/themes/glitch/features/public_timeline/index.js b/app/javascript/themes/glitch/features/public_timeline/index.js
deleted file mode 100644
index f5b3865af..000000000
--- a/app/javascript/themes/glitch/features/public_timeline/index.js
+++ /dev/null
@@ -1,107 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import StatusListContainer from 'themes/glitch/features/ui/containers/status_list_container';
-import Column from 'themes/glitch/components/column';
-import ColumnHeader from 'themes/glitch/components/column_header';
-import {
-  refreshPublicTimeline,
-  expandPublicTimeline,
-} from 'themes/glitch/actions/timelines';
-import { addColumn, removeColumn, moveColumn } from 'themes/glitch/actions/columns';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import ColumnSettingsContainer from './containers/column_settings_container';
-import { connectPublicStream } from 'themes/glitch/actions/streaming';
-
-const messages = defineMessages({
-  title: { id: 'column.public', defaultMessage: 'Federated timeline' },
-});
-
-const mapStateToProps = state => ({
-  hasUnread: state.getIn(['timelines', 'public', 'unread']) > 0,
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class PublicTimeline extends React.PureComponent {
-
-  static propTypes = {
-    dispatch: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-    columnId: PropTypes.string,
-    multiColumn: PropTypes.bool,
-    hasUnread: PropTypes.bool,
-  };
-
-  handlePin = () => {
-    const { columnId, dispatch } = this.props;
-
-    if (columnId) {
-      dispatch(removeColumn(columnId));
-    } else {
-      dispatch(addColumn('PUBLIC', {}));
-    }
-  }
-
-  handleMove = (dir) => {
-    const { columnId, dispatch } = this.props;
-    dispatch(moveColumn(columnId, dir));
-  }
-
-  handleHeaderClick = () => {
-    this.column.scrollTop();
-  }
-
-  componentDidMount () {
-    const { dispatch } = this.props;
-
-    dispatch(refreshPublicTimeline());
-    this.disconnect = dispatch(connectPublicStream());
-  }
-
-  componentWillUnmount () {
-    if (this.disconnect) {
-      this.disconnect();
-      this.disconnect = null;
-    }
-  }
-
-  setRef = c => {
-    this.column = c;
-  }
-
-  handleLoadMore = () => {
-    this.props.dispatch(expandPublicTimeline());
-  }
-
-  render () {
-    const { intl, columnId, hasUnread, multiColumn } = this.props;
-    const pinned = !!columnId;
-
-    return (
-      <Column ref={this.setRef} name='federated'>
-        <ColumnHeader
-          icon='globe'
-          active={hasUnread}
-          title={intl.formatMessage(messages.title)}
-          onPin={this.handlePin}
-          onMove={this.handleMove}
-          onClick={this.handleHeaderClick}
-          pinned={pinned}
-          multiColumn={multiColumn}
-        >
-          <ColumnSettingsContainer />
-        </ColumnHeader>
-
-        <StatusListContainer
-          timelineId='public'
-          loadMore={this.handleLoadMore}
-          trackScroll={!pinned}
-          scrollKey={`public_timeline-${columnId}`}
-          emptyMessage={<FormattedMessage id='empty_column.public' defaultMessage='There is nothing here! Write something publicly, or manually follow users from other instances to fill it up' />}
-        />
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/reblogs/index.js b/app/javascript/themes/glitch/features/reblogs/index.js
deleted file mode 100644
index 8723f7c7c..000000000
--- a/app/javascript/themes/glitch/features/reblogs/index.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import LoadingIndicator from 'themes/glitch/components/loading_indicator';
-import { fetchReblogs } from 'themes/glitch/actions/interactions';
-import { ScrollContainer } from 'react-router-scroll-4';
-import AccountContainer from 'themes/glitch/containers/account_container';
-import Column from 'themes/glitch/features/ui/components/column';
-import ColumnBackButton from 'themes/glitch/components/column_back_button';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const mapStateToProps = (state, props) => ({
-  accountIds: state.getIn(['user_lists', 'reblogged_by', props.params.statusId]),
-});
-
-@connect(mapStateToProps)
-export default class Reblogs extends ImmutablePureComponent {
-
-  static propTypes = {
-    params: PropTypes.object.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    accountIds: ImmutablePropTypes.list,
-  };
-
-  componentWillMount () {
-    this.props.dispatch(fetchReblogs(this.props.params.statusId));
-  }
-
-  componentWillReceiveProps(nextProps) {
-    if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
-      this.props.dispatch(fetchReblogs(nextProps.params.statusId));
-    }
-  }
-
-  render () {
-    const { accountIds } = this.props;
-
-    if (!accountIds) {
-      return (
-        <Column>
-          <LoadingIndicator />
-        </Column>
-      );
-    }
-
-    return (
-      <Column>
-        <ColumnBackButton />
-
-        <ScrollContainer scrollKey='reblogs'>
-          <div className='scrollable reblogs'>
-            {accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} />)}
-          </div>
-        </ScrollContainer>
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/report/components/status_check_box.js b/app/javascript/themes/glitch/features/report/components/status_check_box.js
deleted file mode 100644
index cc9232201..000000000
--- a/app/javascript/themes/glitch/features/report/components/status_check_box.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import Toggle from 'react-toggle';
-
-export default class StatusCheckBox extends React.PureComponent {
-
-  static propTypes = {
-    status: ImmutablePropTypes.map.isRequired,
-    checked: PropTypes.bool,
-    onToggle: PropTypes.func.isRequired,
-    disabled: PropTypes.bool,
-  };
-
-  render () {
-    const { status, checked, onToggle, disabled } = this.props;
-    const content = { __html: status.get('contentHtml') };
-
-    if (status.get('reblog')) {
-      return null;
-    }
-
-    return (
-      <div className='status-check-box'>
-        <div
-          className='status__content'
-          dangerouslySetInnerHTML={content}
-        />
-
-        <div className='status-check-box-toggle'>
-          <Toggle checked={checked} onChange={onToggle} disabled={disabled} />
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/report/containers/status_check_box_container.js b/app/javascript/themes/glitch/features/report/containers/status_check_box_container.js
deleted file mode 100644
index 40d55fb3c..000000000
--- a/app/javascript/themes/glitch/features/report/containers/status_check_box_container.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import { connect } from 'react-redux';
-import StatusCheckBox from '../components/status_check_box';
-import { toggleStatusReport } from 'themes/glitch/actions/reports';
-import { Set as ImmutableSet } from 'immutable';
-
-const mapStateToProps = (state, { id }) => ({
-  status: state.getIn(['statuses', id]),
-  checked: state.getIn(['reports', 'new', 'status_ids'], ImmutableSet()).includes(id),
-});
-
-const mapDispatchToProps = (dispatch, { id }) => ({
-
-  onToggle (e) {
-    dispatch(toggleStatusReport(id, e.target.checked));
-  },
-
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(StatusCheckBox);
diff --git a/app/javascript/themes/glitch/features/standalone/compose/index.js b/app/javascript/themes/glitch/features/standalone/compose/index.js
deleted file mode 100644
index 8a8118178..000000000
--- a/app/javascript/themes/glitch/features/standalone/compose/index.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import React from 'react';
-import ComposeFormContainer from 'themes/glitch/features/compose/containers/compose_form_container';
-import NotificationsContainer from 'themes/glitch/features/ui/containers/notifications_container';
-import LoadingBarContainer from 'themes/glitch/features/ui/containers/loading_bar_container';
-import ModalContainer from 'themes/glitch/features/ui/containers/modal_container';
-
-export default class Compose extends React.PureComponent {
-
-  render () {
-    return (
-      <div>
-        <ComposeFormContainer />
-        <NotificationsContainer />
-        <ModalContainer />
-        <LoadingBarContainer className='loading-bar' />
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/standalone/hashtag_timeline/index.js b/app/javascript/themes/glitch/features/standalone/hashtag_timeline/index.js
deleted file mode 100644
index 7c56f264f..000000000
--- a/app/javascript/themes/glitch/features/standalone/hashtag_timeline/index.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import StatusListContainer from 'themes/glitch/features/ui/containers/status_list_container';
-import {
-  refreshHashtagTimeline,
-  expandHashtagTimeline,
-} from 'themes/glitch/actions/timelines';
-import Column from 'themes/glitch/components/column';
-import ColumnHeader from 'themes/glitch/components/column_header';
-
-@connect()
-export default class HashtagTimeline extends React.PureComponent {
-
-  static propTypes = {
-    dispatch: PropTypes.func.isRequired,
-    hashtag: PropTypes.string.isRequired,
-  };
-
-  handleHeaderClick = () => {
-    this.column.scrollTop();
-  }
-
-  setRef = c => {
-    this.column = c;
-  }
-
-  componentDidMount () {
-    const { dispatch, hashtag } = this.props;
-
-    dispatch(refreshHashtagTimeline(hashtag));
-
-    this.polling = setInterval(() => {
-      dispatch(refreshHashtagTimeline(hashtag));
-    }, 10000);
-  }
-
-  componentWillUnmount () {
-    if (typeof this.polling !== 'undefined') {
-      clearInterval(this.polling);
-      this.polling = null;
-    }
-  }
-
-  handleLoadMore = () => {
-    this.props.dispatch(expandHashtagTimeline(this.props.hashtag));
-  }
-
-  render () {
-    const { hashtag } = this.props;
-
-    return (
-      <Column ref={this.setRef}>
-        <ColumnHeader
-          icon='hashtag'
-          title={hashtag}
-          onClick={this.handleHeaderClick}
-        />
-
-        <StatusListContainer
-          trackScroll={false}
-          scrollKey='standalone_hashtag_timeline'
-          timelineId={`hashtag:${hashtag}`}
-          loadMore={this.handleLoadMore}
-        />
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/standalone/public_timeline/index.js b/app/javascript/themes/glitch/features/standalone/public_timeline/index.js
deleted file mode 100644
index b3fb55288..000000000
--- a/app/javascript/themes/glitch/features/standalone/public_timeline/index.js
+++ /dev/null
@@ -1,76 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import StatusListContainer from 'themes/glitch/features/ui/containers/status_list_container';
-import {
-  refreshPublicTimeline,
-  expandPublicTimeline,
-} from 'themes/glitch/actions/timelines';
-import Column from 'themes/glitch/components/column';
-import ColumnHeader from 'themes/glitch/components/column_header';
-import { defineMessages, injectIntl } from 'react-intl';
-
-const messages = defineMessages({
-  title: { id: 'standalone.public_title', defaultMessage: 'A look inside...' },
-});
-
-@connect()
-@injectIntl
-export default class PublicTimeline extends React.PureComponent {
-
-  static propTypes = {
-    dispatch: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  handleHeaderClick = () => {
-    this.column.scrollTop();
-  }
-
-  setRef = c => {
-    this.column = c;
-  }
-
-  componentDidMount () {
-    const { dispatch } = this.props;
-
-    dispatch(refreshPublicTimeline());
-
-    this.polling = setInterval(() => {
-      dispatch(refreshPublicTimeline());
-    }, 3000);
-  }
-
-  componentWillUnmount () {
-    if (typeof this.polling !== 'undefined') {
-      clearInterval(this.polling);
-      this.polling = null;
-    }
-  }
-
-  handleLoadMore = () => {
-    this.props.dispatch(expandPublicTimeline());
-  }
-
-  render () {
-    const { intl } = this.props;
-
-    return (
-      <Column ref={this.setRef}>
-        <ColumnHeader
-          icon='globe'
-          title={intl.formatMessage(messages.title)}
-          onClick={this.handleHeaderClick}
-        />
-
-        <StatusListContainer
-          timelineId='public'
-          loadMore={this.handleLoadMore}
-          scrollKey='standalone_public_timeline'
-          trackScroll={false}
-        />
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/status/components/action_bar.js b/app/javascript/themes/glitch/features/status/components/action_bar.js
deleted file mode 100644
index 6cda988d1..000000000
--- a/app/javascript/themes/glitch/features/status/components/action_bar.js
+++ /dev/null
@@ -1,129 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import IconButton from 'themes/glitch/components/icon_button';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import DropdownMenuContainer from 'themes/glitch/containers/dropdown_menu_container';
-import { defineMessages, injectIntl } from 'react-intl';
-import { me } from 'themes/glitch/util/initial_state';
-
-const messages = defineMessages({
-  delete: { id: 'status.delete', defaultMessage: 'Delete' },
-  mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
-  reply: { id: 'status.reply', defaultMessage: 'Reply' },
-  reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
-  cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
-  favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
-  report: { id: 'status.report', defaultMessage: 'Report @{name}' },
-  share: { id: 'status.share', defaultMessage: 'Share' },
-  pin: { id: 'status.pin', defaultMessage: 'Pin on profile' },
-  unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
-  embed: { id: 'status.embed', defaultMessage: 'Embed' },
-});
-
-@injectIntl
-export default class ActionBar extends React.PureComponent {
-
-  static contextTypes = {
-    router: PropTypes.object,
-  };
-
-  static propTypes = {
-    status: ImmutablePropTypes.map.isRequired,
-    onReply: PropTypes.func.isRequired,
-    onReblog: PropTypes.func.isRequired,
-    onFavourite: PropTypes.func.isRequired,
-    onDelete: PropTypes.func.isRequired,
-    onMention: PropTypes.func.isRequired,
-    onReport: PropTypes.func,
-    onPin: PropTypes.func,
-    onEmbed: PropTypes.func,
-    intl: PropTypes.object.isRequired,
-  };
-
-  handleReplyClick = () => {
-    this.props.onReply(this.props.status);
-  }
-
-  handleReblogClick = (e) => {
-    this.props.onReblog(this.props.status, e);
-  }
-
-  handleFavouriteClick = () => {
-    this.props.onFavourite(this.props.status);
-  }
-
-  handleDeleteClick = () => {
-    this.props.onDelete(this.props.status);
-  }
-
-  handleMentionClick = () => {
-    this.props.onMention(this.props.status.get('account'), this.context.router.history);
-  }
-
-  handleReport = () => {
-    this.props.onReport(this.props.status);
-  }
-
-  handlePinClick = () => {
-    this.props.onPin(this.props.status);
-  }
-
-  handleShare = () => {
-    navigator.share({
-      text: this.props.status.get('search_index'),
-      url: this.props.status.get('url'),
-    });
-  }
-
-  handleEmbed = () => {
-    this.props.onEmbed(this.props.status);
-  }
-
-  render () {
-    const { status, intl } = this.props;
-
-    const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
-
-    let menu = [];
-
-    if (publicStatus) {
-      menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed });
-    }
-
-    if (me === status.getIn(['account', 'id'])) {
-      if (publicStatus) {
-        menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
-      }
-
-      menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
-    } else {
-      menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
-      menu.push(null);
-      menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
-    }
-
-    const shareButton = ('share' in navigator) && status.get('visibility') === 'public' && (
-      <div className='detailed-status__button'><IconButton title={intl.formatMessage(messages.share)} icon='share-alt' onClick={this.handleShare} /></div>
-    );
-
-    let reblogIcon = 'retweet';
-    //if (status.get('visibility') === 'direct') reblogIcon = 'envelope';
-    // else if (status.get('visibility') === 'private') reblogIcon = 'lock';
-
-    let reblog_disabled = (status.get('visibility') === 'direct' || status.get('visibility') === 'private');
-
-    return (
-      <div className='detailed-status__action-bar'>
-        <div className='detailed-status__button'><IconButton title={intl.formatMessage(messages.reply)} icon={status.get('in_reply_to_id', null) === null ? 'reply' : 'reply-all'} onClick={this.handleReplyClick} /></div>
-        <div className='detailed-status__button'><IconButton disabled={reblog_disabled} active={status.get('reblogged')} title={reblog_disabled ? intl.formatMessage(messages.cannot_reblog) : intl.formatMessage(messages.reblog)} icon={reblogIcon} onClick={this.handleReblogClick} /></div>
-        <div className='detailed-status__button'><IconButton animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} activeStyle={{ color: '#ca8f04' }} /></div>
-        {shareButton}
-
-        <div className='detailed-status__action-bar-dropdown'>
-          <DropdownMenuContainer size={18} icon='ellipsis-h' items={menu} direction='left' ariaLabel='More' />
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/status/components/card.js b/app/javascript/themes/glitch/features/status/components/card.js
deleted file mode 100644
index bb83374b9..000000000
--- a/app/javascript/themes/glitch/features/status/components/card.js
+++ /dev/null
@@ -1,125 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import punycode from 'punycode';
-import classnames from 'classnames';
-
-const IDNA_PREFIX = 'xn--';
-
-const decodeIDNA = domain => {
-  return domain
-    .split('.')
-    .map(part => part.indexOf(IDNA_PREFIX) === 0 ? punycode.decode(part.slice(IDNA_PREFIX.length)) : part)
-    .join('.');
-};
-
-const getHostname = url => {
-  const parser = document.createElement('a');
-  parser.href = url;
-  return parser.hostname;
-};
-
-export default class Card extends React.PureComponent {
-
-  static propTypes = {
-    card: ImmutablePropTypes.map,
-    maxDescription: PropTypes.number,
-  };
-
-  static defaultProps = {
-    maxDescription: 50,
-  };
-
-  state = {
-    width: 0,
-  };
-
-  renderLink () {
-    const { card, maxDescription } = this.props;
-
-    let image    = '';
-    let provider = card.get('provider_name');
-
-    if (card.get('image')) {
-      image = (
-        <div className='status-card__image'>
-          <img src={card.get('image')} alt={card.get('title')} className='status-card__image-image' width={card.get('width')} height={card.get('height')} />
-        </div>
-      );
-    }
-
-    if (provider.length < 1) {
-      provider = decodeIDNA(getHostname(card.get('url')));
-    }
-
-    const className = classnames('status-card', {
-      'horizontal': card.get('width') > card.get('height'),
-    });
-
-    return (
-      <a href={card.get('url')} className={className} target='_blank' rel='noopener'>
-        {image}
-
-        <div className='status-card__content'>
-          <strong className='status-card__title' title={card.get('title')}>{card.get('title')}</strong>
-          <p className='status-card__description'>{(card.get('description') || '').substring(0, maxDescription)}</p>
-          <span className='status-card__host'>{provider}</span>
-        </div>
-      </a>
-    );
-  }
-
-  renderPhoto () {
-    const { card } = this.props;
-
-    return (
-      <a href={card.get('url')} className='status-card-photo' target='_blank' rel='noopener'>
-        <img src={card.get('url')} alt={card.get('title')} width={card.get('width')} height={card.get('height')} />
-      </a>
-    );
-  }
-
-  setRef = c => {
-    if (c) {
-      this.setState({ width: c.offsetWidth });
-    }
-  }
-
-  renderVideo () {
-    const { card }  = this.props;
-    const content   = { __html: card.get('html') };
-    const { width } = this.state;
-    const ratio     = card.get('width') / card.get('height');
-    const height    = card.get('width') > card.get('height') ? (width / ratio) : (width * ratio);
-
-    return (
-      <div
-        ref={this.setRef}
-        className='status-card-video'
-        dangerouslySetInnerHTML={content}
-        style={{ height }}
-      />
-    );
-  }
-
-  render () {
-    const { card } = this.props;
-
-    if (card === null) {
-      return null;
-    }
-
-    switch(card.get('type')) {
-    case 'link':
-      return this.renderLink();
-    case 'photo':
-      return this.renderPhoto();
-    case 'video':
-      return this.renderVideo();
-    case 'rich':
-    default:
-      return null;
-    }
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/status/components/detailed_status.js b/app/javascript/themes/glitch/features/status/components/detailed_status.js
deleted file mode 100644
index df78ce4b6..000000000
--- a/app/javascript/themes/glitch/features/status/components/detailed_status.js
+++ /dev/null
@@ -1,130 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import Avatar from 'themes/glitch/components/avatar';
-import DisplayName from 'themes/glitch/components/display_name';
-import StatusContent from 'themes/glitch/components/status_content';
-import StatusGallery from 'themes/glitch/components/media_gallery';
-import AttachmentList from 'themes/glitch/components/attachment_list';
-import { Link } from 'react-router-dom';
-import { FormattedDate, FormattedNumber } from 'react-intl';
-import CardContainer from '../containers/card_container';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import Video from 'themes/glitch/features/video';
-import VisibilityIcon from 'themes/glitch/components/status_visibility_icon';
-
-export default class DetailedStatus extends ImmutablePureComponent {
-
-  static contextTypes = {
-    router: PropTypes.object,
-  };
-
-  static propTypes = {
-    status: ImmutablePropTypes.map.isRequired,
-    settings: ImmutablePropTypes.map.isRequired,
-    onOpenMedia: PropTypes.func.isRequired,
-    onOpenVideo: PropTypes.func.isRequired,
-  };
-
-  handleAccountClick = (e) => {
-    if (e.button === 0) {
-      e.preventDefault();
-      this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
-    }
-
-    e.stopPropagation();
-  }
-
-  // handleOpenVideo = startTime => {
-  //   this.props.onOpenVideo(this.props.status.getIn(['media_attachments', 0]), startTime);
-  // }
-
-  render () {
-    const status = this.props.status.get('reblog') ? this.props.status.get('reblog') : this.props.status;
-    const { expanded, setExpansion, settings } = this.props;
-
-    let media           = '';
-    let mediaIcon       = null;
-    let applicationLink = '';
-    let reblogLink = '';
-    let reblogIcon = 'retweet';
-
-    if (status.get('media_attachments').size > 0) {
-      if (status.get('media_attachments').some(item => item.get('type') === 'unknown')) {
-        media = <AttachmentList media={status.get('media_attachments')} />;
-      } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
-        media = (
-          <Video
-            sensitive={status.get('sensitive')}
-            media={status.getIn(['media_attachments', 0])}
-            letterbox={settings.getIn(['media', 'letterbox'])}
-            fullwidth={settings.getIn(['media', 'fullwidth'])}
-            onOpenVideo={this.props.onOpenVideo}
-            autoplay
-          />
-        );
-        mediaIcon = 'video-camera';
-      } else {
-        media = (
-          <StatusGallery
-            sensitive={status.get('sensitive')}
-            media={status.get('media_attachments')}
-            letterbox={settings.getIn(['media', 'letterbox'])}
-            onOpenMedia={this.props.onOpenMedia}
-          />
-        );
-        mediaIcon = 'picture-o';
-      }
-    } else media = <CardContainer statusId={status.get('id')} />;
-
-    if (status.get('application')) {
-      applicationLink = <span> · <a className='detailed-status__application' href={status.getIn(['application', 'website'])} target='_blank' rel='noopener'>{status.getIn(['application', 'name'])}</a></span>;
-    }
-
-    if (status.get('visibility') === 'direct') {
-      reblogIcon = 'envelope';
-    } else if (status.get('visibility') === 'private') {
-      reblogIcon = 'lock';
-    }
-
-    if (status.get('visibility') === 'private') {
-      reblogLink = <i className={`fa fa-${reblogIcon}`} />;
-    } else {
-      reblogLink = (<Link to={`/statuses/${status.get('id')}/reblogs`} className='detailed-status__link'>
-        <i className={`fa fa-${reblogIcon}`} />
-        <span className='detailed-status__reblogs'>
-          <FormattedNumber value={status.get('reblogs_count')} />
-        </span>
-      </Link>);
-    }
-
-    return (
-      <div className='detailed-status'>
-        <a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='detailed-status__display-name'>
-          <div className='detailed-status__display-avatar'><Avatar account={status.get('account')} size={48} /></div>
-          <DisplayName account={status.get('account')} />
-        </a>
-
-        <StatusContent
-          status={status}
-          media={media}
-          mediaIcon={mediaIcon}
-          expanded={expanded}
-          setExpansion={setExpansion}
-        />
-
-        <div className='detailed-status__meta'>
-          <a className='detailed-status__datetime' href={status.get('url')} target='_blank' rel='noopener'>
-            <FormattedDate value={new Date(status.get('created_at'))} hour12={false} year='numeric' month='short' day='2-digit' hour='2-digit' minute='2-digit' />
-          </a>{applicationLink} · {reblogLink} · <Link to={`/statuses/${status.get('id')}/favourites`} className='detailed-status__link'>
-            <i className='fa fa-star' />
-            <span className='detailed-status__favorites'>
-              <FormattedNumber value={status.get('favourites_count')} />
-            </span>
-          </Link> · <VisibilityIcon visibility={status.get('visibility')} />
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/status/containers/card_container.js b/app/javascript/themes/glitch/features/status/containers/card_container.js
deleted file mode 100644
index a97404de1..000000000
--- a/app/javascript/themes/glitch/features/status/containers/card_container.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import { connect } from 'react-redux';
-import Card from '../components/card';
-
-const mapStateToProps = (state, { statusId }) => ({
-  card: state.getIn(['cards', statusId], null),
-});
-
-export default connect(mapStateToProps)(Card);
diff --git a/app/javascript/themes/glitch/features/status/index.js b/app/javascript/themes/glitch/features/status/index.js
deleted file mode 100644
index 8561bd4de..000000000
--- a/app/javascript/themes/glitch/features/status/index.js
+++ /dev/null
@@ -1,358 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { fetchStatus } from 'themes/glitch/actions/statuses';
-import MissingIndicator from 'themes/glitch/components/missing_indicator';
-import DetailedStatus from './components/detailed_status';
-import ActionBar from './components/action_bar';
-import Column from 'themes/glitch/features/ui/components/column';
-import {
-  favourite,
-  unfavourite,
-  reblog,
-  unreblog,
-  pin,
-  unpin,
-} from 'themes/glitch/actions/interactions';
-import {
-  replyCompose,
-  mentionCompose,
-} from 'themes/glitch/actions/compose';
-import { deleteStatus } from 'themes/glitch/actions/statuses';
-import { initReport } from 'themes/glitch/actions/reports';
-import { makeGetStatus } from 'themes/glitch/selectors';
-import { ScrollContainer } from 'react-router-scroll-4';
-import ColumnBackButton from 'themes/glitch/components/column_back_button';
-import StatusContainer from 'themes/glitch/containers/status_container';
-import { openModal } from 'themes/glitch/actions/modal';
-import { defineMessages, injectIntl } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { HotKeys } from 'react-hotkeys';
-import { boostModal, deleteModal } from 'themes/glitch/util/initial_state';
-import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from 'themes/glitch/util/fullscreen';
-
-const messages = defineMessages({
-  deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
-  deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
-});
-
-const makeMapStateToProps = () => {
-  const getStatus = makeGetStatus();
-
-  const mapStateToProps = (state, props) => ({
-    status: getStatus(state, props.params.statusId),
-    settings: state.get('local_settings'),
-    ancestorsIds: state.getIn(['contexts', 'ancestors', props.params.statusId]),
-    descendantsIds: state.getIn(['contexts', 'descendants', props.params.statusId]),
-  });
-
-  return mapStateToProps;
-};
-
-@injectIntl
-@connect(makeMapStateToProps)
-export default class Status extends ImmutablePureComponent {
-
-  static contextTypes = {
-    router: PropTypes.object,
-  };
-
-  static propTypes = {
-    params: PropTypes.object.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    status: ImmutablePropTypes.map,
-    settings: ImmutablePropTypes.map.isRequired,
-    ancestorsIds: ImmutablePropTypes.list,
-    descendantsIds: ImmutablePropTypes.list,
-    intl: PropTypes.object.isRequired,
-  };
-
-  state = {
-    fullscreen: false,
-    isExpanded: null,
-  };
-
-  componentWillMount () {
-    this.props.dispatch(fetchStatus(this.props.params.statusId));
-  }
-
-  componentDidMount () {
-    attachFullscreenListener(this.onFullScreenChange);
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
-      this._scrolledIntoView = false;
-      this.props.dispatch(fetchStatus(nextProps.params.statusId));
-    }
-  }
-
-  handleExpandedToggle = () => {
-    if (this.props.status.get('spoiler_text')) {
-      this.setExpansion(this.state.isExpanded ? null : true);
-    }
-  };
-
-  handleFavouriteClick = (status) => {
-    if (status.get('favourited')) {
-      this.props.dispatch(unfavourite(status));
-    } else {
-      this.props.dispatch(favourite(status));
-    }
-  }
-
-  handlePin = (status) => {
-    if (status.get('pinned')) {
-      this.props.dispatch(unpin(status));
-    } else {
-      this.props.dispatch(pin(status));
-    }
-  }
-
-  handleReplyClick = (status) => {
-    this.props.dispatch(replyCompose(status, this.context.router.history));
-  }
-
-  handleModalReblog = (status) => {
-    this.props.dispatch(reblog(status));
-  }
-
-  handleReblogClick = (status, e) => {
-    if (status.get('reblogged')) {
-      this.props.dispatch(unreblog(status));
-    } else {
-      if (e.shiftKey || !boostModal) {
-        this.handleModalReblog(status);
-      } else {
-        this.props.dispatch(openModal('BOOST', { status, onReblog: this.handleModalReblog }));
-      }
-    }
-  }
-
-  handleDeleteClick = (status) => {
-    const { dispatch, intl } = this.props;
-
-    if (!deleteModal) {
-      dispatch(deleteStatus(status.get('id')));
-    } else {
-      dispatch(openModal('CONFIRM', {
-        message: intl.formatMessage(messages.deleteMessage),
-        confirm: intl.formatMessage(messages.deleteConfirm),
-        onConfirm: () => dispatch(deleteStatus(status.get('id'))),
-      }));
-    }
-  }
-
-  handleMentionClick = (account, router) => {
-    this.props.dispatch(mentionCompose(account, router));
-  }
-
-  handleOpenMedia = (media, index) => {
-    this.props.dispatch(openModal('MEDIA', { media, index }));
-  }
-
-  handleOpenVideo = (media, time) => {
-    this.props.dispatch(openModal('VIDEO', { media, time }));
-  }
-
-  handleReport = (status) => {
-    this.props.dispatch(initReport(status.get('account'), status));
-  }
-
-  handleEmbed = (status) => {
-    this.props.dispatch(openModal('EMBED', { url: status.get('url') }));
-  }
-
-  handleHotkeyMoveUp = () => {
-    this.handleMoveUp(this.props.status.get('id'));
-  }
-
-  handleHotkeyMoveDown = () => {
-    this.handleMoveDown(this.props.status.get('id'));
-  }
-
-  handleHotkeyReply = e => {
-    e.preventDefault();
-    this.handleReplyClick(this.props.status);
-  }
-
-  handleHotkeyFavourite = () => {
-    this.handleFavouriteClick(this.props.status);
-  }
-
-  handleHotkeyBoost = () => {
-    this.handleReblogClick(this.props.status);
-  }
-
-  handleHotkeyMention = e => {
-    e.preventDefault();
-    this.handleMentionClick(this.props.status);
-  }
-
-  handleHotkeyOpenProfile = () => {
-    this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
-  }
-
-  handleMoveUp = id => {
-    const { status, ancestorsIds, descendantsIds } = this.props;
-
-    if (id === status.get('id')) {
-      this._selectChild(ancestorsIds.size - 1);
-    } else {
-      let index = ancestorsIds.indexOf(id);
-
-      if (index === -1) {
-        index = descendantsIds.indexOf(id);
-        this._selectChild(ancestorsIds.size + index);
-      } else {
-        this._selectChild(index - 1);
-      }
-    }
-  }
-
-  handleMoveDown = id => {
-    const { status, ancestorsIds, descendantsIds } = this.props;
-
-    if (id === status.get('id')) {
-      this._selectChild(ancestorsIds.size + 1);
-    } else {
-      let index = ancestorsIds.indexOf(id);
-
-      if (index === -1) {
-        index = descendantsIds.indexOf(id);
-        this._selectChild(ancestorsIds.size + index + 2);
-      } else {
-        this._selectChild(index + 1);
-      }
-    }
-  }
-
-  _selectChild (index) {
-    const element = this.node.querySelectorAll('.focusable')[index];
-
-    if (element) {
-      element.focus();
-    }
-  }
-
-  renderChildren (list) {
-    return list.map(id => (
-      <StatusContainer
-        key={id}
-        id={id}
-        onMoveUp={this.handleMoveUp}
-        onMoveDown={this.handleMoveDown}
-      />
-    ));
-  }
-
-  setExpansion = value => {
-    this.setState({ isExpanded: value ? true : null });
-  }
-
-  setRef = c => {
-    this.node = c;
-  }
-
-  componentDidUpdate () {
-    if (this._scrolledIntoView) {
-      return;
-    }
-
-    const { status, ancestorsIds } = this.props;
-
-    if (status && ancestorsIds && ancestorsIds.size > 0) {
-      const element = this.node.querySelectorAll('.focusable')[ancestorsIds.size - 1];
-
-      if (element) {
-        element.scrollIntoView(true);
-        this._scrolledIntoView = true;
-      }
-    }
-  }
-
-  componentWillUnmount () {
-    detachFullscreenListener(this.onFullScreenChange);
-  }
-
-  onFullScreenChange = () => {
-    this.setState({ fullscreen: isFullscreen() });
-  }
-
-  render () {
-    let ancestors, descendants;
-    const { setExpansion } = this;
-    const { status, settings, ancestorsIds, descendantsIds } = this.props;
-    const { fullscreen, isExpanded } = this.state;
-
-    if (status === null) {
-      return (
-        <Column>
-          <ColumnBackButton />
-          <MissingIndicator />
-        </Column>
-      );
-    }
-
-    if (ancestorsIds && ancestorsIds.size > 0) {
-      ancestors = <div>{this.renderChildren(ancestorsIds)}</div>;
-    }
-
-    if (descendantsIds && descendantsIds.size > 0) {
-      descendants = <div>{this.renderChildren(descendantsIds)}</div>;
-    }
-
-    const handlers = {
-      moveUp: this.handleHotkeyMoveUp,
-      moveDown: this.handleHotkeyMoveDown,
-      reply: this.handleHotkeyReply,
-      favourite: this.handleHotkeyFavourite,
-      boost: this.handleHotkeyBoost,
-      mention: this.handleHotkeyMention,
-      openProfile: this.handleHotkeyOpenProfile,
-      toggleSpoiler: this.handleExpandedToggle,
-    };
-
-    return (
-      <Column>
-        <ColumnBackButton />
-
-        <ScrollContainer scrollKey='thread'>
-          <div className={classNames('scrollable', 'detailed-status__wrapper', { fullscreen })} ref={this.setRef}>
-            {ancestors}
-
-            <HotKeys handlers={handlers}>
-              <div className='focusable' tabIndex='0'>
-                <DetailedStatus
-                  status={status}
-                  settings={settings}
-                  onOpenVideo={this.handleOpenVideo}
-                  onOpenMedia={this.handleOpenMedia}
-                  expanded={isExpanded}
-                  setExpansion={setExpansion}
-                />
-
-                <ActionBar
-                  status={status}
-                  onReply={this.handleReplyClick}
-                  onFavourite={this.handleFavouriteClick}
-                  onReblog={this.handleReblogClick}
-                  onDelete={this.handleDeleteClick}
-                  onMention={this.handleMentionClick}
-                  onReport={this.handleReport}
-                  onPin={this.handlePin}
-                  onEmbed={this.handleEmbed}
-                />
-              </div>
-            </HotKeys>
-
-            {descendants}
-          </div>
-        </ScrollContainer>
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/actions_modal.js b/app/javascript/themes/glitch/features/ui/components/actions_modal.js
deleted file mode 100644
index 7a2b78b63..000000000
--- a/app/javascript/themes/glitch/features/ui/components/actions_modal.js
+++ /dev/null
@@ -1,74 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import StatusContent from 'themes/glitch/components/status_content';
-import Avatar from 'themes/glitch/components/avatar';
-import RelativeTimestamp from 'themes/glitch/components/relative_timestamp';
-import DisplayName from 'themes/glitch/components/display_name';
-import IconButton from 'themes/glitch/components/icon_button';
-import classNames from 'classnames';
-
-export default class ActionsModal extends ImmutablePureComponent {
-
-  static propTypes = {
-    status: ImmutablePropTypes.map,
-    actions: PropTypes.array,
-    onClick: PropTypes.func,
-  };
-
-  renderAction = (action, i) => {
-    if (action === null) {
-      return <li key={`sep-${i}`} className='dropdown-menu__separator' />;
-    }
-
-    const { icon = null, text, meta = null, active = false, href = '#' } = action;
-
-    return (
-      <li key={`${text}-${i}`}>
-        <a href={href} target='_blank' rel='noopener' onClick={this.props.onClick} data-index={i} className={classNames({ active })}>
-          {icon && <IconButton title={text} icon={icon} role='presentation' tabIndex='-1' />}
-          <div>
-            <div className={classNames({ 'actions-modal__item-label': !!meta })}>{text}</div>
-            <div>{meta}</div>
-          </div>
-        </a>
-      </li>
-    );
-  }
-
-  render () {
-    const status = this.props.status && (
-      <div className='status light'>
-        <div className='boost-modal__status-header'>
-          <div className='boost-modal__status-time'>
-            <a href={this.props.status.get('url')} className='status__relative-time' target='_blank' rel='noopener'>
-              <RelativeTimestamp timestamp={this.props.status.get('created_at')} />
-            </a>
-          </div>
-
-          <a href={this.props.status.getIn(['account', 'url'])} className='status__display-name'>
-            <div className='status__avatar'>
-              <Avatar account={this.props.status.get('account')} size={48} />
-            </div>
-
-            <DisplayName account={this.props.status.get('account')} />
-          </a>
-        </div>
-
-        <StatusContent status={this.props.status} />
-      </div>
-    );
-
-    return (
-      <div className='modal-root__modal actions-modal'>
-        {status}
-
-        <ul>
-          {this.props.actions.map(this.renderAction)}
-        </ul>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/boost_modal.js b/app/javascript/themes/glitch/features/ui/components/boost_modal.js
deleted file mode 100644
index 49781db10..000000000
--- a/app/javascript/themes/glitch/features/ui/components/boost_modal.js
+++ /dev/null
@@ -1,84 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import Button from 'themes/glitch/components/button';
-import StatusContent from 'themes/glitch/components/status_content';
-import Avatar from 'themes/glitch/components/avatar';
-import RelativeTimestamp from 'themes/glitch/components/relative_timestamp';
-import DisplayName from 'themes/glitch/components/display_name';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const messages = defineMessages({
-  reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
-});
-
-@injectIntl
-export default class BoostModal extends ImmutablePureComponent {
-
-  static contextTypes = {
-    router: PropTypes.object,
-  };
-
-  static propTypes = {
-    status: ImmutablePropTypes.map.isRequired,
-    onReblog: PropTypes.func.isRequired,
-    onClose: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  componentDidMount() {
-    this.button.focus();
-  }
-
-  handleReblog = () => {
-    this.props.onReblog(this.props.status);
-    this.props.onClose();
-  }
-
-  handleAccountClick = (e) => {
-    if (e.button === 0) {
-      e.preventDefault();
-      this.props.onClose();
-      this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
-    }
-  }
-
-  setRef = (c) => {
-    this.button = c;
-  }
-
-  render () {
-    const { status, intl } = this.props;
-
-    return (
-      <div className='modal-root__modal boost-modal'>
-        <div className='boost-modal__container'>
-          <div className='status light'>
-            <div className='boost-modal__status-header'>
-              <div className='boost-modal__status-time'>
-                <a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener'><RelativeTimestamp timestamp={status.get('created_at')} /></a>
-              </div>
-
-              <a onClick={this.handleAccountClick} href={status.getIn(['account', 'url'])} className='status__display-name'>
-                <div className='status__avatar'>
-                  <Avatar account={status.get('account')} size={48} />
-                </div>
-
-                <DisplayName account={status.get('account')} />
-              </a>
-            </div>
-
-            <StatusContent status={status} />
-          </div>
-        </div>
-
-        <div className='boost-modal__action-bar'>
-          <div><FormattedMessage id='boost_modal.combo' defaultMessage='You can press {combo} to skip this next time' values={{ combo: <span>Shift + <i className='fa fa-retweet' /></span> }} /></div>
-          <Button text={intl.formatMessage(messages.reblog)} onClick={this.handleReblog} ref={this.setRef} />
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/bundle.js b/app/javascript/themes/glitch/features/ui/components/bundle.js
deleted file mode 100644
index fc88e0c70..000000000
--- a/app/javascript/themes/glitch/features/ui/components/bundle.js
+++ /dev/null
@@ -1,102 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-const emptyComponent = () => null;
-const noop = () => { };
-
-class Bundle extends React.Component {
-
-  static propTypes = {
-    fetchComponent: PropTypes.func.isRequired,
-    loading: PropTypes.func,
-    error: PropTypes.func,
-    children: PropTypes.func.isRequired,
-    renderDelay: PropTypes.number,
-    onFetch: PropTypes.func,
-    onFetchSuccess: PropTypes.func,
-    onFetchFail: PropTypes.func,
-  }
-
-  static defaultProps = {
-    loading: emptyComponent,
-    error: emptyComponent,
-    renderDelay: 0,
-    onFetch: noop,
-    onFetchSuccess: noop,
-    onFetchFail: noop,
-  }
-
-  static cache = {}
-
-  state = {
-    mod: undefined,
-    forceRender: false,
-  }
-
-  componentWillMount() {
-    this.load(this.props);
-  }
-
-  componentWillReceiveProps(nextProps) {
-    if (nextProps.fetchComponent !== this.props.fetchComponent) {
-      this.load(nextProps);
-    }
-  }
-
-  componentWillUnmount () {
-    if (this.timeout) {
-      clearTimeout(this.timeout);
-    }
-  }
-
-  load = (props) => {
-    const { fetchComponent, onFetch, onFetchSuccess, onFetchFail, renderDelay } = props || this.props;
-
-    onFetch();
-
-    if (Bundle.cache[fetchComponent.name]) {
-      const mod = Bundle.cache[fetchComponent.name];
-
-      this.setState({ mod: mod.default });
-      onFetchSuccess();
-      return Promise.resolve();
-    }
-
-    this.setState({ mod: undefined });
-
-    if (renderDelay !== 0) {
-      this.timestamp = new Date();
-      this.timeout = setTimeout(() => this.setState({ forceRender: true }), renderDelay);
-    }
-
-    return fetchComponent()
-      .then((mod) => {
-        Bundle.cache[fetchComponent.name] = mod;
-        this.setState({ mod: mod.default });
-        onFetchSuccess();
-      })
-      .catch((error) => {
-        this.setState({ mod: null });
-        onFetchFail(error);
-      });
-  }
-
-  render() {
-    const { loading: Loading, error: Error, children, renderDelay } = this.props;
-    const { mod, forceRender } = this.state;
-    const elapsed = this.timestamp ? (new Date() - this.timestamp) : renderDelay;
-
-    if (mod === undefined) {
-      return (elapsed >= renderDelay || forceRender) ? <Loading /> : null;
-    }
-
-    if (mod === null) {
-      return <Error onRetry={this.load} />;
-    }
-
-    return children(mod);
-  }
-
-}
-
-export default Bundle;
diff --git a/app/javascript/themes/glitch/features/ui/components/bundle_column_error.js b/app/javascript/themes/glitch/features/ui/components/bundle_column_error.js
deleted file mode 100644
index daedc6299..000000000
--- a/app/javascript/themes/glitch/features/ui/components/bundle_column_error.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { defineMessages, injectIntl } from 'react-intl';
-
-import Column from './column';
-import ColumnHeader from './column_header';
-import ColumnBackButtonSlim from 'themes/glitch/components/column_back_button_slim';
-import IconButton from 'themes/glitch/components/icon_button';
-
-const messages = defineMessages({
-  title: { id: 'bundle_column_error.title', defaultMessage: 'Network error' },
-  body: { id: 'bundle_column_error.body', defaultMessage: 'Something went wrong while loading this component.' },
-  retry: { id: 'bundle_column_error.retry', defaultMessage: 'Try again' },
-});
-
-class BundleColumnError extends React.Component {
-
-  static propTypes = {
-    onRetry: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  }
-
-  handleRetry = () => {
-    this.props.onRetry();
-  }
-
-  render () {
-    const { intl: { formatMessage } } = this.props;
-
-    return (
-      <Column>
-        <ColumnHeader icon='exclamation-circle' type={formatMessage(messages.title)} />
-        <ColumnBackButtonSlim />
-        <div className='error-column'>
-          <IconButton title={formatMessage(messages.retry)} icon='refresh' onClick={this.handleRetry} size={64} />
-          {formatMessage(messages.body)}
-        </div>
-      </Column>
-    );
-  }
-
-}
-
-export default injectIntl(BundleColumnError);
diff --git a/app/javascript/themes/glitch/features/ui/components/bundle_modal_error.js b/app/javascript/themes/glitch/features/ui/components/bundle_modal_error.js
deleted file mode 100644
index 8cca32ae9..000000000
--- a/app/javascript/themes/glitch/features/ui/components/bundle_modal_error.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { defineMessages, injectIntl } from 'react-intl';
-
-import IconButton from 'themes/glitch/components/icon_button';
-
-const messages = defineMessages({
-  error: { id: 'bundle_modal_error.message', defaultMessage: 'Something went wrong while loading this component.' },
-  retry: { id: 'bundle_modal_error.retry', defaultMessage: 'Try again' },
-  close: { id: 'bundle_modal_error.close', defaultMessage: 'Close' },
-});
-
-class BundleModalError extends React.Component {
-
-  static propTypes = {
-    onRetry: PropTypes.func.isRequired,
-    onClose: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  }
-
-  handleRetry = () => {
-    this.props.onRetry();
-  }
-
-  render () {
-    const { onClose, intl: { formatMessage } } = this.props;
-
-    // Keep the markup in sync with <ModalLoading />
-    // (make sure they have the same dimensions)
-    return (
-      <div className='modal-root__modal error-modal'>
-        <div className='error-modal__body'>
-          <IconButton title={formatMessage(messages.retry)} icon='refresh' onClick={this.handleRetry} size={64} />
-          {formatMessage(messages.error)}
-        </div>
-
-        <div className='error-modal__footer'>
-          <div>
-            <button
-              onClick={onClose}
-              className='error-modal__nav onboarding-modal__skip'
-            >
-              {formatMessage(messages.close)}
-            </button>
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-}
-
-export default injectIntl(BundleModalError);
diff --git a/app/javascript/themes/glitch/features/ui/components/column.js b/app/javascript/themes/glitch/features/ui/components/column.js
deleted file mode 100644
index 73a5bc15e..000000000
--- a/app/javascript/themes/glitch/features/ui/components/column.js
+++ /dev/null
@@ -1,74 +0,0 @@
-import React from 'react';
-import ColumnHeader from './column_header';
-import PropTypes from 'prop-types';
-import { debounce } from 'lodash';
-import { scrollTop } from 'themes/glitch/util/scroll';
-import { isMobile } from 'themes/glitch/util/is_mobile';
-
-export default class Column extends React.PureComponent {
-
-  static propTypes = {
-    heading: PropTypes.string,
-    icon: PropTypes.string,
-    children: PropTypes.node,
-    active: PropTypes.bool,
-    hideHeadingOnMobile: PropTypes.bool,
-    name: PropTypes.string,
-  };
-
-  handleHeaderClick = () => {
-    const scrollable = this.node.querySelector('.scrollable');
-
-    if (!scrollable) {
-      return;
-    }
-
-    this._interruptScrollAnimation = scrollTop(scrollable);
-  }
-
-  scrollTop () {
-    const scrollable = this.node.querySelector('.scrollable');
-
-    if (!scrollable) {
-      return;
-    }
-
-    this._interruptScrollAnimation = scrollTop(scrollable);
-  }
-
-
-  handleScroll = debounce(() => {
-    if (typeof this._interruptScrollAnimation !== 'undefined') {
-      this._interruptScrollAnimation();
-    }
-  }, 200)
-
-  setRef = (c) => {
-    this.node = c;
-  }
-
-  render () {
-    const { heading, icon, children, active, hideHeadingOnMobile, name } = this.props;
-
-    const showHeading = heading && (!hideHeadingOnMobile || (hideHeadingOnMobile && !isMobile(window.innerWidth)));
-
-    const columnHeaderId = showHeading && heading.replace(/ /g, '-');
-    const header = showHeading && (
-      <ColumnHeader icon={icon} active={active} type={heading} onClick={this.handleHeaderClick} columnHeaderId={columnHeaderId} />
-    );
-    return (
-      <div
-        ref={this.setRef}
-        role='region'
-        data-column={name}
-        aria-labelledby={columnHeaderId}
-        className='column'
-        onScroll={this.handleScroll}
-      >
-        {header}
-        {children}
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/column_header.js b/app/javascript/themes/glitch/features/ui/components/column_header.js
deleted file mode 100644
index af195ea9c..000000000
--- a/app/javascript/themes/glitch/features/ui/components/column_header.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-export default class ColumnHeader extends React.PureComponent {
-
-  static propTypes = {
-    icon: PropTypes.string,
-    type: PropTypes.string,
-    active: PropTypes.bool,
-    onClick: PropTypes.func,
-    columnHeaderId: PropTypes.string,
-  };
-
-  handleClick = () => {
-    this.props.onClick();
-  }
-
-  render () {
-    const { type, active, columnHeaderId } = this.props;
-
-    let icon = '';
-
-    if (this.props.icon) {
-      icon = <i className={`fa fa-fw fa-${this.props.icon} column-header__icon`} />;
-    }
-
-    return (
-      <div role='heading' tabIndex='0' className={`column-header ${active ? 'active' : ''}`} onClick={this.handleClick} id={columnHeaderId || null}>
-        {icon}
-        {type}
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/column_link.js b/app/javascript/themes/glitch/features/ui/components/column_link.js
deleted file mode 100644
index b845d1895..000000000
--- a/app/javascript/themes/glitch/features/ui/components/column_link.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { Link } from 'react-router-dom';
-
-const ColumnLink = ({ icon, text, to, onClick, href, method }) => {
-  if (href) {
-    return (
-      <a href={href} className='column-link' data-method={method}>
-        <i className={`fa fa-fw fa-${icon} column-link__icon`} />
-        {text}
-      </a>
-    );
-  } else if (to) {
-    return (
-      <Link to={to} className='column-link'>
-        <i className={`fa fa-fw fa-${icon} column-link__icon`} />
-        {text}
-      </Link>
-    );
-  } else {
-    return (
-      <a onClick={onClick} className='column-link' role='button' tabIndex='0' data-method={method}>
-        <i className={`fa fa-fw fa-${icon} column-link__icon`} />
-        {text}
-      </a>
-    );
-  }
-};
-
-ColumnLink.propTypes = {
-  icon: PropTypes.string.isRequired,
-  text: PropTypes.string.isRequired,
-  to: PropTypes.string,
-  onClick: PropTypes.func,
-  href: PropTypes.string,
-  method: PropTypes.string,
-};
-
-export default ColumnLink;
diff --git a/app/javascript/themes/glitch/features/ui/components/column_loading.js b/app/javascript/themes/glitch/features/ui/components/column_loading.js
deleted file mode 100644
index 75f26218a..000000000
--- a/app/javascript/themes/glitch/features/ui/components/column_loading.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-import Column from 'themes/glitch/components/column';
-import ColumnHeader from 'themes/glitch/components/column_header';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-export default class ColumnLoading extends ImmutablePureComponent {
-
-  static propTypes = {
-    title: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
-    icon: PropTypes.string,
-  };
-
-  static defaultProps = {
-    title: '',
-    icon: '',
-  };
-
-  render() {
-    let { title, icon } = this.props;
-    return (
-      <Column>
-        <ColumnHeader icon={icon} title={title} multiColumn={false} focusable={false} />
-        <div className='scrollable' />
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/column_subheading.js b/app/javascript/themes/glitch/features/ui/components/column_subheading.js
deleted file mode 100644
index 8160c4aa3..000000000
--- a/app/javascript/themes/glitch/features/ui/components/column_subheading.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-const ColumnSubheading = ({ text }) => {
-  return (
-    <div className='column-subheading'>
-      {text}
-    </div>
-  );
-};
-
-ColumnSubheading.propTypes = {
-  text: PropTypes.string.isRequired,
-};
-
-export default ColumnSubheading;
diff --git a/app/javascript/themes/glitch/features/ui/components/columns_area.js b/app/javascript/themes/glitch/features/ui/components/columns_area.js
deleted file mode 100644
index 452950363..000000000
--- a/app/javascript/themes/glitch/features/ui/components/columns_area.js
+++ /dev/null
@@ -1,174 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { injectIntl } from 'react-intl';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-import ReactSwipeableViews from 'react-swipeable-views';
-import { links, getIndex, getLink } from './tabs_bar';
-
-import BundleContainer from '../containers/bundle_container';
-import ColumnLoading from './column_loading';
-import DrawerLoading from './drawer_loading';
-import BundleColumnError from './bundle_column_error';
-import { Compose, Notifications, HomeTimeline, CommunityTimeline, PublicTimeline, HashtagTimeline, DirectTimeline, FavouritedStatuses } from 'themes/glitch/util/async-components';
-
-import detectPassiveEvents from 'detect-passive-events';
-import { scrollRight } from 'themes/glitch/util/scroll';
-
-const componentMap = {
-  'COMPOSE': Compose,
-  'HOME': HomeTimeline,
-  'NOTIFICATIONS': Notifications,
-  'PUBLIC': PublicTimeline,
-  'COMMUNITY': CommunityTimeline,
-  'HASHTAG': HashtagTimeline,
-  'DIRECT': DirectTimeline,
-  'FAVOURITES': FavouritedStatuses,
-};
-
-@component => injectIntl(component, { withRef: true })
-export default class ColumnsArea extends ImmutablePureComponent {
-
-  static contextTypes = {
-    router: PropTypes.object.isRequired,
-  };
-
-  static propTypes = {
-    intl: PropTypes.object.isRequired,
-    columns: ImmutablePropTypes.list.isRequired,
-    singleColumn: PropTypes.bool,
-    children: PropTypes.node,
-  };
-
-  state = {
-    shouldAnimate: false,
-  }
-
-  componentWillReceiveProps() {
-    this.setState({ shouldAnimate: false });
-  }
-
-  componentDidMount() {
-    if (!this.props.singleColumn) {
-      this.node.addEventListener('wheel', this.handleWheel,  detectPassiveEvents.hasSupport ? { passive: true } : false);
-    }
-    this.lastIndex = getIndex(this.context.router.history.location.pathname);
-    this.setState({ shouldAnimate: true });
-  }
-
-  componentWillUpdate(nextProps) {
-    if (this.props.singleColumn !== nextProps.singleColumn && nextProps.singleColumn) {
-      this.node.removeEventListener('wheel', this.handleWheel);
-    }
-  }
-
-  componentDidUpdate(prevProps) {
-    if (this.props.singleColumn !== prevProps.singleColumn && !this.props.singleColumn) {
-      this.node.addEventListener('wheel', this.handleWheel,  detectPassiveEvents.hasSupport ? { passive: true } : false);
-    }
-    this.lastIndex = getIndex(this.context.router.history.location.pathname);
-    this.setState({ shouldAnimate: true });
-  }
-
-  componentWillUnmount () {
-    if (!this.props.singleColumn) {
-      this.node.removeEventListener('wheel', this.handleWheel);
-    }
-  }
-
-  handleChildrenContentChange() {
-    if (!this.props.singleColumn) {
-      this._interruptScrollAnimation = scrollRight(this.node, this.node.scrollWidth - window.innerWidth);
-    }
-  }
-
-  handleSwipe = (index) => {
-    this.pendingIndex = index;
-
-    const nextLinkTranslationId = links[index].props['data-preview-title-id'];
-    const currentLinkSelector = '.tabs-bar__link.active';
-    const nextLinkSelector = `.tabs-bar__link[data-preview-title-id="${nextLinkTranslationId}"]`;
-
-    // HACK: Remove the active class from the current link and set it to the next one
-    // React-router does this for us, but too late, feeling laggy.
-    document.querySelector(currentLinkSelector).classList.remove('active');
-    document.querySelector(nextLinkSelector).classList.add('active');
-  }
-
-  handleAnimationEnd = () => {
-    if (typeof this.pendingIndex === 'number') {
-      this.context.router.history.push(getLink(this.pendingIndex));
-      this.pendingIndex = null;
-    }
-  }
-
-  handleWheel = () => {
-    if (typeof this._interruptScrollAnimation !== 'function') {
-      return;
-    }
-
-    this._interruptScrollAnimation();
-  }
-
-  setRef = (node) => {
-    this.node = node;
-  }
-
-  renderView = (link, index) => {
-    const columnIndex = getIndex(this.context.router.history.location.pathname);
-    const title = this.props.intl.formatMessage({ id: link.props['data-preview-title-id'] });
-    const icon = link.props['data-preview-icon'];
-
-    const view = (index === columnIndex) ?
-      React.cloneElement(this.props.children) :
-      <ColumnLoading title={title} icon={icon} />;
-
-    return (
-      <div className='columns-area' key={index}>
-        {view}
-      </div>
-    );
-  }
-
-  renderLoading = columnId => () => {
-    return columnId === 'COMPOSE' ? <DrawerLoading /> : <ColumnLoading />;
-  }
-
-  renderError = (props) => {
-    return <BundleColumnError {...props} />;
-  }
-
-  render () {
-    const { columns, children, singleColumn } = this.props;
-    const { shouldAnimate } = this.state;
-
-    const columnIndex = getIndex(this.context.router.history.location.pathname);
-    this.pendingIndex = null;
-
-    if (singleColumn) {
-      return columnIndex !== -1 ? (
-        <ReactSwipeableViews index={columnIndex} onChangeIndex={this.handleSwipe} onTransitionEnd={this.handleAnimationEnd} animateTransitions={shouldAnimate} springConfig={{ duration: '400ms', delay: '0s', easeFunction: 'ease' }} style={{ height: '100%' }}>
-          {links.map(this.renderView)}
-        </ReactSwipeableViews>
-      ) : <div className='columns-area'>{children}</div>;
-    }
-
-    return (
-      <div className='columns-area' ref={this.setRef}>
-        {columns.map(column => {
-          const params = column.get('params', null) === null ? null : column.get('params').toJS();
-
-          return (
-            <BundleContainer key={column.get('uuid')} fetchComponent={componentMap[column.get('id')]} loading={this.renderLoading(column.get('id'))} error={this.renderError}>
-              {SpecificComponent => <SpecificComponent columnId={column.get('uuid')} params={params} multiColumn />}
-            </BundleContainer>
-          );
-        })}
-
-        {React.Children.map(children, child => React.cloneElement(child, { multiColumn: true }))}
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/confirmation_modal.js b/app/javascript/themes/glitch/features/ui/components/confirmation_modal.js
deleted file mode 100644
index 3d568aec3..000000000
--- a/app/javascript/themes/glitch/features/ui/components/confirmation_modal.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { injectIntl, FormattedMessage } from 'react-intl';
-import Button from 'themes/glitch/components/button';
-
-@injectIntl
-export default class ConfirmationModal extends React.PureComponent {
-
-  static propTypes = {
-    message: PropTypes.node.isRequired,
-    confirm: PropTypes.string.isRequired,
-    onClose: PropTypes.func.isRequired,
-    onConfirm: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  componentDidMount() {
-    this.button.focus();
-  }
-
-  handleClick = () => {
-    this.props.onClose();
-    this.props.onConfirm();
-  }
-
-  handleCancel = () => {
-    this.props.onClose();
-  }
-
-  setRef = (c) => {
-    this.button = c;
-  }
-
-  render () {
-    const { message, confirm } = this.props;
-
-    return (
-      <div className='modal-root__modal confirmation-modal'>
-        <div className='confirmation-modal__container'>
-          {message}
-        </div>
-
-        <div className='confirmation-modal__action-bar'>
-          <Button onClick={this.handleCancel} className='confirmation-modal__cancel-button'>
-            <FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' />
-          </Button>
-          <Button text={confirm} onClick={this.handleClick} ref={this.setRef} />
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/doodle_modal.js b/app/javascript/themes/glitch/features/ui/components/doodle_modal.js
deleted file mode 100644
index 819656dbf..000000000
--- a/app/javascript/themes/glitch/features/ui/components/doodle_modal.js
+++ /dev/null
@@ -1,614 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Button from 'themes/glitch/components/button';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import Atrament from 'atrament'; // the doodling library
-import { connect } from 'react-redux';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { doodleSet, uploadCompose } from 'themes/glitch/actions/compose';
-import IconButton from 'themes/glitch/components/icon_button';
-import { debounce, mapValues } from 'lodash';
-import classNames from 'classnames';
-
-// palette nicked from MyPaint, CC0
-const palette = [
-  ['rgb(  0,    0,    0)', 'Black'],
-  ['rgb( 38,   38,   38)', 'Gray 15'],
-  ['rgb( 77,   77,   77)', 'Grey 30'],
-  ['rgb(128,  128,  128)', 'Grey 50'],
-  ['rgb(171,  171,  171)', 'Grey 67'],
-  ['rgb(217,  217,  217)', 'Grey 85'],
-  ['rgb(255,  255,  255)', 'White'],
-  ['rgb(128,    0,    0)', 'Maroon'],
-  ['rgb(209,    0,    0)', 'English-red'],
-  ['rgb(255,   54,   34)', 'Tomato'],
-  ['rgb(252,   60,    3)', 'Orange-red'],
-  ['rgb(255,  140,  105)', 'Salmon'],
-  ['rgb(252,  232,   32)', 'Cadium-yellow'],
-  ['rgb(243,  253,   37)', 'Lemon yellow'],
-  ['rgb(121,    5,   35)', 'Dark crimson'],
-  ['rgb(169,   32,   62)', 'Deep carmine'],
-  ['rgb(255,  140,    0)', 'Orange'],
-  ['rgb(255,  168,   18)', 'Dark tangerine'],
-  ['rgb(217,  144,   88)', 'Persian orange'],
-  ['rgb(194,  178,  128)', 'Sand'],
-  ['rgb(255,  229,  180)', 'Peach'],
-  ['rgb(100,   54,   46)', 'Bole'],
-  ['rgb(108,   41,   52)', 'Dark cordovan'],
-  ['rgb(163,   65,   44)', 'Chestnut'],
-  ['rgb(228,  136,  100)', 'Dark salmon'],
-  ['rgb(255,  195,  143)', 'Apricot'],
-  ['rgb(255,  219,  188)', 'Unbleached silk'],
-  ['rgb(242,  227,  198)', 'Straw'],
-  ['rgb( 53,   19,   13)', 'Bistre'],
-  ['rgb( 84,   42,   14)', 'Dark chocolate'],
-  ['rgb(102,   51,   43)', 'Burnt sienna'],
-  ['rgb(184,   66,    0)', 'Sienna'],
-  ['rgb(216,  153,   12)', 'Yellow ochre'],
-  ['rgb(210,  180,  140)', 'Tan'],
-  ['rgb(232,  204,  144)', 'Dark wheat'],
-  ['rgb(  0,   49,   83)', 'Prussian blue'],
-  ['rgb( 48,   69,  119)', 'Dark grey blue'],
-  ['rgb(  0,   71,  171)', 'Cobalt blue'],
-  ['rgb( 31,  117,  254)', 'Blue'],
-  ['rgb(120,  180,  255)', 'Bright french blue'],
-  ['rgb(171,  200,  255)', 'Bright steel blue'],
-  ['rgb(208,  231,  255)', 'Ice blue'],
-  ['rgb( 30,   51,   58)', 'Medium jungle green'],
-  ['rgb( 47,   79,   79)', 'Dark slate grey'],
-  ['rgb( 74,  104,   93)', 'Dark grullo green'],
-  ['rgb(  0,  128,  128)', 'Teal'],
-  ['rgb( 67,  170,  176)', 'Turquoise'],
-  ['rgb(109,  174,  199)', 'Cerulean frost'],
-  ['rgb(173,  217,  186)', 'Tiffany green'],
-  ['rgb( 22,   34,   29)', 'Gray-asparagus'],
-  ['rgb( 36,   48,   45)', 'Medium dark teal'],
-  ['rgb( 74,  104,   93)', 'Xanadu'],
-  ['rgb(119,  198,  121)', 'Mint'],
-  ['rgb(175,  205,  182)', 'Timberwolf'],
-  ['rgb(185,  245,  246)', 'Celeste'],
-  ['rgb(193,  255,  234)', 'Aquamarine'],
-  ['rgb( 29,   52,   35)', 'Cal Poly Pomona'],
-  ['rgb(  1,   68,   33)', 'Forest green'],
-  ['rgb( 42,  128,    0)', 'Napier green'],
-  ['rgb(128,  128,    0)', 'Olive'],
-  ['rgb( 65,  156,  105)', 'Sea green'],
-  ['rgb(189,  246,   29)', 'Green-yellow'],
-  ['rgb(231,  244,  134)', 'Bright chartreuse'],
-  ['rgb(138,   23,  137)', 'Purple'],
-  ['rgb( 78,   39,  138)', 'Violet'],
-  ['rgb(193,   75,  110)', 'Dark thulian pink'],
-  ['rgb(222,   49,   99)', 'Cerise'],
-  ['rgb(255,   20,  147)', 'Deep pink'],
-  ['rgb(255,  102,  204)', 'Rose pink'],
-  ['rgb(255,  203,  219)', 'Pink'],
-  ['rgb(255,  255,  255)', 'White'],
-  ['rgb(229,   17,    1)', 'RGB Red'],
-  ['rgb(  0,  255,    0)', 'RGB Green'],
-  ['rgb(  0,    0,  255)', 'RGB Blue'],
-  ['rgb(  0,  255,  255)', 'CMYK Cyan'],
-  ['rgb(255,    0,  255)', 'CMYK Magenta'],
-  ['rgb(255,  255,    0)', 'CMYK Yellow'],
-];
-
-// re-arrange to the right order for display
-let palReordered = [];
-for (let row = 0; row < 7; row++) {
-  for (let col = 0; col < 11; col++) {
-    palReordered.push(palette[col * 7 + row]);
-  }
-  palReordered.push(null); // null indicates a <br />
-}
-
-// Utility for converting base64 image to binary for upload
-// https://stackoverflow.com/questions/35940290/how-to-convert-base64-string-to-javascript-file-object-like-as-from-file-input-f
-function dataURLtoFile(dataurl, filename) {
-  let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
-    bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
-  while(n--){
-    u8arr[n] = bstr.charCodeAt(n);
-  }
-  return new File([u8arr], filename, { type: mime });
-}
-
-const DOODLE_SIZES = {
-  normal: [500, 500, 'Square 500'],
-  tootbanner: [702, 330, 'Tootbanner'],
-  s640x480: [640, 480, '640×480 - 480p'],
-  s800x600: [800, 600, '800×600 - SVGA'],
-  s720x480: [720, 405, '720x405 - 16:9'],
-};
-
-
-const mapStateToProps = state => ({
-  options: state.getIn(['compose', 'doodle']),
-});
-
-const mapDispatchToProps = dispatch => ({
-  /** Set options in the redux store */
-  setOpt: (opts) => dispatch(doodleSet(opts)),
-  /** Submit doodle for upload */
-  submit: (file) => dispatch(uploadCompose([file])),
-});
-
-/**
- * Doodling dialog with drawing canvas
- *
- * Keyboard shortcuts:
- * - Delete: Clear screen, fill with background color
- * - Backspace, Ctrl+Z: Undo one step
- * - Ctrl held while drawing: Use background color
- * - Shift held while clicking screen: Use fill tool
- *
- * Palette:
- * - Left mouse button: pick foreground
- * - Ctrl + left mouse button: pick background
- * - Right mouse button: pick background
- */
-@connect(mapStateToProps, mapDispatchToProps)
-export default class DoodleModal extends ImmutablePureComponent {
-
-  static propTypes = {
-    options: ImmutablePropTypes.map,
-    onClose: PropTypes.func.isRequired,
-    setOpt: PropTypes.func.isRequired,
-    submit: PropTypes.func.isRequired,
-  };
-
-  //region Option getters/setters
-
-  /** Foreground color */
-  get fg () {
-    return this.props.options.get('fg');
-  }
-  set fg (value) {
-    this.props.setOpt({ fg: value });
-  }
-
-  /** Background color */
-  get bg () {
-    return this.props.options.get('bg');
-  }
-  set bg (value) {
-    this.props.setOpt({ bg: value });
-  }
-
-  /** Swap Fg and Bg for drawing */
-  get swapped () {
-    return this.props.options.get('swapped');
-  }
-  set swapped (value) {
-    this.props.setOpt({ swapped: value });
-  }
-
-  /** Mode - 'draw' or 'fill' */
-  get mode () {
-    return this.props.options.get('mode');
-  }
-  set mode (value) {
-    this.props.setOpt({ mode: value });
-  }
-
-  /** Base line weight */
-  get weight () {
-    return this.props.options.get('weight');
-  }
-  set weight (value) {
-    this.props.setOpt({ weight: value });
-  }
-
-  /** Drawing opacity */
-  get opacity () {
-    return this.props.options.get('opacity');
-  }
-  set opacity (value) {
-    this.props.setOpt({ opacity: value });
-  }
-
-  /** Adaptive stroke - change width with speed */
-  get adaptiveStroke () {
-    return this.props.options.get('adaptiveStroke');
-  }
-  set adaptiveStroke (value) {
-    this.props.setOpt({ adaptiveStroke: value });
-  }
-
-  /** Smoothing (for mouse drawing) */
-  get smoothing () {
-    return this.props.options.get('smoothing');
-  }
-  set smoothing (value) {
-    this.props.setOpt({ smoothing: value });
-  }
-
-  /** Size preset */
-  get size () {
-    return this.props.options.get('size');
-  }
-  set size (value) {
-    this.props.setOpt({ size: value });
-  }
-
-  //endregion
-
-  /** Key up handler */
-  handleKeyUp = (e) => {
-    if (e.target.nodeName === 'INPUT') return;
-
-    if (e.key === 'Delete') {
-      e.preventDefault();
-      this.handleClearBtn();
-      return;
-    }
-
-    if (e.key === 'Backspace' || (e.key === 'z' && (e.ctrlKey || e.metaKey))) {
-      e.preventDefault();
-      this.undo();
-    }
-
-    if (e.key === 'Control' || e.key === 'Meta') {
-      this.controlHeld = false;
-      this.swapped = false;
-    }
-
-    if (e.key === 'Shift') {
-      this.shiftHeld = false;
-      this.mode = 'draw';
-    }
-  };
-
-  /** Key down handler */
-  handleKeyDown = (e) => {
-    if (e.key === 'Control' || e.key === 'Meta') {
-      this.controlHeld = true;
-      this.swapped = true;
-    }
-
-    if (e.key === 'Shift') {
-      this.shiftHeld = true;
-      this.mode = 'fill';
-    }
-  };
-
-  /**
-   * Component installed in the DOM, do some initial set-up
-   */
-  componentDidMount () {
-    this.controlHeld = false;
-    this.shiftHeld = false;
-    this.swapped = false;
-    window.addEventListener('keyup', this.handleKeyUp, false);
-    window.addEventListener('keydown', this.handleKeyDown, false);
-  };
-
-  /**
-   * Tear component down
-   */
-  componentWillUnmount () {
-    window.removeEventListener('keyup', this.handleKeyUp, false);
-    window.removeEventListener('keydown', this.handleKeyDown, false);
-    if (this.sketcher) this.sketcher.destroy();
-  }
-
-  /**
-   * Set reference to the canvas element.
-   * This is called during component init
-   *
-   * @param elem - canvas element
-   */
-  setCanvasRef = (elem) => {
-    this.canvas = elem;
-    if (elem) {
-      elem.addEventListener('dirty', () => {
-        this.saveUndo();
-        this.sketcher._dirty = false;
-      });
-
-      elem.addEventListener('click', () => {
-        // sketcher bug - does not fire dirty on fill
-        if (this.mode === 'fill') {
-          this.saveUndo();
-        }
-      });
-
-      // prevent context menu
-      elem.addEventListener('contextmenu', (e) => {
-        e.preventDefault();
-      });
-
-      elem.addEventListener('mousedown', (e) => {
-        if (e.button === 2) {
-          this.swapped = true;
-        }
-      });
-
-      elem.addEventListener('mouseup', (e) => {
-        if (e.button === 2) {
-          this.swapped = this.controlHeld;
-        }
-      });
-
-      this.initSketcher(elem);
-      this.mode = 'draw'; // Reset mode - it's confusing if left at 'fill'
-    }
-  };
-
-  /**
-   * Set up the sketcher instance
-   *
-   * @param canvas - canvas element. Null if we're just resizing
-   */
-  initSketcher (canvas = null) {
-    const sizepreset = DOODLE_SIZES[this.size];
-
-    if (this.sketcher) this.sketcher.destroy();
-    this.sketcher = new Atrament(canvas || this.canvas, sizepreset[0], sizepreset[1]);
-
-    if (canvas) {
-      this.ctx = this.sketcher.context;
-      this.updateSketcherSettings();
-    }
-
-    this.clearScreen();
-  }
-
-  /**
-   * Done button handler
-   */
-  onDoneButton = () => {
-    const dataUrl = this.sketcher.toImage();
-    const file = dataURLtoFile(dataUrl, 'doodle.png');
-    this.props.submit(file);
-    this.props.onClose(); // close dialog
-  };
-
-  /**
-   * Cancel button handler
-   */
-  onCancelButton = () => {
-    if (this.undos.length > 1 && !confirm('Discard doodle? All changes will be lost!')) {
-      return;
-    }
-
-    this.props.onClose(); // close dialog
-  };
-
-  /**
-   * Update sketcher options based on state
-   */
-  updateSketcherSettings () {
-    if (!this.sketcher) return;
-
-    if (this.oldSize !== this.size) this.initSketcher();
-
-    this.sketcher.color = (this.swapped ? this.bg : this.fg);
-    this.sketcher.opacity = this.opacity;
-    this.sketcher.weight = this.weight;
-    this.sketcher.mode = this.mode;
-    this.sketcher.smoothing = this.smoothing;
-    this.sketcher.adaptiveStroke = this.adaptiveStroke;
-
-    this.oldSize = this.size;
-  }
-
-  /**
-   * Fill screen with background color
-   */
-  clearScreen = () => {
-    this.ctx.fillStyle = this.bg;
-    this.ctx.fillRect(-1, -1, this.canvas.width+2, this.canvas.height+2);
-    this.undos = [];
-
-    this.doSaveUndo();
-  };
-
-  /**
-   * Undo one step
-   */
-  undo = () => {
-    if (this.undos.length > 1) {
-      this.undos.pop();
-      const buf = this.undos.pop();
-
-      this.sketcher.clear();
-      this.ctx.putImageData(buf, 0, 0);
-      this.doSaveUndo();
-    }
-  };
-
-  /**
-   * Save canvas content into the undo buffer immediately
-   */
-  doSaveUndo = () => {
-    this.undos.push(this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height));
-  };
-
-  /**
-   * Called on each canvas change.
-   * Saves canvas content to the undo buffer after some period of inactivity.
-   */
-  saveUndo = debounce(() => {
-    this.doSaveUndo();
-  }, 100);
-
-  /**
-   * Palette left click.
-   * Selects Fg color (or Bg, if Control/Meta is held)
-   *
-   * @param e - event
-   */
-  onPaletteClick = (e) => {
-    const c = e.target.dataset.color;
-
-    if (this.controlHeld) {
-      this.bg = c;
-    } else {
-      this.fg = c;
-    }
-
-    e.target.blur();
-    e.preventDefault();
-  };
-
-  /**
-   * Palette right click.
-   * Selects Bg color
-   *
-   * @param e - event
-   */
-  onPaletteRClick = (e) => {
-    this.bg = e.target.dataset.color;
-    e.target.blur();
-    e.preventDefault();
-  };
-
-  /**
-   * Handle click on the Draw mode button
-   *
-   * @param e - event
-   */
-  setModeDraw = (e) => {
-    this.mode = 'draw';
-    e.target.blur();
-  };
-
-  /**
-   * Handle click on the Fill mode button
-   *
-   * @param e - event
-   */
-  setModeFill = (e) => {
-    this.mode = 'fill';
-    e.target.blur();
-  };
-
-  /**
-   * Handle click on Smooth checkbox
-   *
-   * @param e - event
-   */
-  tglSmooth = (e) => {
-    this.smoothing = !this.smoothing;
-    e.target.blur();
-  };
-
-  /**
-   * Handle click on Adaptive checkbox
-   *
-   * @param e - event
-   */
-  tglAdaptive = (e) => {
-    this.adaptiveStroke = !this.adaptiveStroke;
-    e.target.blur();
-  };
-
-  /**
-   * Handle change of the Weight input field
-   *
-   * @param e - event
-   */
-  setWeight = (e) => {
-    this.weight = +e.target.value || 1;
-  };
-
-  /**
-   * Set size - clalback from the select box
-   *
-   * @param e - event
-   */
-  changeSize = (e) => {
-    let newSize = e.target.value;
-    if (newSize === this.oldSize) return;
-
-    if (this.undos.length > 1 && !confirm('Change size? This will erase your drawing!')) {
-      return;
-    }
-
-    this.size = newSize;
-  };
-
-  handleClearBtn = () => {
-    if (this.undos.length > 1 && !confirm('Clear screen? This will erase your drawing!')) {
-      return;
-    }
-
-    this.clearScreen();
-  };
-
-  /**
-   * Render the component
-   */
-  render () {
-    this.updateSketcherSettings();
-
-    return (
-      <div className='modal-root__modal doodle-modal'>
-        <div className='doodle-modal__container'>
-          <canvas ref={this.setCanvasRef} />
-        </div>
-
-        <div className='doodle-modal__action-bar'>
-          <div className='doodle-toolbar'>
-            <Button text='Done' onClick={this.onDoneButton} />
-            <Button text='Cancel' onClick={this.onCancelButton} />
-          </div>
-          <div className='filler' />
-          <div className='doodle-toolbar with-inputs'>
-            <div>
-              <label htmlFor='dd_smoothing'>Smoothing</label>
-              <span className='val'>
-                <input type='checkbox' id='dd_smoothing' onChange={this.tglSmooth} checked={this.smoothing} />
-              </span>
-            </div>
-            <div>
-              <label htmlFor='dd_adaptive'>Adaptive</label>
-              <span className='val'>
-                <input type='checkbox' id='dd_adaptive' onChange={this.tglAdaptive} checked={this.adaptiveStroke} />
-              </span>
-            </div>
-            <div>
-              <label htmlFor='dd_weight'>Weight</label>
-              <span className='val'>
-                <input type='number' min={1} id='dd_weight' value={this.weight} onChange={this.setWeight} />
-              </span>
-            </div>
-            <div>
-              <select aria-label='Canvas size' onInput={this.changeSize} defaultValue={this.size}>
-                { Object.values(mapValues(DOODLE_SIZES, (val, k) =>
-                  <option key={k} value={k}>{val[2]}</option>
-                )) }
-              </select>
-            </div>
-          </div>
-          <div className='doodle-toolbar'>
-            <IconButton icon='pencil' title='Draw' label='Draw' onClick={this.setModeDraw} size={18} active={this.mode === 'draw'} inverted />
-            <IconButton icon='bath' title='Fill' label='Fill' onClick={this.setModeFill} size={18} active={this.mode === 'fill'} inverted />
-            <IconButton icon='undo' title='Undo' label='Undo' onClick={this.undo} size={18} inverted />
-            <IconButton icon='trash' title='Clear' label='Clear' onClick={this.handleClearBtn} size={18} inverted />
-          </div>
-          <div className='doodle-palette'>
-            {
-              palReordered.map((c, i) =>
-                c === null ?
-                  <br key={i} /> :
-                  <button
-                    key={i}
-                    style={{ backgroundColor: c[0] }}
-                    onClick={this.onPaletteClick}
-                    onContextMenu={this.onPaletteRClick}
-                    data-color={c[0]}
-                    title={c[1]}
-                    className={classNames({
-                      'foreground': this.fg === c[0],
-                      'background': this.bg === c[0],
-                    })}
-                  />
-              )
-            }
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/drawer_loading.js b/app/javascript/themes/glitch/features/ui/components/drawer_loading.js
deleted file mode 100644
index 08b0d2347..000000000
--- a/app/javascript/themes/glitch/features/ui/components/drawer_loading.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import React from 'react';
-
-const DrawerLoading = () => (
-  <div className='drawer'>
-    <div className='drawer__pager'>
-      <div className='drawer__inner' />
-    </div>
-  </div>
-);
-
-export default DrawerLoading;
diff --git a/app/javascript/themes/glitch/features/ui/components/embed_modal.js b/app/javascript/themes/glitch/features/ui/components/embed_modal.js
deleted file mode 100644
index 1afffb51b..000000000
--- a/app/javascript/themes/glitch/features/ui/components/embed_modal.js
+++ /dev/null
@@ -1,84 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { FormattedMessage, injectIntl } from 'react-intl';
-import axios from 'axios';
-
-@injectIntl
-export default class EmbedModal extends ImmutablePureComponent {
-
-  static propTypes = {
-    url: PropTypes.string.isRequired,
-    onClose: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  }
-
-  state = {
-    loading: false,
-    oembed: null,
-  };
-
-  componentDidMount () {
-    const { url } = this.props;
-
-    this.setState({ loading: true });
-
-    axios.post('/api/web/embed', { url }).then(res => {
-      this.setState({ loading: false, oembed: res.data });
-
-      const iframeDocument = this.iframe.contentWindow.document;
-
-      iframeDocument.open();
-      iframeDocument.write(res.data.html);
-      iframeDocument.close();
-
-      iframeDocument.body.style.margin = 0;
-      this.iframe.width  = iframeDocument.body.scrollWidth;
-      this.iframe.height = iframeDocument.body.scrollHeight;
-    });
-  }
-
-  setIframeRef = c =>  {
-    this.iframe = c;
-  }
-
-  handleTextareaClick = (e) => {
-    e.target.select();
-  }
-
-  render () {
-    const { oembed } = this.state;
-
-    return (
-      <div className='modal-root__modal embed-modal'>
-        <h4><FormattedMessage id='status.embed' defaultMessage='Embed' /></h4>
-
-        <div className='embed-modal__container'>
-          <p className='hint'>
-            <FormattedMessage id='embed.instructions' defaultMessage='Embed this status on your website by copying the code below.' />
-          </p>
-
-          <input
-            type='text'
-            className='embed-modal__html'
-            readOnly
-            value={oembed && oembed.html || ''}
-            onClick={this.handleTextareaClick}
-          />
-
-          <p className='hint'>
-            <FormattedMessage id='embed.preview' defaultMessage='Here is what it will look like:' />
-          </p>
-
-          <iframe
-            className='embed-modal__iframe'
-            frameBorder='0'
-            ref={this.setIframeRef}
-            title='preview'
-          />
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/image_loader.js b/app/javascript/themes/glitch/features/ui/components/image_loader.js
deleted file mode 100644
index aad594380..000000000
--- a/app/javascript/themes/glitch/features/ui/components/image_loader.js
+++ /dev/null
@@ -1,152 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
-
-export default class ImageLoader extends React.PureComponent {
-
-  static propTypes = {
-    alt: PropTypes.string,
-    src: PropTypes.string.isRequired,
-    previewSrc: PropTypes.string.isRequired,
-    width: PropTypes.number,
-    height: PropTypes.number,
-  }
-
-  static defaultProps = {
-    alt: '',
-    width: null,
-    height: null,
-  };
-
-  state = {
-    loading: true,
-    error: false,
-  }
-
-  removers = [];
-
-  get canvasContext() {
-    if (!this.canvas) {
-      return null;
-    }
-    this._canvasContext = this._canvasContext || this.canvas.getContext('2d');
-    return this._canvasContext;
-  }
-
-  componentDidMount () {
-    this.loadImage(this.props);
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (this.props.src !== nextProps.src) {
-      this.loadImage(nextProps);
-    }
-  }
-
-  loadImage (props) {
-    this.removeEventListeners();
-    this.setState({ loading: true, error: false });
-    Promise.all([
-      this.loadPreviewCanvas(props),
-      this.hasSize() && this.loadOriginalImage(props),
-    ].filter(Boolean))
-      .then(() => {
-        this.setState({ loading: false, error: false });
-        this.clearPreviewCanvas();
-      })
-      .catch(() => this.setState({ loading: false, error: true }));
-  }
-
-  loadPreviewCanvas = ({ previewSrc, width, height }) => new Promise((resolve, reject) => {
-    const image = new Image();
-    const removeEventListeners = () => {
-      image.removeEventListener('error', handleError);
-      image.removeEventListener('load', handleLoad);
-    };
-    const handleError = () => {
-      removeEventListeners();
-      reject();
-    };
-    const handleLoad = () => {
-      removeEventListeners();
-      this.canvasContext.drawImage(image, 0, 0, width, height);
-      resolve();
-    };
-    image.addEventListener('error', handleError);
-    image.addEventListener('load', handleLoad);
-    image.src = previewSrc;
-    this.removers.push(removeEventListeners);
-  })
-
-  clearPreviewCanvas () {
-    const { width, height } = this.canvas;
-    this.canvasContext.clearRect(0, 0, width, height);
-  }
-
-  loadOriginalImage = ({ src }) => new Promise((resolve, reject) => {
-    const image = new Image();
-    const removeEventListeners = () => {
-      image.removeEventListener('error', handleError);
-      image.removeEventListener('load', handleLoad);
-    };
-    const handleError = () => {
-      removeEventListeners();
-      reject();
-    };
-    const handleLoad = () => {
-      removeEventListeners();
-      resolve();
-    };
-    image.addEventListener('error', handleError);
-    image.addEventListener('load', handleLoad);
-    image.src = src;
-    this.removers.push(removeEventListeners);
-  });
-
-  removeEventListeners () {
-    this.removers.forEach(listeners => listeners());
-    this.removers = [];
-  }
-
-  hasSize () {
-    const { width, height } = this.props;
-    return typeof width === 'number' && typeof height === 'number';
-  }
-
-  setCanvasRef = c => {
-    this.canvas = c;
-  }
-
-  render () {
-    const { alt, src, width, height } = this.props;
-    const { loading } = this.state;
-
-    const className = classNames('image-loader', {
-      'image-loader--loading': loading,
-      'image-loader--amorphous': !this.hasSize(),
-    });
-
-    return (
-      <div className={className}>
-        <canvas
-          className='image-loader__preview-canvas'
-          width={width}
-          height={height}
-          ref={this.setCanvasRef}
-          style={{ opacity: loading ? 1 : 0 }}
-        />
-
-        {!loading && (
-          <img
-            alt={alt}
-            className='image-loader__img'
-            src={src}
-            width={width}
-            height={height}
-          />
-        )}
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/media_modal.js b/app/javascript/themes/glitch/features/ui/components/media_modal.js
deleted file mode 100644
index 1dad972b2..000000000
--- a/app/javascript/themes/glitch/features/ui/components/media_modal.js
+++ /dev/null
@@ -1,126 +0,0 @@
-import React from 'react';
-import ReactSwipeableViews from 'react-swipeable-views';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import ExtendedVideoPlayer from 'themes/glitch/components/extended_video_player';
-import { defineMessages, injectIntl } from 'react-intl';
-import IconButton from 'themes/glitch/components/icon_button';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import ImageLoader from './image_loader';
-
-const messages = defineMessages({
-  close: { id: 'lightbox.close', defaultMessage: 'Close' },
-  previous: { id: 'lightbox.previous', defaultMessage: 'Previous' },
-  next: { id: 'lightbox.next', defaultMessage: 'Next' },
-});
-
-@injectIntl
-export default class MediaModal extends ImmutablePureComponent {
-
-  static propTypes = {
-    media: ImmutablePropTypes.list.isRequired,
-    index: PropTypes.number.isRequired,
-    onClose: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  state = {
-    index: null,
-  };
-
-  handleSwipe = (index) => {
-    this.setState({ index: index % this.props.media.size });
-  }
-
-  handleNextClick = () => {
-    this.setState({ index: (this.getIndex() + 1) % this.props.media.size });
-  }
-
-  handlePrevClick = () => {
-    this.setState({ index: (this.props.media.size + this.getIndex() - 1) % this.props.media.size });
-  }
-
-  handleChangeIndex = (e) => {
-    const index = Number(e.currentTarget.getAttribute('data-index'));
-    this.setState({ index: index % this.props.media.size });
-  }
-
-  handleKeyUp = (e) => {
-    switch(e.key) {
-    case 'ArrowLeft':
-      this.handlePrevClick();
-      break;
-    case 'ArrowRight':
-      this.handleNextClick();
-      break;
-    }
-  }
-
-  componentDidMount () {
-    window.addEventListener('keyup', this.handleKeyUp, false);
-  }
-
-  componentWillUnmount () {
-    window.removeEventListener('keyup', this.handleKeyUp);
-  }
-
-  getIndex () {
-    return this.state.index !== null ? this.state.index : this.props.index;
-  }
-
-  render () {
-    const { media, intl, onClose } = this.props;
-
-    const index = this.getIndex();
-    let pagination = [];
-
-    const leftNav  = media.size > 1 && <button tabIndex='0' className='modal-container__nav modal-container__nav--left' onClick={this.handlePrevClick} aria-label={intl.formatMessage(messages.previous)}><i className='fa fa-fw fa-chevron-left' /></button>;
-    const rightNav = media.size > 1 && <button tabIndex='0' className='modal-container__nav  modal-container__nav--right' onClick={this.handleNextClick} aria-label={intl.formatMessage(messages.next)}><i className='fa fa-fw fa-chevron-right' /></button>;
-
-    if (media.size > 1) {
-      pagination = media.map((item, i) => {
-        const classes = ['media-modal__button'];
-        if (i === index) {
-          classes.push('media-modal__button--active');
-        }
-        return (<li className='media-modal__page-dot' key={i}><button tabIndex='0' className={classes.join(' ')} onClick={this.handleChangeIndex} data-index={i}>{i + 1}</button></li>);
-      });
-    }
-
-    const content = media.map((image) => {
-      const width  = image.getIn(['meta', 'original', 'width']) || null;
-      const height = image.getIn(['meta', 'original', 'height']) || null;
-
-      if (image.get('type') === 'image') {
-        return <ImageLoader previewSrc={image.get('preview_url')} src={image.get('url')} width={width} height={height} alt={image.get('description')} key={image.get('preview_url')} />;
-      } else if (image.get('type') === 'gifv') {
-        return <ExtendedVideoPlayer src={image.get('url')} muted controls={false} width={width} height={height} key={image.get('preview_url')} alt={image.get('description')} />;
-      }
-
-      return null;
-    }).toArray();
-
-    const containerStyle = {
-      alignItems: 'center', // center vertically
-    };
-
-    return (
-      <div className='modal-root__modal media-modal'>
-        {leftNav}
-
-        <div className='media-modal__content'>
-          <IconButton className='media-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={onClose} size={16} />
-          <ReactSwipeableViews containerStyle={containerStyle} onChangeIndex={this.handleSwipe} index={index}>
-            {content}
-          </ReactSwipeableViews>
-        </div>
-        <ul className='media-modal__pagination'>
-          {pagination}
-        </ul>
-
-        {rightNav}
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/modal_loading.js b/app/javascript/themes/glitch/features/ui/components/modal_loading.js
deleted file mode 100644
index e14d20fbb..000000000
--- a/app/javascript/themes/glitch/features/ui/components/modal_loading.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import React from 'react';
-
-import LoadingIndicator from 'themes/glitch/components/loading_indicator';
-
-// Keep the markup in sync with <BundleModalError />
-// (make sure they have the same dimensions)
-const ModalLoading = () => (
-  <div className='modal-root__modal error-modal'>
-    <div className='error-modal__body'>
-      <LoadingIndicator />
-    </div>
-    <div className='error-modal__footer'>
-      <div>
-        <button className='error-modal__nav onboarding-modal__skip' />
-      </div>
-    </div>
-  </div>
-);
-
-export default ModalLoading;
diff --git a/app/javascript/themes/glitch/features/ui/components/modal_root.js b/app/javascript/themes/glitch/features/ui/components/modal_root.js
deleted file mode 100644
index fbe794170..000000000
--- a/app/javascript/themes/glitch/features/ui/components/modal_root.js
+++ /dev/null
@@ -1,131 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import BundleContainer from '../containers/bundle_container';
-import BundleModalError from './bundle_modal_error';
-import ModalLoading from './modal_loading';
-import ActionsModal from './actions_modal';
-import MediaModal from './media_modal';
-import VideoModal from './video_modal';
-import BoostModal from './boost_modal';
-import DoodleModal from './doodle_modal';
-import ConfirmationModal from './confirmation_modal';
-import {
-  OnboardingModal,
-  MuteModal,
-  ReportModal,
-  SettingsModal,
-  EmbedModal,
-} from 'themes/glitch/util/async-components';
-
-const MODAL_COMPONENTS = {
-  'MEDIA': () => Promise.resolve({ default: MediaModal }),
-  'ONBOARDING': OnboardingModal,
-  'VIDEO': () => Promise.resolve({ default: VideoModal }),
-  'BOOST': () => Promise.resolve({ default: BoostModal }),
-  'DOODLE': () => Promise.resolve({ default: DoodleModal }),
-  'CONFIRM': () => Promise.resolve({ default: ConfirmationModal }),
-  'MUTE': MuteModal,
-  'REPORT': ReportModal,
-  'SETTINGS': SettingsModal,
-  'ACTIONS': () => Promise.resolve({ default: ActionsModal }),
-  'EMBED': EmbedModal,
-};
-
-export default class ModalRoot extends React.PureComponent {
-
-  static propTypes = {
-    type: PropTypes.string,
-    props: PropTypes.object,
-    onClose: PropTypes.func.isRequired,
-  };
-
-  state = {
-    revealed: false,
-  };
-
-  handleKeyUp = (e) => {
-    if ((e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27)
-         && !!this.props.type && !this.props.props.noEsc) {
-      this.props.onClose();
-    }
-  }
-
-  componentDidMount () {
-    window.addEventListener('keyup', this.handleKeyUp, false);
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (!!nextProps.type && !this.props.type) {
-      this.activeElement = document.activeElement;
-
-      this.getSiblings().forEach(sibling => sibling.setAttribute('inert', true));
-    } else if (!nextProps.type) {
-      this.setState({ revealed: false });
-    }
-  }
-
-  componentDidUpdate (prevProps) {
-    if (!this.props.type && !!prevProps.type) {
-      this.getSiblings().forEach(sibling => sibling.removeAttribute('inert'));
-      this.activeElement.focus();
-      this.activeElement = null;
-    }
-    if (this.props.type) {
-      requestAnimationFrame(() => {
-        this.setState({ revealed: true });
-      });
-    }
-  }
-
-  componentWillUnmount () {
-    window.removeEventListener('keyup', this.handleKeyUp);
-  }
-
-  getSiblings = () => {
-    return Array(...this.node.parentElement.childNodes).filter(node => node !== this.node);
-  }
-
-  setRef = ref => {
-    this.node = ref;
-  }
-
-  renderLoading = modalId => () => {
-    return ['MEDIA', 'VIDEO', 'BOOST', 'DOODLE', 'CONFIRM', 'ACTIONS'].indexOf(modalId) === -1 ? <ModalLoading /> : null;
-  }
-
-  renderError = (props) => {
-    const { onClose } = this.props;
-
-    return <BundleModalError {...props} onClose={onClose} />;
-  }
-
-  render () {
-    const { type, props, onClose } = this.props;
-    const { revealed } = this.state;
-    const visible = !!type;
-
-    if (!visible) {
-      return (
-        <div className='modal-root' ref={this.setRef} style={{ opacity: 0 }} />
-      );
-    }
-
-    return (
-      <div className='modal-root' ref={this.setRef} style={{ opacity: revealed ? 1 : 0 }}>
-        <div style={{ pointerEvents: visible ? 'auto' : 'none' }}>
-          <div role='presentation' className='modal-root__overlay' onClick={onClose} />
-          <div role='dialog' className='modal-root__container'>
-            {
-              visible ?
-                (<BundleContainer fetchComponent={MODAL_COMPONENTS[type]} loading={this.renderLoading(type)} error={this.renderError} renderDelay={200}>
-                  {(SpecificComponent) => <SpecificComponent {...props} onClose={onClose} />}
-                </BundleContainer>) :
-              null
-            }
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/mute_modal.js b/app/javascript/themes/glitch/features/ui/components/mute_modal.js
deleted file mode 100644
index ffccdc84d..000000000
--- a/app/javascript/themes/glitch/features/ui/components/mute_modal.js
+++ /dev/null
@@ -1,105 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import { injectIntl, FormattedMessage } from 'react-intl';
-import Toggle from 'react-toggle';
-import Button from 'themes/glitch/components/button';
-import { closeModal } from 'themes/glitch/actions/modal';
-import { muteAccount } from 'themes/glitch/actions/accounts';
-import { toggleHideNotifications } from 'themes/glitch/actions/mutes';
-
-
-const mapStateToProps = state => {
-  return {
-    isSubmitting: state.getIn(['reports', 'new', 'isSubmitting']),
-    account: state.getIn(['mutes', 'new', 'account']),
-    notifications: state.getIn(['mutes', 'new', 'notifications']),
-  };
-};
-
-const mapDispatchToProps = dispatch => {
-  return {
-    onConfirm(account, notifications) {
-      dispatch(muteAccount(account.get('id'), notifications));
-    },
-
-    onClose() {
-      dispatch(closeModal());
-    },
-
-    onToggleNotifications() {
-      dispatch(toggleHideNotifications());
-    },
-  };
-};
-
-@connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
-export default class MuteModal extends React.PureComponent {
-
-  static propTypes = {
-    isSubmitting: PropTypes.bool.isRequired,
-    account: PropTypes.object.isRequired,
-    notifications: PropTypes.bool.isRequired,
-    onClose: PropTypes.func.isRequired,
-    onConfirm: PropTypes.func.isRequired,
-    onToggleNotifications: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  componentDidMount() {
-    this.button.focus();
-  }
-
-  handleClick = () => {
-    this.props.onClose();
-    this.props.onConfirm(this.props.account, this.props.notifications);
-  }
-
-  handleCancel = () => {
-    this.props.onClose();
-  }
-
-  setRef = (c) => {
-    this.button = c;
-  }
-
-  toggleNotifications = () => {
-    this.props.onToggleNotifications();
-  }
-
-  render () {
-    const { account, notifications } = this.props;
-
-    return (
-      <div className='modal-root__modal mute-modal'>
-        <div className='mute-modal__container'>
-          <p>
-            <FormattedMessage
-              id='confirmations.mute.message'
-              defaultMessage='Are you sure you want to mute {name}?'
-              values={{ name: <strong>@{account.get('acct')}</strong> }}
-            />
-          </p>
-          <div>
-            <label htmlFor='mute-modal__hide-notifications-checkbox'>
-              <FormattedMessage id='mute_modal.hide_notifications' defaultMessage='Hide notifications from this user?' />
-              {' '}
-              <Toggle id='mute-modal__hide-notifications-checkbox' checked={notifications} onChange={this.toggleNotifications} />
-            </label>
-          </div>
-        </div>
-
-        <div className='mute-modal__action-bar'>
-          <Button onClick={this.handleCancel} className='mute-modal__cancel-button'>
-            <FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' />
-          </Button>
-          <Button onClick={this.handleClick} ref={this.setRef}>
-            <FormattedMessage id='confirmations.mute.confirm' defaultMessage='Mute' />
-          </Button>
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/onboarding_modal.js b/app/javascript/themes/glitch/features/ui/components/onboarding_modal.js
deleted file mode 100644
index 58875262e..000000000
--- a/app/javascript/themes/glitch/features/ui/components/onboarding_modal.js
+++ /dev/null
@@ -1,323 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import ReactSwipeableViews from 'react-swipeable-views';
-import classNames from 'classnames';
-import Permalink from 'themes/glitch/components/permalink';
-import ComposeForm from 'themes/glitch/features/compose/components/compose_form';
-import Search from 'themes/glitch/features/compose/components/search';
-import NavigationBar from 'themes/glitch/features/compose/components/navigation_bar';
-import ColumnHeader from './column_header';
-import {
-  List as ImmutableList,
-  Map as ImmutableMap,
-} from 'immutable';
-import { me } from 'themes/glitch/util/initial_state';
-
-const noop = () => { };
-
-const messages = defineMessages({
-  home_title: { id: 'column.home', defaultMessage: 'Home' },
-  notifications_title: { id: 'column.notifications', defaultMessage: 'Notifications' },
-  local_title: { id: 'column.community', defaultMessage: 'Local timeline' },
-  federated_title: { id: 'column.public', defaultMessage: 'Federated timeline' },
-});
-
-const PageOne = ({ acct, domain }) => (
-  <div className='onboarding-modal__page onboarding-modal__page-one'>
-    <div style={{ flex: '0 0 auto' }}>
-      <div className='onboarding-modal__page-one__elephant-friend' />
-    </div>
-
-    <div>
-      <h1><FormattedMessage id='onboarding.page_one.welcome' defaultMessage='Welcome to {domain}!' values={{ domain }} /></h1>
-      <p><FormattedMessage id='onboarding.page_one.federation' defaultMessage='{domain} is an "instance" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.' values={{ domain }} /></p>
-      <p><FormattedMessage id='onboarding.page_one.handle' defaultMessage='You are on {domain}, so your full handle is {handle}' values={{ domain, handle: <strong>@{acct}@{domain}</strong> }} /></p>
-    </div>
-  </div>
-);
-
-PageOne.propTypes = {
-  acct: PropTypes.string.isRequired,
-  domain: PropTypes.string.isRequired,
-};
-
-const PageTwo = ({ myAccount }) => (
-  <div className='onboarding-modal__page onboarding-modal__page-two'>
-    <div className='figure non-interactive'>
-      <div className='pseudo-drawer'>
-        <NavigationBar onClose={noop} account={myAccount} />
-      </div>
-      <ComposeForm
-        text='Awoo! #introductions'
-        suggestions={ImmutableList()}
-        mentionedDomains={[]}
-        spoiler={false}
-        onChange={noop}
-        onSubmit={noop}
-        onPaste={noop}
-        onPickEmoji={noop}
-        onChangeSpoilerText={noop}
-        onClearSuggestions={noop}
-        onFetchSuggestions={noop}
-        onSuggestionSelected={noop}
-        onPrivacyChange={noop}
-        showSearch
-        settings={ImmutableMap.of('side_arm', 'none')}
-      />
-    </div>
-
-    <p><FormattedMessage id='onboarding.page_two.compose' defaultMessage='Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.' /></p>
-  </div>
-);
-
-PageTwo.propTypes = {
-  myAccount: ImmutablePropTypes.map.isRequired,
-};
-
-const PageThree = ({ myAccount }) => (
-  <div className='onboarding-modal__page onboarding-modal__page-three'>
-    <div className='figure non-interactive'>
-      <Search
-        value=''
-        onChange={noop}
-        onSubmit={noop}
-        onClear={noop}
-        onShow={noop}
-      />
-
-      <div className='pseudo-drawer'>
-        <NavigationBar onClose={noop} account={myAccount} />
-      </div>
-    </div>
-
-    <p><FormattedMessage id='onboarding.page_three.search' defaultMessage='Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.' values={{ illustration: <Permalink to='/timelines/tag/illustration' href='/tags/illustration'>#illustration</Permalink>, introductions: <Permalink to='/timelines/tag/introductions' href='/tags/introductions'>#introductions</Permalink> }} /></p>
-    <p><FormattedMessage id='onboarding.page_three.profile' defaultMessage='Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.' /></p>
-  </div>
-);
-
-PageThree.propTypes = {
-  myAccount: ImmutablePropTypes.map.isRequired,
-};
-
-const PageFour = ({ domain, intl }) => (
-  <div className='onboarding-modal__page onboarding-modal__page-four'>
-    <div className='onboarding-modal__page-four__columns'>
-      <div className='row'>
-        <div>
-          <div className='figure non-interactive'><ColumnHeader icon='home' type={intl.formatMessage(messages.home_title)} /></div>
-          <p><FormattedMessage id='onboarding.page_four.home' defaultMessage='The home timeline shows posts from people you follow.' /></p>
-        </div>
-
-        <div>
-          <div className='figure non-interactive'><ColumnHeader icon='bell' type={intl.formatMessage(messages.notifications_title)} /></div>
-          <p><FormattedMessage id='onboarding.page_four.notifications' defaultMessage='The notifications column shows when someone interacts with you.' /></p>
-        </div>
-      </div>
-
-      <div className='row'>
-        <div>
-          <div className='figure non-interactive' style={{ marginBottom: 0 }}><ColumnHeader icon='users' type={intl.formatMessage(messages.local_title)} /></div>
-        </div>
-
-        <div>
-          <div className='figure non-interactive' style={{ marginBottom: 0 }}><ColumnHeader icon='globe' type={intl.formatMessage(messages.federated_title)} /></div>
-        </div>
-      </div>
-
-      <p><FormattedMessage id='onboarding.page_five.public_timelines' defaultMessage='The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.' values={{ domain }} /></p>
-    </div>
-  </div>
-);
-
-PageFour.propTypes = {
-  domain: PropTypes.string.isRequired,
-  intl: PropTypes.object.isRequired,
-};
-
-const PageSix = ({ admin, domain }) => {
-  let adminSection = '';
-
-  if (admin) {
-    adminSection = (
-      <p>
-        <FormattedMessage id='onboarding.page_six.admin' defaultMessage="Your instance's admin is {admin}." values={{ admin: <Permalink href={admin.get('url')} to={`/accounts/${admin.get('id')}`}>@{admin.get('acct')}</Permalink> }} />
-        <br />
-        <FormattedMessage id='onboarding.page_six.read_guidelines' defaultMessage="Please read {domain}'s {guidelines}!" values={{ domain, guidelines: <a href='/about/more' target='_blank'><FormattedMessage id='onboarding.page_six.guidelines' defaultMessage='community guidelines' /></a> }} />
-      </p>
-    );
-  }
-
-  return (
-    <div className='onboarding-modal__page onboarding-modal__page-six'>
-      <h1><FormattedMessage id='onboarding.page_six.almost_done' defaultMessage='Almost done...' /></h1>
-      {adminSection}
-      <p><FormattedMessage id='onboarding.page_six.github' defaultMessage='{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.' values={{ domain, fork: <a href='https://en.wikipedia.org/wiki/Fork_(software_development)' target='_blank' rel='noopener'>fork</a>, Mastodon: <a href='https://github.com/tootsuite/mastodon' target='_blank' rel='noopener'>Mastodon</a>, github: <a href='https://github.com/glitch-soc/mastodon' target='_blank' rel='noopener'>GitHub</a> }} /></p>
-      <p><FormattedMessage id='onboarding.page_six.apps_available' defaultMessage='There are {apps} available for iOS, Android and other platforms.' values={{ domain, apps: <a href='https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md' target='_blank' rel='noopener'><FormattedMessage id='onboarding.page_six.various_app' defaultMessage='mobile apps' /></a> }} /></p>
-      <p><em><FormattedMessage id='onboarding.page_six.appetoot' defaultMessage='Bon Appetoot!' /></em></p>
-    </div>
-  );
-};
-
-PageSix.propTypes = {
-  admin: ImmutablePropTypes.map,
-  domain: PropTypes.string.isRequired,
-};
-
-const mapStateToProps = state => ({
-  myAccount: state.getIn(['accounts', me]),
-  admin: state.getIn(['accounts', state.getIn(['meta', 'admin'])]),
-  domain: state.getIn(['meta', 'domain']),
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class OnboardingModal extends React.PureComponent {
-
-  static propTypes = {
-    onClose: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-    myAccount: ImmutablePropTypes.map.isRequired,
-    domain: PropTypes.string.isRequired,
-    admin: ImmutablePropTypes.map,
-  };
-
-  state = {
-    currentIndex: 0,
-  };
-
-  componentWillMount() {
-    const { myAccount, admin, domain, intl } = this.props;
-    this.pages = [
-      <PageOne acct={myAccount.get('acct')} domain={domain} />,
-      <PageTwo myAccount={myAccount} />,
-      <PageThree myAccount={myAccount} />,
-      <PageFour domain={domain} intl={intl} />,
-      <PageSix admin={admin} domain={domain} />,
-    ];
-  };
-
-  componentDidMount() {
-    window.addEventListener('keyup', this.handleKeyUp);
-  }
-
-  componentWillUnmount() {
-    window.addEventListener('keyup', this.handleKeyUp);
-  }
-
-  handleSkip = (e) => {
-    e.preventDefault();
-    this.props.onClose();
-  }
-
-  handleDot = (e) => {
-    const i = Number(e.currentTarget.getAttribute('data-index'));
-    e.preventDefault();
-    this.setState({ currentIndex: i });
-  }
-
-  handlePrev = () => {
-    this.setState(({ currentIndex }) => ({
-      currentIndex: Math.max(0, currentIndex - 1),
-    }));
-  }
-
-  handleNext = () => {
-    const { pages } = this;
-    this.setState(({ currentIndex }) => ({
-      currentIndex: Math.min(currentIndex + 1, pages.length - 1),
-    }));
-  }
-
-  handleSwipe = (index) => {
-    this.setState({ currentIndex: index });
-  }
-
-  handleKeyUp = ({ key }) => {
-    switch (key) {
-    case 'ArrowLeft':
-      this.handlePrev();
-      break;
-    case 'ArrowRight':
-      this.handleNext();
-      break;
-    }
-  }
-
-  handleClose = () => {
-    this.props.onClose();
-  }
-
-  render () {
-    const { pages } = this;
-    const { currentIndex } = this.state;
-    const hasMore = currentIndex < pages.length - 1;
-
-    const nextOrDoneBtn = hasMore ? (
-      <button
-        onClick={this.handleNext}
-        className='onboarding-modal__nav onboarding-modal__next'
-      >
-        <FormattedMessage id='onboarding.next' defaultMessage='Next' />
-      </button>
-    ) : (
-      <button
-        onClick={this.handleClose}
-        className='onboarding-modal__nav onboarding-modal__done'
-      >
-        <FormattedMessage id='onboarding.done' defaultMessage='Done' />
-      </button>
-    );
-
-    return (
-      <div className='modal-root__modal onboarding-modal'>
-        <ReactSwipeableViews index={currentIndex} onChangeIndex={this.handleSwipe} className='onboarding-modal__pager'>
-          {pages.map((page, i) => {
-            const className = classNames('onboarding-modal__page__wrapper', {
-              'onboarding-modal__page__wrapper--active': i === currentIndex,
-            });
-            return (
-              <div key={i} className={className}>{page}</div>
-            );
-          })}
-        </ReactSwipeableViews>
-
-        <div className='onboarding-modal__paginator'>
-          <div>
-            <button
-              onClick={this.handleSkip}
-              className='onboarding-modal__nav onboarding-modal__skip'
-            >
-              <FormattedMessage id='onboarding.skip' defaultMessage='Skip' />
-            </button>
-          </div>
-
-          <div className='onboarding-modal__dots'>
-            {pages.map((_, i) => {
-              const className = classNames('onboarding-modal__dot', {
-                active: i === currentIndex,
-              });
-              return (
-                <div
-                  key={`dot-${i}`}
-                  role='button'
-                  tabIndex='0'
-                  data-index={i}
-                  onClick={this.handleDot}
-                  className={className}
-                />
-              );
-            })}
-          </div>
-
-          <div>
-            {nextOrDoneBtn}
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/report_modal.js b/app/javascript/themes/glitch/features/ui/components/report_modal.js
deleted file mode 100644
index e6153948e..000000000
--- a/app/javascript/themes/glitch/features/ui/components/report_modal.js
+++ /dev/null
@@ -1,105 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import { changeReportComment, submitReport } from 'themes/glitch/actions/reports';
-import { refreshAccountTimeline } from 'themes/glitch/actions/timelines';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { makeGetAccount } from 'themes/glitch/selectors';
-import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
-import StatusCheckBox from 'themes/glitch/features/report/containers/status_check_box_container';
-import { OrderedSet } from 'immutable';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import Button from 'themes/glitch/components/button';
-
-const messages = defineMessages({
-  placeholder: { id: 'report.placeholder', defaultMessage: 'Additional comments' },
-  submit: { id: 'report.submit', defaultMessage: 'Submit' },
-});
-
-const makeMapStateToProps = () => {
-  const getAccount = makeGetAccount();
-
-  const mapStateToProps = state => {
-    const accountId = state.getIn(['reports', 'new', 'account_id']);
-
-    return {
-      isSubmitting: state.getIn(['reports', 'new', 'isSubmitting']),
-      account: getAccount(state, accountId),
-      comment: state.getIn(['reports', 'new', 'comment']),
-      statusIds: OrderedSet(state.getIn(['timelines', `account:${accountId}`, 'items'])).union(state.getIn(['reports', 'new', 'status_ids'])),
-    };
-  };
-
-  return mapStateToProps;
-};
-
-@connect(makeMapStateToProps)
-@injectIntl
-export default class ReportModal extends ImmutablePureComponent {
-
-  static propTypes = {
-    isSubmitting: PropTypes.bool,
-    account: ImmutablePropTypes.map,
-    statusIds: ImmutablePropTypes.orderedSet.isRequired,
-    comment: PropTypes.string.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  handleCommentChange = (e) => {
-    this.props.dispatch(changeReportComment(e.target.value));
-  }
-
-  handleSubmit = () => {
-    this.props.dispatch(submitReport());
-  }
-
-  componentDidMount () {
-    this.props.dispatch(refreshAccountTimeline(this.props.account.get('id')));
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (this.props.account !== nextProps.account && nextProps.account) {
-      this.props.dispatch(refreshAccountTimeline(nextProps.account.get('id')));
-    }
-  }
-
-  render () {
-    const { account, comment, intl, statusIds, isSubmitting } = this.props;
-
-    if (!account) {
-      return null;
-    }
-
-    return (
-      <div className='modal-root__modal report-modal'>
-        <div className='report-modal__target'>
-          <FormattedMessage id='report.target' defaultMessage='Report {target}' values={{ target: <strong>{account.get('acct')}</strong> }} />
-        </div>
-
-        <div className='report-modal__container'>
-          <div className='report-modal__statuses'>
-            <div>
-              {statusIds.map(statusId => <StatusCheckBox id={statusId} key={statusId} disabled={isSubmitting} />)}
-            </div>
-          </div>
-
-          <div className='report-modal__comment'>
-            <textarea
-              className='setting-text light'
-              placeholder={intl.formatMessage(messages.placeholder)}
-              value={comment}
-              onChange={this.handleCommentChange}
-              disabled={isSubmitting}
-            />
-          </div>
-        </div>
-
-        <div className='report-modal__action-bar'>
-          <Button disabled={isSubmitting} text={intl.formatMessage(messages.submit)} onClick={this.handleSubmit} />
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/tabs_bar.js b/app/javascript/themes/glitch/features/ui/components/tabs_bar.js
deleted file mode 100644
index ef5deae99..000000000
--- a/app/javascript/themes/glitch/features/ui/components/tabs_bar.js
+++ /dev/null
@@ -1,84 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { NavLink } from 'react-router-dom';
-import { FormattedMessage, injectIntl } from 'react-intl';
-import { debounce } from 'lodash';
-import { isUserTouching } from 'themes/glitch/util/is_mobile';
-
-export const links = [
-  <NavLink className='tabs-bar__link primary' to='/statuses/new' data-preview-title-id='tabs_bar.compose' data-preview-icon='pencil' ><i className='fa fa-fw fa-pencil' /><FormattedMessage id='tabs_bar.compose' defaultMessage='Compose' /></NavLink>,
-  <NavLink className='tabs-bar__link primary' to='/timelines/home' data-preview-title-id='column.home' data-preview-icon='home' ><i className='fa fa-fw fa-home' /><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></NavLink>,
-  <NavLink className='tabs-bar__link primary' to='/notifications' data-preview-title-id='column.notifications' data-preview-icon='bell' ><i className='fa fa-fw fa-bell' /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></NavLink>,
-
-  <NavLink className='tabs-bar__link secondary' to='/timelines/public/local' data-preview-title-id='column.community' data-preview-icon='users' ><i className='fa fa-fw fa-users' /><FormattedMessage id='tabs_bar.local_timeline' defaultMessage='Local' /></NavLink>,
-  <NavLink className='tabs-bar__link secondary' exact to='/timelines/public' data-preview-title-id='column.public' data-preview-icon='globe' ><i className='fa fa-fw fa-globe' /><FormattedMessage id='tabs_bar.federated_timeline' defaultMessage='Federated' /></NavLink>,
-
-  <NavLink className='tabs-bar__link primary' style={{ flexGrow: '0', flexBasis: '30px' }} to='/getting-started' data-preview-title-id='getting_started.heading' data-preview-icon='asterisk' ><i className='fa fa-fw fa-asterisk' /></NavLink>,
-];
-
-export function getIndex (path) {
-  return links.findIndex(link => link.props.to === path);
-}
-
-export function getLink (index) {
-  return links[index].props.to;
-}
-
-@injectIntl
-export default class TabsBar extends React.Component {
-
-  static contextTypes = {
-    router: PropTypes.object.isRequired,
-  }
-
-  static propTypes = {
-    intl: PropTypes.object.isRequired,
-  }
-
-  setRef = ref => {
-    this.node = ref;
-  }
-
-  handleClick = (e) => {
-    // Only apply optimization for touch devices, which we assume are slower
-    // We thus avoid the 250ms delay for non-touch devices and the lag for touch devices
-    if (isUserTouching()) {
-      e.preventDefault();
-      e.persist();
-
-      requestAnimationFrame(() => {
-        const tabs = Array(...this.node.querySelectorAll('.tabs-bar__link'));
-        const currentTab = tabs.find(tab => tab.classList.contains('active'));
-        const nextTab = tabs.find(tab => tab.contains(e.target));
-        const { props: { to } } = links[Array(...this.node.childNodes).indexOf(nextTab)];
-
-
-        if (currentTab !== nextTab) {
-          if (currentTab) {
-            currentTab.classList.remove('active');
-          }
-
-          const listener = debounce(() => {
-            nextTab.removeEventListener('transitionend', listener);
-            this.context.router.history.push(to);
-          }, 50);
-
-          nextTab.addEventListener('transitionend', listener);
-          nextTab.classList.add('active');
-        }
-      });
-    }
-
-  }
-
-  render () {
-    const { intl: { formatMessage } } = this.props;
-
-    return (
-      <nav className='tabs-bar' ref={this.setRef}>
-        {links.map(link => React.cloneElement(link, { key: link.props.to, onClick: this.handleClick, 'aria-label': formatMessage({ id: link.props['data-preview-title-id'] }) }))}
-      </nav>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/upload_area.js b/app/javascript/themes/glitch/features/ui/components/upload_area.js
deleted file mode 100644
index 72a450215..000000000
--- a/app/javascript/themes/glitch/features/ui/components/upload_area.js
+++ /dev/null
@@ -1,52 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Motion from 'themes/glitch/util/optional_motion';
-import spring from 'react-motion/lib/spring';
-import { FormattedMessage } from 'react-intl';
-
-export default class UploadArea extends React.PureComponent {
-
-  static propTypes = {
-    active: PropTypes.bool,
-    onClose: PropTypes.func,
-  };
-
-  handleKeyUp = (e) => {
-    const keyCode = e.keyCode;
-    if (this.props.active) {
-      switch(keyCode) {
-      case 27:
-        e.preventDefault();
-        e.stopPropagation();
-        this.props.onClose();
-        break;
-      }
-    }
-  }
-
-  componentDidMount () {
-    window.addEventListener('keyup', this.handleKeyUp, false);
-  }
-
-  componentWillUnmount () {
-    window.removeEventListener('keyup', this.handleKeyUp);
-  }
-
-  render () {
-    const { active } = this.props;
-
-    return (
-      <Motion defaultStyle={{ backgroundOpacity: 0, backgroundScale: 0.95 }} style={{ backgroundOpacity: spring(active ? 1 : 0, { stiffness: 150, damping: 15 }), backgroundScale: spring(active ? 1 : 0.95, { stiffness: 200, damping: 3 }) }}>
-        {({ backgroundOpacity, backgroundScale }) =>
-          <div className='upload-area' style={{ visibility: active ? 'visible' : 'hidden', opacity: backgroundOpacity }}>
-            <div className='upload-area__drop'>
-              <div className='upload-area__background' style={{ transform: `scale(${backgroundScale})` }} />
-              <div className='upload-area__content'><FormattedMessage id='upload_area.title' defaultMessage='Drag & drop to upload' /></div>
-            </div>
-          </div>
-        }
-      </Motion>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/video_modal.js b/app/javascript/themes/glitch/features/ui/components/video_modal.js
deleted file mode 100644
index 91168c790..000000000
--- a/app/javascript/themes/glitch/features/ui/components/video_modal.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import Video from 'themes/glitch/features/video';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-export default class VideoModal extends ImmutablePureComponent {
-
-  static propTypes = {
-    media: ImmutablePropTypes.map.isRequired,
-    time: PropTypes.number,
-    onClose: PropTypes.func.isRequired,
-  };
-
-  render () {
-    const { media, time, onClose } = this.props;
-
-    return (
-      <div className='modal-root__modal media-modal'>
-        <div>
-          <Video
-            preview={media.get('preview_url')}
-            src={media.get('url')}
-            startTime={time}
-            onCloseVideo={onClose}
-            description={media.get('description')}
-          />
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/containers/bundle_container.js b/app/javascript/themes/glitch/features/ui/containers/bundle_container.js
deleted file mode 100644
index e6f9afcf7..000000000
--- a/app/javascript/themes/glitch/features/ui/containers/bundle_container.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import { connect } from 'react-redux';
-
-import Bundle from '../components/bundle';
-
-import { fetchBundleRequest, fetchBundleSuccess, fetchBundleFail } from 'themes/glitch/actions/bundles';
-
-const mapDispatchToProps = dispatch => ({
-  onFetch () {
-    dispatch(fetchBundleRequest());
-  },
-  onFetchSuccess () {
-    dispatch(fetchBundleSuccess());
-  },
-  onFetchFail (error) {
-    dispatch(fetchBundleFail(error));
-  },
-});
-
-export default connect(null, mapDispatchToProps)(Bundle);
diff --git a/app/javascript/themes/glitch/features/ui/containers/columns_area_container.js b/app/javascript/themes/glitch/features/ui/containers/columns_area_container.js
deleted file mode 100644
index 95f95618b..000000000
--- a/app/javascript/themes/glitch/features/ui/containers/columns_area_container.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import { connect } from 'react-redux';
-import ColumnsArea from '../components/columns_area';
-
-const mapStateToProps = state => ({
-  columns: state.getIn(['settings', 'columns']),
-});
-
-export default connect(mapStateToProps, null, null, { withRef: true })(ColumnsArea);
diff --git a/app/javascript/themes/glitch/features/ui/containers/loading_bar_container.js b/app/javascript/themes/glitch/features/ui/containers/loading_bar_container.js
deleted file mode 100644
index 4bb90fb68..000000000
--- a/app/javascript/themes/glitch/features/ui/containers/loading_bar_container.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import { connect }    from 'react-redux';
-import LoadingBar from 'react-redux-loading-bar';
-
-const mapStateToProps = (state) => ({
-  loading: state.get('loadingBar'),
-});
-
-export default connect(mapStateToProps)(LoadingBar.WrappedComponent);
diff --git a/app/javascript/themes/glitch/features/ui/containers/modal_container.js b/app/javascript/themes/glitch/features/ui/containers/modal_container.js
deleted file mode 100644
index c26f19886..000000000
--- a/app/javascript/themes/glitch/features/ui/containers/modal_container.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { connect } from 'react-redux';
-import { closeModal } from 'themes/glitch/actions/modal';
-import ModalRoot from '../components/modal_root';
-
-const mapStateToProps = state => ({
-  type: state.get('modal').modalType,
-  props: state.get('modal').modalProps,
-});
-
-const mapDispatchToProps = dispatch => ({
-  onClose () {
-    dispatch(closeModal());
-  },
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(ModalRoot);
diff --git a/app/javascript/themes/glitch/features/ui/containers/notifications_container.js b/app/javascript/themes/glitch/features/ui/containers/notifications_container.js
deleted file mode 100644
index 5bd4017f5..000000000
--- a/app/javascript/themes/glitch/features/ui/containers/notifications_container.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { connect } from 'react-redux';
-import { NotificationStack } from 'react-notification';
-import { dismissAlert } from 'themes/glitch/actions/alerts';
-import { getAlerts } from 'themes/glitch/selectors';
-
-const mapStateToProps = state => ({
-  notifications: getAlerts(state),
-});
-
-const mapDispatchToProps = (dispatch) => {
-  return {
-    onDismiss: alert => {
-      dispatch(dismissAlert(alert));
-    },
-  };
-};
-
-export default connect(mapStateToProps, mapDispatchToProps)(NotificationStack);
diff --git a/app/javascript/themes/glitch/features/ui/containers/status_list_container.js b/app/javascript/themes/glitch/features/ui/containers/status_list_container.js
deleted file mode 100644
index 807c82e16..000000000
--- a/app/javascript/themes/glitch/features/ui/containers/status_list_container.js
+++ /dev/null
@@ -1,73 +0,0 @@
-import { connect } from 'react-redux';
-import StatusList from 'themes/glitch/components/status_list';
-import { scrollTopTimeline } from 'themes/glitch/actions/timelines';
-import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
-import { createSelector } from 'reselect';
-import { debounce } from 'lodash';
-import { me } from 'themes/glitch/util/initial_state';
-
-const makeGetStatusIds = () => createSelector([
-  (state, { type }) => state.getIn(['settings', type], ImmutableMap()),
-  (state, { type }) => state.getIn(['timelines', type, 'items'], ImmutableList()),
-  (state)           => state.get('statuses'),
-], (columnSettings, statusIds, statuses) => {
-  const rawRegex = columnSettings.getIn(['regex', 'body'], '').trim();
-  let regex      = null;
-
-  try {
-    regex = rawRegex && new RegExp(rawRegex, 'i');
-  } catch (e) {
-    // Bad regex, don't affect filters
-  }
-
-  return statusIds.filter(id => {
-    const statusForId = statuses.get(id);
-    let showStatus    = true;
-
-    if (columnSettings.getIn(['shows', 'reblog']) === false) {
-      showStatus = showStatus && statusForId.get('reblog') === null;
-    }
-
-    if (columnSettings.getIn(['shows', 'reply']) === false) {
-      showStatus = showStatus && (statusForId.get('in_reply_to_id') === null || statusForId.get('in_reply_to_account_id') === me);
-    }
-
-    if (showStatus && regex && statusForId.get('account') !== me) {
-      const searchIndex = statusForId.get('reblog') ? statuses.getIn([statusForId.get('reblog'), 'search_index']) : statusForId.get('search_index');
-      showStatus = !regex.test(searchIndex);
-    }
-
-    return showStatus;
-  });
-});
-
-const makeMapStateToProps = () => {
-  const getStatusIds = makeGetStatusIds();
-
-  const mapStateToProps = (state, { timelineId }) => ({
-    statusIds: getStatusIds(state, { type: timelineId }),
-    isLoading: state.getIn(['timelines', timelineId, 'isLoading'], true),
-    hasMore: !!state.getIn(['timelines', timelineId, 'next']),
-  });
-
-  return mapStateToProps;
-};
-
-const mapDispatchToProps = (dispatch, { timelineId, loadMore }) => ({
-
-  onScrollToBottom: debounce(() => {
-    dispatch(scrollTopTimeline(timelineId, false));
-    loadMore();
-  }, 300, { leading: true }),
-
-  onScrollToTop: debounce(() => {
-    dispatch(scrollTopTimeline(timelineId, true));
-  }, 100),
-
-  onScroll: debounce(() => {
-    dispatch(scrollTopTimeline(timelineId, false));
-  }, 100),
-
-});
-
-export default connect(makeMapStateToProps, mapDispatchToProps)(StatusList);
diff --git a/app/javascript/themes/glitch/features/ui/index.js b/app/javascript/themes/glitch/features/ui/index.js
deleted file mode 100644
index 3eea63189..000000000
--- a/app/javascript/themes/glitch/features/ui/index.js
+++ /dev/null
@@ -1,443 +0,0 @@
-import React from 'react';
-import NotificationsContainer from './containers/notifications_container';
-import PropTypes from 'prop-types';
-import LoadingBarContainer from './containers/loading_bar_container';
-import TabsBar from './components/tabs_bar';
-import ModalContainer from './containers/modal_container';
-import { connect } from 'react-redux';
-import { Redirect, withRouter } from 'react-router-dom';
-import { isMobile } from 'themes/glitch/util/is_mobile';
-import { debounce } from 'lodash';
-import { uploadCompose, resetCompose } from 'themes/glitch/actions/compose';
-import { refreshHomeTimeline } from 'themes/glitch/actions/timelines';
-import { refreshNotifications } from 'themes/glitch/actions/notifications';
-import { clearHeight } from 'themes/glitch/actions/height_cache';
-import { WrappedSwitch, WrappedRoute } from 'themes/glitch/util/react_router_helpers';
-import UploadArea from './components/upload_area';
-import ColumnsAreaContainer from './containers/columns_area_container';
-import classNames from 'classnames';
-import {
-  Compose,
-  Status,
-  GettingStarted,
-  PublicTimeline,
-  CommunityTimeline,
-  AccountTimeline,
-  AccountGallery,
-  HomeTimeline,
-  Followers,
-  Following,
-  Reblogs,
-  Favourites,
-  DirectTimeline,
-  HashtagTimeline,
-  Notifications,
-  FollowRequests,
-  GenericNotFound,
-  FavouritedStatuses,
-  Blocks,
-  Mutes,
-  PinnedStatuses,
-} from 'themes/glitch/util/async-components';
-import { HotKeys } from 'react-hotkeys';
-import { me } from 'themes/glitch/util/initial_state';
-import { defineMessages, injectIntl } from 'react-intl';
-
-// Dummy import, to make sure that <Status /> ends up in the application bundle.
-// Without this it ends up in ~8 very commonly used bundles.
-import '../../../glitch/components/status';
-
-const messages = defineMessages({
-  beforeUnload: { id: 'ui.beforeunload', defaultMessage: 'Your draft will be lost if you leave Mastodon.' },
-});
-
-const mapStateToProps = state => ({
-  isComposing: state.getIn(['compose', 'is_composing']),
-  hasComposingText: state.getIn(['compose', 'text']) !== '',
-  layout: state.getIn(['local_settings', 'layout']),
-  isWide: state.getIn(['local_settings', 'stretch']),
-  navbarUnder: state.getIn(['local_settings', 'navbar_under']),
-});
-
-const keyMap = {
-  new: 'n',
-  search: 's',
-  forceNew: 'option+n',
-  focusColumn: ['1', '2', '3', '4', '5', '6', '7', '8', '9'],
-  reply: 'r',
-  favourite: 'f',
-  boost: 'b',
-  mention: 'm',
-  open: ['enter', 'o'],
-  openProfile: 'p',
-  moveDown: ['down', 'j'],
-  moveUp: ['up', 'k'],
-  back: 'backspace',
-  goToHome: 'g h',
-  goToNotifications: 'g n',
-  goToLocal: 'g l',
-  goToFederated: 'g t',
-  goToDirect: 'g d',
-  goToStart: 'g s',
-  goToFavourites: 'g f',
-  goToPinned: 'g p',
-  goToProfile: 'g u',
-  goToBlocked: 'g b',
-  goToMuted: 'g m',
-  toggleSpoiler: 'x',
-};
-
-@connect(mapStateToProps)
-@injectIntl
-@withRouter
-export default class UI extends React.Component {
-
-  static contextTypes = {
-    router: PropTypes.object.isRequired,
-  };
-
-  static propTypes = {
-    dispatch: PropTypes.func.isRequired,
-    children: PropTypes.node,
-    layout: PropTypes.string,
-    isWide: PropTypes.bool,
-    systemFontUi: PropTypes.bool,
-    navbarUnder: PropTypes.bool,
-    isComposing: PropTypes.bool,
-    hasComposingText: PropTypes.bool,
-    location: PropTypes.object,
-    intl: PropTypes.object.isRequired,
-  };
-
-  state = {
-    width: window.innerWidth,
-    draggingOver: false,
-  };
-
-  handleBeforeUnload = (e) => {
-    const { intl, isComposing, hasComposingText } = this.props;
-
-    if (isComposing && hasComposingText) {
-      // Setting returnValue to any string causes confirmation dialog.
-      // Many browsers no longer display this text to users,
-      // but we set user-friendly message for other browsers, e.g. Edge.
-      e.returnValue = intl.formatMessage(messages.beforeUnload);
-    }
-  }
-
-  handleResize = debounce(() => {
-    // The cached heights are no longer accurate, invalidate
-    this.props.dispatch(clearHeight());
-
-    this.setState({ width: window.innerWidth });
-  }, 500, {
-    trailing: true,
-  });
-
-  handleDragEnter = (e) => {
-    e.preventDefault();
-
-    if (!this.dragTargets) {
-      this.dragTargets = [];
-    }
-
-    if (this.dragTargets.indexOf(e.target) === -1) {
-      this.dragTargets.push(e.target);
-    }
-
-    if (e.dataTransfer && e.dataTransfer.types.includes('Files')) {
-      this.setState({ draggingOver: true });
-    }
-  }
-
-  handleDragOver = (e) => {
-    e.preventDefault();
-    e.stopPropagation();
-
-    try {
-      e.dataTransfer.dropEffect = 'copy';
-    } catch (err) {
-
-    }
-
-    return false;
-  }
-
-  handleDrop = (e) => {
-    e.preventDefault();
-
-    this.setState({ draggingOver: false });
-
-    if (e.dataTransfer && e.dataTransfer.files.length === 1) {
-      this.props.dispatch(uploadCompose(e.dataTransfer.files));
-    }
-  }
-
-  handleDragLeave = (e) => {
-    e.preventDefault();
-    e.stopPropagation();
-
-    this.dragTargets = this.dragTargets.filter(el => el !== e.target && this.node.contains(el));
-
-    if (this.dragTargets.length > 0) {
-      return;
-    }
-
-    this.setState({ draggingOver: false });
-  }
-
-  closeUploadModal = () => {
-    this.setState({ draggingOver: false });
-  }
-
-  handleServiceWorkerPostMessage = ({ data }) => {
-    if (data.type === 'navigate') {
-      this.context.router.history.push(data.path);
-    } else {
-      console.warn('Unknown message type:', data.type);
-    }
-  }
-
-  componentWillMount () {
-    window.addEventListener('beforeunload', this.handleBeforeUnload, false);
-    window.addEventListener('resize', this.handleResize, { passive: true });
-    document.addEventListener('dragenter', this.handleDragEnter, false);
-    document.addEventListener('dragover', this.handleDragOver, false);
-    document.addEventListener('drop', this.handleDrop, false);
-    document.addEventListener('dragleave', this.handleDragLeave, false);
-    document.addEventListener('dragend', this.handleDragEnd, false);
-
-    if ('serviceWorker' in  navigator) {
-      navigator.serviceWorker.addEventListener('message', this.handleServiceWorkerPostMessage);
-    }
-
-    this.props.dispatch(refreshHomeTimeline());
-    this.props.dispatch(refreshNotifications());
-  }
-
-  componentDidMount () {
-    this.hotkeys.__mousetrap__.stopCallback = (e, element) => {
-      return ['TEXTAREA', 'SELECT', 'INPUT'].includes(element.tagName);
-    };
-  }
-
-  shouldComponentUpdate (nextProps) {
-    if (nextProps.isComposing !== this.props.isComposing) {
-      // Avoid expensive update just to toggle a class
-      this.node.classList.toggle('is-composing', nextProps.isComposing);
-      this.node.classList.toggle('navbar-under', nextProps.navbarUnder);
-
-      return false;
-    }
-
-    // Why isn't this working?!?
-    // return super.shouldComponentUpdate(nextProps, nextState);
-    return true;
-  }
-
-  componentDidUpdate (prevProps) {
-    if (![this.props.location.pathname, '/'].includes(prevProps.location.pathname)) {
-      this.columnsAreaNode.handleChildrenContentChange();
-    }
-  }
-
-  componentWillUnmount () {
-    window.removeEventListener('beforeunload', this.handleBeforeUnload);
-    window.removeEventListener('resize', this.handleResize);
-    document.removeEventListener('dragenter', this.handleDragEnter);
-    document.removeEventListener('dragover', this.handleDragOver);
-    document.removeEventListener('drop', this.handleDrop);
-    document.removeEventListener('dragleave', this.handleDragLeave);
-    document.removeEventListener('dragend', this.handleDragEnd);
-  }
-
-  setRef = c => {
-    this.node = c;
-  }
-
-  setColumnsAreaRef = c => {
-    this.columnsAreaNode = c.getWrappedInstance().getWrappedInstance();
-  }
-
-  handleHotkeyNew = e => {
-    e.preventDefault();
-
-    const element = this.node.querySelector('.compose-form__autosuggest-wrapper textarea');
-
-    if (element) {
-      element.focus();
-    }
-  }
-
-  handleHotkeySearch = e => {
-    e.preventDefault();
-
-    const element = this.node.querySelector('.search__input');
-
-    if (element) {
-      element.focus();
-    }
-  }
-
-  handleHotkeyForceNew = e => {
-    this.handleHotkeyNew(e);
-    this.props.dispatch(resetCompose());
-  }
-
-  handleHotkeyFocusColumn = e => {
-    const index  = (e.key * 1) + 1; // First child is drawer, skip that
-    const column = this.node.querySelector(`.column:nth-child(${index})`);
-
-    if (column) {
-      const status = column.querySelector('.focusable');
-
-      if (status) {
-        status.focus();
-      }
-    }
-  }
-
-  handleHotkeyBack = () => {
-    if (window.history && window.history.length === 1) {
-      this.context.router.history.push('/');
-    } else {
-      this.context.router.history.goBack();
-    }
-  }
-
-  setHotkeysRef = c => {
-    this.hotkeys = c;
-  }
-
-  handleHotkeyGoToHome = () => {
-    this.context.router.history.push('/timelines/home');
-  }
-
-  handleHotkeyGoToNotifications = () => {
-    this.context.router.history.push('/notifications');
-  }
-
-  handleHotkeyGoToLocal = () => {
-    this.context.router.history.push('/timelines/public/local');
-  }
-
-  handleHotkeyGoToFederated = () => {
-    this.context.router.history.push('/timelines/public');
-  }
-
-  handleHotkeyGoToDirect = () => {
-    this.context.router.history.push('/timelines/direct');
-  }
-
-  handleHotkeyGoToStart = () => {
-    this.context.router.history.push('/getting-started');
-  }
-
-  handleHotkeyGoToFavourites = () => {
-    this.context.router.history.push('/favourites');
-  }
-
-  handleHotkeyGoToPinned = () => {
-    this.context.router.history.push('/pinned');
-  }
-
-  handleHotkeyGoToProfile = () => {
-    this.context.router.history.push(`/accounts/${me}`);
-  }
-
-  handleHotkeyGoToBlocked = () => {
-    this.context.router.history.push('/blocks');
-  }
-
-  handleHotkeyGoToMuted = () => {
-    this.context.router.history.push('/mutes');
-  }
-
-  render () {
-    const { width, draggingOver } = this.state;
-    const { children, layout, isWide, navbarUnder } = this.props;
-
-    const columnsClass = layout => {
-      switch (layout) {
-      case 'single':
-        return 'single-column';
-      case 'multiple':
-        return 'multi-columns';
-      default:
-        return 'auto-columns';
-      }
-    };
-
-    const className = classNames('ui', columnsClass(layout), {
-      'wide': isWide,
-      'system-font': this.props.systemFontUi,
-      'navbar-under': navbarUnder,
-    });
-
-    const handlers = {
-      new: this.handleHotkeyNew,
-      search: this.handleHotkeySearch,
-      forceNew: this.handleHotkeyForceNew,
-      focusColumn: this.handleHotkeyFocusColumn,
-      back: this.handleHotkeyBack,
-      goToHome: this.handleHotkeyGoToHome,
-      goToNotifications: this.handleHotkeyGoToNotifications,
-      goToLocal: this.handleHotkeyGoToLocal,
-      goToFederated: this.handleHotkeyGoToFederated,
-      goToDirect: this.handleHotkeyGoToDirect,
-      goToStart: this.handleHotkeyGoToStart,
-      goToFavourites: this.handleHotkeyGoToFavourites,
-      goToPinned: this.handleHotkeyGoToPinned,
-      goToProfile: this.handleHotkeyGoToProfile,
-      goToBlocked: this.handleHotkeyGoToBlocked,
-      goToMuted: this.handleHotkeyGoToMuted,
-    };
-
-    return (
-      <HotKeys keyMap={keyMap} handlers={handlers} ref={this.setHotkeysRef}>
-        <div className={className} ref={this.setRef}>
-          {navbarUnder ? null : (<TabsBar />)}
-
-          <ColumnsAreaContainer ref={this.setColumnsAreaRef} singleColumn={isMobile(width, layout)}>
-            <WrappedSwitch>
-              <Redirect from='/' to='/getting-started' exact />
-              <WrappedRoute path='/getting-started' component={GettingStarted} content={children} />
-              <WrappedRoute path='/timelines/home' component={HomeTimeline} content={children} />
-              <WrappedRoute path='/timelines/public' exact component={PublicTimeline} content={children} />
-              <WrappedRoute path='/timelines/public/local' component={CommunityTimeline} content={children} />
-              <WrappedRoute path='/timelines/direct' component={DirectTimeline} content={children} />
-              <WrappedRoute path='/timelines/tag/:id' component={HashtagTimeline} content={children} />
-
-              <WrappedRoute path='/notifications' component={Notifications} content={children} />
-              <WrappedRoute path='/favourites' component={FavouritedStatuses} content={children} />
-              <WrappedRoute path='/pinned' component={PinnedStatuses} content={children} />
-
-              <WrappedRoute path='/statuses/new' component={Compose} content={children} />
-              <WrappedRoute path='/statuses/:statusId' exact component={Status} content={children} />
-              <WrappedRoute path='/statuses/:statusId/reblogs' component={Reblogs} content={children} />
-              <WrappedRoute path='/statuses/:statusId/favourites' component={Favourites} content={children} />
-
-              <WrappedRoute path='/accounts/:accountId' exact component={AccountTimeline} content={children} />
-              <WrappedRoute path='/accounts/:accountId/followers' component={Followers} content={children} />
-              <WrappedRoute path='/accounts/:accountId/following' component={Following} content={children} />
-              <WrappedRoute path='/accounts/:accountId/media' component={AccountGallery} content={children} />
-
-              <WrappedRoute path='/follow_requests' component={FollowRequests} content={children} />
-              <WrappedRoute path='/blocks' component={Blocks} content={children} />
-              <WrappedRoute path='/mutes' component={Mutes} content={children} />
-
-              <WrappedRoute component={GenericNotFound} content={children} />
-            </WrappedSwitch>
-          </ColumnsAreaContainer>
-
-          <NotificationsContainer />
-          {navbarUnder ? (<TabsBar />) : null}
-          <LoadingBarContainer className='loading-bar' />
-          <ModalContainer />
-          <UploadArea active={draggingOver} onClose={this.closeUploadModal} />
-        </div>
-      </HotKeys>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/video/index.js b/app/javascript/themes/glitch/features/video/index.js
deleted file mode 100644
index 0ecbe37c9..000000000
--- a/app/javascript/themes/glitch/features/video/index.js
+++ /dev/null
@@ -1,288 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import { throttle } from 'lodash';
-import classNames from 'classnames';
-import { isFullscreen, requestFullscreen, exitFullscreen } from 'themes/glitch/util/fullscreen';
-
-const messages = defineMessages({
-  play: { id: 'video.play', defaultMessage: 'Play' },
-  pause: { id: 'video.pause', defaultMessage: 'Pause' },
-  mute: { id: 'video.mute', defaultMessage: 'Mute sound' },
-  unmute: { id: 'video.unmute', defaultMessage: 'Unmute sound' },
-  hide: { id: 'video.hide', defaultMessage: 'Hide video' },
-  expand: { id: 'video.expand', defaultMessage: 'Expand video' },
-  close: { id: 'video.close', defaultMessage: 'Close video' },
-  fullscreen: { id: 'video.fullscreen', defaultMessage: 'Full screen' },
-  exit_fullscreen: { id: 'video.exit_fullscreen', defaultMessage: 'Exit full screen' },
-});
-
-const findElementPosition = el => {
-  let box;
-
-  if (el.getBoundingClientRect && el.parentNode) {
-    box = el.getBoundingClientRect();
-  }
-
-  if (!box) {
-    return {
-      left: 0,
-      top: 0,
-    };
-  }
-
-  const docEl = document.documentElement;
-  const body  = document.body;
-
-  const clientLeft = docEl.clientLeft || body.clientLeft || 0;
-  const scrollLeft = window.pageXOffset || body.scrollLeft;
-  const left       = (box.left + scrollLeft) - clientLeft;
-
-  const clientTop = docEl.clientTop || body.clientTop || 0;
-  const scrollTop = window.pageYOffset || body.scrollTop;
-  const top       = (box.top + scrollTop) - clientTop;
-
-  return {
-    left: Math.round(left),
-    top: Math.round(top),
-  };
-};
-
-const getPointerPosition = (el, event) => {
-  const position = {};
-  const box = findElementPosition(el);
-  const boxW = el.offsetWidth;
-  const boxH = el.offsetHeight;
-  const boxY = box.top;
-  const boxX = box.left;
-
-  let pageY = event.pageY;
-  let pageX = event.pageX;
-
-  if (event.changedTouches) {
-    pageX = event.changedTouches[0].pageX;
-    pageY = event.changedTouches[0].pageY;
-  }
-
-  position.y = Math.max(0, Math.min(1, ((boxY - pageY) + boxH) / boxH));
-  position.x = Math.max(0, Math.min(1, (pageX - boxX) / boxW));
-
-  return position;
-};
-
-@injectIntl
-export default class Video extends React.PureComponent {
-
-  static propTypes = {
-    preview: PropTypes.string,
-    src: PropTypes.string.isRequired,
-    alt: PropTypes.string,
-    width: PropTypes.number,
-    height: PropTypes.number,
-    sensitive: PropTypes.bool,
-    startTime: PropTypes.number,
-    onOpenVideo: PropTypes.func,
-    onCloseVideo: PropTypes.func,
-    letterbox: PropTypes.bool,
-    fullwidth: PropTypes.bool,
-    intl: PropTypes.object.isRequired,
-  };
-
-  state = {
-    progress: 0,
-    paused: true,
-    dragging: false,
-    fullscreen: false,
-    hovered: false,
-    muted: false,
-    revealed: !this.props.sensitive,
-  };
-
-  setPlayerRef = c => {
-    this.player = c;
-  }
-
-  setVideoRef = c => {
-    this.video = c;
-  }
-
-  setSeekRef = c => {
-    this.seek = c;
-  }
-
-  handlePlay = () => {
-    this.setState({ paused: false });
-  }
-
-  handlePause = () => {
-    this.setState({ paused: true });
-  }
-
-  handleTimeUpdate = () => {
-    this.setState({ progress: 100 * (this.video.currentTime / this.video.duration) });
-  }
-
-  handleMouseDown = e => {
-    document.addEventListener('mousemove', this.handleMouseMove, true);
-    document.addEventListener('mouseup', this.handleMouseUp, true);
-    document.addEventListener('touchmove', this.handleMouseMove, true);
-    document.addEventListener('touchend', this.handleMouseUp, true);
-
-    this.setState({ dragging: true });
-    this.video.pause();
-    this.handleMouseMove(e);
-  }
-
-  handleMouseUp = () => {
-    document.removeEventListener('mousemove', this.handleMouseMove, true);
-    document.removeEventListener('mouseup', this.handleMouseUp, true);
-    document.removeEventListener('touchmove', this.handleMouseMove, true);
-    document.removeEventListener('touchend', this.handleMouseUp, true);
-
-    this.setState({ dragging: false });
-    this.video.play();
-  }
-
-  handleMouseMove = throttle(e => {
-    const { x } = getPointerPosition(this.seek, e);
-    this.video.currentTime = this.video.duration * x;
-    this.setState({ progress: x * 100 });
-  }, 60);
-
-  togglePlay = () => {
-    if (this.state.paused) {
-      this.video.play();
-    } else {
-      this.video.pause();
-    }
-  }
-
-  toggleFullscreen = () => {
-    if (isFullscreen()) {
-      exitFullscreen();
-    } else {
-      requestFullscreen(this.player);
-    }
-  }
-
-  componentDidMount () {
-    document.addEventListener('fullscreenchange', this.handleFullscreenChange, true);
-    document.addEventListener('webkitfullscreenchange', this.handleFullscreenChange, true);
-    document.addEventListener('mozfullscreenchange', this.handleFullscreenChange, true);
-    document.addEventListener('MSFullscreenChange', this.handleFullscreenChange, true);
-  }
-
-  componentWillUnmount () {
-    document.removeEventListener('fullscreenchange', this.handleFullscreenChange, true);
-    document.removeEventListener('webkitfullscreenchange', this.handleFullscreenChange, true);
-    document.removeEventListener('mozfullscreenchange', this.handleFullscreenChange, true);
-    document.removeEventListener('MSFullscreenChange', this.handleFullscreenChange, true);
-  }
-
-  handleFullscreenChange = () => {
-    this.setState({ fullscreen: isFullscreen() });
-  }
-
-  handleMouseEnter = () => {
-    this.setState({ hovered: true });
-  }
-
-  handleMouseLeave = () => {
-    this.setState({ hovered: false });
-  }
-
-  toggleMute = () => {
-    this.video.muted = !this.video.muted;
-    this.setState({ muted: this.video.muted });
-  }
-
-  toggleReveal = () => {
-    if (this.state.revealed) {
-      this.video.pause();
-    }
-
-    this.setState({ revealed: !this.state.revealed });
-  }
-
-  handleLoadedData = () => {
-    if (this.props.startTime) {
-      this.video.currentTime = this.props.startTime;
-      this.video.play();
-    }
-  }
-
-  handleProgress = () => {
-    if (this.video.buffered.length > 0) {
-      this.setState({ buffer: this.video.buffered.end(0) / this.video.duration * 100 });
-    }
-  }
-
-  handleOpenVideo = () => {
-    this.video.pause();
-    this.props.onOpenVideo(this.video.currentTime);
-  }
-
-  handleCloseVideo = () => {
-    this.video.pause();
-    this.props.onCloseVideo();
-  }
-
-  render () {
-    const { preview, src, width, height, startTime, onOpenVideo, onCloseVideo, intl, alt, letterbox, fullwidth } = this.props;
-    const { progress, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
-
-    return (
-      <div className={classNames('video-player', { inactive: !revealed, inline: !fullscreen, fullscreen, letterbox, 'full-width': fullwidth })} ref={this.setPlayerRef} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
-        <video
-          ref={this.setVideoRef}
-          src={src}
-          poster={preview}
-          preload={startTime ? 'auto' : 'none'}
-          loop
-          role='button'
-          tabIndex='0'
-          aria-label={alt}
-          width={width}
-          height={height}
-          onClick={this.togglePlay}
-          onPlay={this.handlePlay}
-          onPause={this.handlePause}
-          onTimeUpdate={this.handleTimeUpdate}
-          onLoadedData={this.handleLoadedData}
-          onProgress={this.handleProgress}
-        />
-
-        <button className={classNames('video-player__spoiler', { active: !revealed })} onClick={this.toggleReveal}>
-          <span className='video-player__spoiler__title'><FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' /></span>
-          <span className='video-player__spoiler__subtitle'><FormattedMessage id='status.sensitive_toggle' defaultMessage='Click to view' /></span>
-        </button>
-
-        <div className={classNames('video-player__controls', { active: paused || hovered })}>
-          <div className='video-player__seek' onMouseDown={this.handleMouseDown} ref={this.setSeekRef}>
-            <div className='video-player__seek__buffer' style={{ width: `${buffer}%` }} />
-            <div className='video-player__seek__progress' style={{ width: `${progress}%` }} />
-
-            <span
-              className={classNames('video-player__seek__handle', { active: dragging })}
-              tabIndex='0'
-              style={{ left: `${progress}%` }}
-            />
-          </div>
-
-          <div className='video-player__buttons left'>
-            <button aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} onClick={this.togglePlay}><i className={classNames('fa fa-fw', { 'fa-play': paused, 'fa-pause': !paused })} /></button>
-            <button aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} onClick={this.toggleMute}><i className={classNames('fa fa-fw', { 'fa-volume-off': muted, 'fa-volume-up': !muted })} /></button>
-            {!onCloseVideo && <button aria-label={intl.formatMessage(messages.hide)} onClick={this.toggleReveal}><i className='fa fa-fw fa-eye' /></button>}
-          </div>
-
-          <div className='video-player__buttons right'>
-            {(!fullscreen && onOpenVideo) && <button aria-label={intl.formatMessage(messages.expand)} onClick={this.handleOpenVideo}><i className='fa fa-fw fa-expand' /></button>}
-            {onCloseVideo && <button aria-label={intl.formatMessage(messages.close)} onClick={this.handleCloseVideo}><i className='fa fa-fw fa-times' /></button>}
-            <button aria-label={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)} onClick={this.toggleFullscreen}><i className={classNames('fa fa-fw', { 'fa-arrows-alt': !fullscreen, 'fa-compress': fullscreen })} /></button>
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/index.js b/app/javascript/themes/glitch/index.js
deleted file mode 100644
index 407e1f767..000000000
--- a/app/javascript/themes/glitch/index.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import loadPolyfills from './util/load_polyfills';
-
-// import default stylesheet with variables
-require('font-awesome/css/font-awesome.css');
-
-import './styles/index.scss';
-
-require.context('../../images/', true);
-
-loadPolyfills().then(() => {
-  require('./util/main').default();
-}).catch(e => {
-  console.error(e);
-});
diff --git a/app/javascript/themes/glitch/middleware/errors.js b/app/javascript/themes/glitch/middleware/errors.js
deleted file mode 100644
index c54e7b0a2..000000000
--- a/app/javascript/themes/glitch/middleware/errors.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import { showAlert } from 'themes/glitch/actions/alerts';
-
-const defaultFailSuffix = 'FAIL';
-
-export default function errorsMiddleware() {
-  return ({ dispatch }) => next => action => {
-    if (action.type && !action.skipAlert) {
-      const isFail = new RegExp(`${defaultFailSuffix}$`, 'g');
-
-      if (action.type.match(isFail)) {
-        if (action.error.response) {
-          const { data, status, statusText } = action.error.response;
-
-          let message = statusText;
-          let title   = `${status}`;
-
-          if (data.error) {
-            message = data.error;
-          }
-
-          dispatch(showAlert(title, message));
-        } else {
-          console.error(action.error);
-          dispatch(showAlert('Oops!', 'An unexpected error occurred.'));
-        }
-      }
-    }
-
-    return next(action);
-  };
-};
diff --git a/app/javascript/themes/glitch/middleware/loading_bar.js b/app/javascript/themes/glitch/middleware/loading_bar.js
deleted file mode 100644
index a98f1bb2b..000000000
--- a/app/javascript/themes/glitch/middleware/loading_bar.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import { showLoading, hideLoading } from 'react-redux-loading-bar';
-
-const defaultTypeSuffixes = ['PENDING', 'FULFILLED', 'REJECTED'];
-
-export default function loadingBarMiddleware(config = {}) {
-  const promiseTypeSuffixes = config.promiseTypeSuffixes || defaultTypeSuffixes;
-
-  return ({ dispatch }) => next => (action) => {
-    if (action.type && !action.skipLoading) {
-      const [PENDING, FULFILLED, REJECTED] = promiseTypeSuffixes;
-
-      const isPending = new RegExp(`${PENDING}$`, 'g');
-      const isFulfilled = new RegExp(`${FULFILLED}$`, 'g');
-      const isRejected = new RegExp(`${REJECTED}$`, 'g');
-
-      if (action.type.match(isPending)) {
-        dispatch(showLoading());
-      } else if (action.type.match(isFulfilled) || action.type.match(isRejected)) {
-        dispatch(hideLoading());
-      }
-    }
-
-    return next(action);
-  };
-};
diff --git a/app/javascript/themes/glitch/middleware/sounds.js b/app/javascript/themes/glitch/middleware/sounds.js
deleted file mode 100644
index 3d1e3eaba..000000000
--- a/app/javascript/themes/glitch/middleware/sounds.js
+++ /dev/null
@@ -1,46 +0,0 @@
-const createAudio = sources => {
-  const audio = new Audio();
-  sources.forEach(({ type, src }) => {
-    const source = document.createElement('source');
-    source.type = type;
-    source.src = src;
-    audio.appendChild(source);
-  });
-  return audio;
-};
-
-const play = audio => {
-  if (!audio.paused) {
-    audio.pause();
-    if (typeof audio.fastSeek === 'function') {
-      audio.fastSeek(0);
-    } else {
-      audio.seek(0);
-    }
-  }
-
-  audio.play();
-};
-
-export default function soundsMiddleware() {
-  const soundCache = {
-    boop: createAudio([
-      {
-        src: '/sounds/boop.ogg',
-        type: 'audio/ogg',
-      },
-      {
-        src: '/sounds/boop.mp3',
-        type: 'audio/mpeg',
-      },
-    ]),
-  };
-
-  return () => next => action => {
-    if (action.meta && action.meta.sound && soundCache[action.meta.sound]) {
-      play(soundCache[action.meta.sound]);
-    }
-
-    return next(action);
-  };
-};
diff --git a/app/javascript/themes/glitch/reducers/accounts.js b/app/javascript/themes/glitch/reducers/accounts.js
deleted file mode 100644
index 0a65d3723..000000000
--- a/app/javascript/themes/glitch/reducers/accounts.js
+++ /dev/null
@@ -1,135 +0,0 @@
-import {
-  ACCOUNT_FETCH_SUCCESS,
-  FOLLOWERS_FETCH_SUCCESS,
-  FOLLOWERS_EXPAND_SUCCESS,
-  FOLLOWING_FETCH_SUCCESS,
-  FOLLOWING_EXPAND_SUCCESS,
-  FOLLOW_REQUESTS_FETCH_SUCCESS,
-  FOLLOW_REQUESTS_EXPAND_SUCCESS,
-} from 'themes/glitch/actions/accounts';
-import {
-  BLOCKS_FETCH_SUCCESS,
-  BLOCKS_EXPAND_SUCCESS,
-} from 'themes/glitch/actions/blocks';
-import {
-  MUTES_FETCH_SUCCESS,
-  MUTES_EXPAND_SUCCESS,
-} from 'themes/glitch/actions/mutes';
-import { COMPOSE_SUGGESTIONS_READY } from 'themes/glitch/actions/compose';
-import {
-  REBLOG_SUCCESS,
-  UNREBLOG_SUCCESS,
-  FAVOURITE_SUCCESS,
-  UNFAVOURITE_SUCCESS,
-  REBLOGS_FETCH_SUCCESS,
-  FAVOURITES_FETCH_SUCCESS,
-} from 'themes/glitch/actions/interactions';
-import {
-  TIMELINE_REFRESH_SUCCESS,
-  TIMELINE_UPDATE,
-  TIMELINE_EXPAND_SUCCESS,
-} from 'themes/glitch/actions/timelines';
-import {
-  STATUS_FETCH_SUCCESS,
-  CONTEXT_FETCH_SUCCESS,
-} from 'themes/glitch/actions/statuses';
-import { SEARCH_FETCH_SUCCESS } from 'themes/glitch/actions/search';
-import {
-  NOTIFICATIONS_UPDATE,
-  NOTIFICATIONS_REFRESH_SUCCESS,
-  NOTIFICATIONS_EXPAND_SUCCESS,
-} from 'themes/glitch/actions/notifications';
-import {
-  FAVOURITED_STATUSES_FETCH_SUCCESS,
-  FAVOURITED_STATUSES_EXPAND_SUCCESS,
-} from 'themes/glitch/actions/favourites';
-import { STORE_HYDRATE } from 'themes/glitch/actions/store';
-import emojify from 'themes/glitch/util/emoji';
-import { Map as ImmutableMap, fromJS } from 'immutable';
-import escapeTextContentForBrowser from 'escape-html';
-
-const normalizeAccount = (state, account) => {
-  account = { ...account };
-
-  delete account.followers_count;
-  delete account.following_count;
-  delete account.statuses_count;
-
-  const displayName = account.display_name.length === 0 ? account.username : account.display_name;
-  account.display_name_html = emojify(escapeTextContentForBrowser(displayName));
-  account.note_emojified = emojify(account.note);
-
-  return state.set(account.id, fromJS(account));
-};
-
-const normalizeAccounts = (state, accounts) => {
-  accounts.forEach(account => {
-    state = normalizeAccount(state, account);
-  });
-
-  return state;
-};
-
-const normalizeAccountFromStatus = (state, status) => {
-  state = normalizeAccount(state, status.account);
-
-  if (status.reblog && status.reblog.account) {
-    state = normalizeAccount(state, status.reblog.account);
-  }
-
-  return state;
-};
-
-const normalizeAccountsFromStatuses = (state, statuses) => {
-  statuses.forEach(status => {
-    state = normalizeAccountFromStatus(state, status);
-  });
-
-  return state;
-};
-
-const initialState = ImmutableMap();
-
-export default function accounts(state = initialState, action) {
-  switch(action.type) {
-  case STORE_HYDRATE:
-    return state.merge(action.state.get('accounts'));
-  case ACCOUNT_FETCH_SUCCESS:
-  case NOTIFICATIONS_UPDATE:
-    return normalizeAccount(state, action.account);
-  case FOLLOWERS_FETCH_SUCCESS:
-  case FOLLOWERS_EXPAND_SUCCESS:
-  case FOLLOWING_FETCH_SUCCESS:
-  case FOLLOWING_EXPAND_SUCCESS:
-  case REBLOGS_FETCH_SUCCESS:
-  case FAVOURITES_FETCH_SUCCESS:
-  case COMPOSE_SUGGESTIONS_READY:
-  case FOLLOW_REQUESTS_FETCH_SUCCESS:
-  case FOLLOW_REQUESTS_EXPAND_SUCCESS:
-  case BLOCKS_FETCH_SUCCESS:
-  case BLOCKS_EXPAND_SUCCESS:
-  case MUTES_FETCH_SUCCESS:
-  case MUTES_EXPAND_SUCCESS:
-    return action.accounts ? normalizeAccounts(state, action.accounts) : state;
-  case NOTIFICATIONS_REFRESH_SUCCESS:
-  case NOTIFICATIONS_EXPAND_SUCCESS:
-  case SEARCH_FETCH_SUCCESS:
-    return normalizeAccountsFromStatuses(normalizeAccounts(state, action.accounts), action.statuses);
-  case TIMELINE_REFRESH_SUCCESS:
-  case TIMELINE_EXPAND_SUCCESS:
-  case CONTEXT_FETCH_SUCCESS:
-  case FAVOURITED_STATUSES_FETCH_SUCCESS:
-  case FAVOURITED_STATUSES_EXPAND_SUCCESS:
-    return normalizeAccountsFromStatuses(state, action.statuses);
-  case REBLOG_SUCCESS:
-  case FAVOURITE_SUCCESS:
-  case UNREBLOG_SUCCESS:
-  case UNFAVOURITE_SUCCESS:
-    return normalizeAccountFromStatus(state, action.response);
-  case TIMELINE_UPDATE:
-  case STATUS_FETCH_SUCCESS:
-    return normalizeAccountFromStatus(state, action.status);
-  default:
-    return state;
-  }
-};
diff --git a/app/javascript/themes/glitch/reducers/accounts_counters.js b/app/javascript/themes/glitch/reducers/accounts_counters.js
deleted file mode 100644
index e3728ecd7..000000000
--- a/app/javascript/themes/glitch/reducers/accounts_counters.js
+++ /dev/null
@@ -1,138 +0,0 @@
-import {
-  ACCOUNT_FETCH_SUCCESS,
-  FOLLOWERS_FETCH_SUCCESS,
-  FOLLOWERS_EXPAND_SUCCESS,
-  FOLLOWING_FETCH_SUCCESS,
-  FOLLOWING_EXPAND_SUCCESS,
-  FOLLOW_REQUESTS_FETCH_SUCCESS,
-  FOLLOW_REQUESTS_EXPAND_SUCCESS,
-  ACCOUNT_FOLLOW_SUCCESS,
-  ACCOUNT_UNFOLLOW_SUCCESS,
-} from 'themes/glitch/actions/accounts';
-import {
-  BLOCKS_FETCH_SUCCESS,
-  BLOCKS_EXPAND_SUCCESS,
-} from 'themes/glitch/actions/blocks';
-import {
-  MUTES_FETCH_SUCCESS,
-  MUTES_EXPAND_SUCCESS,
-} from 'themes/glitch/actions/mutes';
-import { COMPOSE_SUGGESTIONS_READY } from 'themes/glitch/actions/compose';
-import {
-  REBLOG_SUCCESS,
-  UNREBLOG_SUCCESS,
-  FAVOURITE_SUCCESS,
-  UNFAVOURITE_SUCCESS,
-  REBLOGS_FETCH_SUCCESS,
-  FAVOURITES_FETCH_SUCCESS,
-} from 'themes/glitch/actions/interactions';
-import {
-  TIMELINE_REFRESH_SUCCESS,
-  TIMELINE_UPDATE,
-  TIMELINE_EXPAND_SUCCESS,
-} from 'themes/glitch/actions/timelines';
-import {
-  STATUS_FETCH_SUCCESS,
-  CONTEXT_FETCH_SUCCESS,
-} from 'themes/glitch/actions/statuses';
-import { SEARCH_FETCH_SUCCESS } from 'themes/glitch/actions/search';
-import {
-  NOTIFICATIONS_UPDATE,
-  NOTIFICATIONS_REFRESH_SUCCESS,
-  NOTIFICATIONS_EXPAND_SUCCESS,
-} from 'themes/glitch/actions/notifications';
-import {
-  FAVOURITED_STATUSES_FETCH_SUCCESS,
-  FAVOURITED_STATUSES_EXPAND_SUCCESS,
-} from 'themes/glitch/actions/favourites';
-import { STORE_HYDRATE } from 'themes/glitch/actions/store';
-import { Map as ImmutableMap, fromJS } from 'immutable';
-
-const normalizeAccount = (state, account) => state.set(account.id, fromJS({
-  followers_count: account.followers_count,
-  following_count: account.following_count,
-  statuses_count: account.statuses_count,
-}));
-
-const normalizeAccounts = (state, accounts) => {
-  accounts.forEach(account => {
-    state = normalizeAccount(state, account);
-  });
-
-  return state;
-};
-
-const normalizeAccountFromStatus = (state, status) => {
-  state = normalizeAccount(state, status.account);
-
-  if (status.reblog && status.reblog.account) {
-    state = normalizeAccount(state, status.reblog.account);
-  }
-
-  return state;
-};
-
-const normalizeAccountsFromStatuses = (state, statuses) => {
-  statuses.forEach(status => {
-    state = normalizeAccountFromStatus(state, status);
-  });
-
-  return state;
-};
-
-const initialState = ImmutableMap();
-
-export default function accountsCounters(state = initialState, action) {
-  switch(action.type) {
-  case STORE_HYDRATE:
-    return state.merge(action.state.get('accounts').map(item => fromJS({
-      followers_count: item.get('followers_count'),
-      following_count: item.get('following_count'),
-      statuses_count: item.get('statuses_count'),
-    })));
-  case ACCOUNT_FETCH_SUCCESS:
-  case NOTIFICATIONS_UPDATE:
-    return normalizeAccount(state, action.account);
-  case FOLLOWERS_FETCH_SUCCESS:
-  case FOLLOWERS_EXPAND_SUCCESS:
-  case FOLLOWING_FETCH_SUCCESS:
-  case FOLLOWING_EXPAND_SUCCESS:
-  case REBLOGS_FETCH_SUCCESS:
-  case FAVOURITES_FETCH_SUCCESS:
-  case COMPOSE_SUGGESTIONS_READY:
-  case FOLLOW_REQUESTS_FETCH_SUCCESS:
-  case FOLLOW_REQUESTS_EXPAND_SUCCESS:
-  case BLOCKS_FETCH_SUCCESS:
-  case BLOCKS_EXPAND_SUCCESS:
-  case MUTES_FETCH_SUCCESS:
-  case MUTES_EXPAND_SUCCESS:
-    return action.accounts ? normalizeAccounts(state, action.accounts) : state;
-  case NOTIFICATIONS_REFRESH_SUCCESS:
-  case NOTIFICATIONS_EXPAND_SUCCESS:
-  case SEARCH_FETCH_SUCCESS:
-    return normalizeAccountsFromStatuses(normalizeAccounts(state, action.accounts), action.statuses);
-  case TIMELINE_REFRESH_SUCCESS:
-  case TIMELINE_EXPAND_SUCCESS:
-  case CONTEXT_FETCH_SUCCESS:
-  case FAVOURITED_STATUSES_FETCH_SUCCESS:
-  case FAVOURITED_STATUSES_EXPAND_SUCCESS:
-    return normalizeAccountsFromStatuses(state, action.statuses);
-  case REBLOG_SUCCESS:
-  case FAVOURITE_SUCCESS:
-  case UNREBLOG_SUCCESS:
-  case UNFAVOURITE_SUCCESS:
-    return normalizeAccountFromStatus(state, action.response);
-  case TIMELINE_UPDATE:
-  case STATUS_FETCH_SUCCESS:
-    return normalizeAccountFromStatus(state, action.status);
-  case ACCOUNT_FOLLOW_SUCCESS:
-    if (action.alreadyFollowing) {
-      return state;
-    }
-    return state.updateIn([action.relationship.id, 'followers_count'], num => num + 1);
-  case ACCOUNT_UNFOLLOW_SUCCESS:
-    return state.updateIn([action.relationship.id, 'followers_count'], num => Math.max(0, num - 1));
-  default:
-    return state;
-  }
-};
diff --git a/app/javascript/themes/glitch/reducers/alerts.js b/app/javascript/themes/glitch/reducers/alerts.js
deleted file mode 100644
index ad66b63f6..000000000
--- a/app/javascript/themes/glitch/reducers/alerts.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import {
-  ALERT_SHOW,
-  ALERT_DISMISS,
-  ALERT_CLEAR,
-} from 'themes/glitch/actions/alerts';
-import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
-
-const initialState = ImmutableList([]);
-
-export default function alerts(state = initialState, action) {
-  switch(action.type) {
-  case ALERT_SHOW:
-    return state.push(ImmutableMap({
-      key: state.size > 0 ? state.last().get('key') + 1 : 0,
-      title: action.title,
-      message: action.message,
-    }));
-  case ALERT_DISMISS:
-    return state.filterNot(item => item.get('key') === action.alert.key);
-  case ALERT_CLEAR:
-    return state.clear();
-  default:
-    return state;
-  }
-};
diff --git a/app/javascript/themes/glitch/reducers/cards.js b/app/javascript/themes/glitch/reducers/cards.js
deleted file mode 100644
index 35be30444..000000000
--- a/app/javascript/themes/glitch/reducers/cards.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { STATUS_CARD_FETCH_SUCCESS } from 'themes/glitch/actions/cards';
-
-import { Map as ImmutableMap, fromJS } from 'immutable';
-
-const initialState = ImmutableMap();
-
-export default function cards(state = initialState, action) {
-  switch(action.type) {
-  case STATUS_CARD_FETCH_SUCCESS:
-    return state.set(action.id, fromJS(action.card));
-  default:
-    return state;
-  }
-};
diff --git a/app/javascript/themes/glitch/reducers/compose.js b/app/javascript/themes/glitch/reducers/compose.js
deleted file mode 100644
index be359fcb4..000000000
--- a/app/javascript/themes/glitch/reducers/compose.js
+++ /dev/null
@@ -1,307 +0,0 @@
-import {
-  COMPOSE_MOUNT,
-  COMPOSE_UNMOUNT,
-  COMPOSE_CHANGE,
-  COMPOSE_REPLY,
-  COMPOSE_REPLY_CANCEL,
-  COMPOSE_MENTION,
-  COMPOSE_SUBMIT_REQUEST,
-  COMPOSE_SUBMIT_SUCCESS,
-  COMPOSE_SUBMIT_FAIL,
-  COMPOSE_UPLOAD_REQUEST,
-  COMPOSE_UPLOAD_SUCCESS,
-  COMPOSE_UPLOAD_FAIL,
-  COMPOSE_UPLOAD_UNDO,
-  COMPOSE_UPLOAD_PROGRESS,
-  COMPOSE_SUGGESTIONS_CLEAR,
-  COMPOSE_SUGGESTIONS_READY,
-  COMPOSE_SUGGESTION_SELECT,
-  COMPOSE_ADVANCED_OPTIONS_CHANGE,
-  COMPOSE_SENSITIVITY_CHANGE,
-  COMPOSE_SPOILERNESS_CHANGE,
-  COMPOSE_SPOILER_TEXT_CHANGE,
-  COMPOSE_VISIBILITY_CHANGE,
-  COMPOSE_COMPOSING_CHANGE,
-  COMPOSE_EMOJI_INSERT,
-  COMPOSE_UPLOAD_CHANGE_REQUEST,
-  COMPOSE_UPLOAD_CHANGE_SUCCESS,
-  COMPOSE_UPLOAD_CHANGE_FAIL,
-  COMPOSE_DOODLE_SET,
-  COMPOSE_RESET,
-} from 'themes/glitch/actions/compose';
-import { TIMELINE_DELETE } from 'themes/glitch/actions/timelines';
-import { STORE_HYDRATE } from 'themes/glitch/actions/store';
-import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrderedSet, fromJS } from 'immutable';
-import uuid from 'themes/glitch/util/uuid';
-import { me } from 'themes/glitch/util/initial_state';
-
-const initialState = ImmutableMap({
-  mounted: false,
-  advanced_options: ImmutableMap({
-    do_not_federate: false,
-  }),
-  sensitive: false,
-  spoiler: false,
-  spoiler_text: '',
-  privacy: null,
-  text: '',
-  focusDate: null,
-  preselectDate: null,
-  in_reply_to: null,
-  is_composing: false,
-  is_submitting: false,
-  is_uploading: false,
-  progress: 0,
-  media_attachments: ImmutableList(),
-  suggestion_token: null,
-  suggestions: ImmutableList(),
-  default_advanced_options: ImmutableMap({
-    do_not_federate: false,
-  }),
-  default_privacy: 'public',
-  default_sensitive: false,
-  resetFileKey: Math.floor((Math.random() * 0x10000)),
-  idempotencyKey: null,
-  doodle: ImmutableMap({
-    fg: 'rgb(  0,    0,    0)',
-    bg: 'rgb(255,  255,  255)',
-    swapped: false,
-    mode: 'draw',
-    size: 'normal',
-    weight: 2,
-    opacity: 1,
-    adaptiveStroke: true,
-    smoothing: false,
-  }),
-});
-
-function statusToTextMentions(state, status) {
-  let set = ImmutableOrderedSet([]);
-
-  if (status.getIn(['account', 'id']) !== me) {
-    set = set.add(`@${status.getIn(['account', 'acct'])} `);
-  }
-
-  return set.union(status.get('mentions').filterNot(mention => mention.get('id') === me).map(mention => `@${mention.get('acct')} `)).join('');
-};
-
-function clearAll(state) {
-  return state.withMutations(map => {
-    map.set('text', '');
-    map.set('spoiler', false);
-    map.set('spoiler_text', '');
-    map.set('is_submitting', false);
-    map.set('in_reply_to', null);
-    map.set('advanced_options', state.get('default_advanced_options'));
-    map.set('privacy', state.get('default_privacy'));
-    map.set('sensitive', false);
-    map.update('media_attachments', list => list.clear());
-    map.set('idempotencyKey', uuid());
-  });
-};
-
-function appendMedia(state, media) {
-  const prevSize = state.get('media_attachments').size;
-
-  return state.withMutations(map => {
-    map.update('media_attachments', list => list.push(media));
-    map.set('is_uploading', false);
-    map.set('resetFileKey', Math.floor((Math.random() * 0x10000)));
-    map.update('text', oldText => `${oldText.trim()} ${media.get('text_url')}`);
-    map.set('focusDate', new Date());
-    map.set('idempotencyKey', uuid());
-
-    if (prevSize === 0 && (state.get('default_sensitive') || state.get('spoiler'))) {
-      map.set('sensitive', true);
-    }
-  });
-};
-
-function removeMedia(state, mediaId) {
-  const media    = state.get('media_attachments').find(item => item.get('id') === mediaId);
-  const prevSize = state.get('media_attachments').size;
-
-  return state.withMutations(map => {
-    map.update('media_attachments', list => list.filterNot(item => item.get('id') === mediaId));
-    map.update('text', text => text.replace(media.get('text_url'), '').trim());
-    map.set('idempotencyKey', uuid());
-
-    if (prevSize === 1) {
-      map.set('sensitive', false);
-    }
-  });
-};
-
-const insertSuggestion = (state, position, token, completion) => {
-  return state.withMutations(map => {
-    map.update('text', oldText => `${oldText.slice(0, position)}${completion}\u200B${oldText.slice(position + token.length)}`);
-    map.set('suggestion_token', null);
-    map.update('suggestions', ImmutableList(), list => list.clear());
-    map.set('focusDate', new Date());
-    map.set('idempotencyKey', uuid());
-  });
-};
-
-const insertEmoji = (state, position, emojiData) => {
-  const emoji = emojiData.native;
-
-  return state.withMutations(map => {
-    map.update('text', oldText => `${oldText.slice(0, position)}${emoji}\u200B${oldText.slice(position)}`);
-    map.set('focusDate', new Date());
-    map.set('idempotencyKey', uuid());
-  });
-};
-
-const privacyPreference = (a, b) => {
-  if (a === 'direct' || b === 'direct') {
-    return 'direct';
-  } else if (a === 'private' || b === 'private') {
-    return 'private';
-  } else if (a === 'unlisted' || b === 'unlisted') {
-    return 'unlisted';
-  } else {
-    return 'public';
-  }
-};
-
-const hydrate = (state, hydratedState) => {
-  state = clearAll(state.merge(hydratedState));
-
-  if (hydratedState.has('text')) {
-    state = state.set('text', hydratedState.get('text'));
-  }
-
-  return state;
-};
-
-export default function compose(state = initialState, action) {
-  switch(action.type) {
-  case STORE_HYDRATE:
-    return hydrate(state, action.state.get('compose'));
-  case COMPOSE_MOUNT:
-    return state.set('mounted', true);
-  case COMPOSE_UNMOUNT:
-    return state
-      .set('mounted', false)
-      .set('is_composing', false);
-  case COMPOSE_ADVANCED_OPTIONS_CHANGE:
-    return state
-      .set('advanced_options',
-        state.get('advanced_options').set(action.option, !state.getIn(['advanced_options', action.option])))
-      .set('idempotencyKey', uuid());
-  case COMPOSE_SENSITIVITY_CHANGE:
-    return state.withMutations(map => {
-      if (!state.get('spoiler')) {
-        map.set('sensitive', !state.get('sensitive'));
-      }
-
-      map.set('idempotencyKey', uuid());
-    });
-  case COMPOSE_SPOILERNESS_CHANGE:
-    return state.withMutations(map => {
-      map.set('spoiler_text', '');
-      map.set('spoiler', !state.get('spoiler'));
-      map.set('idempotencyKey', uuid());
-
-      if (!state.get('sensitive') && state.get('media_attachments').size >= 1) {
-        map.set('sensitive', true);
-      }
-    });
-  case COMPOSE_SPOILER_TEXT_CHANGE:
-    return state
-      .set('spoiler_text', action.text)
-      .set('idempotencyKey', uuid());
-  case COMPOSE_VISIBILITY_CHANGE:
-    return state
-      .set('privacy', action.value)
-      .set('idempotencyKey', uuid());
-  case COMPOSE_CHANGE:
-    return state
-      .set('text', action.text)
-      .set('idempotencyKey', uuid());
-  case COMPOSE_COMPOSING_CHANGE:
-    return state.set('is_composing', action.value);
-  case COMPOSE_REPLY:
-    return state.withMutations(map => {
-      map.set('in_reply_to', action.status.get('id'));
-      map.set('text', statusToTextMentions(state, action.status));
-      map.set('privacy', privacyPreference(action.status.get('visibility'), state.get('default_privacy')));
-      map.set('advanced_options', new ImmutableMap({
-        do_not_federate: /👁\ufe0f?<\/p>$/.test(action.status.get('content')),
-      }));
-      map.set('focusDate', new Date());
-      map.set('preselectDate', new Date());
-      map.set('idempotencyKey', uuid());
-
-      if (action.status.get('spoiler_text').length > 0) {
-        map.set('spoiler', true);
-        map.set('spoiler_text', action.status.get('spoiler_text'));
-      } else {
-        map.set('spoiler', false);
-        map.set('spoiler_text', '');
-      }
-    });
-  case COMPOSE_REPLY_CANCEL:
-  case COMPOSE_RESET:
-    return state.withMutations(map => {
-      map.set('in_reply_to', null);
-      map.set('text', '');
-      map.set('spoiler', false);
-      map.set('spoiler_text', '');
-      map.set('privacy', state.get('default_privacy'));
-      map.set('advanced_options', state.get('default_advanced_options'));
-      map.set('idempotencyKey', uuid());
-    });
-  case COMPOSE_SUBMIT_REQUEST:
-  case COMPOSE_UPLOAD_CHANGE_REQUEST:
-    return state.set('is_submitting', true);
-  case COMPOSE_SUBMIT_SUCCESS:
-    return clearAll(state);
-  case COMPOSE_SUBMIT_FAIL:
-  case COMPOSE_UPLOAD_CHANGE_FAIL:
-    return state.set('is_submitting', false);
-  case COMPOSE_UPLOAD_REQUEST:
-    return state.set('is_uploading', true);
-  case COMPOSE_UPLOAD_SUCCESS:
-    return appendMedia(state, fromJS(action.media));
-  case COMPOSE_UPLOAD_FAIL:
-    return state.set('is_uploading', false);
-  case COMPOSE_UPLOAD_UNDO:
-    return removeMedia(state, action.media_id);
-  case COMPOSE_UPLOAD_PROGRESS:
-    return state.set('progress', Math.round((action.loaded / action.total) * 100));
-  case COMPOSE_MENTION:
-    return state
-      .update('text', text => `${text}@${action.account.get('acct')} `)
-      .set('focusDate', new Date())
-      .set('idempotencyKey', uuid());
-  case COMPOSE_SUGGESTIONS_CLEAR:
-    return state.update('suggestions', ImmutableList(), list => list.clear()).set('suggestion_token', null);
-  case COMPOSE_SUGGESTIONS_READY:
-    return state.set('suggestions', ImmutableList(action.accounts ? action.accounts.map(item => item.id) : action.emojis)).set('suggestion_token', action.token);
-  case COMPOSE_SUGGESTION_SELECT:
-    return insertSuggestion(state, action.position, action.token, action.completion);
-  case TIMELINE_DELETE:
-    if (action.id === state.get('in_reply_to')) {
-      return state.set('in_reply_to', null);
-    } else {
-      return state;
-    }
-  case COMPOSE_EMOJI_INSERT:
-    return insertEmoji(state, action.position, action.emoji);
-  case COMPOSE_UPLOAD_CHANGE_SUCCESS:
-    return state
-      .set('is_submitting', false)
-      .update('media_attachments', list => list.map(item => {
-        if (item.get('id') === action.media.id) {
-          return item.set('description', action.media.description);
-        }
-
-        return item;
-      }));
-  case COMPOSE_DOODLE_SET:
-    return state.mergeIn(['doodle'], action.options);
-  default:
-    return state;
-  }
-};
diff --git a/app/javascript/themes/glitch/reducers/contexts.js b/app/javascript/themes/glitch/reducers/contexts.js
deleted file mode 100644
index 56c930bd5..000000000
--- a/app/javascript/themes/glitch/reducers/contexts.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import { CONTEXT_FETCH_SUCCESS } from 'themes/glitch/actions/statuses';
-import { TIMELINE_DELETE, TIMELINE_CONTEXT_UPDATE } from 'themes/glitch/actions/timelines';
-import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
-
-const initialState = ImmutableMap({
-  ancestors: ImmutableMap(),
-  descendants: ImmutableMap(),
-});
-
-const normalizeContext = (state, id, ancestors, descendants) => {
-  const ancestorsIds   = ImmutableList(ancestors.map(ancestor => ancestor.id));
-  const descendantsIds = ImmutableList(descendants.map(descendant => descendant.id));
-
-  return state.withMutations(map => {
-    map.setIn(['ancestors', id], ancestorsIds);
-    map.setIn(['descendants', id], descendantsIds);
-  });
-};
-
-const deleteFromContexts = (state, id) => {
-  state.getIn(['descendants', id], ImmutableList()).forEach(descendantId => {
-    state = state.updateIn(['ancestors', descendantId], ImmutableList(), list => list.filterNot(itemId => itemId === id));
-  });
-
-  state.getIn(['ancestors', id], ImmutableList()).forEach(ancestorId => {
-    state = state.updateIn(['descendants', ancestorId], ImmutableList(), list => list.filterNot(itemId => itemId === id));
-  });
-
-  state = state.deleteIn(['descendants', id]).deleteIn(['ancestors', id]);
-
-  return state;
-};
-
-const updateContext = (state, status, references) => {
-  return state.update('descendants', map => {
-    references.forEach(parentId => {
-      map = map.update(parentId, ImmutableList(), list => {
-        if (list.includes(status.id)) {
-          return list;
-        }
-
-        return list.push(status.id);
-      });
-    });
-
-    return map;
-  });
-};
-
-export default function contexts(state = initialState, action) {
-  switch(action.type) {
-  case CONTEXT_FETCH_SUCCESS:
-    return normalizeContext(state, action.id, action.ancestors, action.descendants);
-  case TIMELINE_DELETE:
-    return deleteFromContexts(state, action.id);
-  case TIMELINE_CONTEXT_UPDATE:
-    return updateContext(state, action.status, action.references);
-  default:
-    return state;
-  }
-};
diff --git a/app/javascript/themes/glitch/reducers/custom_emojis.js b/app/javascript/themes/glitch/reducers/custom_emojis.js
deleted file mode 100644
index e3f1e0018..000000000
--- a/app/javascript/themes/glitch/reducers/custom_emojis.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { List as ImmutableList } from 'immutable';
-import { STORE_HYDRATE } from 'themes/glitch/actions/store';
-import { search as emojiSearch } from 'themes/glitch/util/emoji/emoji_mart_search_light';
-import { buildCustomEmojis } from 'themes/glitch/util/emoji';
-
-const initialState = ImmutableList();
-
-export default function custom_emojis(state = initialState, action) {
-  switch(action.type) {
-  case STORE_HYDRATE:
-    emojiSearch('', { custom: buildCustomEmojis(action.state.get('custom_emojis', [])) });
-    return action.state.get('custom_emojis');
-  default:
-    return state;
-  }
-};
diff --git a/app/javascript/themes/glitch/reducers/height_cache.js b/app/javascript/themes/glitch/reducers/height_cache.js
deleted file mode 100644
index 93c31b42c..000000000
--- a/app/javascript/themes/glitch/reducers/height_cache.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import { Map as ImmutableMap } from 'immutable';
-import { HEIGHT_CACHE_SET, HEIGHT_CACHE_CLEAR } from 'themes/glitch/actions/height_cache';
-
-const initialState = ImmutableMap();
-
-const setHeight = (state, key, id, height) => {
-  return state.update(key, ImmutableMap(), map => map.set(id, height));
-};
-
-const clearHeights = () => {
-  return ImmutableMap();
-};
-
-export default function statuses(state = initialState, action) {
-  switch(action.type) {
-  case HEIGHT_CACHE_SET:
-    return setHeight(state, action.key, action.id, action.height);
-  case HEIGHT_CACHE_CLEAR:
-    return clearHeights();
-  default:
-    return state;
-  }
-};
diff --git a/app/javascript/themes/glitch/reducers/index.js b/app/javascript/themes/glitch/reducers/index.js
deleted file mode 100644
index aa748421a..000000000
--- a/app/javascript/themes/glitch/reducers/index.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import { combineReducers } from 'redux-immutable';
-import timelines from './timelines';
-import meta from './meta';
-import alerts from './alerts';
-import { loadingBarReducer } from 'react-redux-loading-bar';
-import modal from './modal';
-import user_lists from './user_lists';
-import accounts from './accounts';
-import accounts_counters from './accounts_counters';
-import statuses from './statuses';
-import relationships from './relationships';
-import settings from './settings';
-import local_settings from './local_settings';
-import push_notifications from './push_notifications';
-import status_lists from './status_lists';
-import cards from './cards';
-import mutes from './mutes';
-import reports from './reports';
-import contexts from './contexts';
-import compose from './compose';
-import search from './search';
-import media_attachments from './media_attachments';
-import notifications from './notifications';
-import height_cache from './height_cache';
-import custom_emojis from './custom_emojis';
-
-const reducers = {
-  timelines,
-  meta,
-  alerts,
-  loadingBar: loadingBarReducer,
-  modal,
-  user_lists,
-  status_lists,
-  accounts,
-  accounts_counters,
-  statuses,
-  relationships,
-  settings,
-  local_settings,
-  push_notifications,
-  cards,
-  mutes,
-  reports,
-  contexts,
-  compose,
-  search,
-  media_attachments,
-  notifications,
-  height_cache,
-  custom_emojis,
-};
-
-export default combineReducers(reducers);
diff --git a/app/javascript/themes/glitch/reducers/local_settings.js b/app/javascript/themes/glitch/reducers/local_settings.js
deleted file mode 100644
index b1ffa047e..000000000
--- a/app/javascript/themes/glitch/reducers/local_settings.js
+++ /dev/null
@@ -1,45 +0,0 @@
-//  Package imports.
-import { Map as ImmutableMap } from 'immutable';
-
-//  Our imports.
-import { STORE_HYDRATE } from 'themes/glitch/actions/store';
-import { LOCAL_SETTING_CHANGE } from 'themes/glitch/actions/local_settings';
-
-const initialState = ImmutableMap({
-  layout    : 'auto',
-  stretch   : true,
-  navbar_under : false,
-  side_arm  : 'none',
-  collapsed : ImmutableMap({
-    enabled     : true,
-    auto        : ImmutableMap({
-      all              : false,
-      notifications    : true,
-      lengthy          : true,
-      reblogs          : false,
-      replies          : false,
-      media            : false,
-    }),
-    backgrounds : ImmutableMap({
-      user_backgrounds : false,
-      preview_images   : false,
-    }),
-  }),
-  media     : ImmutableMap({
-    letterbox   : true,
-    fullwidth   : true,
-  }),
-});
-
-const hydrate = (state, localSettings) => state.mergeDeep(localSettings);
-
-export default function localSettings(state = initialState, action) {
-  switch(action.type) {
-  case STORE_HYDRATE:
-    return hydrate(state, action.state.get('local_settings'));
-  case LOCAL_SETTING_CHANGE:
-    return state.setIn(action.key, action.value);
-  default:
-    return state;
-  }
-};
diff --git a/app/javascript/themes/glitch/reducers/media_attachments.js b/app/javascript/themes/glitch/reducers/media_attachments.js
deleted file mode 100644
index 69a44639c..000000000
--- a/app/javascript/themes/glitch/reducers/media_attachments.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import { STORE_HYDRATE } from 'themes/glitch/actions/store';
-import { Map as ImmutableMap } from 'immutable';
-
-const initialState = ImmutableMap({
-  accept_content_types: [],
-});
-
-export default function meta(state = initialState, action) {
-  switch(action.type) {
-  case STORE_HYDRATE:
-    return state.merge(action.state.get('media_attachments'));
-  default:
-    return state;
-  }
-};
diff --git a/app/javascript/themes/glitch/reducers/meta.js b/app/javascript/themes/glitch/reducers/meta.js
deleted file mode 100644
index 2249f1d78..000000000
--- a/app/javascript/themes/glitch/reducers/meta.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { STORE_HYDRATE } from 'themes/glitch/actions/store';
-import { Map as ImmutableMap } from 'immutable';
-
-const initialState = ImmutableMap({
-  streaming_api_base_url: null,
-  access_token: null,
-});
-
-export default function meta(state = initialState, action) {
-  switch(action.type) {
-  case STORE_HYDRATE:
-    return state.merge(action.state.get('meta'));
-  default:
-    return state;
-  }
-};
diff --git a/app/javascript/themes/glitch/reducers/modal.js b/app/javascript/themes/glitch/reducers/modal.js
deleted file mode 100644
index 97fb31203..000000000
--- a/app/javascript/themes/glitch/reducers/modal.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import { MODAL_OPEN, MODAL_CLOSE } from 'themes/glitch/actions/modal';
-
-const initialState = {
-  modalType: null,
-  modalProps: {},
-};
-
-export default function modal(state = initialState, action) {
-  switch(action.type) {
-  case MODAL_OPEN:
-    return { modalType: action.modalType, modalProps: action.modalProps };
-  case MODAL_CLOSE:
-    return initialState;
-  default:
-    return state;
-  }
-};
diff --git a/app/javascript/themes/glitch/reducers/mutes.js b/app/javascript/themes/glitch/reducers/mutes.js
deleted file mode 100644
index 8fe4ae0c3..000000000
--- a/app/javascript/themes/glitch/reducers/mutes.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import Immutable from 'immutable';
-
-import {
-  MUTES_INIT_MODAL,
-  MUTES_TOGGLE_HIDE_NOTIFICATIONS,
-} from 'themes/glitch/actions/mutes';
-
-const initialState = Immutable.Map({
-  new: Immutable.Map({
-    isSubmitting: false,
-    account: null,
-    notifications: true,
-  }),
-});
-
-export default function mutes(state = initialState, action) {
-  switch (action.type) {
-  case MUTES_INIT_MODAL:
-    return state.withMutations((state) => {
-      state.setIn(['new', 'isSubmitting'], false);
-      state.setIn(['new', 'account'], action.account);
-      state.setIn(['new', 'notifications'], true);
-    });
-  case MUTES_TOGGLE_HIDE_NOTIFICATIONS:
-    return state.updateIn(['new', 'notifications'], (old) => !old);
-  default:
-    return state;
-  }
-}
diff --git a/app/javascript/themes/glitch/reducers/notifications.js b/app/javascript/themes/glitch/reducers/notifications.js
deleted file mode 100644
index c4f505053..000000000
--- a/app/javascript/themes/glitch/reducers/notifications.js
+++ /dev/null
@@ -1,191 +0,0 @@
-import {
-  NOTIFICATIONS_UPDATE,
-  NOTIFICATIONS_REFRESH_SUCCESS,
-  NOTIFICATIONS_EXPAND_SUCCESS,
-  NOTIFICATIONS_REFRESH_REQUEST,
-  NOTIFICATIONS_EXPAND_REQUEST,
-  NOTIFICATIONS_REFRESH_FAIL,
-  NOTIFICATIONS_EXPAND_FAIL,
-  NOTIFICATIONS_CLEAR,
-  NOTIFICATIONS_SCROLL_TOP,
-  NOTIFICATIONS_DELETE_MARKED_REQUEST,
-  NOTIFICATIONS_DELETE_MARKED_SUCCESS,
-  NOTIFICATION_MARK_FOR_DELETE,
-  NOTIFICATIONS_DELETE_MARKED_FAIL,
-  NOTIFICATIONS_ENTER_CLEARING_MODE,
-  NOTIFICATIONS_MARK_ALL_FOR_DELETE,
-} from 'themes/glitch/actions/notifications';
-import {
-  ACCOUNT_BLOCK_SUCCESS,
-  ACCOUNT_MUTE_SUCCESS,
-} from 'themes/glitch/actions/accounts';
-import { TIMELINE_DELETE } from 'themes/glitch/actions/timelines';
-import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
-
-const initialState = ImmutableMap({
-  items: ImmutableList(),
-  next: null,
-  top: true,
-  unread: 0,
-  loaded: false,
-  isLoading: true,
-  cleaningMode: false,
-  // notification removal mark of new notifs loaded whilst cleaningMode is true.
-  markNewForDelete: false,
-});
-
-const notificationToMap = (state, notification) => ImmutableMap({
-  id: notification.id,
-  type: notification.type,
-  account: notification.account.id,
-  markedForDelete: state.get('markNewForDelete'),
-  status: notification.status ? notification.status.id : null,
-});
-
-const normalizeNotification = (state, notification) => {
-  const top = state.get('top');
-
-  if (!top) {
-    state = state.update('unread', unread => unread + 1);
-  }
-
-  return state.update('items', list => {
-    if (top && list.size > 40) {
-      list = list.take(20);
-    }
-
-    return list.unshift(notificationToMap(state, notification));
-  });
-};
-
-const normalizeNotifications = (state, notifications, next) => {
-  let items    = ImmutableList();
-  const loaded = state.get('loaded');
-
-  notifications.forEach((n, i) => {
-    items = items.set(i, notificationToMap(state, n));
-  });
-
-  if (state.get('next') === null) {
-    state = state.set('next', next);
-  }
-
-  return state
-    .update('items', list => loaded ? items.concat(list) : list.concat(items))
-    .set('loaded', true)
-    .set('isLoading', false);
-};
-
-const appendNormalizedNotifications = (state, notifications, next) => {
-  let items = ImmutableList();
-
-  notifications.forEach((n, i) => {
-    items = items.set(i, notificationToMap(state, n));
-  });
-
-  return state
-    .update('items', list => list.concat(items))
-    .set('next', next)
-    .set('isLoading', false);
-};
-
-const filterNotifications = (state, relationship) => {
-  return state.update('items', list => list.filterNot(item => item.get('account') === relationship.id));
-};
-
-const updateTop = (state, top) => {
-  if (top) {
-    state = state.set('unread', 0);
-  }
-
-  return state.set('top', top);
-};
-
-const deleteByStatus = (state, statusId) => {
-  return state.update('items', list => list.filterNot(item => item.get('status') === statusId));
-};
-
-const markForDelete = (state, notificationId, yes) => {
-  return state.update('items', list => list.map(item => {
-    if(item.get('id') === notificationId) {
-      return item.set('markedForDelete', yes);
-    } else {
-      return item;
-    }
-  }));
-};
-
-const markAllForDelete = (state, yes) => {
-  return state.update('items', list => list.map(item => {
-    if(yes !== null) {
-      return item.set('markedForDelete', yes);
-    } else {
-      return item.set('markedForDelete', !item.get('markedForDelete'));
-    }
-  }));
-};
-
-const unmarkAllForDelete = (state) => {
-  return state.update('items', list => list.map(item => item.set('markedForDelete', false)));
-};
-
-const deleteMarkedNotifs = (state) => {
-  return state.update('items', list => list.filterNot(item => item.get('markedForDelete')));
-};
-
-export default function notifications(state = initialState, action) {
-  let st;
-
-  switch(action.type) {
-  case NOTIFICATIONS_REFRESH_REQUEST:
-  case NOTIFICATIONS_EXPAND_REQUEST:
-  case NOTIFICATIONS_DELETE_MARKED_REQUEST:
-    return state.set('isLoading', true);
-  case NOTIFICATIONS_DELETE_MARKED_FAIL:
-  case NOTIFICATIONS_REFRESH_FAIL:
-  case NOTIFICATIONS_EXPAND_FAIL:
-    return state.set('isLoading', false);
-  case NOTIFICATIONS_SCROLL_TOP:
-    return updateTop(state, action.top);
-  case NOTIFICATIONS_UPDATE:
-    return normalizeNotification(state, action.notification);
-  case NOTIFICATIONS_REFRESH_SUCCESS:
-    return normalizeNotifications(state, action.notifications, action.next);
-  case NOTIFICATIONS_EXPAND_SUCCESS:
-    return appendNormalizedNotifications(state, action.notifications, action.next);
-  case ACCOUNT_BLOCK_SUCCESS:
-  case ACCOUNT_MUTE_SUCCESS:
-    return filterNotifications(state, action.relationship);
-  case NOTIFICATIONS_CLEAR:
-    return state.set('items', ImmutableList()).set('next', null);
-  case TIMELINE_DELETE:
-    return deleteByStatus(state, action.id);
-
-  case NOTIFICATION_MARK_FOR_DELETE:
-    return markForDelete(state, action.id, action.yes);
-
-  case NOTIFICATIONS_DELETE_MARKED_SUCCESS:
-    return deleteMarkedNotifs(state).set('isLoading', false);
-
-  case NOTIFICATIONS_ENTER_CLEARING_MODE:
-    st = state.set('cleaningMode', action.yes);
-    if (!action.yes) {
-      return unmarkAllForDelete(st).set('markNewForDelete', false);
-    } else {
-      return st;
-    }
-
-  case NOTIFICATIONS_MARK_ALL_FOR_DELETE:
-    st = state;
-    if (action.yes === null) {
-      // Toggle - this is a bit confusing, as it toggles the all-none mode
-      //st = st.set('markNewForDelete', !st.get('markNewForDelete'));
-    } else {
-      st = st.set('markNewForDelete', action.yes);
-    }
-    return markAllForDelete(st, action.yes);
-
-  default:
-    return state;
-  }
-};
diff --git a/app/javascript/themes/glitch/reducers/push_notifications.js b/app/javascript/themes/glitch/reducers/push_notifications.js
deleted file mode 100644
index 744e4a0eb..000000000
--- a/app/javascript/themes/glitch/reducers/push_notifications.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import { STORE_HYDRATE } from 'themes/glitch/actions/store';
-import { SET_BROWSER_SUPPORT, SET_SUBSCRIPTION, CLEAR_SUBSCRIPTION, ALERTS_CHANGE } from 'themes/glitch/actions/push_notifications';
-import Immutable from 'immutable';
-
-const initialState = Immutable.Map({
-  subscription: null,
-  alerts: new Immutable.Map({
-    follow: false,
-    favourite: false,
-    reblog: false,
-    mention: false,
-  }),
-  isSubscribed: false,
-  browserSupport: false,
-});
-
-export default function push_subscriptions(state = initialState, action) {
-  switch(action.type) {
-  case STORE_HYDRATE: {
-    const push_subscription = action.state.get('push_subscription');
-
-    if (push_subscription) {
-      return state
-        .set('subscription', new Immutable.Map({
-          id: push_subscription.get('id'),
-          endpoint: push_subscription.get('endpoint'),
-        }))
-        .set('alerts', push_subscription.get('alerts') || initialState.get('alerts'))
-        .set('isSubscribed', true);
-    }
-
-    return state;
-  }
-  case SET_SUBSCRIPTION:
-    return state
-      .set('subscription', new Immutable.Map({
-        id: action.subscription.id,
-        endpoint: action.subscription.endpoint,
-      }))
-      .set('alerts', new Immutable.Map(action.subscription.alerts))
-      .set('isSubscribed', true);
-  case SET_BROWSER_SUPPORT:
-    return state.set('browserSupport', action.value);
-  case CLEAR_SUBSCRIPTION:
-    return initialState;
-  case ALERTS_CHANGE:
-    return state.setIn(action.key, action.value);
-  default:
-    return state;
-  }
-};
diff --git a/app/javascript/themes/glitch/reducers/relationships.js b/app/javascript/themes/glitch/reducers/relationships.js
deleted file mode 100644
index d9135d6da..000000000
--- a/app/javascript/themes/glitch/reducers/relationships.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import {
-  ACCOUNT_FOLLOW_SUCCESS,
-  ACCOUNT_UNFOLLOW_SUCCESS,
-  ACCOUNT_BLOCK_SUCCESS,
-  ACCOUNT_UNBLOCK_SUCCESS,
-  ACCOUNT_MUTE_SUCCESS,
-  ACCOUNT_UNMUTE_SUCCESS,
-  RELATIONSHIPS_FETCH_SUCCESS,
-} from 'themes/glitch/actions/accounts';
-import {
-  DOMAIN_BLOCK_SUCCESS,
-  DOMAIN_UNBLOCK_SUCCESS,
-} from 'themes/glitch/actions/domain_blocks';
-import { Map as ImmutableMap, fromJS } from 'immutable';
-
-const normalizeRelationship = (state, relationship) => state.set(relationship.id, fromJS(relationship));
-
-const normalizeRelationships = (state, relationships) => {
-  relationships.forEach(relationship => {
-    state = normalizeRelationship(state, relationship);
-  });
-
-  return state;
-};
-
-const initialState = ImmutableMap();
-
-export default function relationships(state = initialState, action) {
-  switch(action.type) {
-  case ACCOUNT_FOLLOW_SUCCESS:
-  case ACCOUNT_UNFOLLOW_SUCCESS:
-  case ACCOUNT_BLOCK_SUCCESS:
-  case ACCOUNT_UNBLOCK_SUCCESS:
-  case ACCOUNT_MUTE_SUCCESS:
-  case ACCOUNT_UNMUTE_SUCCESS:
-    return normalizeRelationship(state, action.relationship);
-  case RELATIONSHIPS_FETCH_SUCCESS:
-    return normalizeRelationships(state, action.relationships);
-  case DOMAIN_BLOCK_SUCCESS:
-    return state.setIn([action.accountId, 'domain_blocking'], true);
-  case DOMAIN_UNBLOCK_SUCCESS:
-    return state.setIn([action.accountId, 'domain_blocking'], false);
-  default:
-    return state;
-  }
-};
diff --git a/app/javascript/themes/glitch/reducers/reports.js b/app/javascript/themes/glitch/reducers/reports.js
deleted file mode 100644
index b714374ea..000000000
--- a/app/javascript/themes/glitch/reducers/reports.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import {
-  REPORT_INIT,
-  REPORT_SUBMIT_REQUEST,
-  REPORT_SUBMIT_SUCCESS,
-  REPORT_SUBMIT_FAIL,
-  REPORT_CANCEL,
-  REPORT_STATUS_TOGGLE,
-  REPORT_COMMENT_CHANGE,
-} from 'themes/glitch/actions/reports';
-import { Map as ImmutableMap, Set as ImmutableSet } from 'immutable';
-
-const initialState = ImmutableMap({
-  new: ImmutableMap({
-    isSubmitting: false,
-    account_id: null,
-    status_ids: ImmutableSet(),
-    comment: '',
-  }),
-});
-
-export default function reports(state = initialState, action) {
-  switch(action.type) {
-  case REPORT_INIT:
-    return state.withMutations(map => {
-      map.setIn(['new', 'isSubmitting'], false);
-      map.setIn(['new', 'account_id'], action.account.get('id'));
-
-      if (state.getIn(['new', 'account_id']) !== action.account.get('id')) {
-        map.setIn(['new', 'status_ids'], action.status ? ImmutableSet([action.status.getIn(['reblog', 'id'], action.status.get('id'))]) : ImmutableSet());
-        map.setIn(['new', 'comment'], '');
-      } else if (action.status) {
-        map.updateIn(['new', 'status_ids'], ImmutableSet(), set => set.add(action.status.getIn(['reblog', 'id'], action.status.get('id'))));
-      }
-    });
-  case REPORT_STATUS_TOGGLE:
-    return state.updateIn(['new', 'status_ids'], ImmutableSet(), set => {
-      if (action.checked) {
-        return set.add(action.statusId);
-      }
-
-      return set.remove(action.statusId);
-    });
-  case REPORT_COMMENT_CHANGE:
-    return state.setIn(['new', 'comment'], action.comment);
-  case REPORT_SUBMIT_REQUEST:
-    return state.setIn(['new', 'isSubmitting'], true);
-  case REPORT_SUBMIT_FAIL:
-    return state.setIn(['new', 'isSubmitting'], false);
-  case REPORT_CANCEL:
-  case REPORT_SUBMIT_SUCCESS:
-    return state.withMutations(map => {
-      map.setIn(['new', 'account_id'], null);
-      map.setIn(['new', 'status_ids'], ImmutableSet());
-      map.setIn(['new', 'comment'], '');
-      map.setIn(['new', 'isSubmitting'], false);
-    });
-  default:
-    return state;
-  }
-};
diff --git a/app/javascript/themes/glitch/reducers/search.js b/app/javascript/themes/glitch/reducers/search.js
deleted file mode 100644
index aec9e2efb..000000000
--- a/app/javascript/themes/glitch/reducers/search.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import {
-  SEARCH_CHANGE,
-  SEARCH_CLEAR,
-  SEARCH_FETCH_SUCCESS,
-  SEARCH_SHOW,
-} from 'themes/glitch/actions/search';
-import { COMPOSE_MENTION, COMPOSE_REPLY } from 'themes/glitch/actions/compose';
-import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
-
-const initialState = ImmutableMap({
-  value: '',
-  submitted: false,
-  hidden: false,
-  results: ImmutableMap(),
-});
-
-export default function search(state = initialState, action) {
-  switch(action.type) {
-  case SEARCH_CHANGE:
-    return state.set('value', action.value);
-  case SEARCH_CLEAR:
-    return state.withMutations(map => {
-      map.set('value', '');
-      map.set('results', ImmutableMap());
-      map.set('submitted', false);
-      map.set('hidden', false);
-    });
-  case SEARCH_SHOW:
-    return state.set('hidden', false);
-  case COMPOSE_REPLY:
-  case COMPOSE_MENTION:
-    return state.set('hidden', true);
-  case SEARCH_FETCH_SUCCESS:
-    return state.set('results', ImmutableMap({
-      accounts: ImmutableList(action.results.accounts.map(item => item.id)),
-      statuses: ImmutableList(action.results.statuses.map(item => item.id)),
-      hashtags: ImmutableList(action.results.hashtags),
-    })).set('submitted', true);
-  default:
-    return state;
-  }
-};
diff --git a/app/javascript/themes/glitch/reducers/settings.js b/app/javascript/themes/glitch/reducers/settings.js
deleted file mode 100644
index c22bbbd8d..000000000
--- a/app/javascript/themes/glitch/reducers/settings.js
+++ /dev/null
@@ -1,119 +0,0 @@
-import { SETTING_CHANGE, SETTING_SAVE } from 'themes/glitch/actions/settings';
-import { COLUMN_ADD, COLUMN_REMOVE, COLUMN_MOVE } from 'themes/glitch/actions/columns';
-import { STORE_HYDRATE } from 'themes/glitch/actions/store';
-import { EMOJI_USE } from 'themes/glitch/actions/emojis';
-import { Map as ImmutableMap, fromJS } from 'immutable';
-import uuid from 'themes/glitch/util/uuid';
-
-const initialState = ImmutableMap({
-  saved: true,
-
-  onboarded: false,
-  layout: 'auto',
-
-  skinTone: 1,
-
-  home: ImmutableMap({
-    shows: ImmutableMap({
-      reblog: true,
-      reply: true,
-    }),
-
-    regex: ImmutableMap({
-      body: '',
-    }),
-  }),
-
-  notifications: ImmutableMap({
-    alerts: ImmutableMap({
-      follow: true,
-      favourite: true,
-      reblog: true,
-      mention: true,
-    }),
-
-    shows: ImmutableMap({
-      follow: true,
-      favourite: true,
-      reblog: true,
-      mention: true,
-    }),
-
-    sounds: ImmutableMap({
-      follow: true,
-      favourite: true,
-      reblog: true,
-      mention: true,
-    }),
-  }),
-
-  community: ImmutableMap({
-    regex: ImmutableMap({
-      body: '',
-    }),
-  }),
-
-  public: ImmutableMap({
-    regex: ImmutableMap({
-      body: '',
-    }),
-  }),
-
-  direct: ImmutableMap({
-    regex: ImmutableMap({
-      body: '',
-    }),
-  }),
-});
-
-const defaultColumns = fromJS([
-  { id: 'COMPOSE', uuid: uuid(), params: {} },
-  { id: 'HOME', uuid: uuid(), params: {} },
-  { id: 'NOTIFICATIONS', uuid: uuid(), params: {} },
-]);
-
-const hydrate = (state, settings) => state.mergeDeep(settings).update('columns', (val = defaultColumns) => val);
-
-const moveColumn = (state, uuid, direction) => {
-  const columns  = state.get('columns');
-  const index    = columns.findIndex(item => item.get('uuid') === uuid);
-  const newIndex = index + direction;
-
-  let newColumns;
-
-  newColumns = columns.splice(index, 1);
-  newColumns = newColumns.splice(newIndex, 0, columns.get(index));
-
-  return state
-    .set('columns', newColumns)
-    .set('saved', false);
-};
-
-const updateFrequentEmojis = (state, emoji) => state.update('frequentlyUsedEmojis', ImmutableMap(), map => map.update(emoji.id, 0, count => count + 1)).set('saved', false);
-
-export default function settings(state = initialState, action) {
-  switch(action.type) {
-  case STORE_HYDRATE:
-    return hydrate(state, action.state.get('settings'));
-  case SETTING_CHANGE:
-    return state
-      .setIn(action.key, action.value)
-      .set('saved', false);
-  case COLUMN_ADD:
-    return state
-      .update('columns', list => list.push(fromJS({ id: action.id, uuid: uuid(), params: action.params })))
-      .set('saved', false);
-  case COLUMN_REMOVE:
-    return state
-      .update('columns', list => list.filterNot(item => item.get('uuid') === action.uuid))
-      .set('saved', false);
-  case COLUMN_MOVE:
-    return moveColumn(state, action.uuid, action.direction);
-  case EMOJI_USE:
-    return updateFrequentEmojis(state, action.emoji);
-  case SETTING_SAVE:
-    return state.set('saved', true);
-  default:
-    return state;
-  }
-};
diff --git a/app/javascript/themes/glitch/reducers/status_lists.js b/app/javascript/themes/glitch/reducers/status_lists.js
deleted file mode 100644
index 8dc7d374e..000000000
--- a/app/javascript/themes/glitch/reducers/status_lists.js
+++ /dev/null
@@ -1,75 +0,0 @@
-import {
-  FAVOURITED_STATUSES_FETCH_SUCCESS,
-  FAVOURITED_STATUSES_EXPAND_SUCCESS,
-} from 'themes/glitch/actions/favourites';
-import {
-  PINNED_STATUSES_FETCH_SUCCESS,
-} from 'themes/glitch/actions/pin_statuses';
-import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
-import {
-  FAVOURITE_SUCCESS,
-  UNFAVOURITE_SUCCESS,
-  PIN_SUCCESS,
-  UNPIN_SUCCESS,
-} from 'themes/glitch/actions/interactions';
-
-const initialState = ImmutableMap({
-  favourites: ImmutableMap({
-    next: null,
-    loaded: false,
-    items: ImmutableList(),
-  }),
-  pins: ImmutableMap({
-    next: null,
-    loaded: false,
-    items: ImmutableList(),
-  }),
-});
-
-const normalizeList = (state, listType, statuses, next) => {
-  return state.update(listType, listMap => listMap.withMutations(map => {
-    map.set('next', next);
-    map.set('loaded', true);
-    map.set('items', ImmutableList(statuses.map(item => item.id)));
-  }));
-};
-
-const appendToList = (state, listType, statuses, next) => {
-  return state.update(listType, listMap => listMap.withMutations(map => {
-    map.set('next', next);
-    map.set('items', map.get('items').concat(statuses.map(item => item.id)));
-  }));
-};
-
-const prependOneToList = (state, listType, status) => {
-  return state.update(listType, listMap => listMap.withMutations(map => {
-    map.set('items', map.get('items').unshift(status.get('id')));
-  }));
-};
-
-const removeOneFromList = (state, listType, status) => {
-  return state.update(listType, listMap => listMap.withMutations(map => {
-    map.set('items', map.get('items').filter(item => item !== status.get('id')));
-  }));
-};
-
-export default function statusLists(state = initialState, action) {
-  switch(action.type) {
-  case FAVOURITED_STATUSES_FETCH_SUCCESS:
-    return normalizeList(state, 'favourites', action.statuses, action.next);
-  case FAVOURITED_STATUSES_EXPAND_SUCCESS:
-    return appendToList(state, 'favourites', action.statuses, action.next);
-  case FAVOURITE_SUCCESS:
-    return prependOneToList(state, 'favourites', action.status);
-  case UNFAVOURITE_SUCCESS:
-    return removeOneFromList(state, 'favourites', action.status);
-  case PINNED_STATUSES_FETCH_SUCCESS:
-    return normalizeList(state, 'pins', action.statuses, action.next);
-  case PIN_SUCCESS:
-    return prependOneToList(state, 'pins', action.status);
-  case UNPIN_SUCCESS:
-    return removeOneFromList(state, 'pins', action.status);
-  default:
-    return state;
-  }
-};
diff --git a/app/javascript/themes/glitch/reducers/statuses.js b/app/javascript/themes/glitch/reducers/statuses.js
deleted file mode 100644
index ef8086865..000000000
--- a/app/javascript/themes/glitch/reducers/statuses.js
+++ /dev/null
@@ -1,148 +0,0 @@
-import {
-  REBLOG_REQUEST,
-  REBLOG_SUCCESS,
-  REBLOG_FAIL,
-  UNREBLOG_SUCCESS,
-  FAVOURITE_REQUEST,
-  FAVOURITE_SUCCESS,
-  FAVOURITE_FAIL,
-  UNFAVOURITE_SUCCESS,
-  PIN_SUCCESS,
-  UNPIN_SUCCESS,
-} from 'themes/glitch/actions/interactions';
-import {
-  STATUS_FETCH_SUCCESS,
-  CONTEXT_FETCH_SUCCESS,
-  STATUS_MUTE_SUCCESS,
-  STATUS_UNMUTE_SUCCESS,
-} from 'themes/glitch/actions/statuses';
-import {
-  TIMELINE_REFRESH_SUCCESS,
-  TIMELINE_UPDATE,
-  TIMELINE_DELETE,
-  TIMELINE_EXPAND_SUCCESS,
-} from 'themes/glitch/actions/timelines';
-import {
-  ACCOUNT_BLOCK_SUCCESS,
-  ACCOUNT_MUTE_SUCCESS,
-} from 'themes/glitch/actions/accounts';
-import {
-  NOTIFICATIONS_UPDATE,
-  NOTIFICATIONS_REFRESH_SUCCESS,
-  NOTIFICATIONS_EXPAND_SUCCESS,
-} from 'themes/glitch/actions/notifications';
-import {
-  FAVOURITED_STATUSES_FETCH_SUCCESS,
-  FAVOURITED_STATUSES_EXPAND_SUCCESS,
-} from 'themes/glitch/actions/favourites';
-import {
-  PINNED_STATUSES_FETCH_SUCCESS,
-} from 'themes/glitch/actions/pin_statuses';
-import { SEARCH_FETCH_SUCCESS } from 'themes/glitch/actions/search';
-import emojify from 'themes/glitch/util/emoji';
-import { Map as ImmutableMap, fromJS } from 'immutable';
-import escapeTextContentForBrowser from 'escape-html';
-
-const domParser = new DOMParser();
-
-const normalizeStatus = (state, status) => {
-  if (!status) {
-    return state;
-  }
-
-  const normalStatus   = { ...status };
-  normalStatus.account = status.account.id;
-
-  if (status.reblog && status.reblog.id) {
-    state               = normalizeStatus(state, status.reblog);
-    normalStatus.reblog = status.reblog.id;
-  }
-
-  const searchContent = [status.spoiler_text, status.content].join('\n\n').replace(/<br \/>/g, '\n').replace(/<\/p><p>/g, '\n\n');
-
-  const emojiMap = normalStatus.emojis.reduce((obj, emoji) => {
-    obj[`:${emoji.shortcode}:`] = emoji;
-    return obj;
-  }, {});
-
-  normalStatus.search_index = domParser.parseFromString(searchContent, 'text/html').documentElement.textContent;
-  normalStatus.contentHtml = emojify(normalStatus.content, emojiMap);
-  normalStatus.spoilerHtml = emojify(escapeTextContentForBrowser(normalStatus.spoiler_text || ''), emojiMap);
-
-  return state.update(status.id, ImmutableMap(), map => map.mergeDeep(fromJS(normalStatus)));
-};
-
-const normalizeStatuses = (state, statuses) => {
-  statuses.forEach(status => {
-    state = normalizeStatus(state, status);
-  });
-
-  return state;
-};
-
-const deleteStatus = (state, id, references) => {
-  references.forEach(ref => {
-    state = deleteStatus(state, ref[0], []);
-  });
-
-  return state.delete(id);
-};
-
-const filterStatuses = (state, relationship) => {
-  state.forEach(status => {
-    if (status.get('account') !== relationship.id) {
-      return;
-    }
-
-    state = deleteStatus(state, status.get('id'), state.filter(item => item.get('reblog') === status.get('id')));
-  });
-
-  return state;
-};
-
-const initialState = ImmutableMap();
-
-export default function statuses(state = initialState, action) {
-  switch(action.type) {
-  case TIMELINE_UPDATE:
-  case STATUS_FETCH_SUCCESS:
-  case NOTIFICATIONS_UPDATE:
-    return normalizeStatus(state, action.status);
-  case REBLOG_SUCCESS:
-  case UNREBLOG_SUCCESS:
-  case FAVOURITE_SUCCESS:
-  case UNFAVOURITE_SUCCESS:
-  case PIN_SUCCESS:
-  case UNPIN_SUCCESS:
-    return normalizeStatus(state, action.response);
-  case FAVOURITE_REQUEST:
-    return state.setIn([action.status.get('id'), 'favourited'], true);
-  case FAVOURITE_FAIL:
-    return state.setIn([action.status.get('id'), 'favourited'], false);
-  case REBLOG_REQUEST:
-    return state.setIn([action.status.get('id'), 'reblogged'], true);
-  case REBLOG_FAIL:
-    return state.setIn([action.status.get('id'), 'reblogged'], false);
-  case STATUS_MUTE_SUCCESS:
-    return state.setIn([action.id, 'muted'], true);
-  case STATUS_UNMUTE_SUCCESS:
-    return state.setIn([action.id, 'muted'], false);
-  case TIMELINE_REFRESH_SUCCESS:
-  case TIMELINE_EXPAND_SUCCESS:
-  case CONTEXT_FETCH_SUCCESS:
-  case NOTIFICATIONS_REFRESH_SUCCESS:
-  case NOTIFICATIONS_EXPAND_SUCCESS:
-  case FAVOURITED_STATUSES_FETCH_SUCCESS:
-  case FAVOURITED_STATUSES_EXPAND_SUCCESS:
-  case PINNED_STATUSES_FETCH_SUCCESS:
-  case SEARCH_FETCH_SUCCESS:
-    return normalizeStatuses(state, action.statuses);
-  case TIMELINE_DELETE:
-    return deleteStatus(state, action.id, action.references);
-  case ACCOUNT_BLOCK_SUCCESS:
-  case ACCOUNT_MUTE_SUCCESS:
-    return filterStatuses(state, action.relationship);
-  default:
-    return state;
-  }
-};
diff --git a/app/javascript/themes/glitch/reducers/timelines.js b/app/javascript/themes/glitch/reducers/timelines.js
deleted file mode 100644
index 7f19a1897..000000000
--- a/app/javascript/themes/glitch/reducers/timelines.js
+++ /dev/null
@@ -1,149 +0,0 @@
-import {
-  TIMELINE_REFRESH_REQUEST,
-  TIMELINE_REFRESH_SUCCESS,
-  TIMELINE_REFRESH_FAIL,
-  TIMELINE_UPDATE,
-  TIMELINE_DELETE,
-  TIMELINE_EXPAND_SUCCESS,
-  TIMELINE_EXPAND_REQUEST,
-  TIMELINE_EXPAND_FAIL,
-  TIMELINE_SCROLL_TOP,
-  TIMELINE_CONNECT,
-  TIMELINE_DISCONNECT,
-} from 'themes/glitch/actions/timelines';
-import {
-  ACCOUNT_BLOCK_SUCCESS,
-  ACCOUNT_MUTE_SUCCESS,
-  ACCOUNT_UNFOLLOW_SUCCESS,
-} from 'themes/glitch/actions/accounts';
-import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
-
-const initialState = ImmutableMap();
-
-const initialTimeline = ImmutableMap({
-  unread: 0,
-  online: false,
-  top: true,
-  loaded: false,
-  isLoading: false,
-  next: false,
-  items: ImmutableList(),
-});
-
-const normalizeTimeline = (state, timeline, statuses, next) => {
-  const oldIds    = state.getIn([timeline, 'items'], ImmutableList());
-  const ids       = ImmutableList(statuses.map(status => status.get('id'))).filter(newId => !oldIds.includes(newId));
-  const wasLoaded = state.getIn([timeline, 'loaded']);
-  const hadNext   = state.getIn([timeline, 'next']);
-
-  return state.update(timeline, initialTimeline, map => map.withMutations(mMap => {
-    mMap.set('loaded', true);
-    mMap.set('isLoading', false);
-    if (!hadNext) mMap.set('next', next);
-    mMap.set('items', wasLoaded ? ids.concat(oldIds) : ids);
-  }));
-};
-
-const appendNormalizedTimeline = (state, timeline, statuses, next) => {
-  const oldIds = state.getIn([timeline, 'items'], ImmutableList());
-  const ids    = ImmutableList(statuses.map(status => status.get('id'))).filter(newId => !oldIds.includes(newId));
-
-  return state.update(timeline, initialTimeline, map => map.withMutations(mMap => {
-    mMap.set('isLoading', false);
-    mMap.set('next', next);
-    mMap.set('items', oldIds.concat(ids));
-  }));
-};
-
-const updateTimeline = (state, timeline, status, references) => {
-  const top        = state.getIn([timeline, 'top']);
-  const ids        = state.getIn([timeline, 'items'], ImmutableList());
-  const includesId = ids.includes(status.get('id'));
-  const unread     = state.getIn([timeline, 'unread'], 0);
-
-  if (includesId) {
-    return state;
-  }
-
-  let newIds = ids;
-
-  return state.update(timeline, initialTimeline, map => map.withMutations(mMap => {
-    if (!top) mMap.set('unread', unread + 1);
-    if (top && ids.size > 40) newIds = newIds.take(20);
-    if (status.getIn(['reblog', 'id'], null) !== null) newIds = newIds.filterNot(item => references.includes(item));
-    mMap.set('items', newIds.unshift(status.get('id')));
-  }));
-};
-
-const deleteStatus = (state, id, accountId, references) => {
-  state.keySeq().forEach(timeline => {
-    state = state.updateIn([timeline, 'items'], list => list.filterNot(item => item === id));
-  });
-
-  // Remove reblogs of deleted status
-  references.forEach(ref => {
-    state = deleteStatus(state, ref[0], ref[1], []);
-  });
-
-  return state;
-};
-
-const filterTimelines = (state, relationship, statuses) => {
-  let references;
-
-  statuses.forEach(status => {
-    if (status.get('account') !== relationship.id) {
-      return;
-    }
-
-    references = statuses.filter(item => item.get('reblog') === status.get('id')).map(item => [item.get('id'), item.get('account')]);
-    state      = deleteStatus(state, status.get('id'), status.get('account'), references);
-  });
-
-  return state;
-};
-
-const filterTimeline = (timeline, state, relationship, statuses) =>
-  state.updateIn([timeline, 'items'], ImmutableList(), list =>
-    list.filterNot(statusId =>
-      statuses.getIn([statusId, 'account']) === relationship.id
-    ));
-
-const updateTop = (state, timeline, top) => {
-  return state.update(timeline, initialTimeline, map => map.withMutations(mMap => {
-    if (top) mMap.set('unread', 0);
-    mMap.set('top', top);
-  }));
-};
-
-export default function timelines(state = initialState, action) {
-  switch(action.type) {
-  case TIMELINE_REFRESH_REQUEST:
-  case TIMELINE_EXPAND_REQUEST:
-    return state.update(action.timeline, initialTimeline, map => map.set('isLoading', true));
-  case TIMELINE_REFRESH_FAIL:
-  case TIMELINE_EXPAND_FAIL:
-    return state.update(action.timeline, initialTimeline, map => map.set('isLoading', false));
-  case TIMELINE_REFRESH_SUCCESS:
-    return normalizeTimeline(state, action.timeline, fromJS(action.statuses), action.next);
-  case TIMELINE_EXPAND_SUCCESS:
-    return appendNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next);
-  case TIMELINE_UPDATE:
-    return updateTimeline(state, action.timeline, fromJS(action.status), action.references);
-  case TIMELINE_DELETE:
-    return deleteStatus(state, action.id, action.accountId, action.references, action.reblogOf);
-  case ACCOUNT_BLOCK_SUCCESS:
-  case ACCOUNT_MUTE_SUCCESS:
-    return filterTimelines(state, action.relationship, action.statuses);
-  case ACCOUNT_UNFOLLOW_SUCCESS:
-    return filterTimeline('home', state, action.relationship, action.statuses);
-  case TIMELINE_SCROLL_TOP:
-    return updateTop(state, action.timeline, action.top);
-  case TIMELINE_CONNECT:
-    return state.update(action.timeline, initialTimeline, map => map.set('online', true));
-  case TIMELINE_DISCONNECT:
-    return state.update(action.timeline, initialTimeline, map => map.set('online', false));
-  default:
-    return state;
-  }
-};
diff --git a/app/javascript/themes/glitch/reducers/user_lists.js b/app/javascript/themes/glitch/reducers/user_lists.js
deleted file mode 100644
index 8c3a7d748..000000000
--- a/app/javascript/themes/glitch/reducers/user_lists.js
+++ /dev/null
@@ -1,80 +0,0 @@
-import {
-  FOLLOWERS_FETCH_SUCCESS,
-  FOLLOWERS_EXPAND_SUCCESS,
-  FOLLOWING_FETCH_SUCCESS,
-  FOLLOWING_EXPAND_SUCCESS,
-  FOLLOW_REQUESTS_FETCH_SUCCESS,
-  FOLLOW_REQUESTS_EXPAND_SUCCESS,
-  FOLLOW_REQUEST_AUTHORIZE_SUCCESS,
-  FOLLOW_REQUEST_REJECT_SUCCESS,
-} from 'themes/glitch/actions/accounts';
-import {
-  REBLOGS_FETCH_SUCCESS,
-  FAVOURITES_FETCH_SUCCESS,
-} from 'themes/glitch/actions/interactions';
-import {
-  BLOCKS_FETCH_SUCCESS,
-  BLOCKS_EXPAND_SUCCESS,
-} from 'themes/glitch/actions/blocks';
-import {
-  MUTES_FETCH_SUCCESS,
-  MUTES_EXPAND_SUCCESS,
-} from 'themes/glitch/actions/mutes';
-import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
-
-const initialState = ImmutableMap({
-  followers: ImmutableMap(),
-  following: ImmutableMap(),
-  reblogged_by: ImmutableMap(),
-  favourited_by: ImmutableMap(),
-  follow_requests: ImmutableMap(),
-  blocks: ImmutableMap(),
-  mutes: ImmutableMap(),
-});
-
-const normalizeList = (state, type, id, accounts, next) => {
-  return state.setIn([type, id], ImmutableMap({
-    next,
-    items: ImmutableList(accounts.map(item => item.id)),
-  }));
-};
-
-const appendToList = (state, type, id, accounts, next) => {
-  return state.updateIn([type, id], map => {
-    return map.set('next', next).update('items', list => list.concat(accounts.map(item => item.id)));
-  });
-};
-
-export default function userLists(state = initialState, action) {
-  switch(action.type) {
-  case FOLLOWERS_FETCH_SUCCESS:
-    return normalizeList(state, 'followers', action.id, action.accounts, action.next);
-  case FOLLOWERS_EXPAND_SUCCESS:
-    return appendToList(state, 'followers', action.id, action.accounts, action.next);
-  case FOLLOWING_FETCH_SUCCESS:
-    return normalizeList(state, 'following', action.id, action.accounts, action.next);
-  case FOLLOWING_EXPAND_SUCCESS:
-    return appendToList(state, 'following', action.id, action.accounts, action.next);
-  case REBLOGS_FETCH_SUCCESS:
-    return state.setIn(['reblogged_by', action.id], ImmutableList(action.accounts.map(item => item.id)));
-  case FAVOURITES_FETCH_SUCCESS:
-    return state.setIn(['favourited_by', action.id], ImmutableList(action.accounts.map(item => item.id)));
-  case FOLLOW_REQUESTS_FETCH_SUCCESS:
-    return state.setIn(['follow_requests', 'items'], ImmutableList(action.accounts.map(item => item.id))).setIn(['follow_requests', 'next'], action.next);
-  case FOLLOW_REQUESTS_EXPAND_SUCCESS:
-    return state.updateIn(['follow_requests', 'items'], list => list.concat(action.accounts.map(item => item.id))).setIn(['follow_requests', 'next'], action.next);
-  case FOLLOW_REQUEST_AUTHORIZE_SUCCESS:
-  case FOLLOW_REQUEST_REJECT_SUCCESS:
-    return state.updateIn(['follow_requests', 'items'], list => list.filterNot(item => item === action.id));
-  case BLOCKS_FETCH_SUCCESS:
-    return state.setIn(['blocks', 'items'], ImmutableList(action.accounts.map(item => item.id))).setIn(['blocks', 'next'], action.next);
-  case BLOCKS_EXPAND_SUCCESS:
-    return state.updateIn(['blocks', 'items'], list => list.concat(action.accounts.map(item => item.id))).setIn(['blocks', 'next'], action.next);
-  case MUTES_FETCH_SUCCESS:
-    return state.setIn(['mutes', 'items'], ImmutableList(action.accounts.map(item => item.id))).setIn(['mutes', 'next'], action.next);
-  case MUTES_EXPAND_SUCCESS:
-    return state.updateIn(['mutes', 'items'], list => list.concat(action.accounts.map(item => item.id))).setIn(['mutes', 'next'], action.next);
-  default:
-    return state;
-  }
-};
diff --git a/app/javascript/themes/glitch/selectors/index.js b/app/javascript/themes/glitch/selectors/index.js
deleted file mode 100644
index d26d1b727..000000000
--- a/app/javascript/themes/glitch/selectors/index.js
+++ /dev/null
@@ -1,87 +0,0 @@
-import { createSelector } from 'reselect';
-import { List as ImmutableList } from 'immutable';
-
-const getAccountBase         = (state, id) => state.getIn(['accounts', id], null);
-const getAccountCounters     = (state, id) => state.getIn(['accounts_counters', id], null);
-const getAccountRelationship = (state, id) => state.getIn(['relationships', id], null);
-
-export const makeGetAccount = () => {
-  return createSelector([getAccountBase, getAccountCounters, getAccountRelationship], (base, counters, relationship) => {
-    if (base === null) {
-      return null;
-    }
-
-    return base.merge(counters).set('relationship', relationship);
-  });
-};
-
-export const makeGetStatus = () => {
-  return createSelector(
-    [
-      (state, id) => state.getIn(['statuses', id]),
-      (state, id) => state.getIn(['statuses', state.getIn(['statuses', id, 'reblog'])]),
-      (state, id) => state.getIn(['accounts', state.getIn(['statuses', id, 'account'])]),
-      (state, id) => state.getIn(['accounts', state.getIn(['statuses', state.getIn(['statuses', id, 'reblog']), 'account'])]),
-    ],
-
-    (statusBase, statusReblog, accountBase, accountReblog) => {
-      if (!statusBase) {
-        return null;
-      }
-
-      if (statusReblog) {
-        statusReblog = statusReblog.set('account', accountReblog);
-      } else {
-        statusReblog = null;
-      }
-
-      return statusBase.withMutations(map => {
-        map.set('reblog', statusReblog);
-        map.set('account', accountBase);
-      });
-    }
-  );
-};
-
-const getAlertsBase = state => state.get('alerts');
-
-export const getAlerts = createSelector([getAlertsBase], (base) => {
-  let arr = [];
-
-  base.forEach(item => {
-    arr.push({
-      message: item.get('message'),
-      title: item.get('title'),
-      key: item.get('key'),
-      dismissAfter: 5000,
-      barStyle: {
-        zIndex: 200,
-      },
-    });
-  });
-
-  return arr;
-});
-
-export const makeGetNotification = () => {
-  return createSelector([
-    (_, base)             => base,
-    (state, _, accountId) => state.getIn(['accounts', accountId]),
-  ], (base, account) => {
-    return base.set('account', account);
-  });
-};
-
-export const getAccountGallery = createSelector([
-  (state, id) => state.getIn(['timelines', `account:${id}:media`, 'items'], ImmutableList()),
-  state       => state.get('statuses'),
-], (statusIds, statuses) => {
-  let medias = ImmutableList();
-
-  statusIds.forEach(statusId => {
-    const status = statuses.get(statusId);
-    medias = medias.concat(status.get('media_attachments').map(media => media.set('status', status)));
-  });
-
-  return medias;
-});
diff --git a/app/javascript/themes/glitch/service_worker/entry.js b/app/javascript/themes/glitch/service_worker/entry.js
deleted file mode 100644
index eea4cfc3c..000000000
--- a/app/javascript/themes/glitch/service_worker/entry.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import './web_push_notifications';
-
-// Cause a new version of a registered Service Worker to replace an existing one
-// that is already installed, and replace the currently active worker on open pages.
-self.addEventListener('install', function(event) {
-  event.waitUntil(self.skipWaiting());
-});
-self.addEventListener('activate', function(event) {
-  event.waitUntil(self.clients.claim());
-});
diff --git a/app/javascript/themes/glitch/service_worker/web_push_notifications.js b/app/javascript/themes/glitch/service_worker/web_push_notifications.js
deleted file mode 100644
index f63cff335..000000000
--- a/app/javascript/themes/glitch/service_worker/web_push_notifications.js
+++ /dev/null
@@ -1,159 +0,0 @@
-const MAX_NOTIFICATIONS = 5;
-const GROUP_TAG = 'tag';
-
-// Avoid loading intl-messageformat and dealing with locales in the ServiceWorker
-const formatGroupTitle = (message, count) => message.replace('%{count}', count);
-
-const notify = options =>
-  self.registration.getNotifications().then(notifications => {
-    if (notifications.length === MAX_NOTIFICATIONS) {
-      // Reached the maximum number of notifications, proceed with grouping
-      const group = {
-        title: formatGroupTitle(options.data.message, notifications.length + 1),
-        body: notifications
-          .sort((n1, n2) => n1.timestamp < n2.timestamp)
-          .map(notification => notification.title).join('\n'),
-        badge: '/badge.png',
-        icon: '/android-chrome-192x192.png',
-        tag: GROUP_TAG,
-        data: {
-          url: (new URL('/web/notifications', self.location)).href,
-          count: notifications.length + 1,
-          message: options.data.message,
-        },
-      };
-
-      notifications.forEach(notification => notification.close());
-
-      return self.registration.showNotification(group.title, group);
-    } else if (notifications.length === 1 && notifications[0].tag === GROUP_TAG) {
-      // Already grouped, proceed with appending the notification to the group
-      const group = cloneNotification(notifications[0]);
-
-      group.title = formatGroupTitle(group.data.message, group.data.count + 1);
-      group.body  = `${options.title}\n${group.body}`;
-      group.data  = { ...group.data, count: group.data.count + 1 };
-
-      return self.registration.showNotification(group.title, group);
-    }
-
-    return self.registration.showNotification(options.title, options);
-  });
-
-const handlePush = (event) => {
-  const options = event.data.json();
-
-  options.body      = options.data.nsfw || options.data.content;
-  options.dir       = options.data.dir;
-  options.image     = options.image || undefined; // Null results in a network request (404)
-  options.timestamp = options.timestamp && new Date(options.timestamp);
-
-  const expandAction = options.data.actions.find(action => action.todo === 'expand');
-
-  if (expandAction) {
-    options.actions          = [expandAction];
-    options.hiddenActions    = options.data.actions.filter(action => action !== expandAction);
-    options.data.hiddenImage = options.image;
-    options.image            = undefined;
-  } else {
-    options.actions = options.data.actions;
-  }
-
-  event.waitUntil(notify(options));
-};
-
-const cloneNotification = (notification) => {
-  const clone = {  };
-
-  for(var k in notification) {
-    clone[k] = notification[k];
-  }
-
-  return clone;
-};
-
-const expandNotification = (notification) => {
-  const nextNotification = cloneNotification(notification);
-
-  nextNotification.body    = notification.data.content;
-  nextNotification.image   = notification.data.hiddenImage;
-  nextNotification.actions = notification.data.actions.filter(action => action.todo !== 'expand');
-
-  return self.registration.showNotification(nextNotification.title, nextNotification);
-};
-
-const makeRequest = (notification, action) =>
-  fetch(action.action, {
-    headers: {
-      'Authorization': `Bearer ${notification.data.access_token}`,
-      'Content-Type': 'application/json',
-    },
-    method: action.method,
-    credentials: 'include',
-  });
-
-const findBestClient = clients => {
-  const focusedClient = clients.find(client => client.focused);
-  const visibleClient = clients.find(client => client.visibilityState === 'visible');
-
-  return focusedClient || visibleClient || clients[0];
-};
-
-const openUrl = url =>
-  self.clients.matchAll({ type: 'window' }).then(clientList => {
-    if (clientList.length !== 0) {
-      const webClients = clientList.filter(client => /\/web\//.test(client.url));
-
-      if (webClients.length !== 0) {
-        const client       = findBestClient(webClients);
-        const { pathname } = new URL(url);
-
-        if (pathname.startsWith('/web/')) {
-          return client.focus().then(client => client.postMessage({
-            type: 'navigate',
-            path: pathname.slice('/web/'.length - 1),
-          }));
-        }
-      } else if ('navigate' in clientList[0]) { // Chrome 42-48 does not support navigate
-        const client = findBestClient(clientList);
-
-        return client.navigate(url).then(client => client.focus());
-      }
-    }
-
-    return self.clients.openWindow(url);
-  });
-
-const removeActionFromNotification = (notification, action) => {
-  const actions          = notification.actions.filter(act => act.action !== action.action);
-  const nextNotification = cloneNotification(notification);
-
-  nextNotification.actions = actions;
-
-  return self.registration.showNotification(nextNotification.title, nextNotification);
-};
-
-const handleNotificationClick = (event) => {
-  const reactToNotificationClick = new Promise((resolve, reject) => {
-    if (event.action) {
-      const action = event.notification.data.actions.find(({ action }) => action === event.action);
-
-      if (action.todo === 'expand') {
-        resolve(expandNotification(event.notification));
-      } else if (action.todo === 'request') {
-        resolve(makeRequest(event.notification, action)
-          .then(() => removeActionFromNotification(event.notification, action)));
-      } else {
-        reject(`Unknown action: ${action.todo}`);
-      }
-    } else {
-      event.notification.close();
-      resolve(openUrl(event.notification.data.url));
-    }
-  });
-
-  event.waitUntil(reactToNotificationClick);
-};
-
-self.addEventListener('push', handlePush);
-self.addEventListener('notificationclick', handleNotificationClick);
diff --git a/app/javascript/themes/glitch/store/configureStore.js b/app/javascript/themes/glitch/store/configureStore.js
deleted file mode 100644
index 1376d4cba..000000000
--- a/app/javascript/themes/glitch/store/configureStore.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import { createStore, applyMiddleware, compose } from 'redux';
-import thunk from 'redux-thunk';
-import appReducer from '../reducers';
-import loadingBarMiddleware from '../middleware/loading_bar';
-import errorsMiddleware from '../middleware/errors';
-import soundsMiddleware from '../middleware/sounds';
-
-export default function configureStore() {
-  return createStore(appReducer, compose(applyMiddleware(
-    thunk,
-    loadingBarMiddleware({ promiseTypeSuffixes: ['REQUEST', 'SUCCESS', 'FAIL'] }),
-    errorsMiddleware(),
-    soundsMiddleware()
-  ), window.devToolsExtension ? window.devToolsExtension() : f => f));
-};
diff --git a/app/javascript/themes/glitch/styles/_mixins.scss b/app/javascript/themes/glitch/styles/_mixins.scss
deleted file mode 100644
index 102723e39..000000000
--- a/app/javascript/themes/glitch/styles/_mixins.scss
+++ /dev/null
@@ -1,52 +0,0 @@
-@mixin avatar-radius() {
-  border-radius: $ui-avatar-border-size;
-  background: transparent no-repeat;
-  background-position: 50%;
-  background-clip: padding-box;
-}
-
-@mixin avatar-size($size:48px) {
-  width: $size;
-  height: $size;
-  background-size: $size $size;
-}
-
-@mixin single-column($media, $parent: '&') {
-  .auto-columns #{$parent} {
-    @media #{$media} {
-      @content;
-    }
-  }
-  .single-column #{$parent} {
-    @content;
-  }
-}
-
-@mixin limited-single-column($media, $parent: '&') {
-  .auto-columns #{$parent}, .single-column #{$parent} {
-    @media #{$media} {
-      @content;
-    }
-  }
-}
-
-@mixin multi-columns($media, $parent: '&') {
-  .auto-columns #{$parent} {
-    @media #{$media} {
-      @content;
-    }
-  }
-  .multi-columns #{$parent} {
-    @content;
-  }
-}
-
-@mixin fullwidth-gallery {
-  &.full-width {
-    margin-left: -22px;
-    margin-right: -22px;
-    width: inherit;
-    max-width: none;
-    height: 250px;
-  }
-}
diff --git a/app/javascript/themes/glitch/styles/about.scss b/app/javascript/themes/glitch/styles/about.scss
deleted file mode 100644
index 4ec689427..000000000
--- a/app/javascript/themes/glitch/styles/about.scss
+++ /dev/null
@@ -1,822 +0,0 @@
-.landing-page {
-  p,
-  li {
-    font-family: 'mastodon-font-sans-serif', sans-serif;
-    font-size: 16px;
-    font-weight: 400;
-    font-size: 16px;
-    line-height: 30px;
-    margin-bottom: 12px;
-    color: $ui-primary-color;
-
-    a {
-      color: $ui-highlight-color;
-      text-decoration: underline;
-    }
-  }
-
-  em {
-    display: inline;
-    margin: 0;
-    padding: 0;
-    font-weight: 500;
-    background: transparent;
-    font-family: inherit;
-    font-size: inherit;
-    line-height: inherit;
-    color: lighten($ui-primary-color, 10%);
-  }
-
-  h1 {
-    font-family: 'mastodon-font-display', sans-serif;
-    font-size: 26px;
-    line-height: 30px;
-    font-weight: 500;
-    margin-bottom: 20px;
-    color: $ui-secondary-color;
-
-    small {
-      font-family: 'mastodon-font-sans-serif', sans-serif;
-      display: block;
-      font-size: 18px;
-      font-weight: 400;
-      color: $ui-base-lighter-color;
-    }
-  }
-
-  h2 {
-    font-family: 'mastodon-font-display', sans-serif;
-    font-size: 22px;
-    line-height: 26px;
-    font-weight: 500;
-    margin-bottom: 20px;
-    color: $ui-secondary-color;
-  }
-
-  h3 {
-    font-family: 'mastodon-font-display', sans-serif;
-    font-size: 18px;
-    line-height: 24px;
-    font-weight: 500;
-    margin-bottom: 20px;
-    color: $ui-secondary-color;
-  }
-
-  h4 {
-    font-family: 'mastodon-font-display', sans-serif;
-    font-size: 16px;
-    line-height: 24px;
-    font-weight: 500;
-    margin-bottom: 20px;
-    color: $ui-secondary-color;
-  }
-
-  h5 {
-    font-family: 'mastodon-font-display', sans-serif;
-    font-size: 14px;
-    line-height: 24px;
-    font-weight: 500;
-    margin-bottom: 20px;
-    color: $ui-secondary-color;
-  }
-
-  h6 {
-    font-family: 'mastodon-font-display', sans-serif;
-    font-size: 12px;
-    line-height: 24px;
-    font-weight: 500;
-    margin-bottom: 20px;
-    color: $ui-secondary-color;
-  }
-
-  ul,
-  ol {
-    margin-left: 20px;
-
-    &[type='a'] {
-      list-style-type: lower-alpha;
-    }
-
-    &[type='i'] {
-      list-style-type: lower-roman;
-    }
-  }
-
-  ul {
-    list-style: disc;
-  }
-
-  ol {
-    list-style: decimal;
-  }
-
-  li > ol,
-  li > ul {
-    margin-top: 6px;
-  }
-
-  hr {
-    border-color: rgba($ui-base-lighter-color, .6);
-  }
-
-  .container {
-    width: 100%;
-    box-sizing: border-box;
-    max-width: 800px;
-    margin: 0 auto;
-    word-wrap: break-word;
-  }
-
-  .header-wrapper {
-    padding-top: 15px;
-    background: $ui-base-color;
-    background: linear-gradient(150deg, lighten($ui-base-color, 8%), $ui-base-color);
-    position: relative;
-
-    &.compact {
-      background: $ui-base-color;
-      padding-bottom: 15px;
-
-      .hero .heading {
-        padding-bottom: 20px;
-        font-family: 'mastodon-font-sans-serif', sans-serif;
-        font-size: 16px;
-        font-weight: 400;
-        font-size: 16px;
-        line-height: 30px;
-        color: $ui-primary-color;
-
-        a {
-          color: $ui-highlight-color;
-          text-decoration: underline;
-        }
-      }
-    }
-
-    .mascot-container {
-      max-width: 800px;
-      margin: 0 auto;
-      position: absolute;
-      top: 0;
-      left: 0;
-      right: 0;
-      height: 100%;
-    }
-
-    .mascot {
-      position: absolute;
-      bottom: -14px;
-      width: auto;
-      height: auto;
-      left: 60px;
-      z-index: 3;
-    }
-  }
-
-  .header {
-    line-height: 30px;
-    overflow: hidden;
-
-    .container {
-      display: flex;
-      justify-content: space-between;
-    }
-
-    .links {
-      position: relative;
-      z-index: 4;
-
-      a {
-        display: flex;
-        justify-content: center;
-        align-items: center;
-        color: $ui-primary-color;
-        text-decoration: none;
-        padding: 12px 16px;
-        line-height: 32px;
-        font-family: 'mastodon-font-display', sans-serif;
-        font-weight: 500;
-        font-size: 14px;
-
-        &:hover {
-          color: $ui-secondary-color;
-        }
-      }
-
-      .brand {
-        a {
-          padding-left: 0;
-          padding-right: 0;
-          color: $white;
-        }
-
-        img {
-          height: 32px;
-          position: relative;
-          top: 4px;
-          left: -10px;
-        }
-      }
-
-      ul {
-        list-style: none;
-        margin: 0;
-
-        li {
-          display: inline-block;
-          vertical-align: bottom;
-          margin: 0;
-
-          &:first-child a {
-            padding-left: 0;
-          }
-
-          &:last-child a {
-            padding-right: 0;
-          }
-        }
-      }
-    }
-
-    .hero {
-      margin-top: 50px;
-      align-items: center;
-      position: relative;
-
-      .floats {
-        position: absolute;
-        width: 100%;
-        height: 100%;
-        top: 0;
-        left: 0;
-
-        div {
-          position: absolute;
-          transition: all 0.1s linear;
-          animation-name: floating;
-          animation-iteration-count: infinite;
-          animation-direction: alternate;
-          animation-timing-function: ease-in-out;
-          z-index: 2;
-        }
-
-        .float-1 {
-          width: 324px;
-          height: 170px;
-          right: -120px;
-          bottom: 0;
-          animation-duration: 3s;
-          background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 447.1875 234.375" height="170" width="324"><path fill="#{hex-color($ui-base-lighter-color)}" d="M21.69 233.366c-6.45-1.268-13.347-5.63-16.704-10.564-10.705-15.734-1.513-37.724 18.632-44.57l4.8-1.632.173-17.753c.146-14.77.515-19.063 2.2-25.55 6.736-25.944 24.46-46.032 47.766-54.137 11.913-4.143 19.558-5.366 34.178-5.47l13.828-.096V71.12c0-4.755 2.853-17.457 5.238-23.327 8.588-21.137 26.735-35.957 52.153-42.593 23.248-6.07 50.153-6.415 71.863-.923 11.14 2.82 25.686 9.957 33.857 16.615 19.335 15.756 31.82 41.05 35.183 71.275.59 5.305.672 5.435 3.11 4.926 11.833-2.474 30.4-3.132 40.065-1.42 24.388 4.32 40.568 19.076 47.214 43.058 2.16 7.8 3.953 23.894 3.59 32.237l-.24 5.498 5.156 1.317c6.392 1.633 14.55 7.098 18.003 12.062 1.435 2.062 3.305 6.597 4.156 10.078 1.428 5.84 1.43 6.8.04 12.44-1.807 7.318-5.672 13.252-10.872 16.694-8.508 5.63 3.756 5.33-211.916 5.216-108.56-.056-199.22-.464-201.47-.906z"/></svg>');
-        }
-
-        .float-2 {
-          width: 241px;
-          height: 100px;
-          right: 210px;
-          bottom: 0;
-          animation-duration: 3.5s;
-          animation-delay: 0.2s;
-          background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 536.25 222.1875" height="100" width="241"><path fill="#{hex-color($ui-base-lighter-color)}" d="M42.626 221.23c-14.104-1.174-26.442-5.133-32.825-10.534-4.194-3.548-7.684-10.66-8.868-18.075-1.934-12.102.633-22.265 7.528-29.81 7.61-8.328 19.998-12.76 39.855-14.257l8.47-.638-2.08-6.223c-4.826-14.422-6.357-24.813-6.37-43.255-.012-14.923.28-18.513 2.1-25.724 2.283-9.048 8.483-23.034 13.345-30.1 14.76-21.45 43.505-38.425 70.535-41.65 30.628-3.655 64.47 12.073 89.668 41.673l5.955 6.995 2.765-4.174c1.52-2.296 5.74-6.93 9.376-10.295 18.382-17.02 43.436-20.676 73.352-10.705 12.158 4.052 21.315 9.53 29.64 17.733 12.752 12.562 18.16 25.718 18.19 44.26l.02 10.98 2.312-3.01c15.64-20.365 42.29-20.485 62.438-.28 3.644 3.653 7.558 8.593 8.697 10.976 4.895 10.24 5.932 25.688 2.486 37.046-.76 2.507-1.388 4.816-1.393 5.13-.006.316 6.845.87 15.224 1.234 53.06 2.297 76.356 12.98 81.817 37.526 3.554 15.973-3.71 28.604-19.566 34.02-4.554 1.555-17.922 1.655-234.517 1.757-126.327.06-233.497-.21-238.154-.597z"/></svg>');
-        }
-
-        .float-3 {
-          width: 267px;
-          height: 140px;
-          right: 110px;
-          top: -30px;
-          animation-duration: 4s;
-          animation-delay: 0.5s;
-          background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 388.125 202.5" height="140" width="267"><path fill="#{hex-color($ui-base-lighter-color)}" d="M181.37 201.458c-17.184-1.81-36.762-8.944-49.523-18.05l-5.774-4.12-8.074 2.63c-11.468 3.738-21.382 4.962-35.815 4.422-14.79-.554-24.577-2.845-36.716-8.594-15.483-7.332-28.498-19.98-35.985-34.968C2.44 128.675-.94 108.435.9 91.356c3.362-31.234 18.197-53.698 43.63-66.074 12.803-6.23 22.384-8.55 37.655-9.122 14.433-.54 24.347.684 35.814 4.42l8.073 2.633 5.635-4.01c24.81-17.656 60.007-23.332 92.914-14.985 10.11 2.565 25.498 9.62 33.102 15.178l5.068 3.704 7.632-2.564c10.89-3.66 21.086-4.916 35.516-4.376 45.816 1.716 76.422 30.03 81.285 75.196 1.84 17.08-1.54 37.32-8.585 51.422-7.487 14.99-20.502 27.636-35.984 34.968-12.14 5.75-21.926 8.04-36.716 8.593-14.43.54-24.626-.716-35.516-4.376l-7.632-2.564-5.068 3.704c-12.844 9.387-32.714 16.488-51.545 18.42-10.607 1.09-13.916 1.08-24.81-.066z"/></svg>');
-        }
-      }
-
-      .heading {
-        position: relative;
-        z-index: 4;
-        padding-bottom: 150px;
-      }
-
-      .simple_form,
-      .closed-registrations-message {
-        background: darken($ui-base-color, 4%);
-        width: 280px;
-        padding: 15px 20px;
-        border-radius: 4px 4px 0 0;
-        line-height: initial;
-        position: relative;
-        z-index: 4;
-
-        .actions {
-          margin-bottom: 0;
-
-          button,
-          .button,
-          .block-button {
-            margin-bottom: 0;
-          }
-        }
-      }
-
-      .closed-registrations-message {
-        min-height: 330px;
-        display: flex;
-        flex-direction: column;
-        justify-content: space-between;
-      }
-    }
-  }
-
-  .about-short {
-    background: darken($ui-base-color, 4%);
-    padding: 50px 0 30px;
-    font-family: 'mastodon-font-sans-serif', sans-serif;
-    font-size: 16px;
-    font-weight: 400;
-    font-size: 16px;
-    line-height: 30px;
-    color: $ui-primary-color;
-
-    a {
-      color: $ui-highlight-color;
-      text-decoration: underline;
-    }
-  }
-
-  .information-board {
-    background: darken($ui-base-color, 4%);
-    padding: 20px 0;
-
-    .container {
-      position: relative;
-      padding-right: 280px + 15px;
-    }
-
-    .information-board-sections {
-      display: flex;
-      justify-content: space-between;
-      flex-wrap: wrap;
-    }
-
-    .section {
-      flex: 1 0 0;
-      font-family: 'mastodon-font-sans-serif', sans-serif;
-      font-size: 16px;
-      line-height: 28px;
-      color: $primary-text-color;
-      text-align: right;
-      padding: 10px 15px;
-
-      span,
-      strong {
-        display: block;
-      }
-
-      span {
-        &:last-child {
-          color: $ui-secondary-color;
-        }
-      }
-
-      strong {
-        font-weight: 500;
-        font-size: 32px;
-        line-height: 48px;
-      }
-    }
-
-    .panel {
-      position: absolute;
-      width: 280px;
-      box-sizing: border-box;
-      background: darken($ui-base-color, 8%);
-      padding: 20px;
-      padding-top: 10px;
-      border-radius: 4px 4px 0 0;
-      right: 0;
-      bottom: -40px;
-
-      .panel-header {
-        font-family: 'mastodon-font-display', sans-serif;
-        font-size: 14px;
-        line-height: 24px;
-        font-weight: 500;
-        color: $ui-primary-color;
-        padding-bottom: 5px;
-        margin-bottom: 15px;
-        border-bottom: 1px solid lighten($ui-base-color, 4%);
-        text-overflow: ellipsis;
-        white-space: nowrap;
-        overflow: hidden;
-
-        a,
-        span {
-          font-weight: 400;
-          color: darken($ui-primary-color, 10%);
-        }
-
-        a {
-          text-decoration: none;
-        }
-      }
-    }
-
-    .owner {
-      text-align: center;
-
-      .avatar {
-        @include avatar-size(80px);
-        margin: 0 auto;
-        margin-bottom: 15px;
-
-        img {
-          @include avatar-radius();
-          @include avatar-size(80px);
-          display: block;
-        }
-      }
-
-      .name {
-        font-size: 14px;
-
-        a {
-          display: block;
-          color: $primary-text-color;
-          text-decoration: none;
-
-          &:hover {
-            .display_name {
-              text-decoration: underline;
-            }
-          }
-        }
-
-        .username {
-          display: block;
-          color: $ui-primary-color;
-        }
-      }
-    }
-  }
-
-  .features {
-    padding: 50px 0;
-
-    .container {
-      display: flex;
-    }
-
-    #mastodon-timeline {
-      display: flex;
-      -webkit-overflow-scrolling: touch;
-      -ms-overflow-style: -ms-autohiding-scrollbar;
-      font-family: 'mastodon-font-sans-serif', sans-serif;
-      font-size: 13px;
-      line-height: 18px;
-      font-weight: 400;
-      color: $primary-text-color;
-      width: 330px;
-      margin-right: 30px;
-      flex: 0 0 auto;
-      background: $ui-base-color;
-      overflow: hidden;
-      border-radius: 4px;
-      box-shadow: 0 0 6px rgba($black, 0.1);
-
-      .column-header {
-        color: inherit;
-        font-family: inherit;
-        font-size: 16px;
-        line-height: inherit;
-        font-weight: inherit;
-        margin: 0;
-        padding: 15px;
-      }
-
-      .column {
-        padding: 0;
-        border-radius: 4px;
-        overflow: hidden;
-      }
-
-      .scrollable {
-        height: 400px;
-      }
-
-      p {
-        font-size: inherit;
-        line-height: inherit;
-        font-weight: inherit;
-        color: $primary-text-color;
-        margin-bottom: 20px;
-
-        &:last-child {
-          margin-bottom: 0;
-        }
-
-        a {
-          color: $ui-secondary-color;
-          text-decoration: none;
-        }
-      }
-    }
-
-    .about-mastodon {
-      max-width: 675px;
-
-      p {
-        margin-bottom: 20px;
-      }
-
-      .features-list {
-        margin-top: 20px;
-
-        .features-list__row {
-          display: flex;
-          padding: 10px 0;
-          justify-content: space-between;
-
-          &:first-child {
-            padding-top: 0;
-          }
-
-          .visual {
-            flex: 0 0 auto;
-            display: flex;
-            align-items: center;
-            margin-left: 15px;
-
-            .fa {
-              display: block;
-              color: $ui-primary-color;
-              font-size: 48px;
-            }
-          }
-
-          .text {
-            font-size: 16px;
-            line-height: 30px;
-            color: $ui-primary-color;
-
-            h6 {
-              font-size: inherit;
-              line-height: inherit;
-              margin-bottom: 0;
-            }
-          }
-        }
-      }
-    }
-  }
-
-  .extended-description {
-    padding: 50px 0;
-    font-family: 'mastodon-font-sans-serif', sans-serif;
-    font-size: 16px;
-    font-weight: 400;
-    font-size: 16px;
-    line-height: 30px;
-    color: $ui-primary-color;
-
-    a {
-      color: $ui-highlight-color;
-      text-decoration: underline;
-    }
-  }
-
-  .footer-links {
-    padding-bottom: 50px;
-    text-align: right;
-    color: $ui-base-lighter-color;
-
-    p {
-      font-size: 14px;
-    }
-
-    a {
-      color: inherit;
-      text-decoration: underline;
-    }
-  }
-
-  @media screen and (max-width: 840px) {
-    .container {
-      padding: 0 20px;
-    }
-
-    .information-board {
-
-      .container {
-        padding-right: 20px;
-      }
-
-      .section {
-        text-align: center;
-      }
-
-      .panel {
-        position: static;
-        margin-top: 20px;
-        width: 100%;
-        border-radius: 4px;
-
-        .panel-header {
-          text-align: center;
-        }
-      }
-    }
-
-    .header-wrapper .mascot {
-      left: 20px;
-    }
-  }
-
-  @media screen and (max-width: 689px) {
-    .header-wrapper .mascot {
-      display: none;
-    }
-  }
-
-  @media screen and (max-width: 675px) {
-    .header-wrapper {
-      padding-top: 0;
-
-      &.compact {
-        padding-bottom: 0;
-      }
-
-      &.compact .hero .heading {
-        text-align: initial;
-      }
-    }
-
-    .header .container,
-    .features .container {
-      display: block;
-    }
-
-    .header {
-
-      .links {
-        padding-top: 15px;
-        background: darken($ui-base-color, 4%);
-
-        a {
-          padding: 12px 8px;
-        }
-
-        .nav {
-          display: flex;
-          flex-flow: row wrap;
-          justify-content: space-around;
-        }
-
-        .brand img {
-          left: 0;
-          top: 0;
-        }
-      }
-
-      .hero {
-        margin-top: 30px;
-        padding: 0;
-
-        .floats {
-          display: none;
-        }
-
-        .heading {
-          padding: 30px 20px;
-          text-align: center;
-        }
-
-        .simple_form,
-        .closed-registrations-message {
-          background: darken($ui-base-color, 8%);
-          width: 100%;
-          border-radius: 0;
-          box-sizing: border-box;
-        }
-      }
-    }
-
-    .features #mastodon-timeline {
-      height: 70vh;
-      width: 100%;
-      margin-bottom: 50px;
-
-      .column {
-        width: 100%;
-      }
-    }
-  }
-
-  .cta {
-    margin: 20px;
-  }
-
-  &.tag-page {
-    .features {
-      padding: 30px 0;
-
-      .container {
-        max-width: 820px;
-
-        #mastodon-timeline {
-          margin-right: 0;
-          border-top-right-radius: 0;
-        }
-
-        .about-mastodon {
-          .about-hashtag {
-            background: darken($ui-base-color, 4%);
-            padding: 0 20px 20px 30px;
-            border-radius: 0 5px 5px 0;
-
-            .brand {
-              padding-top: 20px;
-              margin-bottom: 20px;
-
-              img {
-                height: 48px;
-                width: auto;
-              }
-            }
-
-            p {
-              strong {
-                color: $ui-secondary-color;
-                font-weight: 700;
-              }
-            }
-
-            .cta {
-              margin: 0;
-
-              .button {
-                margin-right: 4px;
-              }
-            }
-          }
-
-          .features-list {
-            margin-left: 30px;
-            margin-right: 10px;
-          }
-        }
-      }
-    }
-
-    @media screen and (max-width: 675px) {
-      .features {
-        padding: 10px 0;
-
-        .container {
-          display: flex;
-          flex-direction: column;
-
-          #mastodon-timeline {
-            order: 2;
-            flex: 0 0 auto;
-            height: 60vh;
-            margin-bottom: 20px;
-            border-top-right-radius: 4px;
-          }
-
-          .about-mastodon {
-            order: 1;
-            flex: 0 0 auto;
-            max-width: 100%;
-
-            .about-hashtag {
-              background: unset;
-              padding: 0;
-              border-radius: 0;
-
-              .cta {
-                margin: 20px 0;
-              }
-            }
-
-            .features-list {
-              display: none;
-            }
-          }
-        }
-      }
-    }
-  }
-}
-
-@keyframes floating {
-  from {
-    transform: translate(0, 0);
-  }
-
-  65% {
-    transform: translate(0, 4px);
-  }
-
-  to {
-    transform: translate(0, -0);
-  }
-}
diff --git a/app/javascript/themes/glitch/styles/accounts.scss b/app/javascript/themes/glitch/styles/accounts.scss
deleted file mode 100644
index 2cf98c642..000000000
--- a/app/javascript/themes/glitch/styles/accounts.scss
+++ /dev/null
@@ -1,589 +0,0 @@
-.card {
-  background-color: lighten($ui-base-color, 4%);
-  background-size: cover;
-  background-position: center;
-  border-radius: 4px 4px 0 0;
-  box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
-  overflow: hidden;
-  position: relative;
-  display: flex;
-
-  &::after {
-    background: rgba(darken($ui-base-color, 8%), 0.5);
-    display: block;
-    content: "";
-    position: absolute;
-    left: 0;
-    top: 0;
-    width: 100%;
-    height: 100%;
-    z-index: 1;
-  }
-
-  @media screen and (max-width: 740px) {
-    border-radius: 0;
-    box-shadow: none;
-  }
-
-  .card__illustration {
-    padding: 60px 0;
-    position: relative;
-    flex: 1 1 auto;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-  }
-
-  .card__bio {
-    max-width: 260px;
-    flex: 1 1 auto;
-    display: flex;
-    flex-direction: column;
-    justify-content: space-between;
-    background: rgba(darken($ui-base-color, 8%), 0.8);
-    position: relative;
-    z-index: 2;
-  }
-
-  &.compact {
-    padding: 30px 0;
-    border-radius: 4px;
-
-    .avatar {
-      margin-bottom: 0;
-
-      img {
-        object-fit: cover;
-      }
-    }
-  }
-
-  .name {
-    display: block;
-    font-size: 20px;
-    line-height: 18px * 1.5;
-    color: $primary-text-color;
-    padding: 10px 15px;
-    padding-bottom: 0;
-    font-weight: 500;
-    position: relative;
-    z-index: 2;
-    margin-bottom: 30px;
-    overflow: hidden;
-    text-overflow: ellipsis;
-
-    small {
-      display: block;
-      font-size: 14px;
-      color: $ui-highlight-color;
-      font-weight: 400;
-      overflow: hidden;
-      text-overflow: ellipsis;
-    }
-  }
-
-  .avatar {
-    @include avatar-size(120px);
-    margin: 0 auto;
-    position: relative;
-    z-index: 2;
-
-    img {
-      @include avatar-radius();
-      @include avatar-size(120px);
-      display: block;
-      box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
-    }
-  }
-
-  .controls {
-    position: absolute;
-    top: 15px;
-    left: 15px;
-    z-index: 2;
-
-    .icon-button {
-      color: rgba($white, 0.8);
-      text-decoration: none;
-      font-size: 13px;
-      line-height: 13px;
-      font-weight: 500;
-
-      .fa {
-        font-weight: 400;
-        margin-right: 5px;
-      }
-
-      &:hover,
-      &:active,
-      &:focus {
-        color: $white;
-      }
-    }
-  }
-
-  .roles {
-    margin-bottom: 30px;
-    padding: 0 15px;
-  }
-
-  .details-counters {
-    margin-top: 30px;
-    display: flex;
-    flex-direction: row;
-    width: 100%;
-  }
-
-  .counter {
-    width: 33.3%;
-    box-sizing: border-box;
-    flex: 0 0 auto;
-    color: $ui-primary-color;
-    padding: 5px 10px 0;
-    margin-bottom: 10px;
-    border-right: 1px solid lighten($ui-base-color, 4%);
-    cursor: default;
-    text-align: center;
-    position: relative;
-
-    a {
-      display: block;
-    }
-
-    &:last-child {
-      border-right: 0;
-    }
-
-    &::after {
-      display: block;
-      content: "";
-      position: absolute;
-      bottom: -10px;
-      left: 0;
-      width: 100%;
-      border-bottom: 4px solid $ui-primary-color;
-      opacity: 0.5;
-      transition: all 400ms ease;
-    }
-
-    &.active {
-      &::after {
-        border-bottom: 4px solid $ui-highlight-color;
-        opacity: 1;
-      }
-    }
-
-    &:hover {
-      &::after {
-        opacity: 1;
-        transition-duration: 100ms;
-      }
-    }
-
-    a {
-      text-decoration: none;
-      color: inherit;
-    }
-
-    .counter-label {
-      font-size: 12px;
-      display: block;
-      margin-bottom: 5px;
-    }
-
-    .counter-number {
-      font-weight: 500;
-      font-size: 18px;
-      color: $primary-text-color;
-      font-family: 'mastodon-font-display', sans-serif;
-    }
-  }
-
-  .bio {
-    font-size: 14px;
-    line-height: 18px;
-    padding: 0 15px;
-    color: $ui-secondary-color;
-  }
-
-  .metadata {
-    $meta-table-border: darken($classic-highlight-color, 20%);//#174f77;
-
-    border-collapse: collapse;
-    padding: 0;
-    margin: 15px -15px -10px -15px;
-    border: 0 none;
-    border-top: 1px solid $meta-table-border;
-    border-bottom: 1px solid $meta-table-border;
-
-    td, th {
-      padding: 10px;
-      border: 0 none;
-      border-bottom: 1px solid $meta-table-border;
-      vertical-align: middle;
-    }
-
-    tr:last-child {
-      td, th {
-        border-bottom: 0 none;
-      }
-    }
-
-    td {
-      color: $ui-primary-color;
-      width:100%; // makes it stretch
-      padding-left: 0;
-    }
-
-    th {
-      padding-left: 15px;
-      font-weight: bold;
-      text-align: left;
-      width: 94px;
-      color: $ui-secondary-color;
-      background: darken($ui-base-color, 8%);
-      //background: #131415;
-    }
-
-    a {
-      color: $classic-highlight-color;
-    }
-  }
-
-  @media screen and (max-width: 480px) {
-    display: block;
-
-    .card__bio {
-      max-width: none;
-    }
-
-    .name,
-    .roles {
-      text-align: center;
-      margin-bottom: 15px;
-    }
-
-    .bio {
-      margin-bottom: 15px;
-    }
-  }
-}
-
-.pagination {
-  padding: 30px 0;
-  text-align: center;
-  overflow: hidden;
-
-  a,
-  .current,
-  .next,
-  .prev,
-  .page,
-  .gap {
-    font-size: 14px;
-    color: $primary-text-color;
-    font-weight: 500;
-    display: inline-block;
-    padding: 6px 10px;
-    text-decoration: none;
-  }
-
-  .current {
-    background: $simple-background-color;
-    border-radius: 100px;
-    color: $ui-base-color;
-    cursor: default;
-    margin: 0 10px;
-  }
-
-  .gap {
-    cursor: default;
-  }
-
-  .prev,
-  .next {
-    text-transform: uppercase;
-    color: $ui-secondary-color;
-  }
-
-  .prev {
-    float: left;
-    padding-left: 0;
-
-    .fa {
-      display: inline-block;
-      margin-right: 5px;
-    }
-  }
-
-  .next {
-    float: right;
-    padding-right: 0;
-
-    .fa {
-      display: inline-block;
-      margin-left: 5px;
-    }
-  }
-
-  .disabled {
-    cursor: default;
-    color: lighten($ui-base-color, 10%);
-  }
-
-  @media screen and (max-width: 700px) {
-    padding: 30px 20px;
-
-    .page {
-      display: none;
-    }
-
-    .next,
-    .prev {
-      display: inline-block;
-    }
-  }
-}
-
-.accounts-grid {
-  box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
-  background: darken($simple-background-color, 8%);
-  border-radius: 0 0 4px 4px;
-  padding: 20px 5px;
-  padding-bottom: 10px;
-  overflow: hidden;
-  display: flex;
-  flex-wrap: wrap;
-  z-index: 2;
-  position: relative;
-
-  @media screen and (max-width: 740px) {
-    border-radius: 0;
-    box-shadow: none;
-  }
-
-  .account-grid-card {
-    box-sizing: border-box;
-    width: 335px;
-    background: $simple-background-color;
-    border-radius: 4px;
-    color: $ui-base-color;
-    margin: 0 5px 10px;
-    position: relative;
-
-    @media screen and (max-width: 740px) {
-      width: calc(100% - 10px);
-    }
-
-    .account-grid-card__header {
-      overflow: hidden;
-      height: 100px;
-      border-radius: 4px 4px 0 0;
-      background-color: lighten($ui-base-color, 4%);
-      background-size: cover;
-      background-position: center;
-      position: relative;
-
-      &::after {
-        background: rgba(darken($ui-base-color, 8%), 0.5);
-        display: block;
-        content: "";
-        position: absolute;
-        left: 0;
-        top: 0;
-        width: 100%;
-        height: 100%;
-        z-index: 1;
-      }
-    }
-
-    .account-grid-card__avatar {
-      box-sizing: border-box;
-      padding: 15px;
-      position: absolute;
-      z-index: 2;
-      top: 100px - (40px + 2px);
-      left: -2px;
-    }
-
-    .avatar {
-      @include avatar-size(80px);
-
-      img {
-        display: block;
-        @include avatar-radius();
-        @include avatar-size(80px);
-        border: 2px solid $simple-background-color;
-        background: $simple-background-color;
-      }
-    }
-
-    .name {
-      padding: 15px;
-      padding-top: 10px;
-      padding-left: 15px + 80px + 15px;
-
-      a {
-        display: block;
-        color: $ui-base-color;
-        text-decoration: none;
-        text-overflow: ellipsis;
-        overflow: hidden;
-        font-weight: 500;
-
-        &:hover {
-          .display_name {
-            text-decoration: underline;
-          }
-        }
-      }
-    }
-
-    .display_name {
-      font-size: 16px;
-      display: block;
-      text-overflow: ellipsis;
-      overflow: hidden;
-    }
-
-    .username {
-      color: lighten($ui-base-color, 34%);
-      font-size: 14px;
-      font-weight: 400;
-    }
-
-    .note {
-      padding: 10px 15px;
-      padding-top: 15px;
-      box-sizing: border-box;
-      color: lighten($ui-base-color, 26%);
-      word-wrap: break-word;
-      min-height: 80px;
-    }
-  }
-}
-
-.nothing-here {
-  width: 100%;
-  display: block;
-  color: $ui-primary-color;
-  font-size: 14px;
-  font-weight: 500;
-  text-align: center;
-  padding: 60px 0;
-  padding-top: 55px;
-  cursor: default;
-}
-
-.account-card {
-  padding: 14px 10px;
-  background: $simple-background-color;
-  border-radius: 4px;
-  text-align: left;
-  box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
-
-  .detailed-status__display-name {
-    display: block;
-    overflow: hidden;
-    margin-bottom: 15px;
-
-    &:last-child {
-      margin-bottom: 0;
-    }
-
-    & > div {
-      @include avatar-size(48px);
-      float: left;
-      margin-right: 10px;
-    }
-
-    .avatar {
-      @include avatar-radius();
-      display: block;
-    }
-
-    .display-name {
-      display: block;
-      max-width: 100%;
-      overflow: hidden;
-      white-space: nowrap;
-      text-overflow: ellipsis;
-      cursor: default;
-
-      strong {
-        font-weight: 500;
-        color: $ui-base-color;
-      }
-
-      span {
-        font-size: 14px;
-        color: $ui-primary-color;
-      }
-    }
-
-    &:hover {
-      .display-name {
-        strong {
-          text-decoration: none;
-        }
-      }
-    }
-  }
-
-  .account__header__content {
-    font-size: 14px;
-    color: $ui-base-color;
-  }
-}
-
-.activity-stream-tabs {
-  background: $simple-background-color;
-  border-bottom: 1px solid $ui-secondary-color;
-  position: relative;
-  z-index: 2;
-
-  a {
-    display: inline-block;
-    padding: 15px;
-    text-decoration: none;
-    color: $ui-highlight-color;
-    text-transform: uppercase;
-    font-weight: 500;
-
-    &:hover,
-    &:active,
-    &:focus {
-      color: lighten($ui-highlight-color, 8%);
-    }
-
-    &.active {
-      color: $ui-base-color;
-      cursor: default;
-    }
-  }
-}
-
-.account-role {
-  display: inline-block;
-  padding: 4px 6px;
-  cursor: default;
-  border-radius: 3px;
-  font-size: 12px;
-  line-height: 12px;
-  font-weight: 500;
-  color: $ui-secondary-color;
-  background-color: rgba($ui-secondary-color, 0.1);
-  border: 1px solid rgba($ui-secondary-color, 0.5);
-
-  &.moderator {
-    color: $success-green;
-    background-color: rgba($success-green, 0.1);
-    border-color: rgba($success-green, 0.5);
-  }
-
-  &.admin {
-    color: lighten($error-red, 12%);
-    background-color: rgba(lighten($error-red, 12%), 0.1);
-    border-color: rgba(lighten($error-red, 12%), 0.5);
-  }
-}
diff --git a/app/javascript/themes/glitch/styles/admin.scss b/app/javascript/themes/glitch/styles/admin.scss
deleted file mode 100644
index 87bc710af..000000000
--- a/app/javascript/themes/glitch/styles/admin.scss
+++ /dev/null
@@ -1,349 +0,0 @@
-.admin-wrapper {
-  display: flex;
-  justify-content: center;
-  height: 100%;
-
-  .sidebar-wrapper {
-    flex: 1;
-    height: 100%;
-    background: $ui-base-color;
-    display: flex;
-    justify-content: flex-end;
-  }
-
-  .sidebar {
-    width: 240px;
-    height: 100%;
-    padding: 0;
-    overflow-y: auto;
-
-    .logo {
-      display: block;
-      margin: 40px auto;
-      width: 100px;
-      height: 100px;
-    }
-
-    ul {
-      list-style: none;
-      border-radius: 4px 0 0 4px;
-      overflow: hidden;
-      margin-bottom: 20px;
-
-      a {
-        display: block;
-        padding: 15px;
-        color: rgba($primary-text-color, 0.7);
-        text-decoration: none;
-        transition: all 200ms linear;
-        border-radius: 4px 0 0 4px;
-
-        i.fa {
-          margin-right: 5px;
-        }
-
-        &:hover {
-          color: $primary-text-color;
-          background-color: darken($ui-base-color, 5%);
-          transition: all 100ms linear;
-        }
-
-        &.selected {
-          background: darken($ui-base-color, 2%);
-          border-radius: 4px 0 0;
-        }
-      }
-
-      ul {
-        background: darken($ui-base-color, 4%);
-        border-radius: 0 0 0 4px;
-        margin: 0;
-
-        a {
-          border: 0;
-          padding: 15px 35px;
-
-          &.selected {
-            color: $primary-text-color;
-            background-color: $ui-highlight-color;
-            border-bottom: 0;
-            border-radius: 0;
-
-            &:hover {
-              background-color: lighten($ui-highlight-color, 5%);
-            }
-          }
-        }
-      }
-    }
-  }
-
-  .content-wrapper {
-    flex: 2;
-    overflow: auto;
-  }
-
-  .content {
-    max-width: 700px;
-    padding: 20px 15px;
-    padding-top: 60px;
-    padding-left: 25px;
-
-    h2 {
-      color: $ui-secondary-color;
-      font-size: 24px;
-      line-height: 28px;
-      font-weight: 400;
-      margin-bottom: 40px;
-    }
-
-    h3 {
-      color: $ui-secondary-color;
-      font-size: 20px;
-      line-height: 28px;
-      font-weight: 400;
-      margin-bottom: 30px;
-    }
-
-    h6 {
-      font-size: 16px;
-      color: $ui-secondary-color;
-      line-height: 28px;
-      font-weight: 400;
-    }
-
-    & > p {
-      font-size: 14px;
-      line-height: 18px;
-      color: $ui-secondary-color;
-      margin-bottom: 20px;
-
-      strong {
-        color: $primary-text-color;
-        font-weight: 500;
-      }
-    }
-
-    hr {
-      margin: 20px 0;
-      border: 0;
-      background: transparent;
-      border-bottom: 1px solid $ui-base-color;
-    }
-
-    .muted-hint {
-      color: $ui-primary-color;
-
-      a {
-        color: $ui-highlight-color;
-      }
-    }
-
-    .positive-hint {
-      color: $valid-value-color;
-      font-weight: 500;
-    }
-  }
-
-  .simple_form {
-    max-width: 400px;
-
-    &.edit_user,
-    &.new_form_admin_settings,
-    &.new_form_two_factor_confirmation,
-    &.new_form_delete_confirmation,
-    &.new_import,
-    &.new_domain_block,
-    &.edit_domain_block {
-      max-width: none;
-    }
-
-    .form_two_factor_confirmation_code,
-    .form_delete_confirmation_password {
-      max-width: 400px;
-    }
-
-    .actions {
-      max-width: 400px;
-    }
-  }
-
-  @media screen and (max-width: 600px) {
-    display: block;
-    overflow-y: auto;
-    -webkit-overflow-scrolling: touch;
-
-    .sidebar-wrapper,
-    .content-wrapper {
-      flex: 0 0 auto;
-      height: auto;
-      overflow: initial;
-    }
-
-    .sidebar {
-      width: 100%;
-      padding: 10px 0;
-      height: auto;
-
-      .logo {
-        margin: 20px auto;
-      }
-    }
-
-    .content {
-      padding-top: 20px;
-    }
-  }
-}
-
-.filters {
-  display: flex;
-  flex-wrap: wrap;
-
-  .filter-subset {
-    flex: 0 0 auto;
-    margin: 0 40px 10px 0;
-
-    &:last-child {
-      margin-bottom: 20px;
-    }
-
-    ul {
-      margin-top: 5px;
-      list-style: none;
-
-      li {
-        display: inline-block;
-        margin-right: 5px;
-      }
-    }
-
-    strong {
-      font-weight: 500;
-      text-transform: uppercase;
-      font-size: 12px;
-    }
-
-    a {
-      display: inline-block;
-      color: rgba($primary-text-color, 0.7);
-      text-decoration: none;
-      text-transform: uppercase;
-      font-size: 12px;
-      font-weight: 500;
-      border-bottom: 2px solid $ui-base-color;
-
-      &:hover {
-        color: $primary-text-color;
-        border-bottom: 2px solid lighten($ui-base-color, 5%);
-      }
-
-      &.selected {
-        color: $ui-highlight-color;
-        border-bottom: 2px solid $ui-highlight-color;
-      }
-    }
-  }
-}
-
-.report-accounts {
-  display: flex;
-  flex-wrap: wrap;
-  margin-bottom: 20px;
-}
-
-.report-accounts__item {
-  display: flex;
-  flex: 250px;
-  flex-direction: column;
-  margin: 0 5px;
-
-  & > strong {
-    display: block;
-    margin: 0 0 10px -5px;
-    font-weight: 500;
-    font-size: 14px;
-    line-height: 18px;
-    color: $ui-secondary-color;
-  }
-
-  .account-card {
-    flex: 1 1 auto;
-  }
-}
-
-.report-status,
-.account-status {
-  display: flex;
-  margin-bottom: 10px;
-
-  .activity-stream {
-    flex: 2 0 0;
-    margin-right: 20px;
-    max-width: calc(100% - 60px);
-
-    .entry {
-      border-radius: 4px;
-    }
-  }
-}
-
-.report-status__actions,
-.account-status__actions {
-  flex: 0 0 auto;
-  display: flex;
-  flex-direction: column;
-
-  .icon-button {
-    font-size: 24px;
-    width: 24px;
-    text-align: center;
-    margin-bottom: 10px;
-  }
-}
-
-.batch-form-box {
-  display: flex;
-  flex-wrap: wrap;
-  margin-bottom: 5px;
-
-  #form_status_batch_action {
-    margin: 0 5px 5px 0;
-    font-size: 14px;
-  }
-
-  input.button {
-    margin: 0 5px 5px 0;
-  }
-
-  .media-spoiler-toggle-buttons {
-    margin-left: auto;
-
-    .button {
-      overflow: visible;
-      margin: 0 0 5px 5px;
-      float: right;
-    }
-  }
-}
-
-.batch-checkbox,
-.batch-checkbox-all {
-  display: flex;
-  align-items: center;
-  margin-right: 5px;
-}
-
-.back-link {
-  margin-bottom: 10px;
-  font-size: 14px;
-
-  a {
-    color: $classic-highlight-color;
-    text-decoration: none;
-
-    &:hover {
-      text-decoration: underline;
-    }
-  }
-}
diff --git a/app/javascript/themes/glitch/styles/basics.scss b/app/javascript/themes/glitch/styles/basics.scss
deleted file mode 100644
index b5d77ff63..000000000
--- a/app/javascript/themes/glitch/styles/basics.scss
+++ /dev/null
@@ -1,122 +0,0 @@
-body {
-  font-family: 'mastodon-font-sans-serif', sans-serif;
-  background: $ui-base-color;
-  background-size: cover;
-  background-attachment: fixed;
-  font-size: 13px;
-  line-height: 18px;
-  font-weight: 400;
-  color: $primary-text-color;
-  padding-bottom: 20px;
-  text-rendering: optimizelegibility;
-  font-feature-settings: "kern";
-  text-size-adjust: none;
-  -webkit-tap-highlight-color: rgba(0,0,0,0);
-  -webkit-tap-highlight-color: transparent;
-
-  &.system-font {
-    // system-ui => standard property (Chrome/Android WebView 56+, Opera 43+, Safari 11+)
-    // -apple-system => Safari <11 specific
-    // BlinkMacSystemFont => Chrome <56 on macOS specific
-    // Segoe UI => Windows 7/8/10
-    // Oxygen => KDE
-    // Ubuntu => Unity/Ubuntu
-    // Cantarell => GNOME
-    // Fira Sans => Firefox OS
-    // Droid Sans => Older Androids (<4.0)
-    // Helvetica Neue => Older macOS <10.11
-    // mastodon-font-sans-serif => web-font (Roboto) fallback and newer Androids (>=4.0)
-    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", mastodon-font-sans-serif, sans-serif;
-  }
-
-  &.app-body {
-    position: absolute;
-    width: 100%;
-    height: 100%;
-    padding: 0;
-    background: $ui-base-color;
-  }
-
-  &.about-body {
-    background: darken($ui-base-color, 8%);
-    padding-bottom: 0;
-  }
-
-  &.tag-body {
-    background: darken($ui-base-color, 8%);
-    padding-bottom: 0;
-  }
-
-  &.embed {
-    background: transparent;
-    margin: 0;
-    padding-bottom: 0;
-
-    .container {
-      position: absolute;
-      width: 100%;
-      height: 100%;
-      overflow: hidden;
-    }
-  }
-
-  &.admin {
-    background: darken($ui-base-color, 4%);
-    position: fixed;
-    width: 100%;
-    height: 100%;
-    padding: 0;
-  }
-
-  &.error {
-    position: absolute;
-    text-align: center;
-    color: $ui-primary-color;
-    background: $ui-base-color;
-    width: 100%;
-    height: 100%;
-    padding: 0;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-
-    .dialog {
-      vertical-align: middle;
-      margin: 20px;
-
-      img {
-        display: block;
-        max-width: 470px;
-        width: 100%;
-        height: auto;
-        margin-top: -120px;
-      }
-
-      h1 {
-        font-size: 20px;
-        line-height: 28px;
-        font-weight: 400;
-      }
-    }
-  }
-}
-
-button {
-  font-family: inherit;
-  cursor: pointer;
-
-  &:focus {
-    outline: none;
-  }
-}
-
-.app-holder {
-  &,
-  & > div {
-    display: flex;
-    width: 100%;
-    height: 100%;
-    align-items: center;
-    justify-content: center;
-  }
-}
diff --git a/app/javascript/themes/glitch/styles/boost.scss b/app/javascript/themes/glitch/styles/boost.scss
deleted file mode 100644
index b07b72f8e..000000000
--- a/app/javascript/themes/glitch/styles/boost.scss
+++ /dev/null
@@ -1,28 +0,0 @@
-@function hex-color($color) {
-  @if type-of($color) == 'color' {
-    $color: str-slice(ie-hex-str($color), 4);
-  }
-  @return '%23' + unquote($color)
-}
-
-button.icon-button i.fa-retweet {
-  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='209'><path d='M4.97 3.16c-.1.03-.17.1-.22.18L.8 8.24c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77L5.5 3.35c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.02-2.4.02H7.1l2.32 2.85.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($ui-base-lighter-color)}' stroke-width='0'/><path d='M7.78 19.66c-.24.02-.44.25-.44.5v2.46h-.06c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v4.47c0 4.26-.56 3.62 3.65 3.62H8.5l-1.3-1.06c-.1-.08-.18-.2-.2-.3-.02-.17.06-.35.2-.45l1.33-1.1H7.28c-.44 0-.72-.3-.72-.7v-4.48c0-.44.28-.72.72-.72h.06v2.5c0 .38.54.63.82.38l4.9-3.93c.25-.18.25-.6 0-.78l-4.9-3.92c-.1-.1-.24-.14-.38-.12zm9.34 2.93c-.54-.02-1.3.02-2.4.02h-1.25l1.3 1.07c.1.07.18.2.2.33.02.16-.06.3-.2.4l-1.33 1.1h1.28c.42 0 .72.28.72.72v4.47c0 .42-.3.72-.72.72h-.1v-2.47c0-.3-.3-.53-.6-.47-.07 0-.14.05-.2.1l-4.9 3.93c-.26.18-.26.6 0 .78l4.9 3.92c.27.25.82 0 .8-.38v-2.5h.1c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.15.4-3.62-1.25-3.66zM10.34 38.66c-.24.02-.44.25-.43.5v2.47H7.3c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.47c0 3.66-.23 3.7 2.34 3.66l-1.34-1.1c-.1-.08-.18-.2-.2-.3 0-.17.07-.35.2-.45l1.96-1.6c-.03-.06-.04-.13-.04-.2v-4.48c0-.44.28-.72.72-.72H9.9v2.5c0 .36.5.6.8.38l4.93-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.08-.23-.13-.36-.12zm5.63 2.93l1.34 1.1c.1.07.18.2.2.33.02.16-.03.3-.16.4l-1.96 1.6c.02.07.06.13.06.22v4.47c0 .42-.3.72-.72.72h-2.66v-2.47c0-.3-.3-.53-.6-.47-.06.02-.12.05-.18.1l-4.94 3.93c-.24.18-.24.6 0 .78l4.94 3.92c.28.22.78-.02.78-.38v-2.5h2.66c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.66.34-3.7-2.4-3.66zM13.06 57.66c-.23.03-.4.26-.4.5v2.47H7.28c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.87l2.93-2.37v-2.5c0-.44.28-.72.72-.72h5.38v2.5c0 .36.5.6.78.38l4.94-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.1-.24-.14-.38-.12zm5.3 6.15l-2.92 2.4v2.52c0 .42-.3.72-.72.72h-5.4v-2.47c0-.3-.32-.53-.6-.47-.07.02-.13.05-.2.1L3.6 70.52c-.25.18-.25.6 0 .78l4.93 3.92c.28.22.78-.02.78-.38v-2.5h5.42c4.27 0 3.65.67 3.65-3.62v-4.47-.44zM19.25 78.8c-.1.03-.2.1-.28.17l-.9.9c-.44-.3-1.36-.25-3.35-.25H7.28c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v.7l2.93.3v-1c0-.44.28-.72.72-.72h7.44c.2 0 .37.08.5.2l-1.8 1.8c-.25.26-.08.76.27.8l6.27.7c.28.03.56-.25.53-.53l-.7-6.25c0-.27-.3-.48-.55-.44zm-17.2 6.1c-.2.07-.36.3-.33.54l.7 6.25c.02.36.58.55.83.27l.8-.8c.02 0 .04-.02.04 0 .46.24 1.37.17 3.18.17h7.44c4.27 0 3.65.67 3.65-3.62v-.75l-2.93-.3v1.05c0 .42-.3.72-.72.72H7.28c-.15 0-.3-.03-.4-.1L8.8 86.4c.3-.24.1-.8-.27-.84l-6.28-.65h-.2zM4.88 98.6c-1.33 0-1.34.48-1.3 2.3l1.14-1.37c.08-.1.22-.17.34-.2.16 0 .34.08.44.2l1.66 2.03c.04 0 .07-.03.12-.03h7.44c.34 0 .57.2.65.5h-2.43c-.34.05-.53.52-.3.78l3.92 4.95c.18.24.6.24.78 0l3.94-4.94c.22-.27-.02-.76-.37-.77H18.4c.02-3.9.6-3.4-3.66-3.4H7.28c-1.08 0-1.86-.04-2.4-.04zm.15 2.46c-.1.03-.2.1-.28.2l-3.94 4.9c-.2.28.03.77.4.78H3.6c-.02 3.94-.45 3.4 3.66 3.4h7.44c3.65 0 3.74.3 3.7-2.25l-1.1 1.34c-.1.1-.2.17-.32.2-.16 0-.34-.08-.44-.2l-1.65-2.03c-.06.02-.1.04-.18.04H7.28c-.35 0-.57-.2-.66-.5h2.44c.37 0 .63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.23-.47-.2zM4.88 117.6c-1.16 0-1.3.3-1.3 1.56l1.14-1.38c.08-.1.22-.14.34-.16.16 0 .34.04.44.16l2.22 2.75h7c.42 0 .72.28.72.72v.53h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-.53c0-4.2.72-3.63-3.66-3.63H7.28c-1.08 0-1.86-.03-2.4-.03zm.1 1.74c-.1.03-.17.1-.23.16L.8 124.44c-.2.28.03.77.4.78H3.6v.5c0 4.26-.55 3.62 3.66 3.62h7.44c1.03 0 1.74.02 2.28 0-.16.02-.34-.03-.44-.15l-2.22-2.76H7.28c-.44 0-.72-.3-.72-.72v-.5h2.5c.37.02.63-.5.4-.78L5.5 119.5c-.12-.15-.34-.22-.53-.16zm12.02 10c1.2-.02 1.4-.25 1.4-1.53l-1.1 1.36c-.07.1-.17.17-.3.18zM5.94 136.6l2.37 2.93h6.42c.42 0 .72.28.72.72v1.25h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.25c0-4.2.72-3.63-3.66-3.63H7.28c-.6 0-.92-.02-1.34-.03zm-1.72.06c-.4.08-.54.3-.6.75l.6-.74zm.84.93c-.12 0-.24.08-.3.18l-3.95 4.9c-.24.3 0 .83.4.82H3.6v1.22c0 4.26-.55 3.62 3.66 3.62h7.44c.63 0 .97.02 1.4.03l-2.37-2.93H7.28c-.44 0-.72-.3-.72-.72v-1.22h2.5c.4.04.67-.53.4-.8l-3.96-4.92c-.1-.13-.27-.2-.44-.2zm13.28 10.03l-.56.7c.36-.07.5-.3.56-.7zM17.13 155.6c-.55-.02-1.32.03-2.4.03h-8.2l2.38 2.9h5.82c.42 0 .72.28.72.72v1.97H12.9c-.32.06-.48.52-.28.78l3.94 4.94c.2.23.6.22.78-.03l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.97c0-3.15.4-3.62-1.25-3.66zm-12.1.28c-.1.02-.2.1-.28.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v1.96c0 4.26-.55 3.62 3.66 3.62h8.24l-2.36-2.9H7.28c-.44 0-.72-.3-.72-.72v-1.97h2.5c.37.02.63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.22-.47-.2zM5.13 174.5c-.15 0-.3.07-.38.2L.8 179.6c-.24.27 0 .82.4.8H3.6v2.32c0 4.26-.55 3.62 3.66 3.62h7.94l-2.35-2.9h-5.6c-.43 0-.7-.3-.7-.72v-2.3h2.5c.38.03.66-.54.4-.83l-3.97-4.9c-.1-.13-.23-.2-.38-.2zm12 .1c-.55-.02-1.32.03-2.4.03H6.83l2.35 2.9h5.52c.42 0 .72.28.72.72v2.34h-2.6c-.3.1-.43.53-.2.78l3.92 4.9c.18.24.6.24.78 0l3.94-4.9c.22-.3-.02-.78-.37-.8H18.4v-2.33c0-3.15.4-3.62-1.25-3.66zM4.97 193.16c-.1.03-.17.1-.22.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77l-3.96-4.9c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.03-2.4.03H7.1l2.32 2.84.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($ui-highlight-color)}' stroke-width='0'/></svg>");
-
-  &:hover {
-    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='209'><path d='M4.97 3.16c-.1.03-.17.1-.22.18L.8 8.24c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77L5.5 3.35c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.02-2.4.02H7.1l2.32 2.85.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color(lighten($ui-base-color, 33%))}' stroke-width='0'/><path d='M7.78 19.66c-.24.02-.44.25-.44.5v2.46h-.06c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v4.47c0 4.26-.56 3.62 3.65 3.62H8.5l-1.3-1.06c-.1-.08-.18-.2-.2-.3-.02-.17.06-.35.2-.45l1.33-1.1H7.28c-.44 0-.72-.3-.72-.7v-4.48c0-.44.28-.72.72-.72h.06v2.5c0 .38.54.63.82.38l4.9-3.93c.25-.18.25-.6 0-.78l-4.9-3.92c-.1-.1-.24-.14-.38-.12zm9.34 2.93c-.54-.02-1.3.02-2.4.02h-1.25l1.3 1.07c.1.07.18.2.2.33.02.16-.06.3-.2.4l-1.33 1.1h1.28c.42 0 .72.28.72.72v4.47c0 .42-.3.72-.72.72h-.1v-2.47c0-.3-.3-.53-.6-.47-.07 0-.14.05-.2.1l-4.9 3.93c-.26.18-.26.6 0 .78l4.9 3.92c.27.25.82 0 .8-.38v-2.5h.1c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.15.4-3.62-1.25-3.66zM10.34 38.66c-.24.02-.44.25-.43.5v2.47H7.3c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.47c0 3.66-.23 3.7 2.34 3.66l-1.34-1.1c-.1-.08-.18-.2-.2-.3 0-.17.07-.35.2-.45l1.96-1.6c-.03-.06-.04-.13-.04-.2v-4.48c0-.44.28-.72.72-.72H9.9v2.5c0 .36.5.6.8.38l4.93-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.08-.23-.13-.36-.12zm5.63 2.93l1.34 1.1c.1.07.18.2.2.33.02.16-.03.3-.16.4l-1.96 1.6c.02.07.06.13.06.22v4.47c0 .42-.3.72-.72.72h-2.66v-2.47c0-.3-.3-.53-.6-.47-.06.02-.12.05-.18.1l-4.94 3.93c-.24.18-.24.6 0 .78l4.94 3.92c.28.22.78-.02.78-.38v-2.5h2.66c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.66.34-3.7-2.4-3.66zM13.06 57.66c-.23.03-.4.26-.4.5v2.47H7.28c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.87l2.93-2.37v-2.5c0-.44.28-.72.72-.72h5.38v2.5c0 .36.5.6.78.38l4.94-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.1-.24-.14-.38-.12zm5.3 6.15l-2.92 2.4v2.52c0 .42-.3.72-.72.72h-5.4v-2.47c0-.3-.32-.53-.6-.47-.07.02-.13.05-.2.1L3.6 70.52c-.25.18-.25.6 0 .78l4.93 3.92c.28.22.78-.02.78-.38v-2.5h5.42c4.27 0 3.65.67 3.65-3.62v-4.47-.44zM19.25 78.8c-.1.03-.2.1-.28.17l-.9.9c-.44-.3-1.36-.25-3.35-.25H7.28c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v.7l2.93.3v-1c0-.44.28-.72.72-.72h7.44c.2 0 .37.08.5.2l-1.8 1.8c-.25.26-.08.76.27.8l6.27.7c.28.03.56-.25.53-.53l-.7-6.25c0-.27-.3-.48-.55-.44zm-17.2 6.1c-.2.07-.36.3-.33.54l.7 6.25c.02.36.58.55.83.27l.8-.8c.02 0 .04-.02.04 0 .46.24 1.37.17 3.18.17h7.44c4.27 0 3.65.67 3.65-3.62v-.75l-2.93-.3v1.05c0 .42-.3.72-.72.72H7.28c-.15 0-.3-.03-.4-.1L8.8 86.4c.3-.24.1-.8-.27-.84l-6.28-.65h-.2zM4.88 98.6c-1.33 0-1.34.48-1.3 2.3l1.14-1.37c.08-.1.22-.17.34-.2.16 0 .34.08.44.2l1.66 2.03c.04 0 .07-.03.12-.03h7.44c.34 0 .57.2.65.5h-2.43c-.34.05-.53.52-.3.78l3.92 4.95c.18.24.6.24.78 0l3.94-4.94c.22-.27-.02-.76-.37-.77H18.4c.02-3.9.6-3.4-3.66-3.4H7.28c-1.08 0-1.86-.04-2.4-.04zm.15 2.46c-.1.03-.2.1-.28.2l-3.94 4.9c-.2.28.03.77.4.78H3.6c-.02 3.94-.45 3.4 3.66 3.4h7.44c3.65 0 3.74.3 3.7-2.25l-1.1 1.34c-.1.1-.2.17-.32.2-.16 0-.34-.08-.44-.2l-1.65-2.03c-.06.02-.1.04-.18.04H7.28c-.35 0-.57-.2-.66-.5h2.44c.37 0 .63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.23-.47-.2zM4.88 117.6c-1.16 0-1.3.3-1.3 1.56l1.14-1.38c.08-.1.22-.14.34-.16.16 0 .34.04.44.16l2.22 2.75h7c.42 0 .72.28.72.72v.53h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-.53c0-4.2.72-3.63-3.66-3.63H7.28c-1.08 0-1.86-.03-2.4-.03zm.1 1.74c-.1.03-.17.1-.23.16L.8 124.44c-.2.28.03.77.4.78H3.6v.5c0 4.26-.55 3.62 3.66 3.62h7.44c1.03 0 1.74.02 2.28 0-.16.02-.34-.03-.44-.15l-2.22-2.76H7.28c-.44 0-.72-.3-.72-.72v-.5h2.5c.37.02.63-.5.4-.78L5.5 119.5c-.12-.15-.34-.22-.53-.16zm12.02 10c1.2-.02 1.4-.25 1.4-1.53l-1.1 1.36c-.07.1-.17.17-.3.18zM5.94 136.6l2.37 2.93h6.42c.42 0 .72.28.72.72v1.25h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.25c0-4.2.72-3.63-3.66-3.63H7.28c-.6 0-.92-.02-1.34-.03zm-1.72.06c-.4.08-.54.3-.6.75l.6-.74zm.84.93c-.12 0-.24.08-.3.18l-3.95 4.9c-.24.3 0 .83.4.82H3.6v1.22c0 4.26-.55 3.62 3.66 3.62h7.44c.63 0 .97.02 1.4.03l-2.37-2.93H7.28c-.44 0-.72-.3-.72-.72v-1.22h2.5c.4.04.67-.53.4-.8l-3.96-4.92c-.1-.13-.27-.2-.44-.2zm13.28 10.03l-.56.7c.36-.07.5-.3.56-.7zM17.13 155.6c-.55-.02-1.32.03-2.4.03h-8.2l2.38 2.9h5.82c.42 0 .72.28.72.72v1.97H12.9c-.32.06-.48.52-.28.78l3.94 4.94c.2.23.6.22.78-.03l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.97c0-3.15.4-3.62-1.25-3.66zm-12.1.28c-.1.02-.2.1-.28.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v1.96c0 4.26-.55 3.62 3.66 3.62h8.24l-2.36-2.9H7.28c-.44 0-.72-.3-.72-.72v-1.97h2.5c.37.02.63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.22-.47-.2zM5.13 174.5c-.15 0-.3.07-.38.2L.8 179.6c-.24.27 0 .82.4.8H3.6v2.32c0 4.26-.55 3.62 3.66 3.62h7.94l-2.35-2.9h-5.6c-.43 0-.7-.3-.7-.72v-2.3h2.5c.38.03.66-.54.4-.83l-3.97-4.9c-.1-.13-.23-.2-.38-.2zm12 .1c-.55-.02-1.32.03-2.4.03H6.83l2.35 2.9h5.52c.42 0 .72.28.72.72v2.34h-2.6c-.3.1-.43.53-.2.78l3.92 4.9c.18.24.6.24.78 0l3.94-4.9c.22-.3-.02-.78-.37-.8H18.4v-2.33c0-3.15.4-3.62-1.25-3.66zM4.97 193.16c-.1.03-.17.1-.22.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77l-3.96-4.9c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.03-2.4.03H7.1l2.32 2.84.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($ui-highlight-color)}' stroke-width='0'/></svg>");
-  }
-}
-
-// Disabled variant
-button.icon-button.disabled i.fa-retweet {
-  &, &:hover {
-    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='209'><path d='M4.97 3.16c-.1.03-.17.1-.22.18L.8 8.24c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77L5.5 3.35c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.02-2.4.02H7.1l2.32 2.85.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color(lighten($ui-base-color, 13%))}' stroke-width='0'/></svg>");
-  }
-}
-
-// Disabled variant for use with DMs
-.status-direct button.icon-button.disabled i.fa-retweet {
-  &, &:hover {
-    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='209'><path d='M4.97 3.16c-.1.03-.17.1-.22.18L.8 8.24c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77L5.5 3.35c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.02-2.4.02H7.1l2.32 2.85.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color(lighten($ui-base-color, 16%))}' stroke-width='0'/></svg>");
-  }
-}
diff --git a/app/javascript/themes/glitch/styles/compact_header.scss b/app/javascript/themes/glitch/styles/compact_header.scss
deleted file mode 100644
index 90d98cc8c..000000000
--- a/app/javascript/themes/glitch/styles/compact_header.scss
+++ /dev/null
@@ -1,34 +0,0 @@
-.compact-header {
-  h1 {
-    font-size: 24px;
-    line-height: 28px;
-    color: $ui-primary-color;
-    font-weight: 500;
-    margin-bottom: 20px;
-    padding: 0 10px;
-    word-wrap: break-word;
-
-    @media screen and (max-width: 740px) {
-      text-align: center;
-      padding: 20px 10px 0;
-    }
-
-    a {
-      color: inherit;
-      text-decoration: none;
-    }
-
-    small {
-      font-weight: 400;
-      color: $ui-secondary-color;
-    }
-
-    img {
-      display: inline-block;
-      margin-bottom: -5px;
-      margin-right: 15px;
-      width: 36px;
-      height: 36px;
-    }
-  }
-}
diff --git a/app/javascript/themes/glitch/styles/components.scss b/app/javascript/themes/glitch/styles/components.scss
deleted file mode 100644
index ade8df018..000000000
--- a/app/javascript/themes/glitch/styles/components.scss
+++ /dev/null
@@ -1,4832 +0,0 @@
-@import 'variables';
-
-.app-body {
-  -webkit-overflow-scrolling: touch;
-  -ms-overflow-style: -ms-autohiding-scrollbar;
-}
-
-.button {
-  background-color: darken($ui-highlight-color, 3%);
-  border: 10px none;
-  border-radius: 4px;
-  box-sizing: border-box;
-  color: $primary-text-color;
-  cursor: pointer;
-  display: inline-block;
-  font-family: inherit;
-  font-size: 14px;
-  font-weight: 500;
-  height: 36px;
-  letter-spacing: 0;
-  line-height: 36px;
-  overflow: hidden;
-  padding: 0 16px;
-  position: relative;
-  text-align: center;
-  text-transform: uppercase;
-  text-decoration: none;
-  text-overflow: ellipsis;
-  transition: all 100ms ease-in;
-  white-space: nowrap;
-  width: auto;
-
-  &:active,
-  &:focus,
-  &:hover {
-    background-color: lighten($ui-highlight-color, 7%);
-    transition: all 200ms ease-out;
-  }
-
-  &:disabled {
-    background-color: $ui-primary-color;
-    cursor: default;
-  }
-
-  &.button-alternative {
-    font-size: 16px;
-    line-height: 36px;
-    height: auto;
-    color: $ui-base-color;
-    background: $ui-primary-color;
-    text-transform: none;
-    padding: 4px 16px;
-
-    &:active,
-    &:focus,
-    &:hover {
-      background-color: lighten($ui-primary-color, 4%);
-    }
-  }
-
-  &.button-secondary {
-    font-size: 16px;
-    line-height: 36px;
-    height: auto;
-    color: $ui-primary-color;
-    text-transform: none;
-    background: transparent;
-    padding: 3px 15px;
-    border-radius: 4px;
-    border: 1px solid $ui-primary-color;
-
-    &:active,
-    &:focus,
-    &:hover {
-      border-color: lighten($ui-primary-color, 4%);
-      color: lighten($ui-primary-color, 4%);
-    }
-  }
-
-  &.button--block {
-    display: block;
-    width: 100%;
-  }
-}
-
-.column__wrapper {
-  display: flex;
-  flex: 1 1 auto;
-  position: relative;
-}
-
-.column-icon {
-  background: lighten($ui-base-color, 4%);
-  color: $ui-primary-color;
-  cursor: pointer;
-  font-size: 16px;
-  padding: 15px;
-  position: absolute;
-  right: 0;
-  top: -48px;
-  z-index: 3;
-
-  &:hover {
-    color: lighten($ui-primary-color, 7%);
-  }
-}
-
-.icon-button {
-  display: inline-block;
-  padding: 0;
-  color: $ui-base-lighter-color;
-  border: none;
-  background: transparent;
-  cursor: pointer;
-  transition: color 100ms ease-in;
-
-  &:hover,
-  &:active,
-  &:focus {
-    color: lighten($ui-base-color, 33%);
-    transition: color 200ms ease-out;
-  }
-
-  &.disabled {
-    color: lighten($ui-base-color, 13%);
-    cursor: default;
-  }
-
-  &.active {
-    color: $ui-highlight-color;
-  }
-
-  &::-moz-focus-inner {
-    border: 0;
-  }
-
-  &::-moz-focus-inner,
-  &:focus,
-  &:active {
-    outline: 0 !important;
-  }
-
-  &.inverted {
-    color: lighten($ui-base-color, 33%);
-
-    &:hover,
-    &:active,
-    &:focus {
-      color: $ui-base-lighter-color;
-    }
-
-    &.disabled {
-      color: $ui-primary-color;
-    }
-
-    &.active {
-      color: $ui-highlight-color;
-
-      &.disabled {
-        color: lighten($ui-highlight-color, 13%);
-      }
-    }
-  }
-
-  &.overlayed {
-    box-sizing: content-box;
-    background: rgba($base-overlay-background, 0.6);
-    color: rgba($primary-text-color, 0.7);
-    border-radius: 4px;
-    padding: 2px;
-
-    &:hover {
-      background: rgba($base-overlay-background, 0.9);
-    }
-  }
-}
-
-.text-icon-button {
-  color: lighten($ui-base-color, 33%);
-  border: none;
-  background: transparent;
-  cursor: pointer;
-  font-weight: 600;
-  font-size: 11px;
-  padding: 0 3px;
-  line-height: 27px;
-  outline: 0;
-  transition: color 100ms ease-in;
-
-  &:hover,
-  &:active,
-  &:focus {
-    color: $ui-base-lighter-color;
-    transition: color 200ms ease-out;
-  }
-
-  &.disabled {
-    color: lighten($ui-base-color, 13%);
-    cursor: default;
-  }
-
-  &.active {
-    color: $ui-highlight-color;
-  }
-
-  &::-moz-focus-inner {
-    border: 0;
-  }
-
-  &::-moz-focus-inner,
-  &:focus,
-  &:active {
-    outline: 0 !important;
-  }
-}
-
-.dropdown-menu {
-  position: absolute;
-}
-
-.dropdown--active .icon-button {
-  color: $ui-highlight-color;
-}
-
-.dropdown--active::after {
-  @media screen and (min-width: 631px) {
-    content: "";
-    display: block;
-    position: absolute;
-    width: 0;
-    height: 0;
-    border-style: solid;
-    border-width: 0 4.5px 7.8px;
-    border-color: transparent transparent $ui-secondary-color;
-    bottom: 8px;
-    right: 104px;
-  }
-}
-
-.invisible {
-  font-size: 0;
-  line-height: 0;
-  display: inline-block;
-  width: 0;
-  height: 0;
-  position: absolute;
-
-  img,
-  svg {
-    margin: 0 !important;
-    border: 0 !important;
-    padding: 0 !important;
-    width: 0 !important;
-    height: 0 !important;
-  }
-}
-
-.ellipsis {
-  &::after {
-    content: "…";
-  }
-}
-
-.lightbox .icon-button {
-  color: $ui-base-color;
-}
-
-.compose-form {
-  padding: 10px;
-}
-
-.compose-form__warning {
-  color: darken($ui-secondary-color, 65%);
-  margin-bottom: 15px;
-  background: $ui-primary-color;
-  box-shadow: 0 2px 6px rgba($base-shadow-color, 0.3);
-  padding: 8px 10px;
-  border-radius: 4px;
-  font-size: 13px;
-  font-weight: 400;
-
-  strong {
-    color: darken($ui-secondary-color, 65%);
-    font-weight: 500;
-  }
-
-  a {
-    color: darken($ui-primary-color, 33%);
-    font-weight: 500;
-    text-decoration: underline;
-
-    &:hover,
-    &:active,
-    &:focus {
-      text-decoration: none;
-    }
-  }
-}
-
-.compose-form__modifiers {
-  color: $ui-base-color;
-  font-family: inherit;
-  font-size: 14px;
-  background: $simple-background-color;
-}
-
-.compose-form__buttons-wrapper {
-  display: flex;
-  justify-content: space-between;
-}
-
-.compose-form__buttons {
-  padding: 10px;
-  background: darken($simple-background-color, 8%);
-  box-shadow: inset 0 5px 5px rgba($base-shadow-color, 0.05);
-  border-radius: 0 0 4px 4px;
-  display: flex;
-
-  .icon-button {
-    box-sizing: content-box;
-    padding: 0 3px;
-  }
-}
-
-.compose-form__buttons-separator {
-  border-left: 1px solid #c3c3c3;
-  margin: 0 3px;
-}
-
-.compose-form__upload-button-icon {
-  line-height: 27px;
-}
-
-.compose-form__sensitive-button {
-  display: none;
-
-  &.compose-form__sensitive-button--visible {
-    display: block;
-  }
-
-  .compose-form__sensitive-button__icon {
-    line-height: 27px;
-  }
-}
-
-.compose-form__upload-wrapper {
-  overflow: hidden;
-}
-
-.compose-form__uploads-wrapper {
-  display: flex;
-  flex-direction: row;
-  padding: 5px;
-  flex-wrap: wrap;
-}
-
-.compose-form__upload {
-  flex: 1 1 0;
-  min-width: 40%;
-  margin: 5px;
-
-  &-description {
-    position: absolute;
-    z-index: 2;
-    bottom: 0;
-    left: 0;
-    right: 0;
-    box-sizing: border-box;
-    background: linear-gradient(0deg, rgba($base-shadow-color, 0.8) 0, rgba($base-shadow-color, 0.35) 80%, transparent);
-    padding: 10px;
-    opacity: 0;
-    transition: opacity .1s ease;
-
-    input {
-      background: transparent;
-      color: $ui-secondary-color;
-      border: 0;
-      padding: 0;
-      margin: 0;
-      width: 100%;
-      font-family: inherit;
-      font-size: 14px;
-      font-weight: 500;
-
-      &:focus {
-        color: $white;
-      }
-
-      &::placeholder {
-        opacity: 0.54;
-        color: $ui-secondary-color;
-      }
-    }
-
-    &.active {
-      opacity: 1;
-    }
-  }
-
-  .icon-button {
-    mix-blend-mode: difference;
-  }
-}
-
-.compose-form__upload-thumbnail {
-  border-radius: 4px;
-  background-position: center;
-  background-size: cover;
-  background-repeat: no-repeat;
-  height: 100px;
-  width: 100%;
-}
-
-.compose-form__label {
-  display: block;
-  line-height: 24px;
-  vertical-align: middle;
-
-  &.with-border {
-    border-top: 1px solid $ui-base-color;
-    padding-top: 10px;
-  }
-
-  .compose-form__label__text {
-    display: inline-block;
-    vertical-align: middle;
-    margin-bottom: 14px;
-    margin-left: 8px;
-    color: $ui-primary-color;
-  }
-}
-
-.compose-form__textarea,
-.follow-form__input {
-  background: $simple-background-color;
-
-  &:disabled {
-    background: $ui-secondary-color;
-  }
-}
-
-.compose-form__autosuggest-wrapper {
-  position: relative;
-
-  .emoji-picker-dropdown {
-    position: absolute;
-    right: 5px;
-    top: 5px;
-
-    ::-webkit-scrollbar-track:hover,
-    ::-webkit-scrollbar-track:active {
-      background-color: rgba($base-overlay-background, 0.3);
-    }
-  }
-}
-
-.compose-form__publish {
-  display: flex;
-  justify-content: flex-end;
-  min-width: 0;
-}
-
-.compose-form__publish-button-wrapper {
-  overflow: hidden;
-  padding-top: 10px;
-  white-space: nowrap;
-  display: flex;
-
-  button {
-    text-overflow: unset;
-  }
-}
-
-.compose-form__publish__side-arm {
-  padding: 0 !important;
-  width: 36px;
-  text-align: center;
-  margin-right: 2px;
-}
-
-.compose-form__publish__primary {
-  padding: 0 10px !important;
-}
-
-.emojione {
-  display: inline-block;
-  font-size: inherit;
-  vertical-align: middle;
-  object-fit: contain;
-  margin: -.2ex .15em .2ex;
-  width: 16px;
-  height: 16px;
-
-  img {
-    width: auto;
-  }
-}
-
-.reply-indicator {
-  border-radius: 4px 4px 0 0;
-  position: relative;
-  bottom: -2px;
-  background: $ui-primary-color;
-  padding: 10px;
-}
-
-.reply-indicator__header {
-  margin-bottom: 5px;
-  overflow: hidden;
-}
-
-.reply-indicator__cancel {
-  float: right;
-  line-height: 24px;
-}
-
-.reply-indicator__display-name {
-  color: $ui-base-color;
-  display: block;
-  max-width: 100%;
-  line-height: 24px;
-  overflow: hidden;
-  padding-right: 25px;
-  text-decoration: none;
-}
-
-.reply-indicator__display-avatar {
-  float: left;
-  margin-right: 5px;
-}
-
-.status__content--with-action {
-  cursor: pointer;
-}
-
-.status-check-box {
-  .status__content,
-  .reply-indicator__content {
-    color: #3a3a3a;
-    a {
-      color: #005aa9;
-    }
-  }
-}
-
-.status__content,
-.reply-indicator__content {
-  position: relative;
-  margin: 10px 0;
-  padding: 0 12px;
-  font-size: 15px;
-  line-height: 20px;
-  color: $primary-text-color;
-  word-wrap: break-word;
-  font-weight: 400;
-  overflow: visible;
-  white-space: pre-wrap;
-  padding-top: 5px;
-
-  &.status__content--with-spoiler {
-    white-space: normal;
-
-    .status__content__text {
-      white-space: pre-wrap;
-    }
-  }
-
-  .emojione {
-    width: 20px;
-    height: 20px;
-    margin: -5px 0 0;
-  }
-
-  p {
-    margin-bottom: 20px;
-
-    &:last-child {
-      margin-bottom: 0;
-    }
-  }
-
-  a {
-    color: $ui-secondary-color;
-    text-decoration: none;
-
-    &:hover {
-      text-decoration: underline;
-
-      .fa {
-        color: lighten($ui-base-color, 40%);
-      }
-    }
-
-    &.mention {
-      &:hover {
-        text-decoration: none;
-
-        span {
-          text-decoration: underline;
-        }
-      }
-    }
-
-    .fa {
-      color: lighten($ui-base-color, 30%);
-    }
-  }
-
-  .status__content__spoiler {
-    display: none;
-
-    &.status__content__spoiler--visible {
-      display: block;
-    }
-  }
-}
-
-.status__content__spoiler-link {
-  display: inline-block;
-  border-radius: 2px;
-  background: lighten($ui-base-color, 30%);
-  border: none;
-  color: lighten($ui-base-color, 8%);
-  font-weight: 500;
-  font-size: 11px;
-  padding: 0 5px;
-  text-transform: uppercase;
-  line-height: inherit;
-  cursor: pointer;
-  vertical-align: bottom;
-
-  &:hover {
-    background: lighten($ui-base-color, 33%);
-    text-decoration: none;
-  }
-
-  .status__content__spoiler-icon {
-    display: inline-block;
-    margin: 0 0 0 5px;
-    border-left: 1px solid currentColor;
-    padding: 0 0 0 4px;
-    font-size: 16px;
-    vertical-align: -2px;
-  }
-}
-
-.status__prepend-icon-wrapper {
-  float: left;
-  margin: 0 10px 0 -58px;
-  width: 48px;
-  text-align: right;
-}
-
-.notif-cleaning {
-  .status, .notification-follow {
-    padding-right: ($dismiss-overlay-width + 0.5rem);
-  }
-}
-
-.notification-follow {
-  position: relative;
-
-  // same like Status
-  border-bottom: 1px solid lighten($ui-base-color, 8%);
-
-  .account {
-    border-bottom: 0 none;
-  }
-}
-
-.focusable {
-  &:focus {
-    outline: 0;
-    background: lighten($ui-base-color, 4%);
-
-    .status.status-direct {
-      background: lighten($ui-base-color, 12%);
-    }
-
-    .detailed-status,
-    .detailed-status__action-bar {
-      background: lighten($ui-base-color, 8%);
-    }
-  }
-}
-
-.status {
-  padding: 8px 10px;
-  position: relative;
-  height: auto;
-  min-height: 48px;
-  border-bottom: 1px solid lighten($ui-base-color, 8%);
-  cursor: default;
-
-  @supports (-ms-overflow-style: -ms-autohiding-scrollbar) {
-    // Add margin to avoid Edge auto-hiding scrollbar appearing over content.
-    // On Edge 16 this is 16px and Edge <=15 it's 12px, so aim for 16px.
-    padding-right: 26px; // 10px + 16px
-  }
-
-  @keyframes fade {
-    0% { opacity: 0; }
-    100% { opacity: 1; }
-  }
-
-  opacity: 1;
-  animation: fade 150ms linear;
-
-  .video-player {
-    margin-top: 8px;
-  }
-
-  &.status-direct {
-    background: lighten($ui-base-color, 8%);
-
-    .icon-button.disabled {
-      color: lighten($ui-base-color, 16%);
-    }
-  }
-
-  &.light {
-    .status__relative-time {
-      color: $ui-primary-color;
-    }
-
-    .status__display-name {
-      color: $ui-base-color;
-    }
-
-    .display-name {
-      strong {
-        color: $ui-base-color;
-      }
-
-      span {
-        color: $ui-primary-color;
-      }
-    }
-
-    .status__content {
-      color: $ui-base-color;
-
-      a {
-        color: $ui-highlight-color;
-      }
-
-      a.status__content__spoiler-link {
-        color: $primary-text-color;
-        background: $ui-primary-color;
-
-        &:hover {
-          background: lighten($ui-primary-color, 8%);
-        }
-      }
-    }
-  }
-
-  &.collapsed {
-    background-position: center;
-    background-size: cover;
-    user-select: none;
-
-    &.has-background::before {
-      display: block;
-      position: absolute;
-      left: 0;
-      right: 0;
-      top: 0;
-      bottom: 0;
-    	background-image: linear-gradient(to bottom, rgba($base-shadow-color, .75), rgba($base-shadow-color, .65) 24px, rgba($base-shadow-color, .8));
-      content: "";
-    }
-
-    .display-name:hover .display-name__html {
-      text-decoration: none;
-    }
-
-    .status__content {
-      height: 20px;
-      overflow: hidden;
-      text-overflow: ellipsis;
-
-      a:hover {
-        text-decoration: none;
-      }
-    }
-  }
-
-  .notification__message {
-    margin: -10px -10px 10px;
-  }
-}
-
-.notification-favourite {
-  .status.status-direct {
-    background: transparent;
-
-    .icon-button.disabled {
-      color: lighten($ui-base-color, 13%);
-    }
-  }
-}
-
-.status__relative-time {
-  display: inline-block;
-  margin-left: auto;
-  padding-left: 18px;
-  width: 120px;
-  color: $ui-base-lighter-color;
-  font-size: 14px;
-  text-align: right;
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
-}
-
-.status__display-name {
-  margin: 0 auto 0 0;
-  color: $ui-base-lighter-color;
-  overflow: hidden;
-}
-
-.status__info {
-  display: flex;
-  margin: 2px 0 5px;
-  font-size: 15px;
-  line-height: 24px;
-}
-
-.status__info__icons {
-  flex: none;
-  position: relative;
-  color: lighten($ui-base-color, 26%);
-
-  .status__visibility-icon {
-    padding-left: 6px;
-  }
-}
-
-.status-check-box {
-  border-bottom: 1px solid $ui-secondary-color;
-  display: flex;
-
-  .status__content {
-    flex: 1 1 auto;
-    padding: 10px;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    white-space: nowrap;
-  }
-}
-
-.status-check-box-toggle {
-  align-items: center;
-  display: flex;
-  flex: 0 0 auto;
-  justify-content: center;
-  padding: 10px;
-}
-
-.status__prepend {
-  margin: -10px -10px 10px;
-  color: $ui-base-lighter-color;
-  padding: 8px 10px 0 68px;
-  font-size: 14px;
-  position: relative;
-
-  .status__display-name strong {
-    color: $ui-base-lighter-color;
-  }
-
-  > span {
-    display: block;
-    overflow: hidden;
-    text-overflow: ellipsis;
-  }
-}
-
-.status__action-bar {
-  align-items: center;
-  display: flex;
-  margin: 10px 4px 0;
-}
-
-.status__action-bar-button {
-  float: left;
-  margin-right: 18px;
-  flex: 0 0 auto;
-}
-
-.status__action-bar-dropdown {
-  float: left;
-  height: 23.15px;
-  width: 23.15px;
-
-  // Dropdown style override for centering on the icon
-  .dropdown--active {
-    position: relative;
-
-    .dropdown__content.dropdown__right {
-      left: calc(50% + 3px);
-      right: initial;
-      transform: translate(-50%, 0);
-      top: 22px;
-    }
-
-    &::after {
-      right: 1px;
-      bottom: -2px;
-    }
-  }
-}
-
-.detailed-status__action-bar-dropdown {
-  flex: 1 1 auto;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  position: relative;
-}
-
-.detailed-status {
-  background: lighten($ui-base-color, 4%);
-  padding: 14px 10px;
-
-  .status__content {
-    font-size: 19px;
-    line-height: 24px;
-
-    .emojione {
-      width: 24px;
-      height: 24px;
-      margin: -5px 0 0;
-    }
-  }
-
-  .video-player {
-    margin-top: 8px;
-  }
-}
-
-.detailed-status__meta {
-  margin-top: 15px;
-  color: $ui-base-lighter-color;
-  font-size: 14px;
-  line-height: 18px;
-}
-
-.detailed-status__action-bar {
-  background: lighten($ui-base-color, 4%);
-  border-top: 1px solid lighten($ui-base-color, 8%);
-  border-bottom: 1px solid lighten($ui-base-color, 8%);
-  display: flex;
-  flex-direction: row;
-  padding: 10px 0;
-}
-
-.detailed-status__link {
-  color: inherit;
-  text-decoration: none;
-}
-
-.detailed-status__favorites,
-.detailed-status__reblogs {
-  display: inline-block;
-  font-weight: 500;
-  font-size: 12px;
-  margin-left: 6px;
-}
-
-.reply-indicator__content {
-  color: $ui-base-color;
-  font-size: 14px;
-
-  a {
-    color: lighten($ui-base-color, 20%);
-  }
-}
-
-.account {
-  padding: 10px;
-  border-bottom: 1px solid lighten($ui-base-color, 8%);
-
-  .account__display-name {
-    flex: 1 1 auto;
-    display: block;
-    color: $ui-primary-color;
-    overflow: hidden;
-    text-decoration: none;
-    font-size: 14px;
-  }
-}
-
-.account__wrapper {
-  display: flex;
-}
-
-.account__avatar-wrapper {
-  float: left;
-  margin: 6px 16px 6px 6px;
-}
-
-.account__avatar {
-  @include avatar-radius();
-  position: relative;
-  cursor: pointer;
-
-  &-inline {
-    display: inline-block;
-    vertical-align: middle;
-    margin-right: 5px;
-  }
-}
-
-.account__avatar-overlay {
-  position: relative;
-  @include avatar-size(48px);
-
-  &-base {
-    @include avatar-radius();
-    @include avatar-size(36px);
-  }
-
-  &-overlay {
-    @include avatar-radius();
-    @include avatar-size(24px);
-
-    position: absolute;
-    bottom: 0;
-    right: 0;
-    z-index: 1;
-  }
-}
-
-.account__relationship {
-  height: 18px;
-  padding: 12px 10px;
-  white-space: nowrap;
-}
-
-.account__header__wrapper {
-  flex: 0 0 auto;
-  background: lighten($ui-base-color, 4%);
-}
-
-.account__header {
-  text-align: center;
-  background-size: cover;
-  background-position: center;
-  position: relative;
-
-  & > div {
-    background: rgba(lighten($ui-base-color, 4%), 0.9);
-    padding: 20px 10px;
-  }
-
-  .account__header__content {
-    color: $ui-secondary-color;
-  }
-
-  .account__avatar {
-    @include avatar-radius();
-    @include avatar-size(90px);
-    display: block;
-    margin: 0 auto 10px;
-    overflow: hidden;
-  }
-
-  .account__header__display-name {
-    color: $primary-text-color;
-    display: inline-block;
-    width: 100%;
-    font-size: 20px;
-    line-height: 27px;
-    font-weight: 500;
-    overflow: hidden;
-    text-overflow: ellipsis;
-  }
-
-  .account__header__username {
-    color: $ui-highlight-color;
-    font-size: 14px;
-    font-weight: 400;
-    display: block;
-    margin-bottom: 10px;
-    overflow: hidden;
-    text-overflow: ellipsis;
-  }
-}
-
-.account__disclaimer {
-  padding: 10px;
-  border-top: 1px solid lighten($ui-base-color, 8%);
-  color: $ui-base-lighter-color;
-
-  strong {
-    font-weight: 500;
-  }
-
-  a {
-    font-weight: 500;
-    color: inherit;
-    text-decoration: underline;
-
-    &:hover,
-    &:focus,
-    &:active {
-      text-decoration: none;
-    }
-  }
-}
-
-.account__header__content {
-  color: $ui-primary-color;
-  font-size: 14px;
-  font-weight: 400;
-  overflow: hidden;
-  word-break: normal;
-  word-wrap: break-word;
-
-  p {
-    margin-bottom: 20px;
-
-    &:last-child {
-      margin-bottom: 0;
-    }
-  }
-
-  a {
-    color: inherit;
-    text-decoration: underline;
-
-    &:hover {
-      text-decoration: none;
-    }
-  }
-}
-
-.account__header__display-name {
-  .emojione {
-    width: 25px;
-    height: 25px;
-  }
-}
-
-.account__metadata {
-  width: 100%;
-  font-size: 15px;
-  line-height: 20px;
-  overflow: hidden;
-  border-collapse: collapse;
-
-  a {
-    text-decoration: none;
-
-    &:hover{
-      text-decoration: underline;
-    }
-  }
-
-  tr {
-    border-top: 1px solid lighten($ui-base-color, 8%);
-  }
-
-  th, td {
-    padding: 14px 20px;
-    vertical-align: middle;
-
-    & > div {
-      max-height: 40px;
-      overflow-y: auto;
-      white-space: pre-wrap;
-      text-overflow: ellipsis;
-    }
-  }
-
-  th {
-    color: $ui-primary-color;
-    background: lighten($ui-base-color, 13%);
-    font-variant: small-caps;
-    max-width: 120px;
-
-    a {
-      color: $primary-text-color;
-    }
-  }
-
-  td {
-    flex: auto;
-    color: $primary-text-color;
-    background: $ui-base-color;
-
-    a {
-      color: $ui-highlight-color;
-    }
-  }
-}
-
-.account__action-bar {
-  border-top: 1px solid lighten($ui-base-color, 8%);
-  border-bottom: 1px solid lighten($ui-base-color, 8%);
-  line-height: 36px;
-  overflow: hidden;
-  flex: 0 0 auto;
-  display: flex;
-}
-
-.account__action-bar-dropdown {
-  flex: 0 1 calc(50% - 140px);
-  padding: 10px;
-
-  .dropdown--active {
-    .dropdown__content.dropdown__right {
-      left: 6px;
-      right: initial;
-    }
-
-    &::after {
-      bottom: initial;
-      margin-left: 11px;
-      margin-top: -7px;
-      right: initial;
-    }
-  }
-}
-
-.account__action-bar-links {
-  display: flex;
-  flex: 1 1 auto;
-  line-height: 18px;
-}
-
-.account__action-bar__tab {
-  text-decoration: none;
-  overflow: hidden;
-  flex: 0 1 80px;
-  border-left: 1px solid lighten($ui-base-color, 8%);
-  padding: 10px 5px;
-
-  & > span {
-    display: block;
-    text-transform: uppercase;
-    font-size: 11px;
-    color: $ui-primary-color;
-  }
-
-  strong {
-    display: block;
-    font-size: 15px;
-    font-weight: 500;
-    color: $primary-text-color;
-  }
-
-  abbr {
-    color: $ui-base-lighter-color;
-  }
-}
-
-.account-authorize {
-  padding: 14px 10px;
-
-  .detailed-status__display-name {
-    display: block;
-    margin-bottom: 15px;
-    overflow: hidden;
-  }
-}
-
-.account-authorize__avatar {
-  float: left;
-  margin-right: 10px;
-}
-
-.status__display-name,
-.status__relative-time,
-.detailed-status__display-name,
-.detailed-status__datetime,
-.detailed-status__application,
-.account__display-name {
-  text-decoration: none;
-}
-
-.status__display-name,
-.account__display-name {
-  strong {
-    color: $primary-text-color;
-  }
-}
-
-.muted {
-  .emojione {
-    opacity: 0.5;
-  }
-}
-
-.account__display-name strong {
-  display: block;
-  overflow: hidden;
-  text-overflow: ellipsis;
-}
-
-.detailed-status__application,
-.detailed-status__datetime {
-  color: inherit;
-}
-
-.detailed-status__display-name {
-  color: $ui-secondary-color;
-  display: block;
-  line-height: 24px;
-  margin-bottom: 15px;
-  overflow: hidden;
-
-  strong,
-  span {
-    display: block;
-    text-overflow: ellipsis;
-    overflow: hidden;
-  }
-
-  strong {
-    font-size: 16px;
-    color: $primary-text-color;
-  }
-}
-
-.detailed-status__display-avatar {
-  float: left;
-  margin-right: 10px;
-}
-
-.status__avatar {
-  flex: none;
-  margin: 0 10px 0 0;
-  height: 48px;
-  width: 48px;
-}
-
-.muted {
-  .status__content p,
-  .status__content a {
-    color: $ui-base-lighter-color;
-  }
-
-  .status__display-name strong {
-    color: $ui-base-lighter-color;
-  }
-
-  .status__avatar, .emojione {
-    opacity: 0.5;
-  }
-
-  a.status__content__spoiler-link {
-    background: $ui-base-lighter-color;
-    color: lighten($ui-base-color, 4%);
-
-    &:hover {
-      background: lighten($ui-base-color, 29%);
-      text-decoration: none;
-    }
-  }
-}
-
-.notification__message {
-  padding: 8px 10px 0 68px;
-  cursor: default;
-  color: $ui-primary-color;
-  font-size: 15px;
-  position: relative;
-
-  .fa {
-    color: $ui-highlight-color;
-  }
-
-  > span {
-    display: block;
-    overflow: hidden;
-    text-overflow: ellipsis;
-  }
-}
-
-.notification__favourite-icon-wrapper {
-  float: left;
-  margin: 0 10px 0 -58px;
-  width: 48px;
-  text-align: right;
-
-  .star-icon {
-    color: $gold-star;
-  }
-}
-
-.star-icon.active {
-  color: $gold-star;
-}
-
-.notification__display-name {
-  color: inherit;
-  font-weight: 500;
-  text-decoration: none;
-
-  &:hover {
-    color: $primary-text-color;
-    text-decoration: underline;
-  }
-}
-
-.display-name {
-  display: block;
-  padding: 6px 0;
-  max-width: 100%;
-  height: 36px;
-  overflow: hidden;
-
-  strong {
-    display: block;
-    height: 18px;
-    font-size: 16px;
-    font-weight: 500;
-    line-height: 18px;
-    text-overflow: ellipsis;
-    overflow: hidden;
-    white-space: nowrap;
-  }
-
-  span {
-    display: block;
-    height: 18px;
-    font-size: 15px;
-    line-height: 18px;
-    text-overflow: ellipsis;
-    overflow: hidden;
-    white-space: nowrap;
-  }
-
-  &:hover {
-    strong {
-      text-decoration: underline;
-    }
-  }
-}
-
-.status__relative-time,
-.detailed-status__datetime {
-  &:hover {
-    text-decoration: underline;
-  }
-}
-
-.image-loader {
-  position: relative;
-
-  &.image-loader--loading {
-    .image-loader__preview-canvas {
-      filter: blur(2px);
-    }
-  }
-
-  .image-loader__img {
-    position: absolute;
-    top: 0;
-    left: 0;
-    right: 0;
-    max-width: 100%;
-    max-height: 100%;
-    background-image: none;
-  }
-
-  &.image-loader--amorphous {
-    position: static;
-
-    .image-loader__preview-canvas {
-      display: none;
-    }
-
-    .image-loader__img {
-      position: static;
-      width: auto;
-      height: auto;
-    }
-  }
-}
-
-.navigation-bar {
-  padding: 10px;
-  display: flex;
-  flex-shrink: 0;
-  cursor: default;
-  color: $ui-primary-color;
-
-  strong {
-    color: $primary-text-color;
-  }
-
-  .permalink {
-    text-decoration: none;
-  }
-
-  .icon-button {
-    pointer-events: none;
-    opacity: 0;
-  }
-}
-
-.navigation-bar__profile {
-  flex: 1 1 auto;
-  margin-left: 8px;
-  overflow: hidden;
-}
-
-.navigation-bar__profile-account {
-  display: block;
-  font-weight: 500;
-  overflow: hidden;
-  text-overflow: ellipsis;
-}
-
-.navigation-bar__profile-edit {
-  color: inherit;
-  text-decoration: none;
-}
-
-.dropdown {
-  display: inline-block;
-}
-
-.dropdown__content {
-  display: none;
-  position: absolute;
-}
-
-.dropdown-menu__separator {
-  border-bottom: 1px solid darken($ui-secondary-color, 8%);
-  margin: 5px 7px 6px;
-  height: 0;
-}
-
-.dropdown-menu {
-  background: $ui-secondary-color;
-  padding: 4px 0;
-  border-radius: 4px;
-  box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
-
-  ul {
-    list-style: none;
-  }
-}
-
-.dropdown-menu__arrow {
-  position: absolute;
-  width: 0;
-  height: 0;
-  border: 0 solid transparent;
-
-  &.left {
-    right: -5px;
-    margin-top: -5px;
-    border-width: 5px 0 5px 5px;
-    border-left-color: $ui-secondary-color;
-  }
-
-  &.top {
-    bottom: -5px;
-    margin-left: -13px;
-    border-width: 5px 7px 0;
-    border-top-color: $ui-secondary-color;
-  }
-
-  &.bottom {
-    top: -5px;
-    margin-left: -13px;
-    border-width: 0 7px 5px;
-    border-bottom-color: $ui-secondary-color;
-  }
-
-  &.right {
-    left: -5px;
-    margin-top: -5px;
-    border-width: 5px 5px 5px 0;
-    border-right-color: $ui-secondary-color;
-  }
-}
-
-.dropdown-menu__item {
-  a {
-    font-size: 13px;
-    line-height: 18px;
-    display: block;
-    padding: 4px 14px;
-    box-sizing: border-box;
-    text-decoration: none;
-    background: $ui-secondary-color;
-    color: $ui-base-color;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    white-space: nowrap;
-
-    &:focus,
-    &:hover,
-    &:active {
-      background: $ui-highlight-color;
-      color: $ui-secondary-color;
-      outline: 0;
-    }
-  }
-}
-
-.dropdown--active .dropdown__content {
-  display: block;
-  line-height: 18px;
-  max-width: 311px;
-  right: 0;
-  text-align: left;
-  z-index: 9999;
-
-  & > ul {
-    list-style: none;
-    background: $ui-secondary-color;
-    padding: 4px 0;
-    border-radius: 4px;
-    box-shadow: 0 0 15px rgba($base-shadow-color, 0.4);
-    min-width: 140px;
-    position: relative;
-  }
-
-  &.dropdown__right {
-    right: 0;
-  }
-
-  &.dropdown__left {
-    & > ul {
-      left: -98px;
-    }
-  }
-
-  & > ul > li > a {
-    font-size: 13px;
-    line-height: 18px;
-    display: block;
-    padding: 4px 14px;
-    box-sizing: border-box;
-    text-decoration: none;
-    background: $ui-secondary-color;
-    color: $ui-base-color;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    white-space: nowrap;
-
-    &:focus {
-      outline: 0;
-    }
-
-    &:hover {
-      background: $ui-highlight-color;
-      color: $ui-secondary-color;
-    }
-  }
-}
-
-.dropdown__icon {
-  vertical-align: middle;
-}
-
-.static-content {
-  padding: 10px;
-  padding-top: 20px;
-  color: $ui-base-lighter-color;
-
-  h1 {
-    font-size: 16px;
-    font-weight: 500;
-    margin-bottom: 40px;
-    text-align: center;
-  }
-
-  p {
-    font-size: 13px;
-    margin-bottom: 20px;
-  }
-}
-
-.columns-area {
-  display: flex;
-  flex: 1 1 auto;
-  flex-direction: row;
-  justify-content: flex-start;
-  overflow-x: auto;
-  position: relative;
-  padding: 10px;
-}
-
-@include limited-single-column('screen and (max-width: 360px)', $parent: null) {
-  .columns-area {
-    padding: 0;
-  }
-
-  .react-swipeable-view-container .columns-area {
-    height: calc(100% - 20px) !important;
-  }
-}
-
-.react-swipeable-view-container {
-  &,
-  .columns-area,
-  .drawer,
-  .column {
-    height: 100%;
-  }
-}
-
-.react-swipeable-view-container > * {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  height: 100%;
-}
-
-.column {
-  width: 330px;
-  position: relative;
-  box-sizing: border-box;
-  display: flex;
-  flex-direction: column;
-  overflow: hidden;
-
-  .wide & {
-    flex: auto;
-    min-width: 330px;
-    max-width: 400px;
-  }
-
-  > .scrollable {
-    background: $ui-base-color;
-  }
-}
-
-.ui {
-  flex: 0 0 auto;
-  display: flex;
-  flex-direction: column;
-  width: 100%;
-  height: 100%;
-  background: darken($ui-base-color, 7%);
-}
-
-.drawer {
-  width: 300px;
-  box-sizing: border-box;
-  display: flex;
-  flex-direction: column;
-  overflow-y: auto;
-
-  .wide & {
-    flex: 1 1 200px;
-    min-width: 300px;
-    max-width: 400px;
-  }
-}
-
-.drawer__tab {
-  display: block;
-  flex: 1 1 auto;
-  padding: 15px 5px 13px;
-  color: $ui-primary-color;
-  text-decoration: none;
-  text-align: center;
-  font-size: 16px;
-  border-bottom: 2px solid transparent;
-  outline: none;
-  cursor: pointer;
-}
-
-.column,
-.drawer {
-  flex: 1 1 100%;
-  overflow: hidden;
-}
-
-@include limited-single-column('screen and (max-width: 360px)', $parent: null) {
-  .tabs-bar {
-    margin: 0;
-  }
-
-  .search {
-    margin-bottom: 0;
-  }
-}
-
-:root {  //  Overrides .wide stylings for mobile view
-  @include single-column('screen and (max-width: 630px)', $parent: null) {
-    .column,
-    .drawer {
-      flex: auto;
-      width: 100%;
-      min-width: 0;
-      max-width: none;
-      padding: 0;
-    }
-
-    .columns-area {
-      flex-direction: column;
-    }
-
-    .search__input,
-    .autosuggest-textarea__textarea {
-      font-size: 16px;
-    }
-  }
-}
-
-@include multi-columns('screen and (min-width: 631px)', $parent: null) {
-  .columns-area {
-    padding: 0;
-  }
-
-  .column,
-  .drawer {
-    padding: 10px;
-    padding-left: 5px;
-    padding-right: 5px;
-
-    &:first-child {
-      padding-left: 10px;
-    }
-
-    &:last-child {
-      padding-right: 10px;
-    }
-  }
-
-  .columns-area > div {
-    .column,
-    .drawer {
-      padding-left: 5px;
-      padding-right: 5px;
-    }
-  }
-}
-
-.drawer__pager {
-  box-sizing: border-box;
-  padding: 0;
-  flex: 1 1 auto;
-  position: relative;
-}
-
-.drawer__inner {
-  background: lighten($ui-base-color, 13%);
-  box-sizing: border-box;
-  padding: 0;
-  position: absolute;
-  height: 100%;
-  width: 100%;
-
-  &.darker {
-    position: absolute;
-    top: 0;
-    left: 0;
-    background: $ui-base-color;
-    width: 100%;
-    height: 100%;
-  }
-}
-
-.pseudo-drawer {
-  background: lighten($ui-base-color, 13%);
-  font-size: 13px;
-  text-align: left;
-}
-
-.drawer__header {
-  flex: 0 0 auto;
-  font-size: 16px;
-  background: lighten($ui-base-color, 8%);
-  margin-bottom: 10px;
-  display: flex;
-  flex-direction: row;
-
-  a {
-    transition: background 100ms ease-in;
-
-    &:hover {
-      background: lighten($ui-base-color, 3%);
-      transition: background 200ms ease-out;
-    }
-  }
-}
-
-.tabs-bar {
-  display: flex;
-  background: lighten($ui-base-color, 8%);
-  flex: 0 0 auto;
-  overflow-y: auto;
-  margin: 10px;
-  margin-bottom: 0;
-}
-
-.tabs-bar__link {
-  display: block;
-  flex: 1 1 auto;
-  padding: 15px 10px;
-  color: $primary-text-color;
-  text-decoration: none;
-  text-align: center;
-  font-size: 14px;
-  font-weight: 500;
-  border-bottom: 2px solid lighten($ui-base-color, 8%);
-  transition: all 200ms linear;
-
-  .fa {
-    font-weight: 400;
-    font-size: 16px;
-  }
-
-  &.active {
-    border-bottom: 2px solid $ui-highlight-color;
-    color: $ui-highlight-color;
-  }
-
-  &:hover,
-  &:focus,
-  &:active {
-    @include multi-columns('screen and (min-width: 631px)') {
-      background: lighten($ui-base-color, 14%);
-      transition: all 100ms linear;
-    }
-  }
-
-  span {
-    margin-left: 5px;
-    display: none;
-  }
-}
-
-@include limited-single-column('screen and (max-width: 600px)', $parent: null) {
-  .tabs-bar__link {
-    span {
-      display: inline;
-    }
-  }
-}
-
-@include multi-columns('screen and (min-width: 631px)', $parent: null) {
-  .tabs-bar {
-    display: none;
-  }
-}
-
-.scrollable {
-  overflow-y: scroll;
-  overflow-x: hidden;
-  flex: 1 1 auto;
-  -webkit-overflow-scrolling: touch;
-  will-change: transform; // improves perf in mobile Chrome
-
-  &.optionally-scrollable {
-    overflow-y: auto;
-  }
-
-  @supports(display: grid) { // hack to fix Chrome <57
-    contain: strict;
-  }
-}
-
-.scrollable.fullscreen {
-  @supports(display: grid) { // hack to fix Chrome <57
-    contain: none;
-  }
-}
-
-.column-back-button {
-  background: lighten($ui-base-color, 4%);
-  color: $ui-highlight-color;
-  cursor: pointer;
-  flex: 0 0 auto;
-  font-size: 16px;
-  border: 0;
-  text-align: unset;
-  padding: 15px;
-  margin: 0;
-  z-index: 3;
-
-  &:hover {
-    text-decoration: underline;
-  }
-}
-
-.column-header__back-button {
-  background: lighten($ui-base-color, 4%);
-  border: 0;
-  font-family: inherit;
-  color: $ui-highlight-color;
-  cursor: pointer;
-  flex: 0 0 auto;
-  font-size: 16px;
-  padding: 0 5px 0 0;
-  z-index: 3;
-
-  &:hover {
-    text-decoration: underline;
-  }
-
-  &:last-child {
-    padding: 0 15px 0 0;
-  }
-}
-
-.column-back-button__icon {
-  display: inline-block;
-  margin-right: 5px;
-}
-
-.column-back-button--slim {
-  position: relative;
-}
-
-.column-back-button--slim-button {
-  cursor: pointer;
-  flex: 0 0 auto;
-  font-size: 16px;
-  padding: 15px;
-  position: absolute;
-  right: 0;
-  top: -48px;
-}
-
-.react-toggle {
-  display: inline-block;
-  position: relative;
-  cursor: pointer;
-  background-color: transparent;
-  border: 0;
-  padding: 0;
-  user-select: none;
-  -webkit-tap-highlight-color: rgba($base-overlay-background, 0);
-  -webkit-tap-highlight-color: transparent;
-}
-
-.react-toggle-screenreader-only {
-  border: 0;
-  clip: rect(0 0 0 0);
-  height: 1px;
-  margin: -1px;
-  overflow: hidden;
-  padding: 0;
-  position: absolute;
-  width: 1px;
-}
-
-.react-toggle--disabled {
-  cursor: not-allowed;
-  opacity: 0.5;
-  transition: opacity 0.25s;
-}
-
-.react-toggle-track {
-  width: 50px;
-  height: 24px;
-  padding: 0;
-  border-radius: 30px;
-  background-color: $ui-base-color;
-  transition: all 0.2s ease;
-}
-
-.react-toggle:hover:not(.react-toggle--disabled) .react-toggle-track {
-  background-color: darken($ui-base-color, 10%);
-}
-
-.react-toggle--checked .react-toggle-track {
-  background-color: $ui-highlight-color;
-}
-
-.react-toggle--checked:hover:not(.react-toggle--disabled) .react-toggle-track {
-  background-color: lighten($ui-highlight-color, 10%);
-}
-
-.react-toggle-track-check {
-  position: absolute;
-  width: 14px;
-  height: 10px;
-  top: 0;
-  bottom: 0;
-  margin-top: auto;
-  margin-bottom: auto;
-  line-height: 0;
-  left: 8px;
-  opacity: 0;
-  transition: opacity 0.25s ease;
-}
-
-.react-toggle--checked .react-toggle-track-check {
-  opacity: 1;
-  transition: opacity 0.25s ease;
-}
-
-.react-toggle-track-x {
-  position: absolute;
-  width: 10px;
-  height: 10px;
-  top: 0;
-  bottom: 0;
-  margin-top: auto;
-  margin-bottom: auto;
-  line-height: 0;
-  right: 10px;
-  opacity: 1;
-  transition: opacity 0.25s ease;
-}
-
-.react-toggle--checked .react-toggle-track-x {
-  opacity: 0;
-}
-
-.react-toggle-thumb {
-  transition: all 0.5s cubic-bezier(0.23, 1, 0.32, 1) 0ms;
-  position: absolute;
-  top: 1px;
-  left: 1px;
-  width: 22px;
-  height: 22px;
-  border: 1px solid $ui-base-color;
-  border-radius: 50%;
-  background-color: darken($simple-background-color, 2%);
-  box-sizing: border-box;
-  transition: all 0.25s ease;
-}
-
-.react-toggle--checked .react-toggle-thumb {
-  left: 27px;
-  border-color: $ui-highlight-color;
-}
-
-.column-link {
-  background: lighten($ui-base-color, 8%);
-  color: $primary-text-color;
-  display: block;
-  font-size: 16px;
-  padding: 15px;
-  text-decoration: none;
-  cursor: pointer;
-  outline: none;
-
-  &:hover {
-    background: lighten($ui-base-color, 11%);
-  }
-}
-
-.column-link__icon {
-  display: inline-block;
-  margin-right: 5px;
-}
-
-.column-subheading {
-  background: $ui-base-color;
-  color: $ui-base-lighter-color;
-  padding: 8px 20px;
-  font-size: 12px;
-  font-weight: 500;
-  text-transform: uppercase;
-  cursor: default;
-}
-
-.autosuggest-textarea,
-.spoiler-input {
-  position: relative;
-}
-
-.autosuggest-textarea__textarea,
-.spoiler-input__input {
-  display: block;
-  box-sizing: border-box;
-  width: 100%;
-  margin: 0;
-  color: $ui-base-color;
-  background: $simple-background-color;
-  padding: 10px;
-  font-family: inherit;
-  font-size: 14px;
-  resize: vertical;
-  border: 0;
-  outline: 0;
-
-  &:focus {
-    outline: 0;
-  }
-
-  @include limited-single-column('screen and (max-width: 600px)') {
-    font-size: 16px;
-  }
-}
-
-.spoiler-input__input {
-  border-radius: 4px;
-}
-
-.autosuggest-textarea__textarea {
-  min-height: 100px;
-  border-radius: 4px 4px 0 0;
-  padding-bottom: 0;
-  padding-right: 10px + 22px;
-  resize: none;
-
-  @include limited-single-column('screen and (max-width: 600px)') {
-    height: 100px !important; // prevent auto-resize textarea
-    resize: vertical;
-  }
-}
-
-.autosuggest-textarea__suggestions {
-  box-sizing: border-box;
-  display: none;
-  position: absolute;
-  top: 100%;
-  width: 100%;
-  z-index: 99;
-  box-shadow: 4px 4px 6px rgba($base-shadow-color, 0.4);
-  background: $ui-secondary-color;
-  border-radius: 0 0 4px 4px;
-  color: $ui-base-color;
-  font-size: 14px;
-  padding: 6px;
-
-  &.autosuggest-textarea__suggestions--visible {
-    display: block;
-  }
-}
-
-.autosuggest-textarea__suggestions__item {
-  padding: 10px;
-  cursor: pointer;
-  border-radius: 4px;
-
-  &:hover,
-  &:focus,
-  &:active,
-  &.selected {
-    background: darken($ui-secondary-color, 10%);
-  }
-}
-
-.autosuggest-account,
-.autosuggest-emoji {
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  justify-content: flex-start;
-  line-height: 18px;
-  font-size: 14px;
-}
-
-.autosuggest-account-icon,
-.autosuggest-emoji img {
-  display: block;
-  margin-right: 8px;
-  width: 16px;
-  height: 16px;
-}
-
-.autosuggest-account .display-name__account {
-  color: lighten($ui-base-color, 36%);
-}
-
-.character-counter__wrapper {
-  line-height: 36px;
-  margin: 0 16px 0 8px;
-  padding-top: 10px;
-}
-
-.character-counter {
-  cursor: default;
-  font-size: 16px;
-}
-
-.character-counter--over {
-  color: $warning-red;
-}
-
-.getting-started__wrapper {
-  position: relative;
-  overflow-y: auto;
-}
-
-.getting-started__footer {
-  display: flex;
-  flex-direction: column;
-}
-
-.getting-started {
-  box-sizing: border-box;
-  padding-bottom: 235px;
-  background: url('~images/mastodon-getting-started.png') no-repeat 0 100%;
-  flex: 1 0 auto;
-
-  p {
-    color: $ui-secondary-color;
-  }
-
-  a {
-    color: $ui-base-lighter-color;
-  }
-}
-
-.setting-text {
-  color: $ui-primary-color;
-  background: transparent;
-  border: none;
-  border-bottom: 2px solid $ui-primary-color;
-  box-sizing: border-box;
-  display: block;
-  font-family: inherit;
-  margin-bottom: 10px;
-  padding: 7px 0;
-  width: 100%;
-
-  &:focus,
-  &:active {
-    color: $primary-text-color;
-    border-bottom-color: $ui-highlight-color;
-  }
-
-  @include limited-single-column('screen and (max-width: 600px)') {
-    font-size: 16px;
-  }
-
-  &.light {
-    color: $ui-base-color;
-    border-bottom: 2px solid lighten($ui-base-color, 27%);
-
-    &:focus,
-    &:active {
-      color: $ui-base-color;
-      border-bottom-color: $ui-highlight-color;
-    }
-  }
-}
-
-@import 'boost';
-
-button.icon-button i.fa-retweet {
-  background-position: 0 0;
-  height: 19px;
-  transition: background-position 0.9s steps(10);
-  transition-duration: 0s;
-  vertical-align: middle;
-  width: 22px;
-
-  &::before {
-    display: none !important;
-  }
-}
-
-button.icon-button.active i.fa-retweet {
-  transition-duration: 0.9s;
-  background-position: 0 100%;
-}
-
-.status-card {
-  display: flex;
-  cursor: pointer;
-  font-size: 14px;
-  border: 1px solid lighten($ui-base-color, 8%);
-  border-radius: 4px;
-  color: $ui-base-lighter-color;
-  margin-top: 14px;
-  text-decoration: none;
-  overflow: hidden;
-
-  &:hover {
-    background: lighten($ui-base-color, 8%);
-  }
-}
-
-.status-card-video,
-.status-card-rich,
-.status-card-photo {
-  margin-top: 14px;
-  overflow: hidden;
-
-  iframe {
-    width: 100%;
-    height: auto;
-  }
-}
-
-.status-card-photo {
-  display: block;
-  text-decoration: none;
-
-  img {
-    display: block;
-    width: 100%;
-    height: auto;
-    margin: 0;
-  }
-}
-
-.status-card-video {
-  iframe {
-    width: 100%;
-    height: 100%;
-  }
-}
-
-.status-card__title {
-  display: block;
-  font-weight: 500;
-  margin-bottom: 5px;
-  color: $ui-primary-color;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-}
-
-.status-card__content {
-  flex: 1 1 auto;
-  overflow: hidden;
-  padding: 14px 14px 14px 8px;
-}
-
-.status-card__description {
-  color: $ui-primary-color;
-}
-
-.status-card__host {
-  display: block;
-  margin-top: 5px;
-  font-size: 13px;
-}
-
-.status-card__image {
-  flex: 0 0 100px;
-  background: lighten($ui-base-color, 8%);
-}
-
-.status-card.horizontal {
-  display: block;
-
-  .status-card__image {
-    width: 100%;
-  }
-
-  .status-card__image-image {
-    border-radius: 4px 4px 0 0;
-  }
-}
-
-.status-card__image-image {
-  border-radius: 4px 0 0 4px;
-  display: block;
-  height: auto;
-  margin: 0;
-  width: 100%;
-}
-
-.load-more {
-  display: block;
-  color: $ui-base-lighter-color;
-  background-color: transparent;
-  border: 0;
-  font-size: inherit;
-  text-align: center;
-  line-height: inherit;
-  margin: 0;
-  padding: 15px;
-  width: 100%;
-  clear: both;
-
-  &:hover {
-    background: lighten($ui-base-color, 2%);
-  }
-}
-
-.missing-indicator {
-  text-align: center;
-  font-size: 16px;
-  font-weight: 500;
-  color: lighten($ui-base-color, 16%);
-  background: $ui-base-color;
-  cursor: default;
-  display: flex;
-  flex: 1 1 auto;
-  align-items: center;
-  justify-content: center;
-
-  & > div {
-    background: url('~images/mastodon-not-found.png') no-repeat center -50px;
-    padding-top: 210px;
-    width: 100%;
-  }
-}
-
-.column-header__wrapper {
-  position: relative;
-  flex: 0 0 auto;
-
-  &.active {
-    &::before {
-      display: block;
-      content: "";
-      position: absolute;
-      top: 35px;
-      left: 0;
-      right: 0;
-      margin: 0 auto;
-      width: 60%;
-      pointer-events: none;
-      height: 28px;
-      z-index: 1;
-      background: radial-gradient(ellipse, rgba($ui-highlight-color, 0.23) 0%, rgba($ui-highlight-color, 0) 60%);
-    }
-  }
-}
-
-.column-header {
-  display: flex;
-  padding: 15px;
-  font-size: 16px;
-  background: lighten($ui-base-color, 4%);
-  flex: 0 0 auto;
-  cursor: pointer;
-  position: relative;
-  z-index: 2;
-  outline: 0;
-
-  &.active {
-    box-shadow: 0 1px 0 rgba($ui-highlight-color, 0.3);
-
-    .column-header__icon {
-      color: $ui-highlight-color;
-      text-shadow: 0 0 10px rgba($ui-highlight-color, 0.4);
-    }
-  }
-
-  &:focus,
-  &:active {
-    outline: 0;
-  }
-}
-
-.column-header__buttons {
-  height: 48px;
-  display: flex;
-  margin: -15px;
-  margin-left: 0;
-}
-
-.column-header__button {
-  background: lighten($ui-base-color, 4%);
-  border: 0;
-  color: $ui-primary-color;
-  cursor: pointer;
-  font-size: 16px;
-  padding: 0 15px;
-
-  &:hover {
-    color: lighten($ui-primary-color, 7%);
-  }
-
-  &.active {
-    color: $primary-text-color;
-    background: lighten($ui-base-color, 8%);
-
-    &:hover {
-      color: $primary-text-color;
-      background: lighten($ui-base-color, 8%);
-    }
-  }
-
-  // glitch - added focus ring for keyboard navigation
-  &:focus {
-    text-shadow: 0 0 4px darken($ui-highlight-color, 5%);
-  }
-}
-
-.scrollable > div > :first-child .notification__dismiss-overlay > .wrappy {
-  border-top: 1px solid $ui-base-color;
-}
-
-.notification__dismiss-overlay {
-  overflow: hidden;
-  position: absolute;
-  top: 0;
-  right: 0;
-  bottom: -1px;
-  padding-left: 15px; // space for the box shadow to be visible
-
-  z-index: 999;
-  align-items: center;
-  justify-content: flex-end;
-  cursor: pointer;
-
-  display: flex;
-
-  .wrappy {
-    width: $dismiss-overlay-width;
-    align-self: stretch;
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-    justify-content: center;
-    background: lighten($ui-base-color, 8%);
-    border-left: 1px solid lighten($ui-base-color, 20%);
-    box-shadow: 0 0 5px black;
-    border-bottom: 1px solid $ui-base-color;
-  }
-
-  .ckbox {
-    border: 2px solid $ui-primary-color;
-    border-radius: 2px;
-    width: 30px;
-    height: 30px;
-    font-size: 20px;
-    color: $ui-primary-color;
-    text-shadow: 0 0 5px black;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-  }
-
-  &:focus {
-    outline: 0 !important;
-
-    .ckbox {
-      box-shadow: 0 0 1px 1px $ui-highlight-color;
-    }
-  }
-}
-
-.column-header__notif-cleaning-buttons {
-  display: flex;
-  align-items: stretch;
-  justify-content: space-around;
-
-  button {
-    @extend .column-header__button;
-    background: transparent;
-    text-align: center;
-    padding: 10px 0;
-    white-space: pre-wrap;
-  }
-
-  b {
-    font-weight: bold;
-  }
-}
-
-// The notifs drawer with no padding to have more space for the buttons
-.column-header__collapsible-inner.nopad-drawer {
-  padding: 0;
-}
-
-.column-header__collapsible {
-  max-height: 70vh;
-  overflow: hidden;
-  overflow-y: auto;
-  color: $ui-primary-color;
-  transition: max-height 150ms ease-in-out, opacity 300ms linear;
-  opacity: 1;
-
-  &.collapsed {
-    max-height: 0;
-    opacity: 0.5;
-  }
-
-  &.animating {
-    overflow-y: hidden;
-  }
-
-  // notif cleaning drawer
-  &.ncd {
-    transition: none;
-    &.collapsed {
-      max-height: 0;
-      opacity: 0.7;
-    }
-  }
-}
-
-.column-header__collapsible-inner {
-  background: lighten($ui-base-color, 8%);
-  padding: 15px;
-}
-
-.column-header__setting-btn {
-  &:hover {
-    color: lighten($ui-primary-color, 4%);
-    text-decoration: underline;
-  }
-}
-
-.column-header__setting-arrows {
-  float: right;
-
-  .column-header__setting-btn {
-    padding: 0 10px;
-
-    &:last-child {
-      padding-right: 0;
-    }
-  }
-}
-
-.column-header__title {
-  display: inline-block;
-  text-overflow: ellipsis;
-  overflow: hidden;
-  white-space: nowrap;
-  flex: 1;
-}
-
-.text-btn {
-  display: inline-block;
-  padding: 0;
-  font-family: inherit;
-  font-size: inherit;
-  color: inherit;
-  border: 0;
-  background: transparent;
-  cursor: pointer;
-}
-
-.column-header__icon {
-  display: inline-block;
-  margin-right: 5px;
-}
-
-.loading-indicator {
-  color: lighten($ui-base-color, 26%);
-  font-size: 12px;
-  font-weight: 400;
-  text-transform: uppercase;
-  overflow: visible;
-  position: absolute;
-  top: 50%;
-  left: 50%;
-  transform: translate(-50%, -50%);
-
-  span {
-    display: block;
-    float: left;
-    margin-left: 50%;
-    transform: translateX(-50%);
-    margin: 82px 0 0 50%;
-    white-space: nowrap;
-    animation: loader-label 1.15s infinite cubic-bezier(0.215, 0.610, 0.355, 1.000);
-  }
-}
-
-.loading-indicator__figure {
-  position: absolute;
-  top: 50%;
-  left: 50%;
-  transform: translate(-50%, -50%);
-  width: 0;
-  height: 0;
-  box-sizing: border-box;
-  border: 0 solid lighten($ui-base-color, 26%);
-  border-radius: 50%;
-  animation: loader-figure 1.15s infinite cubic-bezier(0.215, 0.610, 0.355, 1.000);
-}
-
-@keyframes loader-figure {
-  0% {
-    width: 0;
-    height: 0;
-    background-color: lighten($ui-base-color, 26%);
-  }
-
-  29% {
-    background-color: lighten($ui-base-color, 26%);
-  }
-
-  30% {
-    width: 42px;
-    height: 42px;
-    background-color: transparent;
-    border-width: 21px;
-    opacity: 1;
-  }
-
-  100% {
-    width: 42px;
-    height: 42px;
-    border-width: 0;
-    opacity: 0;
-    background-color: transparent;
-  }
-}
-
-@keyframes loader-label {
-  0% { opacity: 0.25; }
-  30% { opacity: 1; }
-  100% { opacity: 0.25; }
-}
-
-.video-error-cover {
-  align-items: center;
-  background: $base-overlay-background;
-  color: $primary-text-color;
-  cursor: pointer;
-  display: flex;
-  flex-direction: column;
-  height: 100%;
-  justify-content: center;
-  margin-top: 8px;
-  position: relative;
-  text-align: center;
-  z-index: 100;
-}
-
-.media-spoiler {
-  background: $base-overlay-background;
-  color: $ui-primary-color;
-  border: 0;
-  width: 100%;
-  height: 100%;
-  justify-content: center;
-  position: relative;
-  text-align: center;
-  z-index: 100;
-  display: flex;
-  flex-direction: column;
-  align-items: stretch;
-
-  .status__content > & {
-    margin-top: 15px; // Add margin when used bare for NSFW video player
-  }
-
-  @include fullwidth-gallery;
-}
-
-.media-spoiler__warning {
-  display: block;
-  font-size: 14px;
-}
-
-.media-spoiler__trigger {
-  display: block;
-  font-size: 11px;
-  font-weight: 500;
-}
-
-.spoiler-button {
-  display: none;
-  left: 4px;
-  position: absolute;
-  text-shadow: 0 1px 1px $base-shadow-color, 1px 0 1px $base-shadow-color;
-  top: 4px;
-  z-index: 100;
-
-  &.spoiler-button--visible {
-    display: block;
-  }
-}
-
-.modal-container--preloader {
-  background: lighten($ui-base-color, 8%);
-}
-
-.account--panel {
-  background: lighten($ui-base-color, 4%);
-  border-top: 1px solid lighten($ui-base-color, 8%);
-  border-bottom: 1px solid lighten($ui-base-color, 8%);
-  display: flex;
-  flex-direction: row;
-  padding: 10px 0;
-}
-
-.account--panel__button,
-.detailed-status__button {
-  flex: 1 1 auto;
-  text-align: center;
-}
-
-.column-settings__outer {
-  background: lighten($ui-base-color, 8%);
-  padding: 15px;
-}
-
-.column-settings__section {
-  color: $ui-primary-color;
-  cursor: default;
-  display: block;
-  font-weight: 500;
-  margin-bottom: 10px;
-}
-
-.column-settings__row {
-  .text-btn {
-    margin-bottom: 15px;
-  }
-}
-
-.modal-container__nav {
-  align-items: center;
-  background: rgba($base-overlay-background, 0.5);
-  box-sizing: border-box;
-  border: 0;
-  color: $primary-text-color;
-  cursor: pointer;
-  display: flex;
-  font-size: 24px;
-  height: 100%;
-  padding: 30px 15px;
-  position: absolute;
-  top: 0;
-}
-
-.modal-container__nav--left {
-  left: -61px;
-}
-
-.modal-container__nav--right {
-  right: -61px;
-}
-
-.account--follows-info {
-  color: $primary-text-color;
-  position: absolute;
-  top: 10px;
-  left: 10px;
-  opacity: 0.7;
-  display: inline-block;
-  vertical-align: top;
-  background-color: rgba($base-overlay-background, 0.4);
-  text-transform: uppercase;
-  font-size: 11px;
-  font-weight: 500;
-  padding: 4px;
-  border-radius: 4px;
-}
-
-.account--action-button {
-  position: absolute;
-  top: 10px;
-  right: 20px;
-}
-
-.setting-toggle {
-  display: block;
-  line-height: 24px;
-}
-
-.setting-toggle__label,
-.setting-meta__label {
-  color: $ui-primary-color;
-  display: inline-block;
-  margin-bottom: 14px;
-  margin-left: 8px;
-  vertical-align: middle;
-}
-
-.setting-meta__label {
-  color: $ui-primary-color;
-  float: right;
-}
-
-.empty-column-indicator,
-.error-column {
-  color: lighten($ui-base-color, 20%);
-  background: $ui-base-color;
-  text-align: center;
-  padding: 20px;
-  font-size: 15px;
-  font-weight: 400;
-  cursor: default;
-  display: flex;
-  flex: 1 1 auto;
-  align-items: center;
-  justify-content: center;
-  @supports(display: grid) { // hack to fix Chrome <57
-    contain: strict;
-  }
-
-  a {
-    color: $ui-highlight-color;
-    text-decoration: none;
-
-    &:hover {
-      text-decoration: underline;
-    }
-  }
-}
-
-.error-column {
-  flex-direction: column;
-}
-
-@keyframes heartbeat {
-  from {
-    transform: scale(1);
-    transform-origin: center center;
-    animation-timing-function: ease-out;
-  }
-
-  10% {
-    transform: scale(0.91);
-    animation-timing-function: ease-in;
-  }
-
-  17% {
-    transform: scale(0.98);
-    animation-timing-function: ease-out;
-  }
-
-  33% {
-    transform: scale(0.87);
-    animation-timing-function: ease-in;
-  }
-
-  45% {
-    transform: scale(1);
-    animation-timing-function: ease-out;
-  }
-}
-
-.pulse-loading {
-  animation: heartbeat 1.5s ease-in-out infinite both;
-}
-
-.emoji-picker-dropdown__menu {
-  background: $simple-background-color;
-  position: absolute;
-  box-shadow: 4px 4px 6px rgba($base-shadow-color, 0.4);
-  border-radius: 4px;
-  margin-top: 5px;
-
-  .emoji-mart-scroll {
-    transition: opacity 200ms ease;
-  }
-
-  &.selecting .emoji-mart-scroll {
-    opacity: 0.5;
-  }
-}
-
-.emoji-picker-dropdown__modifiers {
-  position: absolute;
-  top: 60px;
-  right: 11px;
-  cursor: pointer;
-}
-
-.emoji-picker-dropdown__modifiers__menu {
-  position: absolute;
-  z-index: 4;
-  top: -4px;
-  left: -8px;
-  background: $simple-background-color;
-  border-radius: 4px;
-  box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2);
-  overflow: hidden;
-
-  button {
-    display: block;
-    cursor: pointer;
-    border: 0;
-    padding: 4px 8px;
-    background: transparent;
-
-    &:hover,
-    &:focus,
-    &:active {
-      background: rgba($ui-secondary-color, 0.4);
-    }
-  }
-
-  .emoji-mart-emoji {
-    height: 22px;
-  }
-}
-
-.emoji-mart-emoji {
-  span {
-    background-repeat: no-repeat;
-  }
-}
-
-.upload-area {
-  align-items: center;
-  background: rgba($base-overlay-background, 0.8);
-  display: flex;
-  height: 100%;
-  justify-content: center;
-  left: 0;
-  opacity: 0;
-  position: absolute;
-  top: 0;
-  visibility: hidden;
-  width: 100%;
-  z-index: 2000;
-
-  * {
-    pointer-events: none;
-  }
-}
-
-.upload-area__drop {
-  width: 320px;
-  height: 160px;
-  display: flex;
-  box-sizing: border-box;
-  position: relative;
-  padding: 8px;
-}
-
-.upload-area__background {
-  position: absolute;
-  top: 0;
-  right: 0;
-  bottom: 0;
-  left: 0;
-  z-index: -1;
-  border-radius: 4px;
-  background: $ui-base-color;
-  box-shadow: 0 0 5px rgba($base-shadow-color, 0.2);
-}
-
-.upload-area__content {
-  flex: 1;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  color: $ui-secondary-color;
-  font-size: 18px;
-  font-weight: 500;
-  border: 2px dashed $ui-base-lighter-color;
-  border-radius: 4px;
-}
-
-.upload-progress {
-  padding: 10px;
-  color: $ui-base-lighter-color;
-  overflow: hidden;
-  display: flex;
-
-  .fa {
-    font-size: 34px;
-    margin-right: 10px;
-  }
-
-  span {
-    font-size: 12px;
-    text-transform: uppercase;
-    font-weight: 500;
-    display: block;
-  }
-}
-
-.upload-progess__message {
-  flex: 1 1 auto;
-}
-
-.upload-progress__backdrop {
-  width: 100%;
-  height: 6px;
-  border-radius: 6px;
-  background: $ui-base-lighter-color;
-  position: relative;
-  margin-top: 5px;
-}
-
-.upload-progress__tracker {
-  position: absolute;
-  left: 0;
-  top: 0;
-  height: 6px;
-  background: $ui-highlight-color;
-  border-radius: 6px;
-}
-
-.emoji-button {
-  display: block;
-  font-size: 24px;
-  line-height: 24px;
-  margin-left: 2px;
-  width: 24px;
-  outline: 0;
-  cursor: pointer;
-
-  &:active,
-  &:focus {
-    outline: 0 !important;
-  }
-
-  img {
-    filter: grayscale(100%);
-    opacity: 0.8;
-    display: block;
-    margin: 0;
-    width: 22px;
-    height: 22px;
-    margin-top: 2px;
-  }
-
-  &:hover,
-  &:active,
-  &:focus {
-    img {
-      opacity: 1;
-      filter: none;
-    }
-  }
-}
-
-.dropdown--active .emoji-button img {
-  opacity: 1;
-  filter: none;
-}
-
-.privacy-dropdown__dropdown {
-  position: absolute;
-  background: $simple-background-color;
-  box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
-  border-radius: 4px;
-  margin-left: 40px;
-  overflow: hidden;
-}
-
-.privacy-dropdown__option {
-  color: $ui-base-color;
-  padding: 10px;
-  cursor: pointer;
-  display: flex;
-
-  &:hover,
-  &.active {
-    background: $ui-highlight-color;
-    color: $primary-text-color;
-
-    .privacy-dropdown__option__content {
-      color: $primary-text-color;
-
-      strong {
-        color: $primary-text-color;
-      }
-    }
-  }
-
-  &.active:hover {
-    background: lighten($ui-highlight-color, 4%);
-  }
-}
-
-.privacy-dropdown__option__icon {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  margin-right: 10px;
-}
-
-.privacy-dropdown__option__content {
-  flex: 1 1 auto;
-  color: darken($ui-primary-color, 24%);
-
-  strong {
-    font-weight: 500;
-    display: block;
-    color: $ui-base-color;
-  }
-}
-
-.privacy-dropdown.active {
-  .privacy-dropdown__value {
-    background: $simple-background-color;
-    border-radius: 4px 4px 0 0;
-    box-shadow: 0 -4px 4px rgba($base-shadow-color, 0.1);
-
-    .icon-button {
-      transition: none;
-    }
-
-    &.active {
-      background: $ui-highlight-color;
-
-      .icon-button {
-        color: $primary-text-color;
-      }
-    }
-  }
-
-  .privacy-dropdown__dropdown {
-    display: block;
-    box-shadow: 2px 4px 6px rgba($base-shadow-color, 0.1);
-  }
-}
-
-.advanced-options-dropdown {
-  position: relative;
-}
-
-.advanced-options-dropdown__dropdown {
-  display: none;
-  position: absolute;
-  left: 0;
-  top: 27px;
-  width: 210px;
-  background: $simple-background-color;
-  border-radius: 0 4px 4px;
-  z-index: 2;
-  overflow: hidden;
-}
-
-.advanced-options-dropdown__option {
-  color: $ui-base-color;
-  padding: 10px;
-  cursor: pointer;
-  display: flex;
-
-  &:hover,
-  &.active {
-    background: $ui-highlight-color;
-    color: $primary-text-color;
-
-    .advanced-options-dropdown__option__content {
-      color: $primary-text-color;
-
-      strong {
-        color: $primary-text-color;
-      }
-    }
-  }
-
-  &.active:hover {
-    background: lighten($ui-highlight-color, 4%);
-  }
-}
-
-.advanced-options-dropdown__option__toggle {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  margin-right: 10px;
-}
-
-.advanced-options-dropdown__option__content {
-  flex: 1 1 auto;
-  color: darken($ui-primary-color, 24%);
-
-  strong {
-    font-weight: 500;
-    display: block;
-    color: $ui-base-color;
-  }
-}
-
-.advanced-options-dropdown.open {
-  .advanced-options-dropdown__value {
-    background: $simple-background-color;
-    border-radius: 4px 4px 0 0;
-    box-shadow: 0 -4px 4px rgba($base-shadow-color, 0.1);
-  }
-
-  .advanced-options-dropdown__dropdown {
-    display: block;
-    box-shadow: 2px 4px 6px rgba($base-shadow-color, 0.1);
-  }
-}
-
-
-.search {
-  position: relative;
-  margin-bottom: 10px;
-}
-
-.search__input {
-  outline: 0;
-  box-sizing: border-box;
-  display: block;
-  width: 100%;
-  border: none;
-  padding: 10px;
-  padding-right: 30px;
-  font-family: inherit;
-  background: $ui-base-color;
-  color: $ui-primary-color;
-  font-size: 14px;
-  margin: 0;
-
-  &::-moz-focus-inner {
-    border: 0;
-  }
-
-  &::-moz-focus-inner,
-  &:focus,
-  &:active {
-    outline: 0 !important;
-  }
-
-  &:focus {
-    background: lighten($ui-base-color, 4%);
-  }
-
-  @include limited-single-column('screen and (max-width: 600px)') {
-    font-size: 16px;
-  }
-}
-
-.search__icon {
-  .fa {
-    position: absolute;
-    top: 10px;
-    right: 10px;
-    z-index: 2;
-    display: inline-block;
-    opacity: 0;
-    transition: all 100ms linear;
-    font-size: 18px;
-    width: 18px;
-    height: 18px;
-    color: $ui-secondary-color;
-    cursor: default;
-    pointer-events: none;
-
-    &.active {
-      pointer-events: auto;
-      opacity: 0.3;
-    }
-  }
-
-  .fa-search {
-    transform: rotate(90deg);
-
-    &.active {
-      pointer-events: none;
-      transform: rotate(0deg);
-    }
-  }
-
-  .fa-times-circle {
-    top: 11px;
-    transform: rotate(0deg);
-    cursor: pointer;
-
-    &.active {
-      transform: rotate(90deg);
-    }
-
-    &:hover {
-      color: $primary-text-color;
-    }
-  }
-}
-
-.search-results__header {
-  color: $ui-base-lighter-color;
-  background: lighten($ui-base-color, 2%);
-  border-bottom: 1px solid darken($ui-base-color, 4%);
-  padding: 15px 10px;
-  font-size: 14px;
-  font-weight: 500;
-}
-
-.search-results__section {
-  background: $ui-base-color;
-}
-
-.search-results__hashtag {
-  display: block;
-  padding: 10px;
-  color: $ui-secondary-color;
-  text-decoration: none;
-
-  &:hover,
-  &:active,
-  &:focus {
-    color: lighten($ui-secondary-color, 4%);
-    text-decoration: underline;
-  }
-}
-
-.modal-root {
-  transition: opacity 0.3s linear;
-  will-change: opacity;
-  z-index: 9999;
-}
-
-.modal-root__overlay {
-  position: absolute;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  background: rgba($base-overlay-background, 0.7);
-}
-
-.modal-root__container {
-  position: absolute;
-  top: 0;
-  left: 0;
-  width: 100%;
-  height: 100%;
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-  align-content: space-around;
-  z-index: 9999;
-  pointer-events: none;
-  user-select: none;
-}
-
-.modal-root__modal {
-  pointer-events: auto;
-  display: flex;
-  z-index: 9999;
-}
-
-.media-modal {
-  max-width: 80vw;
-  max-height: 80vh;
-  position: relative;
-
-  .extended-video-player,
-  img,
-  canvas,
-  video {
-    max-width: 80vw;
-    max-height: 80vh;
-    width: auto;
-    height: auto;
-    margin: auto;
-  }
-
-  .extended-video-player,
-  video {
-    display: flex;
-    width: 80vw;
-    height: 80vh;
-  }
-
-  img,
-  canvas {
-    display: block;
-    background: url('~images/void.png') repeat;
-    object-fit: contain;
-  }
-
-  .react-swipeable-view-container {
-    max-width: 80vw;
-  }
-}
-
-.media-modal__content {
-  background: $base-overlay-background;
-}
-
-.media-modal__pagination {
-  width: 100%;
-  text-align: center;
-  position: absolute;
-  left: 0;
-  bottom: -40px;
-}
-
-.media-modal__page-dot {
-  display: inline-block;
-}
-
-.media-modal__button {
-  background-color: $white;
-  height: 12px;
-  width: 12px;
-  border-radius: 6px;
-  margin: 10px;
-  padding: 0;
-  border: 0;
-  font-size: 0;
-}
-
-.media-modal__button--active {
-  background-color: $ui-highlight-color;
-}
-
-.media-modal__close {
-  position: absolute;
-  right: 4px;
-  top: 4px;
-  z-index: 100;
-}
-
-.onboarding-modal,
-.error-modal,
-.embed-modal {
-  background: $ui-secondary-color;
-  color: $ui-base-color;
-  border-radius: 8px;
-  overflow: hidden;
-  display: flex;
-  flex-direction: column;
-}
-
-.onboarding-modal__pager {
-  height: 80vh;
-  width: 80vw;
-  max-width: 520px;
-  max-height: 420px;
-
-  .react-swipeable-view-container > div {
-    width: 100%;
-    height: 100%;
-    box-sizing: border-box;
-    padding: 25px;
-    display: none;
-    flex-direction: column;
-    align-items: center;
-    justify-content: center;
-    display: flex;
-    user-select: text;
-  }
-}
-
-.error-modal__body {
-  height: 80vh;
-  width: 80vw;
-  max-width: 520px;
-  max-height: 420px;
-  position: relative;
-
-  & > div {
-    position: absolute;
-    top: 0;
-    left: 0;
-    width: 100%;
-    height: 100%;
-    box-sizing: border-box;
-    padding: 25px;
-    display: none;
-    flex-direction: column;
-    align-items: center;
-    justify-content: center;
-    display: flex;
-    opacity: 0;
-    user-select: text;
-  }
-}
-
-.error-modal__body {
-  display: flex;
-  flex-direction: column;
-  justify-content: center;
-  align-items: center;
-  text-align: center;
-}
-
-@media screen and (max-width: 550px) {
-  .onboarding-modal {
-    width: 100%;
-    height: 100%;
-    border-radius: 0;
-  }
-
-  .onboarding-modal__pager {
-    width: 100%;
-    height: auto;
-    max-width: none;
-    max-height: none;
-    flex: 1 1 auto;
-  }
-}
-
-.onboarding-modal__paginator,
-.error-modal__footer {
-  flex: 0 0 auto;
-  background: darken($ui-secondary-color, 8%);
-  display: flex;
-  padding: 25px;
-
-  & > div {
-    min-width: 33px;
-  }
-
-  .onboarding-modal__nav,
-  .error-modal__nav {
-    color: darken($ui-secondary-color, 34%);
-    background-color: transparent;
-    border: 0;
-    font-size: 14px;
-    font-weight: 500;
-    padding: 0;
-    line-height: inherit;
-    height: auto;
-
-    &:hover,
-    &:focus,
-    &:active {
-      color: darken($ui-secondary-color, 38%);
-    }
-
-    &.onboarding-modal__done,
-    &.onboarding-modal__next {
-      color: $ui-highlight-color;
-    }
-  }
-}
-
-.error-modal__footer {
-  justify-content: center;
-}
-
-.onboarding-modal__dots {
-  flex: 1 1 auto;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-}
-
-.onboarding-modal__dot {
-  width: 14px;
-  height: 14px;
-  border-radius: 14px;
-  background: darken($ui-secondary-color, 16%);
-  margin: 0 3px;
-  cursor: pointer;
-
-  &:hover {
-    background: darken($ui-secondary-color, 18%);
-  }
-
-  &.active {
-    cursor: default;
-    background: darken($ui-secondary-color, 24%);
-  }
-}
-
-.onboarding-modal__page__wrapper {
-  pointer-events: none;
-
-  &.onboarding-modal__page__wrapper--active {
-    pointer-events: auto;
-  }
-}
-
-.onboarding-modal__page {
-  cursor: default;
-  line-height: 21px;
-
-  h1 {
-    font-size: 18px;
-    font-weight: 500;
-    color: $ui-base-color;
-    margin-bottom: 20px;
-  }
-
-  a {
-    color: $ui-highlight-color;
-
-    &:hover,
-    &:focus,
-    &:active {
-      color: lighten($ui-highlight-color, 4%);
-    }
-  }
-
-  p {
-    font-size: 16px;
-    color: lighten($ui-base-color, 8%);
-    margin-top: 10px;
-    margin-bottom: 10px;
-
-    &:last-child {
-      margin-bottom: 0;
-    }
-
-    strong {
-      font-weight: 500;
-      background: $ui-base-color;
-      color: $ui-secondary-color;
-      border-radius: 4px;
-      font-size: 14px;
-      padding: 3px 6px;
-    }
-  }
-}
-
-.onboarding-modal__page-one {
-  display: flex;
-  align-items: center;
-}
-
-.onboarding-modal__page-one__elephant-friend {
-  background: url('~images/elephant-friend-1.png') no-repeat center center / contain;
-  width: 155px;
-  height: 193px;
-  margin-right: 15px;
-}
-
-@media screen and (max-width: 400px) {
-  .onboarding-modal__page-one {
-    flex-direction: column;
-    align-items: normal;
-  }
-
-  .onboarding-modal__page-one__elephant-friend {
-    width: 100%;
-    height: 30vh;
-    max-height: 160px;
-    margin-bottom: 5vh;
-  }
-}
-
-.onboarding-modal__page-two,
-.onboarding-modal__page-three,
-.onboarding-modal__page-four,
-.onboarding-modal__page-five {
-  p {
-    text-align: left;
-  }
-
-  .figure {
-    background: darken($ui-base-color, 8%);
-    color: $ui-secondary-color;
-    margin-bottom: 20px;
-    border-radius: 4px;
-    padding: 10px;
-    text-align: center;
-    font-size: 14px;
-    box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.3);
-
-    .onboarding-modal__image {
-      border-radius: 4px;
-      margin-bottom: 10px;
-    }
-
-    &.non-interactive {
-      pointer-events: none;
-      text-align: left;
-    }
-  }
-}
-
-.onboarding-modal__page-four__columns {
-  .row {
-    display: flex;
-    margin-bottom: 20px;
-
-    & > div {
-      flex: 1 1 0;
-      margin: 0 10px;
-
-      &:first-child {
-        margin-left: 0;
-      }
-
-      &:last-child {
-        margin-right: 0;
-      }
-
-      p {
-        text-align: center;
-      }
-    }
-
-    &:last-child {
-      margin-bottom: 0;
-    }
-  }
-
-  .column-header {
-    color: $primary-text-color;
-  }
-}
-
-@media screen and (max-width: 320px) and (max-height: 600px) {
-  .onboarding-modal__page p {
-    font-size: 14px;
-    line-height: 20px;
-  }
-
-  .onboarding-modal__page-two .figure,
-  .onboarding-modal__page-three .figure,
-  .onboarding-modal__page-four .figure,
-  .onboarding-modal__page-five .figure {
-    font-size: 12px;
-    margin-bottom: 10px;
-  }
-
-  .onboarding-modal__page-four__columns .row {
-    margin-bottom: 10px;
-  }
-
-  .onboarding-modal__page-four__columns .column-header {
-    padding: 5px;
-    font-size: 12px;
-  }
-}
-
-.onboarding-modal__image {
-  border-radius: 8px;
-  width: 70vw;
-  max-width: 450px;
-  max-height: auto;
-  display: block;
-  margin: auto;
-  margin-bottom: 20px;
-}
-
-.onboard-sliders {
-  display: inline-block;
-  max-width: 30px;
-  max-height: auto;
-  margin-left: 10px;
-}
-
-.boost-modal,
-.confirmation-modal,
-.report-modal,
-.actions-modal,
-.mute-modal {
-  background: lighten($ui-secondary-color, 8%);
-  color: $ui-base-color;
-  border-radius: 8px;
-  overflow: hidden;
-  max-width: 90vw;
-  width: 480px;
-  position: relative;
-  flex-direction: column;
-
-  .status__display-name {
-    display: flex;
-  }
-}
-
-.actions-modal {
-  .status {
-    background: $white;
-    border-bottom-color: $ui-secondary-color;
-    padding-top: 10px;
-    padding-bottom: 10px;
-  }
-
-  .dropdown-menu__separator {
-    border-bottom-color: $ui-secondary-color;
-  }
-}
-
-.boost-modal__container {
-  overflow-x: scroll;
-  padding: 10px;
-
-  .status {
-    user-select: text;
-    border-bottom: 0;
-  }
-}
-
-.boost-modal__action-bar,
-.confirmation-modal__action-bar,
-.mute-modal__action-bar,
-.report-modal__action-bar {
-  display: flex;
-  justify-content: space-between;
-  background: $ui-secondary-color;
-  padding: 10px;
-  line-height: 36px;
-
-  & > div {
-    flex: 1 1 auto;
-    text-align: right;
-    color: lighten($ui-base-color, 33%);
-    padding-right: 10px;
-  }
-
-  .button {
-    flex: 0 0 auto;
-  }
-}
-
-.boost-modal__status-header {
-  font-size: 15px;
-}
-
-.boost-modal__status-time {
-  float: right;
-  font-size: 14px;
-}
-
-.confirmation-modal {
-  max-width: 85vw;
-
-  @media screen and (min-width: 480px) {
-    max-width: 380px;
-  }
-}
-
-.mute-modal {
-  line-height: 24px;
-}
-
-.mute-modal .react-toggle {
-  vertical-align: middle;
-}
-
-.report-modal__statuses,
-.report-modal__comment {
-  padding: 10px;
-}
-
-.report-modal__statuses {
-  min-height: 20vh;
-  max-height: 40vh;
-  overflow-y: auto;
-  overflow-x: hidden;
-}
-
-.report-modal__comment {
-  .setting-text {
-    margin-top: 10px;
-  }
-}
-
-.actions-modal {
-  .status {
-    overflow-y: auto;
-    max-height: 300px;
-  }
-
-  max-height: 80vh;
-  max-width: 80vw;
-
-  .actions-modal__item-label {
-    font-weight: 500;
-  }
-
-  ul {
-    overflow-y: auto;
-    flex-shrink: 0;
-
-    li:empty {
-      margin: 0;
-    }
-
-    li:not(:empty) {
-      a {
-        color: $ui-base-color;
-        display: flex;
-        padding: 12px 16px;
-        font-size: 15px;
-        align-items: center;
-        text-decoration: none;
-
-        &,
-        button {
-          transition: none;
-        }
-
-        &.active,
-        &:hover,
-        &:active,
-        &:focus {
-          &,
-          button {
-            background: $ui-highlight-color;
-            color: $primary-text-color;
-          }
-        }
-
-        button:first-child {
-          margin-right: 10px;
-        }
-      }
-    }
-  }
-}
-
-.confirmation-modal__action-bar,
-.mute-modal__action-bar {
-  .confirmation-modal__cancel-button,
-  .mute-modal__cancel-button {
-    background-color: transparent;
-    color: darken($ui-secondary-color, 34%);
-    font-size: 14px;
-    font-weight: 500;
-
-    &:hover,
-    &:focus,
-    &:active {
-      color: darken($ui-secondary-color, 38%);
-    }
-  }
-}
-
-.confirmation-modal__container,
-.mute-modal__container,
-.report-modal__target {
-  padding: 30px;
-  font-size: 16px;
-  text-align: center;
-
-  strong {
-    font-weight: 500;
-  }
-}
-
-.loading-bar {
-  background-color: $ui-highlight-color;
-  height: 3px;
-  position: absolute;
-  top: 0;
-  left: 0;
-}
-
-.media-gallery__gifv__label {
-  display: block;
-  position: absolute;
-  color: $primary-text-color;
-  background: rgba($base-overlay-background, 0.5);
-  bottom: 6px;
-  left: 6px;
-  padding: 2px 6px;
-  border-radius: 2px;
-  font-size: 11px;
-  font-weight: 600;
-  z-index: 1;
-  pointer-events: none;
-  opacity: 0.9;
-  transition: opacity 0.1s ease;
-}
-
-.media-gallery__gifv {
-  &.autoplay {
-    .media-gallery__gifv__label {
-      display: none;
-    }
-  }
-
-  &:hover {
-    .media-gallery__gifv__label {
-      opacity: 1;
-    }
-  }
-}
-
-.attachment-list {
-  display: flex;
-  font-size: 14px;
-  border: 1px solid lighten($ui-base-color, 8%);
-  border-radius: 4px;
-  margin-top: 14px;
-  overflow: hidden;
-}
-
-.attachment-list__icon {
-  flex: 0 0 auto;
-  color: $ui-base-lighter-color;
-  padding: 8px 18px;
-  cursor: default;
-  border-right: 1px solid lighten($ui-base-color, 8%);
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-  font-size: 26px;
-
-  .fa {
-    display: block;
-  }
-}
-
-.attachment-list__list {
-  list-style: none;
-  padding: 4px 0;
-  padding-left: 8px;
-  display: flex;
-  flex-direction: column;
-  justify-content: center;
-
-  li {
-    display: block;
-    padding: 4px 0;
-  }
-
-  a {
-    text-decoration: none;
-    color: $ui-base-lighter-color;
-    font-weight: 500;
-
-    &:hover {
-      text-decoration: underline;
-    }
-  }
-}
-
-/* Media Gallery */
-.media-gallery {
-  box-sizing: border-box;
-  margin-top: 15px;
-  overflow: hidden;
-  position: relative;
-  background: $base-shadow-color;
-  width: 100%;
-  height: 110px;
-
-  .detailed-status & {
-    margin-left: -12px;
-    width: calc(100% + 24px);
-    height: 250px;
-  }
-
-  @include fullwidth-gallery;
-}
-
-.media-gallery__item {
-  border: none;
-  box-sizing: border-box;
-  display: block;
-  float: left;
-  position: relative;
-
-  &.standalone {
-    .media-gallery__item-gifv-thumbnail {
-      transform: none;
-    }
-  }
-}
-
-.media-gallery__item-thumbnail {
-  cursor: zoom-in;
-  text-decoration: none;
-  width: 100%;
-  height: 100%;
-  line-height: 0;
-  display: flex;
-
-  img {
-    width: 100%;
-    object-fit: contain;
-
-    &:not(.letterbox) {
-      height: 100%;
-      object-fit: cover;
-    }
-  }
-}
-
-.media-gallery__gifv {
-  height: 100%;
-  overflow: hidden;
-  position: relative;
-  width: 100%;
-  display: flex;
-  justify-content: center;
-}
-
-.media-gallery__item-gifv-thumbnail {
-  cursor: zoom-in;
-  height: 100%;
-  position: relative;
-  z-index: 1;
-  object-fit: contain;
-
-  &:not(.letterbox) {
-    height: 100%;
-    object-fit: cover;
-  }
-}
-
-.media-gallery__item-thumbnail-label {
-  clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
-  clip: rect(1px, 1px, 1px, 1px);
-  overflow: hidden;
-  position: absolute;
-}
-/* End Media Gallery */
-
-/* Status Video Player */
-.status__video-player {
-  display: flex;
-  align-items: center;
-  background: $base-shadow-color;
-  box-sizing: border-box;
-  cursor: default; /* May not be needed */
-  margin-top: 15px;
-  overflow: hidden;
-  position: relative;
-  width: 100%;
-
-  @include fullwidth-gallery;
-}
-
-.status__video-player-video {
-  position: relative;
-  width: 100%;
-  z-index: 1;
-
-  &:not(.letterbox) {
-    height: 100%;
-    object-fit: cover;
-  }
-}
-
-.status__video-player-expand,
-.status__video-player-mute {
-  color: $primary-text-color;
-  opacity: 0.8;
-  position: absolute;
-  right: 4px;
-  text-shadow: 0 1px 1px $base-shadow-color, 1px 0 1px $base-shadow-color;
-}
-
-.status__video-player-spoiler {
-  display: none;
-  color: $primary-text-color;
-  left: 4px;
-  position: absolute;
-  text-shadow: 0 1px 1px $base-shadow-color, 1px 0 1px $base-shadow-color;
-  top: 4px;
-  z-index: 100;
-
-  &.status__video-player-spoiler--visible {
-    display: block;
-  }
-}
-
-.status__video-player-expand {
-  bottom: 4px;
-  z-index: 100;
-}
-
-.status__video-player-mute {
-  top: 4px;
-  z-index: 5;
-}
-
-.video-player {
-  overflow: hidden;
-  position: relative;
-  background: $base-shadow-color;
-  width: 100%;
-  max-width: 100%;
-  height: 110px;
-
-  .detailed-status & {
-    margin-left: -12px;
-    width: calc(100% + 24px);
-    height: 250px;
-  }
-
-  @include fullwidth-gallery;
-
-  video {
-    height: 100%;
-    width: 100%;
-    z-index: 1;
-  }
-
-  &.fullscreen {
-    width: 100% !important;
-    height: 100% !important;
-    margin: 0;
-
-    video {
-      max-width: 100% !important;
-      max-height: 100% !important;
-    }
-  }
-
-  &.inline {
-    video {
-      object-fit: cover;
-      position: relative;
-      top: 50%;
-      transform: translateY(-50%);
-    }
-  }
-
-  &__controls {
-    position: absolute;
-    z-index: 2;
-    bottom: 0;
-    left: 0;
-    right: 0;
-    box-sizing: border-box;
-    background: linear-gradient(0deg, rgba($base-shadow-color, 0.8) 0, rgba($base-shadow-color, 0.35) 60%, transparent);
-    padding: 0 10px;
-    opacity: 0;
-    transition: opacity .1s ease;
-
-    &.active {
-      opacity: 1;
-    }
-  }
-
-  &.inactive {
-    video,
-    .video-player__controls {
-      visibility: hidden;
-    }
-  }
-
-  &__spoiler {
-    display: none;
-    position: absolute;
-    top: 0;
-    left: 0;
-    width: 100%;
-    height: 100%;
-    z-index: 4;
-    border: 0;
-    background: $base-shadow-color;
-    color: $ui-primary-color;
-    transition: none;
-    pointer-events: none;
-
-    &.active {
-      display: block;
-      pointer-events: auto;
-
-      &:hover,
-      &:active,
-      &:focus {
-        color: lighten($ui-primary-color, 8%);
-      }
-    }
-
-    &__title {
-      display: block;
-      font-size: 14px;
-    }
-
-    &__subtitle {
-      display: block;
-      font-size: 11px;
-      font-weight: 500;
-    }
-  }
-
-  &__buttons {
-    padding-bottom: 10px;
-    font-size: 16px;
-
-    &.left {
-      float: left;
-
-      button {
-        padding-right: 10px;
-      }
-    }
-
-    &.right {
-      float: right;
-
-      button {
-        padding-left: 10px;
-      }
-    }
-
-    button {
-      background: transparent;
-      padding: 0;
-      border: 0;
-      color: $white;
-
-      &:active,
-      &:hover,
-      &:focus {
-        color: $ui-highlight-color;
-      }
-    }
-  }
-
-  &__seek {
-    cursor: pointer;
-    height: 24px;
-    position: relative;
-
-    &::before {
-      content: "";
-      width: 100%;
-      background: rgba($white, 0.35);
-      display: block;
-      position: absolute;
-      height: 4px;
-      top: 10px;
-    }
-
-    &__progress,
-    &__buffer {
-      display: block;
-      position: absolute;
-      height: 4px;
-      top: 10px;
-      background: $ui-highlight-color;
-    }
-
-    &__buffer {
-      background: rgba($white, 0.2);
-    }
-
-    &__handle {
-      position: absolute;
-      z-index: 3;
-      opacity: 0;
-      border-radius: 50%;
-      width: 12px;
-      height: 12px;
-      top: 6px;
-      margin-left: -6px;
-      transition: opacity .1s ease;
-      background: $ui-highlight-color;
-      pointer-events: none;
-
-      &.active {
-        opacity: 1;
-      }
-    }
-
-    &:hover {
-      .video-player__seek__handle {
-        opacity: 1;
-      }
-    }
-  }
-}
-
-.media-spoiler-video {
-  background-size: cover;
-  background-repeat: no-repeat;
-  background-position: center;
-  cursor: pointer;
-  margin-top: 15px;
-  position: relative;
-  width: 100%;
-
-  @include fullwidth-gallery;
-
-  border: 0;
-  display: block;
-}
-
-.media-spoiler-video-play-icon {
-  border-radius: 100px;
-  color: rgba($primary-text-color, 0.8);
-  font-size: 36px;
-  left: 50%;
-  padding: 5px;
-  position: absolute;
-  top: 50%;
-  transform: translate(-50%, -50%);
-}
-/* End Video Player */
-
-.account-gallery__container {
-  margin: -2px;
-  padding: 4px;
-  display: flex;
-  flex-wrap: wrap;
-}
-
-.account-gallery__item {
-  flex: 1 1 auto;
-  width: calc(100% / 3 - 4px);
-  height: 95px;
-  margin: 2px;
-
-  a {
-    display: block;
-    width: 100%;
-    height: 100%;
-    background-color: $base-overlay-background;
-    background-size: cover;
-    background-position: center;
-    position: relative;
-    color: inherit;
-    text-decoration: none;
-
-    &:hover,
-    &:active,
-    &:focus {
-      outline: 0;
-    }
-  }
-}
-
-.account-section-headline {
-  color: $ui-base-lighter-color;
-  background: lighten($ui-base-color, 2%);
-  border-bottom: 1px solid lighten($ui-base-color, 4%);
-  padding: 15px 10px;
-  font-size: 14px;
-  font-weight: 500;
-  position: relative;
-  cursor: default;
-
-  &::before,
-  &::after {
-    display: block;
-    content: "";
-    position: absolute;
-    bottom: 0;
-    left: 18px;
-    width: 0;
-    height: 0;
-    border-style: solid;
-    border-width: 0 10px 10px;
-    border-color: transparent transparent lighten($ui-base-color, 4%);
-  }
-
-  &::after {
-    bottom: -1px;
-    border-color: transparent transparent $ui-base-color;
-  }
-}
-
-::-webkit-scrollbar-thumb {
-  border-radius: 0;
-}
-
-.search-popout {
-  background: $simple-background-color;
-  border-radius: 4px;
-  padding: 10px 14px;
-  padding-bottom: 14px;
-  margin-top: 10px;
-  color: $ui-primary-color;
-  box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
-
-  h4 {
-    text-transform: uppercase;
-    color: $ui-primary-color;
-    font-size: 13px;
-    font-weight: 500;
-    margin-bottom: 10px;
-  }
-
-  li {
-    padding: 4px 0;
-  }
-
-  ul {
-    margin-bottom: 10px;
-  }
-
-  em {
-    font-weight: 500;
-    color: $ui-base-color;
-  }
-}
-
-noscript {
-  text-align: center;
-
-  img {
-    width: 200px;
-    opacity: 0.5;
-    animation: flicker 4s infinite;
-  }
-
-  div {
-    font-size: 14px;
-    margin: 30px auto;
-    color: $ui-secondary-color;
-    max-width: 400px;
-
-    a {
-      color: $ui-highlight-color;
-      text-decoration: underline;
-
-      &:hover {
-        text-decoration: none;
-      }
-    }
-  }
-}
-
-@keyframes flicker {
-  0% { opacity: 1; }
-  30% { opacity: 0.75; }
-  100% { opacity: 1; }
-}
-
-@media screen and (max-width: 630px) and (max-height: 400px) {
-  $duration: 400ms;
-  $delay: 100ms;
-
-  .tabs-bar,
-  .search {
-    will-change: margin-top;
-    transition: margin-top $duration $delay;
-  }
-
-  .navigation-bar {
-    will-change: padding-bottom;
-    transition: padding-bottom $duration $delay;
-  }
-
-  .navigation-bar {
-    & > a:first-child {
-      will-change: margin-top, margin-left, width;
-      transition: margin-top $duration $delay, margin-left $duration ($duration + $delay);
-    }
-
-    & > .navigation-bar__profile-edit {
-      will-change: margin-top;
-      transition: margin-top $duration $delay;
-    }
-
-    & > .icon-button {
-      will-change: opacity;
-      transition: opacity $duration $delay;
-    }
-  }
-
-  .is-composing {
-    .tabs-bar,
-    .search {
-      margin-top: -50px;
-    }
-
-    .navigation-bar {
-      padding-bottom: 0;
-
-      & > a:first-child {
-        margin-top: -50px;
-        margin-left: -40px;
-      }
-
-      .navigation-bar__profile {
-        padding-top: 2px;
-      }
-
-      .navigation-bar__profile-edit {
-        position: absolute;
-        margin-top: -50px;
-      }
-
-      .icon-button {
-        pointer-events: auto;
-        opacity: 1;
-      }
-    }
-  }
-
-  // fixes for the navbar-under mode
-  .is-composing.navbar-under {
-    .search {
-      margin-top: -20px;
-      margin-bottom: -20px;
-      .search__icon {
-        display: none;
-      }
-    }
-  }
-}
-
-// more fixes for the navbar-under mode
-@mixin fix-margins-for-navbar-under {
-  .tabs-bar {
-    margin-top: 0 !important;
-    margin-bottom: -6px !important;
-  }
-}
-
-.single-column.navbar-under {
-  @include fix-margins-for-navbar-under;
-}
-
-.auto-columns.navbar-under {
-  @media screen and (max-width: 360px) {
-    @include fix-margins-for-navbar-under;
-  }
-}
-
-.auto-columns.navbar-under .react-swipeable-view-container .columns-area,
-.single-column.navbar-under .react-swipeable-view-container .columns-area {
-  @media screen and (max-width: 360px) {
-    height: 100% !important;
-  }
-}
-
-.embed-modal {
-  max-width: 80vw;
-  max-height: 80vh;
-
-  h4 {
-    padding: 30px;
-    font-weight: 500;
-    font-size: 16px;
-    text-align: center;
-  }
-
-  .embed-modal__container {
-    padding: 10px;
-
-    .hint {
-      margin-bottom: 15px;
-    }
-
-    .embed-modal__html {
-      color: $ui-secondary-color;
-      outline: 0;
-      box-sizing: border-box;
-      display: block;
-      width: 100%;
-      border: none;
-      padding: 10px;
-      font-family: 'mastodon-font-monospace', monospace;
-      background: $ui-base-color;
-      color: $ui-primary-color;
-      font-size: 14px;
-      margin: 0;
-      margin-bottom: 15px;
-
-      &::-moz-focus-inner {
-        border: 0;
-      }
-
-      &::-moz-focus-inner,
-      &:focus,
-      &:active {
-        outline: 0 !important;
-      }
-
-      &:focus {
-        background: lighten($ui-base-color, 4%);
-      }
-
-      @media screen and (max-width: 600px) {
-        font-size: 16px;
-      }
-    }
-
-    .embed-modal__iframe {
-      width: 400px;
-      max-width: 100%;
-      overflow: hidden;
-      border: 0;
-    }
-  }
-}
-
-@import 'doodle';
diff --git a/app/javascript/themes/glitch/styles/containers.scss b/app/javascript/themes/glitch/styles/containers.scss
deleted file mode 100644
index af2589e23..000000000
--- a/app/javascript/themes/glitch/styles/containers.scss
+++ /dev/null
@@ -1,116 +0,0 @@
-.container {
-  width: 700px;
-  margin: 0 auto;
-  margin-top: 40px;
-
-  @media screen and (max-width: 740px) {
-    width: 100%;
-    margin: 0;
-  }
-}
-
-.logo-container {
-  margin: 100px auto;
-  margin-bottom: 50px;
-
-  @media screen and (max-width: 400px) {
-    margin: 30px auto;
-    margin-bottom: 20px;
-  }
-
-  h1 {
-    display: flex;
-    justify-content: center;
-    align-items: center;
-
-    img {
-      height: 42px;
-      margin-right: 10px;
-    }
-
-    a {
-      display: flex;
-      justify-content: center;
-      align-items: center;
-      color: $primary-text-color;
-      text-decoration: none;
-      outline: 0;
-      padding: 12px 16px;
-      line-height: 32px;
-      font-family: 'mastodon-font-display', sans-serif;
-      font-weight: 500;
-      font-size: 14px;
-    }
-  }
-}
-
-.compose-standalone {
-  .compose-form {
-    width: 400px;
-    margin: 0 auto;
-    padding: 20px 0;
-    margin-top: 40px;
-    box-sizing: border-box;
-
-    @media screen and (max-width: 400px) {
-      width: 100%;
-      margin-top: 0;
-      padding: 20px;
-    }
-  }
-}
-
-.account-header {
-  width: 400px;
-  margin: 0 auto;
-  display: flex;
-  font-size: 13px;
-  line-height: 18px;
-  box-sizing: border-box;
-  padding: 20px 0;
-  padding-bottom: 0;
-  margin-bottom: -30px;
-  margin-top: 40px;
-
-  @media screen and (max-width: 440px) {
-    width: 100%;
-    margin: 0;
-    margin-bottom: 10px;
-    padding: 20px;
-    padding-bottom: 0;
-  }
-
-  .avatar {
-    width: 40px;
-    height: 40px;
-    margin-right: 8px;
-
-    img {
-      width: 100%;
-      height: 100%;
-      display: block;
-      margin: 0;
-      border-radius: 4px;
-    }
-  }
-
-  .name {
-    flex: 1 1 auto;
-    color: $ui-secondary-color;
-    width: calc(100% - 88px);
-
-    .username {
-      display: block;
-      font-weight: 500;
-      text-overflow: ellipsis;
-      overflow: hidden;
-    }
-  }
-
-  .logout-link {
-    display: block;
-    font-size: 32px;
-    line-height: 40px;
-    margin-left: 8px;
-  }
-}
diff --git a/app/javascript/themes/glitch/styles/doodle.scss b/app/javascript/themes/glitch/styles/doodle.scss
deleted file mode 100644
index a4a1cfc84..000000000
--- a/app/javascript/themes/glitch/styles/doodle.scss
+++ /dev/null
@@ -1,86 +0,0 @@
-$doodleBg: #d9e1e8;
-.doodle-modal {
-  @extend .boost-modal;
-  width: unset;
-}
-
-.doodle-modal__container {
-  background: $doodleBg;
-  text-align: center;
-  line-height: 0; // remove weird gap under canvas
-  canvas {
-    border: 5px solid $doodleBg;
-  }
-}
-
-.doodle-modal__action-bar {
-  @extend .boost-modal__action-bar;
-
-  .filler {
-    flex-grow: 1;
-    margin: 0;
-    padding: 0;
-  }
-
-  .doodle-toolbar {
-    line-height: 1;
-
-    display: flex;
-    flex-direction: column;
-    flex-grow: 0;
-    justify-content: space-around;
-
-    &.with-inputs {
-      label {
-        display: inline-block;
-        width: 70px;
-        text-align: right;
-        margin-right: 2px;
-      }
-
-      input[type="number"],input[type="text"] {
-        width: 40px;
-      }
-      span.val {
-        display: inline-block;
-        text-align: left;
-        width: 50px;
-      }
-    }
-  }
-
-  .doodle-palette {
-    padding-right: 0 !important;
-    border: 1px solid black;
-    line-height: .2rem;
-    flex-grow: 0;
-    background: white;
-
-    button {
-      appearance: none;
-      width: 1rem;
-      height: 1rem;
-      margin: 0; padding: 0;
-      text-align: center;
-      color: black;
-      text-shadow: 0 0 1px white;
-      cursor: pointer;
-      box-shadow: inset 0 0 1px rgba(white, .5);
-      border: 1px solid black;
-      outline-offset:-1px;
-
-      &.foreground {
-        outline: 1px dashed white;
-      }
-
-      &.background {
-        outline: 1px dashed red;
-      }
-
-      &.foreground.background {
-        outline: 1px dashed red;
-        border-color: white;
-      }
-    }
-  }
-}
diff --git a/app/javascript/themes/glitch/styles/emoji_picker.scss b/app/javascript/themes/glitch/styles/emoji_picker.scss
deleted file mode 100644
index 2b46d30fc..000000000
--- a/app/javascript/themes/glitch/styles/emoji_picker.scss
+++ /dev/null
@@ -1,199 +0,0 @@
-.emoji-mart {
-  &,
-  * {
-    box-sizing: border-box;
-    line-height: 1.15;
-  }
-
-  font-size: 13px;
-  display: inline-block;
-  color: $ui-base-color;
-
-  .emoji-mart-emoji {
-    padding: 6px;
-  }
-}
-
-.emoji-mart-bar {
-  border: 0 solid darken($ui-secondary-color, 8%);
-
-  &:first-child {
-    border-bottom-width: 1px;
-    border-top-left-radius: 5px;
-    border-top-right-radius: 5px;
-    background: $ui-secondary-color;
-  }
-
-  &:last-child {
-    border-top-width: 1px;
-    border-bottom-left-radius: 5px;
-    border-bottom-right-radius: 5px;
-    display: none;
-  }
-}
-
-.emoji-mart-anchors {
-  display: flex;
-  justify-content: space-between;
-  padding: 0 6px;
-  color: $ui-primary-color;
-  line-height: 0;
-}
-
-.emoji-mart-anchor {
-  position: relative;
-  flex: 1;
-  text-align: center;
-  padding: 12px 4px;
-  overflow: hidden;
-  transition: color .1s ease-out;
-  cursor: pointer;
-
-  &:hover {
-    color: darken($ui-primary-color, 4%);
-  }
-}
-
-.emoji-mart-anchor-selected {
-  color: darken($ui-highlight-color, 3%);
-
-  &:hover {
-    color: darken($ui-highlight-color, 3%);
-  }
-
-  .emoji-mart-anchor-bar {
-    bottom: 0;
-  }
-}
-
-.emoji-mart-anchor-bar {
-  position: absolute;
-  bottom: -3px;
-  left: 0;
-  width: 100%;
-  height: 3px;
-  background-color: darken($ui-highlight-color, 3%);
-}
-
-.emoji-mart-anchors {
-  i {
-    display: inline-block;
-    width: 100%;
-    max-width: 22px;
-  }
-
-  svg {
-    fill: currentColor;
-    max-height: 18px;
-  }
-}
-
-.emoji-mart-scroll {
-  overflow-y: scroll;
-  height: 270px;
-  max-height: 35vh;
-  padding: 0 6px 6px;
-  background: $simple-background-color;
-  will-change: transform;
-}
-
-.emoji-mart-search {
-  padding: 10px;
-  padding-right: 45px;
-  background: $simple-background-color;
-
-  input {
-    font-size: 14px;
-    font-weight: 400;
-    padding: 7px 9px;
-    font-family: inherit;
-    display: block;
-    width: 100%;
-    background: rgba($ui-secondary-color, 0.3);
-    color: $ui-primary-color;
-    border: 1px solid $ui-secondary-color;
-    border-radius: 4px;
-
-    &::-moz-focus-inner {
-      border: 0;
-    }
-
-    &::-moz-focus-inner,
-    &:focus,
-    &:active {
-      outline: 0 !important;
-    }
-  }
-}
-
-.emoji-mart-category .emoji-mart-emoji {
-  cursor: pointer;
-
-  span {
-    z-index: 1;
-    position: relative;
-    text-align: center;
-  }
-
-  &:hover::before {
-    z-index: 0;
-    content: "";
-    position: absolute;
-    top: 0;
-    left: 0;
-    width: 100%;
-    height: 100%;
-    background-color: rgba($ui-secondary-color, 0.7);
-    border-radius: 100%;
-  }
-}
-
-.emoji-mart-category-label {
-  z-index: 2;
-  position: relative;
-  position: -webkit-sticky;
-  position: sticky;
-  top: 0;
-
-  span {
-    display: block;
-    width: 100%;
-    font-weight: 500;
-    padding: 5px 6px;
-    background: $simple-background-color;
-  }
-}
-
-.emoji-mart-emoji {
-  position: relative;
-  display: inline-block;
-  font-size: 0;
-
-  span {
-    width: 22px;
-    height: 22px;
-  }
-}
-
-.emoji-mart-no-results {
-  font-size: 14px;
-  text-align: center;
-  padding-top: 70px;
-  color: $ui-primary-color;
-
-  .emoji-mart-category-label {
-    display: none;
-  }
-
-  .emoji-mart-no-results-label {
-    margin-top: .2em;
-  }
-
-  .emoji-mart-emoji:hover::before {
-    content: none;
-  }
-}
-
-.emoji-mart-preview {
-  display: none;
-}
diff --git a/app/javascript/themes/glitch/styles/footer.scss b/app/javascript/themes/glitch/styles/footer.scss
deleted file mode 100644
index 2d953b34e..000000000
--- a/app/javascript/themes/glitch/styles/footer.scss
+++ /dev/null
@@ -1,30 +0,0 @@
-.footer {
-  text-align: center;
-  margin-top: 30px;
-  font-size: 12px;
-  color: darken($ui-secondary-color, 25%);
-
-  .domain {
-    font-weight: 500;
-
-    a {
-      color: inherit;
-      text-decoration: none;
-    }
-  }
-
-  .powered-by,
-  .single-user-login {
-    font-weight: 400;
-
-    a {
-      color: inherit;
-      text-decoration: underline;
-      font-weight: 500;
-
-      &:hover {
-        text-decoration: none;
-      }
-    }
-  }
-}
diff --git a/app/javascript/themes/glitch/styles/forms.scss b/app/javascript/themes/glitch/styles/forms.scss
deleted file mode 100644
index 61fcf286f..000000000
--- a/app/javascript/themes/glitch/styles/forms.scss
+++ /dev/null
@@ -1,540 +0,0 @@
-code {
-  font-family: 'mastodon-font-monospace', monospace;
-  font-weight: 400;
-}
-
-.form-container {
-  max-width: 400px;
-  padding: 20px;
-  margin: 0 auto;
-}
-
-.simple_form {
-  .input {
-    margin-bottom: 15px;
-    overflow: hidden;
-  }
-
-  span.hint {
-    display: block;
-    color: $ui-primary-color;
-    font-size: 12px;
-    margin-top: 4px;
-  }
-
-  h4 {
-    text-transform: uppercase;
-    font-size: 13px;
-    font-weight: 500;
-    color: $ui-primary-color;
-    padding-bottom: 8px;
-    margin-bottom: 8px;
-    border-bottom: 1px solid lighten($ui-base-color, 8%);
-  }
-
-  p.hint {
-    margin-bottom: 15px;
-    color: $ui-primary-color;
-
-    &.subtle-hint {
-      text-align: center;
-      font-size: 12px;
-      line-height: 18px;
-      margin-top: 15px;
-      margin-bottom: 0;
-      color: $ui-primary-color;
-
-      a {
-        color: $ui-highlight-color;
-      }
-    }
-  }
-
-  .card {
-    margin-bottom: 15px;
-  }
-
-  strong {
-    font-weight: 500;
-  }
-
-  .label_input {
-    display: flex;
-
-    label {
-      flex: 0 0 auto;
-    }
-
-    input {
-      flex: 1 1 auto;
-    }
-  }
-
-  .input.with_label {
-    padding: 15px 0;
-    margin-bottom: 0;
-
-    .label_input {
-      flex-wrap: wrap;
-      align-items: flex-start;
-    }
-
-    &.select .label_input {
-      align-items: initial;
-    }
-
-    .label_input > label {
-      font-family: inherit;
-      font-size: 16px;
-      color: $primary-text-color;
-      display: block;
-      padding-top: 5px;
-      margin-bottom: 5px;
-      flex: 1;
-      min-width: 150px;
-      word-wrap: break-word;
-
-      &.select {
-        flex: 0;
-      }
-
-      & ~ * {
-        margin-left: 10px;
-      }
-    }
-
-    ul {
-      flex: 390px;
-    }
-
-    &.boolean {
-      padding: initial;
-      margin-bottom: initial;
-
-      .label_input > label {
-        font-family: inherit;
-        font-size: 14px;
-        color: $primary-text-color;
-        display: block;
-        width: auto;
-      }
-
-      label.checkbox {
-        position: relative;
-        padding-left: 25px;
-        flex: 1 1 auto;
-      }
-    }
-  }
-
-  .input.with_block_label {
-    & > label {
-      font-family: inherit;
-      font-size: 16px;
-      color: $primary-text-color;
-      display: block;
-      padding-top: 5px;
-    }
-
-    .hint {
-      margin-bottom: 15px;
-    }
-
-    li {
-      float: left;
-      width: 50%;
-    }
-  }
-
-  .fields-group {
-    margin-bottom: 25px;
-  }
-
-  .input.radio_buttons .radio label {
-    margin-bottom: 5px;
-    font-family: inherit;
-    font-size: 14px;
-    color: $primary-text-color;
-    display: block;
-    width: auto;
-  }
-
-  .input.boolean {
-    margin-bottom: 5px;
-
-    label {
-      font-family: inherit;
-      font-size: 14px;
-      color: $primary-text-color;
-      display: block;
-      width: auto;
-    }
-
-    label.checkbox {
-      position: relative;
-      padding-left: 25px;
-      flex: 1 1 auto;
-    }
-
-    input[type=checkbox] {
-      position: absolute;
-      left: 0;
-      top: 5px;
-      margin: 0;
-    }
-
-    .hint {
-      padding-left: 25px;
-      margin-left: 0;
-    }
-  }
-
-  .check_boxes {
-    .checkbox {
-      label {
-        font-family: inherit;
-        font-size: 14px;
-        color: $primary-text-color;
-        display: block;
-        width: auto;
-        position: relative;
-        padding-top: 5px;
-        padding-left: 25px;
-        flex: 1 1 auto;
-      }
-
-      input[type=checkbox] {
-        position: absolute;
-        left: 0;
-        top: 5px;
-        margin: 0;
-      }
-    }
-  }
-
-  input[type=text],
-  input[type=number],
-  input[type=email],
-  input[type=password],
-  textarea {
-    background: transparent;
-    box-sizing: border-box;
-    border: 0;
-    border-bottom: 2px solid $ui-primary-color;
-    border-radius: 2px 2px 0 0;
-    padding: 7px 4px;
-    font-size: 16px;
-    color: $primary-text-color;
-    display: block;
-    width: 100%;
-    outline: 0;
-    font-family: inherit;
-    resize: vertical;
-
-    &:invalid {
-      box-shadow: none;
-    }
-
-    &:focus:invalid {
-      border-bottom-color: $error-value-color;
-    }
-
-    &:required:valid {
-      border-bottom-color: $valid-value-color;
-    }
-
-    &:active,
-    &:focus {
-      border-bottom-color: $ui-highlight-color;
-      background: rgba($base-overlay-background, 0.1);
-    }
-  }
-
-  .input.field_with_errors {
-    label {
-      color: $error-value-color;
-    }
-
-    input[type=text],
-    input[type=email],
-    input[type=password] {
-      border-bottom-color: $error-value-color;
-    }
-
-    .error {
-      display: block;
-      font-weight: 500;
-      color: $error-value-color;
-      margin-top: 4px;
-    }
-  }
-
-  .actions {
-    margin-top: 30px;
-    display: flex;
-  }
-
-  button,
-  .button,
-  .block-button {
-    display: block;
-    width: 100%;
-    border: 0;
-    border-radius: 4px;
-    background: $ui-highlight-color;
-    color: $primary-text-color;
-    font-size: 18px;
-    line-height: inherit;
-    height: auto;
-    padding: 10px;
-    text-transform: uppercase;
-    text-decoration: none;
-    text-align: center;
-    box-sizing: border-box;
-    cursor: pointer;
-    font-weight: 500;
-    outline: 0;
-    margin-bottom: 10px;
-    margin-right: 10px;
-
-    &:last-child {
-      margin-right: 0;
-    }
-
-    &:hover {
-      background-color: lighten($ui-highlight-color, 5%);
-    }
-
-    &:active,
-    &:focus {
-      background-color: darken($ui-highlight-color, 5%);
-    }
-
-    &.negative {
-      background: $error-value-color;
-
-      &:hover {
-        background-color: lighten($error-value-color, 5%);
-      }
-
-      &:active,
-      &:focus {
-        background-color: darken($error-value-color, 5%);
-      }
-    }
-  }
-
-  select {
-    font-size: 16px;
-    max-height: 29px;
-  }
-
-  .input-with-append {
-    position: relative;
-
-    .input input {
-      padding-right: 127px;
-    }
-
-    .append {
-      position: absolute;
-      right: 0;
-      top: 0;
-      padding: 7px 4px;
-      padding-bottom: 9px;
-      font-size: 16px;
-      color: $ui-base-lighter-color;
-      font-family: inherit;
-      pointer-events: none;
-      cursor: default;
-    }
-  }
-}
-
-.flash-message {
-  background: lighten($ui-base-color, 8%);
-  color: $ui-primary-color;
-  border-radius: 4px;
-  padding: 15px 10px;
-  margin-bottom: 30px;
-  box-shadow: 0 0 5px rgba($base-shadow-color, 0.2);
-  text-align: center;
-
-  p {
-    margin-bottom: 15px;
-  }
-
-  .oauth-code {
-    color: $ui-secondary-color;
-    outline: 0;
-    box-sizing: border-box;
-    display: block;
-    width: 100%;
-    border: none;
-    padding: 10px;
-    font-family: 'mastodon-font-monospace', monospace;
-    background: $ui-base-color;
-    color: $ui-primary-color;
-    font-size: 14px;
-    margin: 0;
-
-    &::-moz-focus-inner {
-      border: 0;
-    }
-
-    &::-moz-focus-inner,
-    &:focus,
-    &:active {
-      outline: 0 !important;
-    }
-
-    &:focus {
-      background: lighten($ui-base-color, 4%);
-    }
-  }
-
-  strong {
-    font-weight: 500;
-  }
-
-  @media screen and (max-width: 740px) and (min-width: 441px) {
-    margin-top: 40px;
-  }
-}
-
-.form-footer {
-  margin-top: 30px;
-  text-align: center;
-
-  a {
-    color: $ui-primary-color;
-    text-decoration: none;
-
-    &:hover {
-      text-decoration: underline;
-    }
-  }
-}
-
-.oauth-prompt,
-.follow-prompt {
-  margin-bottom: 30px;
-  text-align: center;
-  color: $ui-primary-color;
-
-  h2 {
-    font-size: 16px;
-    margin-bottom: 30px;
-  }
-
-  strong {
-    color: $ui-secondary-color;
-    font-weight: 500;
-  }
-
-  @media screen and (max-width: 740px) and (min-width: 441px) {
-    margin-top: 40px;
-  }
-}
-
-.qr-wrapper {
-  display: flex;
-  flex-wrap: wrap;
-  align-items: flex-start;
-}
-
-.qr-code {
-  flex: 0 0 auto;
-  background: $simple-background-color;
-  padding: 4px;
-  margin: 0 10px 20px 0;
-  box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
-  display: inline-block;
-
-  svg {
-    display: block;
-    margin: 0;
-  }
-}
-
-.qr-alternative {
-  margin-bottom: 20px;
-  color: $ui-secondary-color;
-  flex: 150px;
-
-  samp {
-    display: block;
-    font-size: 14px;
-  }
-}
-
-.table-form {
-  p {
-    margin-bottom: 15px;
-
-    strong {
-      font-weight: 500;
-    }
-  }
-}
-
-.simple_form,
-.table-form {
-  .warning {
-    box-sizing: border-box;
-    background: rgba($error-value-color, 0.5);
-    color: $primary-text-color;
-    text-shadow: 1px 1px 0 rgba($base-shadow-color, 0.3);
-    box-shadow: 0 2px 6px rgba($base-shadow-color, 0.4);
-    border-radius: 4px;
-    padding: 10px;
-    margin-bottom: 15px;
-
-    a {
-      color: $primary-text-color;
-      text-decoration: underline;
-
-      &:hover,
-      &:focus,
-      &:active {
-        text-decoration: none;
-      }
-    }
-
-    strong {
-      font-weight: 600;
-      display: block;
-      margin-bottom: 5px;
-
-      .fa {
-        font-weight: 400;
-      }
-    }
-  }
-}
-
-.action-pagination {
-  display: flex;
-  flex-wrap: wrap;
-  align-items: center;
-
-  .actions,
-  .pagination {
-    flex: 1 1 auto;
-  }
-
-  .actions {
-    padding: 30px 0;
-    padding-right: 20px;
-    flex: 0 0 auto;
-  }
-}
-
-.post-follow-actions {
-  text-align: center;
-  color: $ui-primary-color;
-
-  div {
-    margin-bottom: 4px;
-  }
-}
diff --git a/app/javascript/themes/glitch/styles/index.scss b/app/javascript/themes/glitch/styles/index.scss
deleted file mode 100644
index c962a1f62..000000000
--- a/app/javascript/themes/glitch/styles/index.scss
+++ /dev/null
@@ -1,22 +0,0 @@
-@import 'mixins';
-@import 'variables';
-@import 'styles/fonts/roboto';
-@import 'styles/fonts/roboto-mono';
-@import 'styles/fonts/montserrat';
-
-@import 'reset';
-@import 'basics';
-@import 'containers';
-@import 'lists';
-@import 'footer';
-@import 'compact_header';
-@import 'landing_strip';
-@import 'forms';
-@import 'accounts';
-@import 'stream_entries';
-@import 'components';
-@import 'emoji_picker';
-@import 'about';
-@import 'tables';
-@import 'admin';
-@import 'rtl';
diff --git a/app/javascript/themes/glitch/styles/landing_strip.scss b/app/javascript/themes/glitch/styles/landing_strip.scss
deleted file mode 100644
index 0bf9daafd..000000000
--- a/app/javascript/themes/glitch/styles/landing_strip.scss
+++ /dev/null
@@ -1,36 +0,0 @@
-.landing-strip,
-.memoriam-strip {
-  background: rgba(darken($ui-base-color, 7%), 0.8);
-  color: $ui-primary-color;
-  font-weight: 400;
-  padding: 14px;
-  border-radius: 4px;
-  margin-bottom: 20px;
-  display: flex;
-  align-items: center;
-
-  strong,
-  a {
-    font-weight: 500;
-  }
-
-  a {
-    color: inherit;
-    text-decoration: underline;
-  }
-
-  .logo {
-    width: 30px;
-    height: 30px;
-    flex: 0 0 auto;
-    margin-right: 15px;
-  }
-
-  @media screen and (max-width: 740px) {
-    margin-bottom: 0;
-  }
-}
-
-.memoriam-strip {
-  background: rgba($base-shadow-color, 0.7);
-}
diff --git a/app/javascript/themes/glitch/styles/lists.scss b/app/javascript/themes/glitch/styles/lists.scss
deleted file mode 100644
index 6019cd800..000000000
--- a/app/javascript/themes/glitch/styles/lists.scss
+++ /dev/null
@@ -1,19 +0,0 @@
-.no-list {
-  list-style: none;
-
-  li {
-    display: inline-block;
-    margin: 0 5px;
-  }
-}
-
-.recovery-codes {
-  list-style: none;
-  margin: 0 auto;
-
-  li {
-    font-size: 125%;
-    line-height: 1.5;
-    letter-spacing: 1px;
-  }
-}
diff --git a/app/javascript/themes/glitch/styles/reset copy.scss b/app/javascript/themes/glitch/styles/reset copy.scss
deleted file mode 100644
index cc5ba9d7c..000000000
--- a/app/javascript/themes/glitch/styles/reset copy.scss
+++ /dev/null
@@ -1,91 +0,0 @@
-/* http://meyerweb.com/eric/tools/css/reset/
-   v2.0 | 20110126
-   License: none (public domain)
-*/
-
-html, body, div, span, applet, object, iframe,
-h1, h2, h3, h4, h5, h6, p, blockquote, pre,
-a, abbr, acronym, address, big, cite, code,
-del, dfn, em, img, ins, kbd, q, s, samp,
-small, strike, strong, sub, sup, tt, var,
-b, u, i, center,
-dl, dt, dd, ol, ul, li,
-fieldset, form, label, legend,
-table, caption, tbody, tfoot, thead, tr, th, td,
-article, aside, canvas, details, embed,
-figure, figcaption, footer, header, hgroup,
-menu, nav, output, ruby, section, summary,
-time, mark, audio, video {
-  margin: 0;
-  padding: 0;
-  border: 0;
-  font-size: 100%;
-  font: inherit;
-  vertical-align: baseline;
-}
-
-/* HTML5 display-role reset for older browsers */
-article, aside, details, figcaption, figure,
-footer, header, hgroup, menu, nav, section {
-  display: block;
-}
-
-body {
-  line-height: 1;
-}
-
-ol, ul {
-  list-style: none;
-}
-
-blockquote, q {
-  quotes: none;
-}
-
-blockquote:before, blockquote:after,
-q:before, q:after {
-  content: '';
-  content: none;
-}
-
-table {
-  border-collapse: collapse;
-  border-spacing: 0;
-}
-
-::-webkit-scrollbar {
-  width: 8px;
-  height: 8px;
-}
-
-::-webkit-scrollbar-thumb {
-  background: lighten($ui-base-color, 4%);
-  border: 0px none $base-border-color;
-  border-radius: 50px;
-}
-
-::-webkit-scrollbar-thumb:hover {
-  background: lighten($ui-base-color, 6%);
-}
-
-::-webkit-scrollbar-thumb:active {
-  background: lighten($ui-base-color, 4%);
-}
-
-::-webkit-scrollbar-track {
-  border: 0px none $base-border-color;
-  border-radius: 0;
-  background: rgba($base-overlay-background, 0.1);
-}
-
-::-webkit-scrollbar-track:hover {
-  background: $ui-base-color;
-}
-
-::-webkit-scrollbar-track:active {
-  background: $ui-base-color;
-}
-
-::-webkit-scrollbar-corner {
-  background: transparent;
-}
diff --git a/app/javascript/themes/glitch/styles/reset.scss b/app/javascript/themes/glitch/styles/reset.scss
deleted file mode 100644
index cc5ba9d7c..000000000
--- a/app/javascript/themes/glitch/styles/reset.scss
+++ /dev/null
@@ -1,91 +0,0 @@
-/* http://meyerweb.com/eric/tools/css/reset/
-   v2.0 | 20110126
-   License: none (public domain)
-*/
-
-html, body, div, span, applet, object, iframe,
-h1, h2, h3, h4, h5, h6, p, blockquote, pre,
-a, abbr, acronym, address, big, cite, code,
-del, dfn, em, img, ins, kbd, q, s, samp,
-small, strike, strong, sub, sup, tt, var,
-b, u, i, center,
-dl, dt, dd, ol, ul, li,
-fieldset, form, label, legend,
-table, caption, tbody, tfoot, thead, tr, th, td,
-article, aside, canvas, details, embed,
-figure, figcaption, footer, header, hgroup,
-menu, nav, output, ruby, section, summary,
-time, mark, audio, video {
-  margin: 0;
-  padding: 0;
-  border: 0;
-  font-size: 100%;
-  font: inherit;
-  vertical-align: baseline;
-}
-
-/* HTML5 display-role reset for older browsers */
-article, aside, details, figcaption, figure,
-footer, header, hgroup, menu, nav, section {
-  display: block;
-}
-
-body {
-  line-height: 1;
-}
-
-ol, ul {
-  list-style: none;
-}
-
-blockquote, q {
-  quotes: none;
-}
-
-blockquote:before, blockquote:after,
-q:before, q:after {
-  content: '';
-  content: none;
-}
-
-table {
-  border-collapse: collapse;
-  border-spacing: 0;
-}
-
-::-webkit-scrollbar {
-  width: 8px;
-  height: 8px;
-}
-
-::-webkit-scrollbar-thumb {
-  background: lighten($ui-base-color, 4%);
-  border: 0px none $base-border-color;
-  border-radius: 50px;
-}
-
-::-webkit-scrollbar-thumb:hover {
-  background: lighten($ui-base-color, 6%);
-}
-
-::-webkit-scrollbar-thumb:active {
-  background: lighten($ui-base-color, 4%);
-}
-
-::-webkit-scrollbar-track {
-  border: 0px none $base-border-color;
-  border-radius: 0;
-  background: rgba($base-overlay-background, 0.1);
-}
-
-::-webkit-scrollbar-track:hover {
-  background: $ui-base-color;
-}
-
-::-webkit-scrollbar-track:active {
-  background: $ui-base-color;
-}
-
-::-webkit-scrollbar-corner {
-  background: transparent;
-}
diff --git a/app/javascript/themes/glitch/styles/rtl.scss b/app/javascript/themes/glitch/styles/rtl.scss
deleted file mode 100644
index 67bfa8a38..000000000
--- a/app/javascript/themes/glitch/styles/rtl.scss
+++ /dev/null
@@ -1,254 +0,0 @@
-body.rtl {
-  direction: rtl;
-
-  .column-link__icon,
-  .column-header__icon {
-    margin-right: 0;
-    margin-left: 5px;
-  }
-
-  .character-counter__wrapper {
-    margin-right: 8px;
-    margin-left: 16px;
-  }
-
-  .navigation-bar__profile {
-    margin-left: 0;
-    margin-right: 8px;
-  }
-
-  .search__input {
-    padding-right: 10px;
-    padding-left: 30px;
-  }
-
-  .search__icon .fa {
-    right: auto;
-    left: 10px;
-  }
-
-  .column-header__buttons {
-    left: 0;
-    right: auto;
-  }
-
-  .column-header__back-button {
-    padding-left: 5px;
-    padding-right: 0;
-  }
-
-  .column-header__setting-arrows {
-    float: left;
-  }
-
-  .compose-form__modifiers {
-    border-radius: 0 0 0 4px;
-  }
-
-  .setting-toggle {
-    margin-left: 0;
-    margin-right: 8px;
-  }
-
-  .setting-meta__label {
-    float: left;
-  }
-
-  .status__avatar {
-    left: auto;
-    right: 10px;
-  }
-
-  .status,
-  .activity-stream .status.light {
-    padding-left: 10px;
-    padding-right: 68px;
-  }
-
-  .status__info .status__display-name,
-  .activity-stream .status.light .status__display-name {
-    padding-left: 25px;
-    padding-right: 0;
-  }
-
-  .activity-stream .pre-header {
-    padding-right: 68px;
-    padding-left: 0;
-  }
-
-  .status__prepend {
-    margin-left: 0;
-    margin-right: 68px;
-  }
-
-  .status__prepend-icon-wrapper {
-    left: auto;
-    right: -26px;
-  }
-
-  .activity-stream .pre-header .pre-header__icon {
-    left: auto;
-    right: 42px;
-  }
-
-  .account__avatar-overlay-overlay {
-    right: auto;
-    left: 0;
-  }
-
-  .column-back-button--slim-button {
-    right: auto;
-    left: 0;
-  }
-
-  .status__relative-time,
-  .activity-stream .status.light .status__header .status__meta {
-    float: left;
-  }
-
-  .activity-stream .detailed-status.light .detailed-status__display-name > div {
-    float: right;
-    margin-right: 0;
-    margin-left: 10px;
-  }
-
-  .activity-stream .detailed-status.light .detailed-status__meta span > span {
-    margin-left: 0;
-    margin-right: 6px;
-  }
-
-  .status__action-bar-button {
-    float: right;
-    margin-right: 0;
-    margin-left: 18px;
-  }
-
-  .status__action-bar-dropdown {
-    float: right;
-  }
-
-  .privacy-dropdown__dropdown {
-    margin-left: 0;
-    margin-right: 40px;
-  }
-
-  .privacy-dropdown__option__icon {
-    margin-left: 10px;
-    margin-right: 0;
-  }
-
-  .detailed-status__display-avatar {
-    margin-right: 0;
-    margin-left: 10px;
-    float: right;
-  }
-
-  .detailed-status__favorites,
-  .detailed-status__reblogs {
-    margin-left: 0;
-    margin-right: 6px;
-  }
-
-  .fa-ul {
-    margin-left: 0;
-    margin-left: 2.14285714em;
-  }
-
-  .fa-li {
-    left: auto;
-    right: -2.14285714em;
-  }
-
-  .admin-wrapper .sidebar ul a i.fa,
-  a.table-action-link i.fa {
-    margin-right: 0;
-    margin-left: 5px;
-  }
-
-  .simple_form .check_boxes .checkbox label,
-  .simple_form .input.with_label.boolean label.checkbox {
-    padding-left: 0;
-    padding-right: 25px;
-  }
-
-  .simple_form .check_boxes .checkbox input[type="checkbox"],
-  .simple_form .input.boolean input[type="checkbox"] {
-    left: auto;
-    right: 0;
-  }
-
-  .simple_form .input-with-append .input input {
-    padding-left: 127px;
-    padding-right: 0;
-  }
-
-  .simple_form .input-with-append .append {
-    right: auto;
-    left: 0;
-  }
-
-  .table th,
-  .table td {
-    text-align: right;
-  }
-
-  .filters .filter-subset {
-    margin-right: 0;
-    margin-left: 45px;
-  }
-
-  .landing-page .header-wrapper .mascot {
-    right: 60px;
-    left: auto;
-  }
-
-  .landing-page .header .hero .floats .float-1 {
-    left: -120px;
-    right: auto;
-  }
-
-  .landing-page .header .hero .floats .float-2 {
-    left: 210px;
-    right: auto;
-  }
-
-  .landing-page .header .hero .floats .float-3 {
-    left: 110px;
-    right: auto;
-  }
-
-  .landing-page .header .links .brand img {
-    left: 0;
-  }
-
-  .landing-page .fa-external-link {
-    padding-right: 5px;
-    padding-left: 0 !important;
-  }
-
-  .landing-page .features #mastodon-timeline {
-    margin-right: 0;
-    margin-left: 30px;
-  }
-
-  @media screen and (min-width: 631px) {
-    .column,
-    .drawer {
-      padding-left: 5px;
-      padding-right: 5px;
-
-      &:first-child {
-        padding-left: 5px;
-        padding-right: 10px;
-      }
-    }
-
-    .columns-area > div {
-      .column,
-      .drawer {
-        padding-left: 5px;
-        padding-right: 5px;
-      }
-    }
-  }
-}
diff --git a/app/javascript/themes/glitch/styles/stream_entries.scss b/app/javascript/themes/glitch/styles/stream_entries.scss
deleted file mode 100644
index 453070b7c..000000000
--- a/app/javascript/themes/glitch/styles/stream_entries.scss
+++ /dev/null
@@ -1,335 +0,0 @@
-.activity-stream {
-  clear: both;
-  box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
-
-  .entry {
-    background: $simple-background-color;
-
-    .detailed-status.light,
-    .status.light {
-      border-bottom: 1px solid $ui-secondary-color;
-      animation: none;
-    }
-
-    &:last-child {
-      &,
-      .detailed-status.light,
-      .status.light {
-        border-bottom: 0;
-        border-radius: 0 0 4px 4px;
-      }
-    }
-
-    &:first-child {
-      &,
-      .detailed-status.light,
-      .status.light {
-        border-radius: 4px 4px 0 0;
-      }
-
-      &:last-child {
-        &,
-        .detailed-status.light,
-        .status.light {
-          border-radius: 4px;
-        }
-      }
-    }
-
-    @media screen and (max-width: 740px) {
-      &,
-      .detailed-status.light,
-      .status.light {
-        border-radius: 0 !important;
-      }
-    }
-  }
-
-  &.with-header {
-    .entry {
-      &:first-child {
-        &,
-        .detailed-status.light,
-        .status.light {
-          border-radius: 0;
-        }
-
-        &:last-child {
-          &,
-          .detailed-status.light,
-          .status.light {
-            border-radius: 0 0 4px 4px;
-          }
-        }
-      }
-    }
-  }
-
-  .status.light {
-    padding: 14px 14px 14px (48px + 14px * 2);
-    position: relative;
-    min-height: 48px;
-    cursor: default;
-
-    .status__header {
-      font-size: 15px;
-
-      .status__meta {
-        float: right;
-        font-size: 14px;
-
-        .status__relative-time {
-          color: $ui-primary-color;
-        }
-      }
-    }
-
-    .status__display-name {
-      display: block;
-      max-width: 100%;
-      padding-right: 25px;
-      color: $ui-base-color;
-    }
-
-    .status__avatar {
-      position: absolute;
-      @include avatar-size(48px);
-      margin-left: -62px;
-
-      & > div {
-        @include avatar-size(48px);
-      }
-
-      img {
-        @include avatar-radius();
-        display: block;
-      }
-    }
-
-    .display-name {
-      display: block;
-      max-width: 100%;
-      //overflow: hidden;
-      //white-space: nowrap;
-      //text-overflow: ellipsis;
-
-      strong {
-        font-weight: 500;
-        color: $ui-base-color;
-      }
-
-      span {
-        font-size: 14px;
-        color: $ui-primary-color;
-      }
-    }
-
-    .status__content {
-      color: $ui-base-color;
-
-      a {
-        color: $ui-highlight-color;
-      }
-
-      a.status__content__spoiler-link {
-        color: $primary-text-color;
-        background: $ui-primary-color;
-
-        &:hover {
-          background: lighten($ui-primary-color, 8%);
-        }
-      }
-    }
-  }
-
-  .detailed-status.light {
-    padding: 14px;
-    background: $simple-background-color;
-    cursor: default;
-
-    .detailed-status__display-name {
-      display: block;
-      overflow: hidden;
-      margin-bottom: 15px;
-
-      & > div {
-        float: left;
-        margin-right: 10px;
-      }
-
-      .display-name {
-        display: block;
-        max-width: 100%;
-        overflow: hidden;
-        white-space: nowrap;
-        text-overflow: ellipsis;
-
-        strong {
-          font-weight: 500;
-          color: $ui-base-color;
-        }
-
-        span {
-          font-size: 14px;
-          color: $ui-primary-color;
-        }
-      }
-    }
-
-    .avatar {
-      @include avatar-size(48px);
-
-      img {
-        @include avatar-radius();
-        display: block;
-      }
-    }
-
-    .status__content {
-      color: $ui-base-color;
-
-      a {
-        color: $ui-highlight-color;
-      }
-
-      a.status__content__spoiler-link {
-        color: $primary-text-color;
-        background: $ui-primary-color;
-
-        &:hover {
-          background: lighten($ui-primary-color, 8%);
-        }
-      }
-    }
-
-    .detailed-status__meta {
-      margin-top: 15px;
-      color: $ui-primary-color;
-      font-size: 14px;
-      line-height: 18px;
-
-      a {
-        color: inherit;
-      }
-
-      span > span {
-        font-weight: 500;
-        font-size: 12px;
-        margin-left: 6px;
-        display: inline-block;
-      }
-    }
-
-    .status-card {
-      border-color: lighten($ui-secondary-color, 4%);
-      color: darken($ui-primary-color, 4%);
-
-      &:hover {
-        background: lighten($ui-secondary-color, 4%);
-      }
-    }
-
-    .status-card__title,
-    .status-card__description {
-      color: $ui-base-color;
-    }
-
-    .status-card__image {
-      background: $ui-secondary-color;
-    }
-  }
-
-  .media-spoiler {
-    background: $ui-primary-color;
-    color: $white;
-    transition: all 100ms linear;
-
-    &:hover,
-    &:active,
-    &:focus {
-      background: darken($ui-primary-color, 5%);
-      color: unset;
-    }
-  }
-
-  .pre-header {
-    padding: 14px 0;
-    padding-left: (48px + 14px * 2);
-    padding-bottom: 0;
-    margin-bottom: -4px;
-    color: $ui-primary-color;
-    font-size: 14px;
-    position: relative;
-
-    .pre-header__icon {
-      position: absolute;
-      left: (48px + 14px * 2 - 30px);
-    }
-
-    .status__display-name.muted strong {
-      color: $ui-primary-color;
-    }
-  }
-
-  .open-in-web-link {
-    text-decoration: none;
-
-    &:hover {
-      text-decoration: underline;
-    }
-  }
-}
-
-.embed {
-  .activity-stream {
-    box-shadow: none;
-
-    .entry {
-
-      .detailed-status.light {
-        display: flex;
-        flex-wrap: wrap;
-        justify-content: space-between;
-        align-items: flex-start;
-
-        .detailed-status__display-name {
-          flex: 1;
-          margin: 0 5px 15px 0;
-        }
-
-        .button.button-secondary.logo-button {
-          flex: 0 auto;
-          font-size: 14px;
-
-          svg {
-            width: 20px;
-            height: auto;
-            vertical-align: middle;
-            margin-right: 5px;
-
-            path:first-child {
-              fill: $ui-primary-color;
-            }
-
-            path:last-child {
-              fill: $simple-background-color;
-            }
-          }
-
-          &:active,
-          &:focus,
-          &:hover {
-            svg path:first-child {
-              fill: lighten($ui-primary-color, 4%);
-            }
-          }
-        }
-
-        .status__content,
-        .detailed-status__meta {
-          flex: 100%;
-        }
-      }
-    }
-  }
-}
diff --git a/app/javascript/themes/glitch/styles/tables.scss b/app/javascript/themes/glitch/styles/tables.scss
deleted file mode 100644
index ad46f5f9f..000000000
--- a/app/javascript/themes/glitch/styles/tables.scss
+++ /dev/null
@@ -1,76 +0,0 @@
-.table {
-  width: 100%;
-  max-width: 100%;
-  border-spacing: 0;
-  border-collapse: collapse;
-
-  th,
-  td {
-    padding: 8px;
-    line-height: 18px;
-    vertical-align: top;
-    border-top: 1px solid $ui-base-color;
-    text-align: left;
-  }
-
-  & > thead > tr > th {
-    vertical-align: bottom;
-    border-bottom: 2px solid $ui-base-color;
-    border-top: 0;
-    font-weight: 500;
-  }
-
-  & > tbody > tr > th {
-    font-weight: 500;
-  }
-
-  & > tbody > tr:nth-child(odd) > td,
-  & > tbody > tr:nth-child(odd) > th {
-    background: $ui-base-color;
-  }
-
-  a {
-    color: $ui-highlight-color;
-    text-decoration: underline;
-
-    &:hover {
-      text-decoration: none;
-    }
-  }
-
-  strong {
-    font-weight: 500;
-  }
-
-  &.inline-table > tbody > tr:nth-child(odd) > td,
-  &.inline-table > tbody > tr:nth-child(odd) > th {
-    background: transparent;
-  }
-}
-
-.table-wrapper {
-  overflow: auto;
-  margin-bottom: 20px;
-}
-
-samp {
-  font-family: 'mastodon-font-monospace', monospace;
-}
-
-a.table-action-link {
-  text-decoration: none;
-  display: inline-block;
-  margin-right: 5px;
-  padding: 0 10px;
-  color: rgba($primary-text-color, 0.7);
-  font-weight: 500;
-
-  &:hover {
-    color: $primary-text-color;
-  }
-
-  i.fa {
-    font-weight: 400;
-    margin-right: 5px;
-  }
-}
diff --git a/app/javascript/themes/glitch/styles/variables.scss b/app/javascript/themes/glitch/styles/variables.scss
deleted file mode 100644
index f42d9c8c5..000000000
--- a/app/javascript/themes/glitch/styles/variables.scss
+++ /dev/null
@@ -1,35 +0,0 @@
-// Commonly used web colors
-$black: #000000;            // Black
-$white: #ffffff;            // White
-$success-green: #79bd9a;    // Padua
-$error-red: #df405a;        // Cerise
-$warning-red: #ff5050;      // Sunset Orange
-$gold-star: #ca8f04;        // Dark Goldenrod
-
-// Values from the classic Mastodon UI
-$classic-base-color: #282c37;         // Midnight Express
-$classic-primary-color: #9baec8;      // Echo Blue
-$classic-secondary-color: #d9e1e8;    // Pattens Blue
-$classic-highlight-color: #2b90d9;    // Summer Sky
-
-// Variables for defaults in UI
-$base-shadow-color: $black !default;
-$base-overlay-background: $black !default;
-$base-border-color: $white !default;
-$simple-background-color: $white !default;
-$primary-text-color: $white !default;
-$valid-value-color: $success-green !default;
-$error-value-color: $error-red !default;
-
-// Tell UI to use selected colors
-$ui-base-color: $classic-base-color !default;                  // Darkest
-$ui-base-lighter-color: lighten($ui-base-color, 26%) !default; // Lighter darkest
-$ui-primary-color: $classic-primary-color !default;            // Lighter
-$ui-secondary-color: $classic-secondary-color !default;        // Lightest
-$ui-highlight-color: $classic-highlight-color !default;        // Vibrant
-
-// Avatar border size (8% default, 100% for rounded avatars)
-$ui-avatar-border-size: 8%;
-
-// More variables
-$dismiss-overlay-width: 4rem;
diff --git a/app/javascript/themes/glitch/theme.yml b/app/javascript/themes/glitch/theme.yml
deleted file mode 100644
index 49fba8f40..000000000
--- a/app/javascript/themes/glitch/theme.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-#  (REQUIRED) The location of the pack file inside `pack_directory`.
-pack: index.js
-
-#  (OPTIONAL) The directory which contains the pack file.
-#  Defaults to the theme directory (`app/javascript/themes/[theme]`),
-#  but in the case of the vanilla Mastodon theme the pack file is
-#  somewhere else.
-#    pack_directory: app/javascript/packs
-
-#  (OPTIONAL) Additional javascript resources to preload, for use with
-#  lazy-loaded components. It is **STRONGLY RECOMMENDED** that you
-#  derive these pathnames from `themes/[your-theme]` to ensure that
-#  they stay unique. (Of course, vanilla doesn't do this ^^;;)
-preload:
-- themes/glitch/async/getting_started
-- themes/glitch/async/compose
-- themes/glitch/async/home_timeline
-- themes/glitch/async/notifications
diff --git a/app/javascript/themes/glitch/util/api.js b/app/javascript/themes/glitch/util/api.js
deleted file mode 100644
index ecc703c0a..000000000
--- a/app/javascript/themes/glitch/util/api.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import axios from 'axios';
-import LinkHeader from './link_header';
-
-export const getLinks = response => {
-  const value = response.headers.link;
-
-  if (!value) {
-    return { refs: [] };
-  }
-
-  return LinkHeader.parse(value);
-};
-
-export default getState => axios.create({
-  headers: {
-    'Authorization': `Bearer ${getState().getIn(['meta', 'access_token'], '')}`,
-  },
-
-  transformResponse: [function (data) {
-    try {
-      return JSON.parse(data);
-    } catch(Exception) {
-      return data;
-    }
-  }],
-});
diff --git a/app/javascript/themes/glitch/util/async-components.js b/app/javascript/themes/glitch/util/async-components.js
deleted file mode 100644
index 91e85fed5..000000000
--- a/app/javascript/themes/glitch/util/async-components.js
+++ /dev/null
@@ -1,115 +0,0 @@
-export function EmojiPicker () {
-  return import(/* webpackChunkName: "themes/glitch/async/emoji_picker" */'themes/glitch/util/emoji/emoji_picker');
-}
-
-export function Compose () {
-  return import(/* webpackChunkName: "themes/glitch/async/compose" */'themes/glitch/features/compose');
-}
-
-export function Notifications () {
-  return import(/* webpackChunkName: "themes/glitch/async/notifications" */'themes/glitch/features/notifications');
-}
-
-export function HomeTimeline () {
-  return import(/* webpackChunkName: "themes/glitch/async/home_timeline" */'themes/glitch/features/home_timeline');
-}
-
-export function PublicTimeline () {
-  return import(/* webpackChunkName: "themes/glitch/async/public_timeline" */'themes/glitch/features/public_timeline');
-}
-
-export function CommunityTimeline () {
-  return import(/* webpackChunkName: "themes/glitch/async/community_timeline" */'themes/glitch/features/community_timeline');
-}
-
-export function HashtagTimeline () {
-  return import(/* webpackChunkName: "themes/glitch/async/hashtag_timeline" */'themes/glitch/features/hashtag_timeline');
-}
-
-export function DirectTimeline() {
-  return import(/* webpackChunkName: "themes/glitch/async/direct_timeline" */'themes/glitch/features/direct_timeline');
-}
-
-export function Status () {
-  return import(/* webpackChunkName: "themes/glitch/async/status" */'themes/glitch/features/status');
-}
-
-export function GettingStarted () {
-  return import(/* webpackChunkName: "themes/glitch/async/getting_started" */'themes/glitch/features/getting_started');
-}
-
-export function PinnedStatuses () {
-  return import(/* webpackChunkName: "themes/glitch/async/pinned_statuses" */'themes/glitch/features/pinned_statuses');
-}
-
-export function AccountTimeline () {
-  return import(/* webpackChunkName: "themes/glitch/async/account_timeline" */'themes/glitch/features/account_timeline');
-}
-
-export function AccountGallery () {
-  return import(/* webpackChunkName: "themes/glitch/async/account_gallery" */'themes/glitch/features/account_gallery');
-}
-
-export function Followers () {
-  return import(/* webpackChunkName: "themes/glitch/async/followers" */'themes/glitch/features/followers');
-}
-
-export function Following () {
-  return import(/* webpackChunkName: "themes/glitch/async/following" */'themes/glitch/features/following');
-}
-
-export function Reblogs () {
-  return import(/* webpackChunkName: "themes/glitch/async/reblogs" */'themes/glitch/features/reblogs');
-}
-
-export function Favourites () {
-  return import(/* webpackChunkName: "themes/glitch/async/favourites" */'themes/glitch/features/favourites');
-}
-
-export function FollowRequests () {
-  return import(/* webpackChunkName: "themes/glitch/async/follow_requests" */'themes/glitch/features/follow_requests');
-}
-
-export function GenericNotFound () {
-  return import(/* webpackChunkName: "themes/glitch/async/generic_not_found" */'themes/glitch/features/generic_not_found');
-}
-
-export function FavouritedStatuses () {
-  return import(/* webpackChunkName: "themes/glitch/async/favourited_statuses" */'themes/glitch/features/favourited_statuses');
-}
-
-export function Blocks () {
-  return import(/* webpackChunkName: "themes/glitch/async/blocks" */'themes/glitch/features/blocks');
-}
-
-export function Mutes () {
-  return import(/* webpackChunkName: "themes/glitch/async/mutes" */'themes/glitch/features/mutes');
-}
-
-export function OnboardingModal () {
-  return import(/* webpackChunkName: "themes/glitch/async/onboarding_modal" */'themes/glitch/features/ui/components/onboarding_modal');
-}
-
-export function MuteModal () {
-  return import(/* webpackChunkName: "themes/glitch/async/mute_modal" */'themes/glitch/features/ui/components/mute_modal');
-}
-
-export function ReportModal () {
-  return import(/* webpackChunkName: "themes/glitch/async/report_modal" */'themes/glitch/features/ui/components/report_modal');
-}
-
-export function SettingsModal () {
-  return import(/* webpackChunkName: "themes/glitch/async/settings_modal" */'themes/glitch/features/local_settings');
-}
-
-export function MediaGallery () {
-  return import(/* webpackChunkName: "themes/glitch/async/media_gallery" */'themes/glitch/components/media_gallery');
-}
-
-export function Video () {
-  return import(/* webpackChunkName: "themes/glitch/async/video" */'themes/glitch/features/video');
-}
-
-export function EmbedModal () {
-  return import(/* webpackChunkName: "themes/glitch/async/embed_modal" */'themes/glitch/features/ui/components/embed_modal');
-}
diff --git a/app/javascript/themes/glitch/util/base_polyfills.js b/app/javascript/themes/glitch/util/base_polyfills.js
deleted file mode 100644
index 7856b26f9..000000000
--- a/app/javascript/themes/glitch/util/base_polyfills.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import 'intl';
-import 'intl/locale-data/jsonp/en';
-import 'es6-symbol/implement';
-import includes from 'array-includes';
-import assign from 'object-assign';
-import isNaN from 'is-nan';
-
-if (!Array.prototype.includes) {
-  includes.shim();
-}
-
-if (!Object.assign) {
-  Object.assign = assign;
-}
-
-if (!Number.isNaN) {
-  Number.isNaN = isNaN;
-}
diff --git a/app/javascript/themes/glitch/util/bio_metadata.js b/app/javascript/themes/glitch/util/bio_metadata.js
deleted file mode 100644
index 599ec20e2..000000000
--- a/app/javascript/themes/glitch/util/bio_metadata.js
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
-
-`util/bio_metadata`
-===================
-
->   For more information on the contents of this file, please contact:
->
->   - kibigo! [@kibi@glitch.social]
-
-This file provides two functions for dealing with bio metadata. The
-functions are:
-
- -  __`processBio(content)` :__
-    Processes `content` to extract any frontmatter. The returned
-    object has two properties: `text`, which contains the text of
-    `content` sans-frontmatter, and `metadata`, which is an array
-    of key-value pairs (in two-element array format). If no
-    frontmatter was provided in `content`, then `metadata` will be
-    an empty array.
-
- -  __`createBio(note, data)` :__
-    Reverses the process in `processBio()`; takes a `note` and an
-    array of two-element arrays (which should give keys and values)
-    and outputs a string containing a well-formed bio with
-    frontmatter.
-
-*/
-
-//  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-
-/*********************************************************************\
-
-                                       To my lovely code maintainers,
-
-  The syntax recognized by the Mastodon frontend for its bio metadata
-  feature is a subset of that provided by the YAML 1.2 specification.
-  In particular, Mastodon recognizes metadata which is provided as an
-  implicit YAML map, where each key-value pair takes up only a single
-  line (no multi-line values are permitted). To simplify the level of
-  processing required, Mastodon metadata frontmatter has been limited
-  to only allow those characters in the `c-printable` set, as defined
-  by the YAML 1.2 specification, instead of permitting those from the
-  `nb-json` characters inside double-quoted strings like YAML proper.
-    ¶ It is important to note that Mastodon only borrows the *syntax*
-  of YAML, not its semantics. This is to say, Mastodon won't make any
-  attempt to interpret the data it receives. `true` will not become a
-  boolean; `56` will not be interpreted as a number. Rather, each key
-  and every value will be read as a string, and as a string they will
-  remain. The order of the pairs is unchanged, and any duplicate keys
-  are preserved. However, YAML escape sequences will be replaced with
-  the proper interpretations according to the YAML 1.2 specification.
-    ¶ The implementation provided below interprets `<br>` as `\n` and
-  allows for an open <p> tag at the beginning of the bio. It replaces
-  the escaped character entities `&apos;` and `&quot;` with single or
-  double quotes, respectively, prior to processing. However, no other
-  escaped characters are replaced, not even those which might have an
-  impact on the syntax otherwise. These minor allowances are provided
-  because the Mastodon backend will insert these things automatically
-  into a bio before sending it through the API, so it is important we
-  account for them. Aside from this, the YAML frontmatter must be the
-  very first thing in the bio, leading with three consecutive hyphen-
-  minues (`---`), and ending with the same or, alternatively, instead
-  with three periods (`...`). No limits have been set with respect to
-  the number of characters permitted in the frontmatter, although one
-  should note that only limited space is provided for them in the UI.
-    ¶ The regular expression used to check the existence of, and then
-  process, the YAML frontmatter has been split into a number of small
-  components in the code below, in the vain hope that it will be much
-  easier to read and to maintain. I leave it to the future readers of
-  this code to determine the extent of my successes in this endeavor.
-
-  UPDATE 19 Oct 2017: We no longer allow character escapes inside our
-  double-quoted strings for ease of processing. We now internally use
-  the name "ƔAML" in our code to clarify that this is Not Quite YAML.
-
-                                       Sending love + warmth eternal,
-                                       - kibigo [@kibi@glitch.social]
-
-\*********************************************************************/
-
-/*  "u" FLAG COMPATABILITY  */
-
-let compat_mode = false;
-try {
-  new RegExp('.', 'u');
-} catch (e) {
-  compat_mode = true;
-}
-
-/*  CONVENIENCE FUNCTIONS  */
-
-const unirex = str => compat_mode ? new RegExp(str) : new RegExp(str, 'u');
-const rexstr = exp => '(?:' + exp.source + ')';
-
-/*  CHARACTER CLASSES  */
-
-const DOCUMENT_START    = /^/;
-const DOCUMENT_END      = /$/;
-const ALLOWED_CHAR      =  unirex( //  `c-printable` in the YAML 1.2 spec.
-    compat_mode ? '[\t\n\r\x20-\x7e\x85\xa0-\ufffd]' : '[\t\n\r\x20-\x7e\x85\xa0-\ud7ff\ue000-\ufffd\u{10000}-\u{10FFFF}]'
-  );
-const WHITE_SPACE       = /[ \t]/;
-const LINE_BREAK        = /\r?\n|\r|<br\s*\/?>/;
-const INDICATOR         = /[-?:,[\]{}&#*!|>'"%@`]/;
-const FLOW_CHAR         = /[,[\]{}]/;
-
-/*  NEGATED CHARACTER CLASSES  */
-
-const NOT_WHITE_SPACE   = unirex('(?!' + rexstr(WHITE_SPACE) + ')[^]');
-const NOT_LINE_BREAK    = unirex('(?!' + rexstr(LINE_BREAK) + ')[^]');
-const NOT_INDICATOR     = unirex('(?!' + rexstr(INDICATOR) + ')[^]');
-const NOT_FLOW_CHAR     = unirex('(?!' + rexstr(FLOW_CHAR) + ')[^]');
-const NOT_ALLOWED_CHAR  = unirex(
-  '(?!' + rexstr(ALLOWED_CHAR) + ')[^]'
-);
-
-/*  BASIC CONSTRUCTS  */
-
-const ANY_WHITE_SPACE   = unirex(rexstr(WHITE_SPACE) + '*');
-const ANY_ALLOWED_CHARS = unirex(rexstr(ALLOWED_CHAR) + '*');
-const NEW_LINE          = unirex(
-  rexstr(ANY_WHITE_SPACE) + rexstr(LINE_BREAK)
-);
-const SOME_NEW_LINES    = unirex(
-  '(?:' + rexstr(NEW_LINE) + ')+'
-);
-const POSSIBLE_STARTS   = unirex(
-  rexstr(DOCUMENT_START) + rexstr(/<p[^<>]*>/) + '?'
-);
-const POSSIBLE_ENDS     = unirex(
-  rexstr(SOME_NEW_LINES) + '|' +
-  rexstr(DOCUMENT_END) + '|' +
-  rexstr(/<\/p>/)
-);
-const QUOTE_CHAR         = unirex(
-  '(?=' + rexstr(NOT_LINE_BREAK) + ')[^"]'
-);
-const ANY_QUOTE_CHAR    = unirex(
-  rexstr(QUOTE_CHAR) + '*'
-);
-
-const ESCAPED_APOS      = unirex(
-  '(?=' + rexstr(NOT_LINE_BREAK) + ')' + rexstr(/[^']|''/)
-);
-const ANY_ESCAPED_APOS  = unirex(
-  rexstr(ESCAPED_APOS) + '*'
-);
-const FIRST_KEY_CHAR    = unirex(
-  '(?=' + rexstr(NOT_LINE_BREAK) + ')' +
-  '(?=' + rexstr(NOT_WHITE_SPACE) + ')' +
-  rexstr(NOT_INDICATOR) + '|' +
-  rexstr(/[?:-]/) +
-  '(?=' + rexstr(NOT_LINE_BREAK) + ')' +
-  '(?=' + rexstr(NOT_WHITE_SPACE) + ')' +
-  '(?=' + rexstr(NOT_FLOW_CHAR) + ')'
-);
-const FIRST_VALUE_CHAR  = unirex(
-  '(?=' + rexstr(NOT_LINE_BREAK) + ')' +
-  '(?=' + rexstr(NOT_WHITE_SPACE) + ')' +
-  rexstr(NOT_INDICATOR) + '|' +
-  rexstr(/[?:-]/) +
-  '(?=' + rexstr(NOT_LINE_BREAK) + ')' +
-  '(?=' + rexstr(NOT_WHITE_SPACE) + ')'
-  //  Flow indicators are allowed in values.
-);
-const LATER_KEY_CHAR    = unirex(
-  rexstr(WHITE_SPACE) + '|' +
-  '(?=' + rexstr(NOT_LINE_BREAK) + ')' +
-  '(?=' + rexstr(NOT_WHITE_SPACE) + ')' +
-  '(?=' + rexstr(NOT_FLOW_CHAR) + ')' +
-  rexstr(/[^:#]#?/) + '|' +
-  rexstr(/:/) + '(?=' + rexstr(NOT_WHITE_SPACE) + ')'
-);
-const LATER_VALUE_CHAR  = unirex(
-  rexstr(WHITE_SPACE) + '|' +
-  '(?=' + rexstr(NOT_LINE_BREAK) + ')' +
-  '(?=' + rexstr(NOT_WHITE_SPACE) + ')' +
-  //  Flow indicators are allowed in values.
-  rexstr(/[^:#]#?/) + '|' +
-  rexstr(/:/) + '(?=' + rexstr(NOT_WHITE_SPACE) + ')'
-);
-
-/*  YAML CONSTRUCTS  */
-
-const ƔAML_START        = unirex(
-  rexstr(ANY_WHITE_SPACE) + '---'
-);
-const ƔAML_END          = unirex(
-  rexstr(ANY_WHITE_SPACE) + '(?:---|\.\.\.)'
-);
-const ƔAML_LOOKAHEAD    = unirex(
-  '(?=' +
-    rexstr(ƔAML_START) +
-    rexstr(ANY_ALLOWED_CHARS) + rexstr(NEW_LINE) +
-    rexstr(ƔAML_END) + rexstr(POSSIBLE_ENDS) +
-  ')'
-);
-const ƔAML_DOUBLE_QUOTE = unirex(
-  '"' + rexstr(ANY_QUOTE_CHAR) + '"'
-);
-const ƔAML_SINGLE_QUOTE = unirex(
-  '\'' + rexstr(ANY_ESCAPED_APOS) + '\''
-);
-const ƔAML_SIMPLE_KEY   = unirex(
-  rexstr(FIRST_KEY_CHAR) + rexstr(LATER_KEY_CHAR) + '*'
-);
-const ƔAML_SIMPLE_VALUE = unirex(
-  rexstr(FIRST_VALUE_CHAR) + rexstr(LATER_VALUE_CHAR) + '*'
-);
-const ƔAML_KEY          = unirex(
-  rexstr(ƔAML_DOUBLE_QUOTE) + '|' +
-  rexstr(ƔAML_SINGLE_QUOTE) + '|' +
-  rexstr(ƔAML_SIMPLE_KEY)
-);
-const ƔAML_VALUE        = unirex(
-  rexstr(ƔAML_DOUBLE_QUOTE) + '|' +
-  rexstr(ƔAML_SINGLE_QUOTE) + '|' +
-  rexstr(ƔAML_SIMPLE_VALUE)
-);
-const ƔAML_SEPARATOR    = unirex(
-  rexstr(ANY_WHITE_SPACE) +
-  ':' + rexstr(WHITE_SPACE) +
-  rexstr(ANY_WHITE_SPACE)
-);
-const ƔAML_LINE         = unirex(
-  '(' + rexstr(ƔAML_KEY) + ')' +
-  rexstr(ƔAML_SEPARATOR) +
-  '(' + rexstr(ƔAML_VALUE) + ')'
-);
-
-/*  FRONTMATTER REGEX  */
-
-const ƔAML_FRONTMATTER  = unirex(
-  rexstr(POSSIBLE_STARTS) +
-  rexstr(ƔAML_LOOKAHEAD) +
-  rexstr(ƔAML_START) + rexstr(SOME_NEW_LINES) +
-  '(?:' +
-    rexstr(ANY_WHITE_SPACE) + rexstr(ƔAML_LINE) + rexstr(SOME_NEW_LINES) +
-  '){0,5}' +
-  rexstr(ƔAML_END) + rexstr(POSSIBLE_ENDS)
-);
-
-/*  SEARCHES  */
-
-const FIND_ƔAML_LINE    = unirex(
-  rexstr(NEW_LINE) + rexstr(ANY_WHITE_SPACE) + rexstr(ƔAML_LINE)
-);
-
-/*  STRING PROCESSING  */
-
-function processString (str) {
-  switch (str.charAt(0)) {
-  case '"':
-    return str.substring(1, str.length - 1);
-  case '\'':
-    return str
-      .substring(1, str.length - 1)
-      .replace(/''/g, '\'');
-  default:
-    return str;
-  }
-}
-
-/*  BIO PROCESSING  */
-
-export function processBio(content) {
-  content = content.replace(/&quot;/g, '"').replace(/&apos;/g, '\'');
-  let result = {
-    text: content,
-    metadata: [],
-  };
-  let ɣaml = content.match(ƔAML_FRONTMATTER);
-  if (!ɣaml) {
-    return result;
-  } else {
-    ɣaml = ɣaml[0];
-  }
-  const start = content.search(ƔAML_START);
-  const end = start + ɣaml.length - ɣaml.search(ƔAML_START);
-  result.text = content.substr(end);
-  let metadata = null;
-  let query = new RegExp(rexstr(FIND_ƔAML_LINE), 'g');  //  Some browsers don't allow flags unless both args are strings
-  while ((metadata = query.exec(ɣaml))) {
-    result.metadata.push([
-      processString(metadata[1]),
-      processString(metadata[2]),
-    ]);
-  }
-  return result;
-}
-
-/*  BIO CREATION  */
-
-export function createBio(note, data) {
-  if (!note) note = '';
-  let frontmatter = '';
-  if ((data && data.length) || note.match(/^\s*---\s+/)) {
-    if (!data) frontmatter = '---\n...\n';
-    else {
-      frontmatter += '---\n';
-      for (let i = 0; i < data.length; i++) {
-        let key = '' + data[i][0];
-        let val = '' + data[i][1];
-
-        //  Key processing
-        if (key === (key.match(ƔAML_SIMPLE_KEY) || [])[0]) /*  do nothing  */;
-        else if (key === (key.match(ANY_QUOTE_CHAR) || [])[0]) key = '"' + key + '"';
-        else {
-          key = key
-            .replace(/'/g, '\'\'')
-            .replace(new RegExp(rexstr(NOT_ALLOWED_CHAR), compat_mode ? 'g' : 'gu'), '�');
-          key = '\'' + key + '\'';
-        }
-
-        //  Value processing
-        if (val === (val.match(ƔAML_SIMPLE_VALUE) || [])[0]) /*  do nothing  */;
-        else if (val === (val.match(ANY_QUOTE_CHAR) || [])[0]) val = '"' + val + '"';
-        else {
-          key = key
-            .replace(/'/g, '\'\'')
-            .replace(new RegExp(rexstr(NOT_ALLOWED_CHAR), compat_mode ? 'g' : 'gu'), '�');
-          key = '\'' + key + '\'';
-        }
-
-        frontmatter += key + ': ' + val + '\n';
-      }
-      frontmatter += '...\n';
-    }
-  }
-  return frontmatter + note;
-}
diff --git a/app/javascript/themes/glitch/util/counter.js b/app/javascript/themes/glitch/util/counter.js
deleted file mode 100644
index 700ba2163..000000000
--- a/app/javascript/themes/glitch/util/counter.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import { urlRegex } from './url_regex';
-
-const urlPlaceholder = 'xxxxxxxxxxxxxxxxxxxxxxx';
-
-export function countableText(inputText) {
-  return inputText
-    .replace(urlRegex, urlPlaceholder)
-    .replace(/(^|[^\/\w])@(([a-z0-9_]+)@[a-z0-9\.\-]+[a-z0-9]+)/ig, '$1@$3');
-};
diff --git a/app/javascript/themes/glitch/util/emoji/emoji_compressed.js b/app/javascript/themes/glitch/util/emoji/emoji_compressed.js
deleted file mode 100644
index e5b834a74..000000000
--- a/app/javascript/themes/glitch/util/emoji/emoji_compressed.js
+++ /dev/null
@@ -1,93 +0,0 @@
-// @preval
-// http://www.unicode.org/Public/emoji/5.0/emoji-test.txt
-// This file contains the compressed version of the emoji data from
-// both emoji_map.json and from emoji-mart's emojiIndex and data objects.
-// It's designed to be emitted in an array format to take up less space
-// over the wire.
-
-const { unicodeToFilename } = require('./unicode_to_filename');
-const { unicodeToUnifiedName } = require('./unicode_to_unified_name');
-const emojiMap         = require('./emoji_map.json');
-const { emojiIndex } = require('emoji-mart');
-const { default: emojiMartData } = require('emoji-mart/dist/data');
-
-const excluded       = ['®', '©', '™'];
-const skins          = ['🏻', '🏼', '🏽', '🏾', '🏿'];
-const shortcodeMap   = {};
-
-const shortCodesToEmojiData = {};
-const emojisWithoutShortCodes = [];
-
-Object.keys(emojiIndex.emojis).forEach(key => {
-  shortcodeMap[emojiIndex.emojis[key].native] = emojiIndex.emojis[key].id;
-});
-
-const stripModifiers = unicode => {
-  skins.forEach(tone => {
-    unicode = unicode.replace(tone, '');
-  });
-
-  return unicode;
-};
-
-Object.keys(emojiMap).forEach(key => {
-  if (excluded.includes(key)) {
-    delete emojiMap[key];
-    return;
-  }
-
-  const normalizedKey = stripModifiers(key);
-  let shortcode       = shortcodeMap[normalizedKey];
-
-  if (!shortcode) {
-    shortcode = shortcodeMap[normalizedKey + '\uFE0F'];
-  }
-
-  const filename = emojiMap[key];
-
-  const filenameData = [key];
-
-  if (unicodeToFilename(key) !== filename) {
-    // filename can't be derived using unicodeToFilename
-    filenameData.push(filename);
-  }
-
-  if (typeof shortcode === 'undefined') {
-    emojisWithoutShortCodes.push(filenameData);
-  } else {
-    if (!Array.isArray(shortCodesToEmojiData[shortcode])) {
-      shortCodesToEmojiData[shortcode] = [[]];
-    }
-    shortCodesToEmojiData[shortcode][0].push(filenameData);
-  }
-});
-
-Object.keys(emojiIndex.emojis).forEach(key => {
-  const { native } = emojiIndex.emojis[key];
-  let { short_names, search, unified } = emojiMartData.emojis[key];
-  if (short_names[0] !== key) {
-    throw new Error('The compresser expects the first short_code to be the ' +
-      'key. It may need to be rewritten if the emoji change such that this ' +
-      'is no longer the case.');
-  }
-
-  short_names = short_names.slice(1); // first short name can be inferred from the key
-
-  const searchData = [native, short_names, search];
-  if (unicodeToUnifiedName(native) !== unified) {
-    // unified name can't be derived from unicodeToUnifiedName
-    searchData.push(unified);
-  }
-
-  shortCodesToEmojiData[key].push(searchData);
-});
-
-// JSON.parse/stringify is to emulate what @preval is doing and avoid any
-// inconsistent behavior in dev mode
-module.exports = JSON.parse(JSON.stringify([
-  shortCodesToEmojiData,
-  emojiMartData.skins,
-  emojiMartData.categories,
-  emojiMartData.short_names,
-  emojisWithoutShortCodes,
-]));
diff --git a/app/javascript/themes/glitch/util/emoji/emoji_map.json b/app/javascript/themes/glitch/util/emoji/emoji_map.json
deleted file mode 100644
index 13753ba84..000000000
--- a/app/javascript/themes/glitch/util/emoji/emoji_map.json
+++ /dev/null
@@ -1 +0,0 @@
-{"😀":"1f600","😁":"1f601","😂":"1f602","🤣":"1f923","😃":"1f603","😄":"1f604","😅":"1f605","😆":"1f606","😉":"1f609","😊":"1f60a","😋":"1f60b","😎":"1f60e","😍":"1f60d","😘":"1f618","😗":"1f617","😙":"1f619","😚":"1f61a","☺":"263a","🙂":"1f642","🤗":"1f917","🤩":"1f929","🤔":"1f914","🤨":"1f928","😐":"1f610","😑":"1f611","😶":"1f636","🙄":"1f644","😏":"1f60f","😣":"1f623","😥":"1f625","😮":"1f62e","🤐":"1f910","😯":"1f62f","😪":"1f62a","😫":"1f62b","😴":"1f634","😌":"1f60c","😛":"1f61b","😜":"1f61c","😝":"1f61d","🤤":"1f924","😒":"1f612","😓":"1f613","😔":"1f614","😕":"1f615","🙃":"1f643","🤑":"1f911","😲":"1f632","☹":"2639","🙁":"1f641","😖":"1f616","😞":"1f61e","😟":"1f61f","😤":"1f624","😢":"1f622","😭":"1f62d","😦":"1f626","😧":"1f627","😨":"1f628","😩":"1f629","🤯":"1f92f","😬":"1f62c","😰":"1f630","😱":"1f631","😳":"1f633","🤪":"1f92a","😵":"1f635","😡":"1f621","😠":"1f620","🤬":"1f92c","😷":"1f637","🤒":"1f912","🤕":"1f915","🤢":"1f922","🤮":"1f92e","🤧":"1f927","😇":"1f607","🤠":"1f920","🤡":"1f921","🤥":"1f925","🤫":"1f92b","🤭":"1f92d","🧐":"1f9d0","🤓":"1f913","😈":"1f608","👿":"1f47f","👹":"1f479","👺":"1f47a","💀":"1f480","☠":"2620","👻":"1f47b","👽":"1f47d","👾":"1f47e","🤖":"1f916","💩":"1f4a9","😺":"1f63a","😸":"1f638","😹":"1f639","😻":"1f63b","😼":"1f63c","😽":"1f63d","🙀":"1f640","😿":"1f63f","😾":"1f63e","🙈":"1f648","🙉":"1f649","🙊":"1f64a","👶":"1f476","🧒":"1f9d2","👦":"1f466","👧":"1f467","🧑":"1f9d1","👨":"1f468","👩":"1f469","🧓":"1f9d3","👴":"1f474","👵":"1f475","👮":"1f46e","🕵":"1f575","💂":"1f482","👷":"1f477","🤴":"1f934","👸":"1f478","👳":"1f473","👲":"1f472","🧕":"1f9d5","🧔":"1f9d4","👱":"1f471","🤵":"1f935","👰":"1f470","🤰":"1f930","🤱":"1f931","👼":"1f47c","🎅":"1f385","🤶":"1f936","🧙":"1f9d9","🧚":"1f9da","🧛":"1f9db","🧜":"1f9dc","🧝":"1f9dd","🧞":"1f9de","🧟":"1f9df","🙍":"1f64d","🙎":"1f64e","🙅":"1f645","🙆":"1f646","💁":"1f481","🙋":"1f64b","🙇":"1f647","🤦":"1f926","🤷":"1f937","💆":"1f486","💇":"1f487","🚶":"1f6b6","🏃":"1f3c3","💃":"1f483","🕺":"1f57a","👯":"1f46f","🧖":"1f9d6","🧗":"1f9d7","🧘":"1f9d8","🛀":"1f6c0","🛌":"1f6cc","🕴":"1f574","🗣":"1f5e3","👤":"1f464","👥":"1f465","🤺":"1f93a","🏇":"1f3c7","⛷":"26f7","🏂":"1f3c2","🏌":"1f3cc","🏄":"1f3c4","🚣":"1f6a3","🏊":"1f3ca","⛹":"26f9","🏋":"1f3cb","🚴":"1f6b4","🚵":"1f6b5","🏎":"1f3ce","🏍":"1f3cd","🤸":"1f938","🤼":"1f93c","🤽":"1f93d","🤾":"1f93e","🤹":"1f939","👫":"1f46b","👬":"1f46c","👭":"1f46d","💏":"1f48f","💑":"1f491","👪":"1f46a","🤳":"1f933","💪":"1f4aa","👈":"1f448","👉":"1f449","☝":"261d","👆":"1f446","🖕":"1f595","👇":"1f447","✌":"270c","🤞":"1f91e","🖖":"1f596","🤘":"1f918","🤙":"1f919","🖐":"1f590","✋":"270b","👌":"1f44c","👍":"1f44d","👎":"1f44e","✊":"270a","👊":"1f44a","🤛":"1f91b","🤜":"1f91c","🤚":"1f91a","👋":"1f44b","🤟":"1f91f","✍":"270d","👏":"1f44f","👐":"1f450","🙌":"1f64c","🤲":"1f932","🙏":"1f64f","🤝":"1f91d","💅":"1f485","👂":"1f442","👃":"1f443","👣":"1f463","👀":"1f440","👁":"1f441","🧠":"1f9e0","👅":"1f445","👄":"1f444","💋":"1f48b","💘":"1f498","❤":"2764","💓":"1f493","💔":"1f494","💕":"1f495","💖":"1f496","💗":"1f497","💙":"1f499","💚":"1f49a","💛":"1f49b","🧡":"1f9e1","💜":"1f49c","🖤":"1f5a4","💝":"1f49d","💞":"1f49e","💟":"1f49f","❣":"2763","💌":"1f48c","💤":"1f4a4","💢":"1f4a2","💣":"1f4a3","💥":"1f4a5","💦":"1f4a6","💨":"1f4a8","💫":"1f4ab","💬":"1f4ac","🗨":"1f5e8","🗯":"1f5ef","💭":"1f4ad","🕳":"1f573","👓":"1f453","🕶":"1f576","👔":"1f454","👕":"1f455","👖":"1f456","🧣":"1f9e3","🧤":"1f9e4","🧥":"1f9e5","🧦":"1f9e6","👗":"1f457","👘":"1f458","👙":"1f459","👚":"1f45a","👛":"1f45b","👜":"1f45c","👝":"1f45d","🛍":"1f6cd","🎒":"1f392","👞":"1f45e","👟":"1f45f","👠":"1f460","👡":"1f461","👢":"1f462","👑":"1f451","👒":"1f452","🎩":"1f3a9","🎓":"1f393","🧢":"1f9e2","⛑":"26d1","📿":"1f4ff","💄":"1f484","💍":"1f48d","💎":"1f48e","🐵":"1f435","🐒":"1f412","🦍":"1f98d","🐶":"1f436","🐕":"1f415","🐩":"1f429","🐺":"1f43a","🦊":"1f98a","🐱":"1f431","🐈":"1f408","🦁":"1f981","🐯":"1f42f","🐅":"1f405","🐆":"1f406","🐴":"1f434","🐎":"1f40e","🦄":"1f984","🦓":"1f993","🦌":"1f98c","🐮":"1f42e","🐂":"1f402","🐃":"1f403","🐄":"1f404","🐷":"1f437","🐖":"1f416","🐗":"1f417","🐽":"1f43d","🐏":"1f40f","🐑":"1f411","🐐":"1f410","🐪":"1f42a","🐫":"1f42b","🦒":"1f992","🐘":"1f418","🦏":"1f98f","🐭":"1f42d","🐁":"1f401","🐀":"1f400","🐹":"1f439","🐰":"1f430","🐇":"1f407","🐿":"1f43f","🦔":"1f994","🦇":"1f987","🐻":"1f43b","🐨":"1f428","🐼":"1f43c","🐾":"1f43e","🦃":"1f983","🐔":"1f414","🐓":"1f413","🐣":"1f423","🐤":"1f424","🐥":"1f425","🐦":"1f426","🐧":"1f427","🕊":"1f54a","🦅":"1f985","🦆":"1f986","🦉":"1f989","🐸":"1f438","🐊":"1f40a","🐢":"1f422","🦎":"1f98e","🐍":"1f40d","🐲":"1f432","🐉":"1f409","🦕":"1f995","🦖":"1f996","🐳":"1f433","🐋":"1f40b","🐬":"1f42c","🐟":"1f41f","🐠":"1f420","🐡":"1f421","🦈":"1f988","🐙":"1f419","🐚":"1f41a","🦀":"1f980","🦐":"1f990","🦑":"1f991","🐌":"1f40c","🦋":"1f98b","🐛":"1f41b","🐜":"1f41c","🐝":"1f41d","🐞":"1f41e","🦗":"1f997","🕷":"1f577","🕸":"1f578","🦂":"1f982","💐":"1f490","🌸":"1f338","💮":"1f4ae","🏵":"1f3f5","🌹":"1f339","🥀":"1f940","🌺":"1f33a","🌻":"1f33b","🌼":"1f33c","🌷":"1f337","🌱":"1f331","🌲":"1f332","🌳":"1f333","🌴":"1f334","🌵":"1f335","🌾":"1f33e","🌿":"1f33f","☘":"2618","🍀":"1f340","🍁":"1f341","🍂":"1f342","🍃":"1f343","🍇":"1f347","🍈":"1f348","🍉":"1f349","🍊":"1f34a","🍋":"1f34b","🍌":"1f34c","🍍":"1f34d","🍎":"1f34e","🍏":"1f34f","🍐":"1f350","🍑":"1f351","🍒":"1f352","🍓":"1f353","🥝":"1f95d","🍅":"1f345","🥥":"1f965","🥑":"1f951","🍆":"1f346","🥔":"1f954","🥕":"1f955","🌽":"1f33d","🌶":"1f336","🥒":"1f952","🥦":"1f966","🍄":"1f344","🥜":"1f95c","🌰":"1f330","🍞":"1f35e","🥐":"1f950","🥖":"1f956","🥨":"1f968","🥞":"1f95e","🧀":"1f9c0","🍖":"1f356","🍗":"1f357","🥩":"1f969","🥓":"1f953","🍔":"1f354","🍟":"1f35f","🍕":"1f355","🌭":"1f32d","🥪":"1f96a","🌮":"1f32e","🌯":"1f32f","🥙":"1f959","🥚":"1f95a","🍳":"1f373","🥘":"1f958","🍲":"1f372","🥣":"1f963","🥗":"1f957","🍿":"1f37f","🥫":"1f96b","🍱":"1f371","🍘":"1f358","🍙":"1f359","🍚":"1f35a","🍛":"1f35b","🍜":"1f35c","🍝":"1f35d","🍠":"1f360","🍢":"1f362","🍣":"1f363","🍤":"1f364","🍥":"1f365","🍡":"1f361","🥟":"1f95f","🥠":"1f960","🥡":"1f961","🍦":"1f366","🍧":"1f367","🍨":"1f368","🍩":"1f369","🍪":"1f36a","🎂":"1f382","🍰":"1f370","🥧":"1f967","🍫":"1f36b","🍬":"1f36c","🍭":"1f36d","🍮":"1f36e","🍯":"1f36f","🍼":"1f37c","🥛":"1f95b","☕":"2615","🍵":"1f375","🍶":"1f376","🍾":"1f37e","🍷":"1f377","🍸":"1f378","🍹":"1f379","🍺":"1f37a","🍻":"1f37b","🥂":"1f942","🥃":"1f943","🥤":"1f964","🥢":"1f962","🍽":"1f37d","🍴":"1f374","🥄":"1f944","🔪":"1f52a","🏺":"1f3fa","🌍":"1f30d","🌎":"1f30e","🌏":"1f30f","🌐":"1f310","🗺":"1f5fa","🗾":"1f5fe","🏔":"1f3d4","⛰":"26f0","🌋":"1f30b","🗻":"1f5fb","🏕":"1f3d5","🏖":"1f3d6","🏜":"1f3dc","🏝":"1f3dd","🏞":"1f3de","🏟":"1f3df","🏛":"1f3db","🏗":"1f3d7","🏘":"1f3d8","🏙":"1f3d9","🏚":"1f3da","🏠":"1f3e0","🏡":"1f3e1","🏢":"1f3e2","🏣":"1f3e3","🏤":"1f3e4","🏥":"1f3e5","🏦":"1f3e6","🏨":"1f3e8","🏩":"1f3e9","🏪":"1f3ea","🏫":"1f3eb","🏬":"1f3ec","🏭":"1f3ed","🏯":"1f3ef","🏰":"1f3f0","💒":"1f492","🗼":"1f5fc","🗽":"1f5fd","⛪":"26ea","🕌":"1f54c","🕍":"1f54d","⛩":"26e9","🕋":"1f54b","⛲":"26f2","⛺":"26fa","🌁":"1f301","🌃":"1f303","🌄":"1f304","🌅":"1f305","🌆":"1f306","🌇":"1f307","🌉":"1f309","♨":"2668","🌌":"1f30c","🎠":"1f3a0","🎡":"1f3a1","🎢":"1f3a2","💈":"1f488","🎪":"1f3aa","🎭":"1f3ad","🖼":"1f5bc","🎨":"1f3a8","🎰":"1f3b0","🚂":"1f682","🚃":"1f683","🚄":"1f684","🚅":"1f685","🚆":"1f686","🚇":"1f687","🚈":"1f688","🚉":"1f689","🚊":"1f68a","🚝":"1f69d","🚞":"1f69e","🚋":"1f68b","🚌":"1f68c","🚍":"1f68d","🚎":"1f68e","🚐":"1f690","🚑":"1f691","🚒":"1f692","🚓":"1f693","🚔":"1f694","🚕":"1f695","🚖":"1f696","🚗":"1f697","🚘":"1f698","🚙":"1f699","🚚":"1f69a","🚛":"1f69b","🚜":"1f69c","🚲":"1f6b2","🛴":"1f6f4","🛵":"1f6f5","🚏":"1f68f","🛣":"1f6e3","🛤":"1f6e4","⛽":"26fd","🚨":"1f6a8","🚥":"1f6a5","🚦":"1f6a6","🚧":"1f6a7","🛑":"1f6d1","⚓":"2693","⛵":"26f5","🛶":"1f6f6","🚤":"1f6a4","🛳":"1f6f3","⛴":"26f4","🛥":"1f6e5","🚢":"1f6a2","✈":"2708","🛩":"1f6e9","🛫":"1f6eb","🛬":"1f6ec","💺":"1f4ba","🚁":"1f681","🚟":"1f69f","🚠":"1f6a0","🚡":"1f6a1","🛰":"1f6f0","🚀":"1f680","🛸":"1f6f8","🛎":"1f6ce","🚪":"1f6aa","🛏":"1f6cf","🛋":"1f6cb","🚽":"1f6bd","🚿":"1f6bf","🛁":"1f6c1","⌛":"231b","⏳":"23f3","⌚":"231a","⏰":"23f0","⏱":"23f1","⏲":"23f2","🕰":"1f570","🕛":"1f55b","🕧":"1f567","🕐":"1f550","🕜":"1f55c","🕑":"1f551","🕝":"1f55d","🕒":"1f552","🕞":"1f55e","🕓":"1f553","🕟":"1f55f","🕔":"1f554","🕠":"1f560","🕕":"1f555","🕡":"1f561","🕖":"1f556","🕢":"1f562","🕗":"1f557","🕣":"1f563","🕘":"1f558","🕤":"1f564","🕙":"1f559","🕥":"1f565","🕚":"1f55a","🕦":"1f566","🌑":"1f311","🌒":"1f312","🌓":"1f313","🌔":"1f314","🌕":"1f315","🌖":"1f316","🌗":"1f317","🌘":"1f318","🌙":"1f319","🌚":"1f31a","🌛":"1f31b","🌜":"1f31c","🌡":"1f321","☀":"2600","🌝":"1f31d","🌞":"1f31e","⭐":"2b50","🌟":"1f31f","🌠":"1f320","☁":"2601","⛅":"26c5","⛈":"26c8","🌤":"1f324","🌥":"1f325","🌦":"1f326","🌧":"1f327","🌨":"1f328","🌩":"1f329","🌪":"1f32a","🌫":"1f32b","🌬":"1f32c","🌀":"1f300","🌈":"1f308","🌂":"1f302","☂":"2602","☔":"2614","⛱":"26f1","⚡":"26a1","❄":"2744","☃":"2603","⛄":"26c4","☄":"2604","🔥":"1f525","💧":"1f4a7","🌊":"1f30a","🎃":"1f383","🎄":"1f384","🎆":"1f386","🎇":"1f387","✨":"2728","🎈":"1f388","🎉":"1f389","🎊":"1f38a","🎋":"1f38b","🎍":"1f38d","🎎":"1f38e","🎏":"1f38f","🎐":"1f390","🎑":"1f391","🎀":"1f380","🎁":"1f381","🎗":"1f397","🎟":"1f39f","🎫":"1f3ab","🎖":"1f396","🏆":"1f3c6","🏅":"1f3c5","🥇":"1f947","🥈":"1f948","🥉":"1f949","⚽":"26bd","⚾":"26be","🏀":"1f3c0","🏐":"1f3d0","🏈":"1f3c8","🏉":"1f3c9","🎾":"1f3be","🎱":"1f3b1","🎳":"1f3b3","🏏":"1f3cf","🏑":"1f3d1","🏒":"1f3d2","🏓":"1f3d3","🏸":"1f3f8","🥊":"1f94a","🥋":"1f94b","🥅":"1f945","🎯":"1f3af","⛳":"26f3","⛸":"26f8","🎣":"1f3a3","🎽":"1f3bd","🎿":"1f3bf","🛷":"1f6f7","🥌":"1f94c","🎮":"1f3ae","🕹":"1f579","🎲":"1f3b2","♠":"2660","♥":"2665","♦":"2666","♣":"2663","🃏":"1f0cf","🀄":"1f004","🎴":"1f3b4","🔇":"1f507","🔈":"1f508","🔉":"1f509","🔊":"1f50a","📢":"1f4e2","📣":"1f4e3","📯":"1f4ef","🔔":"1f514","🔕":"1f515","🎼":"1f3bc","🎵":"1f3b5","🎶":"1f3b6","🎙":"1f399","🎚":"1f39a","🎛":"1f39b","🎤":"1f3a4","🎧":"1f3a7","📻":"1f4fb","🎷":"1f3b7","🎸":"1f3b8","🎹":"1f3b9","🎺":"1f3ba","🎻":"1f3bb","🥁":"1f941","📱":"1f4f1","📲":"1f4f2","☎":"260e","📞":"1f4de","📟":"1f4df","📠":"1f4e0","🔋":"1f50b","🔌":"1f50c","💻":"1f4bb","🖥":"1f5a5","🖨":"1f5a8","⌨":"2328","🖱":"1f5b1","🖲":"1f5b2","💽":"1f4bd","💾":"1f4be","💿":"1f4bf","📀":"1f4c0","🎥":"1f3a5","🎞":"1f39e","📽":"1f4fd","🎬":"1f3ac","📺":"1f4fa","📷":"1f4f7","📸":"1f4f8","📹":"1f4f9","📼":"1f4fc","🔍":"1f50d","🔎":"1f50e","🔬":"1f52c","🔭":"1f52d","📡":"1f4e1","🕯":"1f56f","💡":"1f4a1","🔦":"1f526","🏮":"1f3ee","📔":"1f4d4","📕":"1f4d5","📖":"1f4d6","📗":"1f4d7","📘":"1f4d8","📙":"1f4d9","📚":"1f4da","📓":"1f4d3","📒":"1f4d2","📃":"1f4c3","📜":"1f4dc","📄":"1f4c4","📰":"1f4f0","🗞":"1f5de","📑":"1f4d1","🔖":"1f516","🏷":"1f3f7","💰":"1f4b0","💴":"1f4b4","💵":"1f4b5","💶":"1f4b6","💷":"1f4b7","💸":"1f4b8","💳":"1f4b3","💹":"1f4b9","💱":"1f4b1","💲":"1f4b2","✉":"2709","📧":"1f4e7","📨":"1f4e8","📩":"1f4e9","📤":"1f4e4","📥":"1f4e5","📦":"1f4e6","📫":"1f4eb","📪":"1f4ea","📬":"1f4ec","📭":"1f4ed","📮":"1f4ee","🗳":"1f5f3","✏":"270f","✒":"2712","🖋":"1f58b","🖊":"1f58a","🖌":"1f58c","🖍":"1f58d","📝":"1f4dd","💼":"1f4bc","📁":"1f4c1","📂":"1f4c2","🗂":"1f5c2","📅":"1f4c5","📆":"1f4c6","🗒":"1f5d2","🗓":"1f5d3","📇":"1f4c7","📈":"1f4c8","📉":"1f4c9","📊":"1f4ca","📋":"1f4cb","📌":"1f4cc","📍":"1f4cd","📎":"1f4ce","🖇":"1f587","📏":"1f4cf","📐":"1f4d0","✂":"2702","🗃":"1f5c3","🗄":"1f5c4","🗑":"1f5d1","🔒":"1f512","🔓":"1f513","🔏":"1f50f","🔐":"1f510","🔑":"1f511","🗝":"1f5dd","🔨":"1f528","⛏":"26cf","⚒":"2692","🛠":"1f6e0","🗡":"1f5e1","⚔":"2694","🔫":"1f52b","🏹":"1f3f9","🛡":"1f6e1","🔧":"1f527","🔩":"1f529","⚙":"2699","🗜":"1f5dc","⚗":"2697","⚖":"2696","🔗":"1f517","⛓":"26d3","💉":"1f489","💊":"1f48a","🚬":"1f6ac","⚰":"26b0","⚱":"26b1","🗿":"1f5ff","🛢":"1f6e2","🔮":"1f52e","🛒":"1f6d2","🏧":"1f3e7","🚮":"1f6ae","🚰":"1f6b0","♿":"267f","🚹":"1f6b9","🚺":"1f6ba","🚻":"1f6bb","🚼":"1f6bc","🚾":"1f6be","🛂":"1f6c2","🛃":"1f6c3","🛄":"1f6c4","🛅":"1f6c5","⚠":"26a0","🚸":"1f6b8","⛔":"26d4","🚫":"1f6ab","🚳":"1f6b3","🚭":"1f6ad","🚯":"1f6af","🚱":"1f6b1","🚷":"1f6b7","📵":"1f4f5","🔞":"1f51e","☢":"2622","☣":"2623","⬆":"2b06","↗":"2197","➡":"27a1","↘":"2198","⬇":"2b07","↙":"2199","⬅":"2b05","↖":"2196","↕":"2195","↔":"2194","↩":"21a9","↪":"21aa","⤴":"2934","⤵":"2935","🔃":"1f503","🔄":"1f504","🔙":"1f519","🔚":"1f51a","🔛":"1f51b","🔜":"1f51c","🔝":"1f51d","🛐":"1f6d0","⚛":"269b","🕉":"1f549","✡":"2721","☸":"2638","☯":"262f","✝":"271d","☦":"2626","☪":"262a","☮":"262e","🕎":"1f54e","🔯":"1f52f","♈":"2648","♉":"2649","♊":"264a","♋":"264b","♌":"264c","♍":"264d","♎":"264e","♏":"264f","♐":"2650","♑":"2651","♒":"2652","♓":"2653","⛎":"26ce","🔀":"1f500","🔁":"1f501","🔂":"1f502","▶":"25b6","⏩":"23e9","⏭":"23ed","⏯":"23ef","◀":"25c0","⏪":"23ea","⏮":"23ee","🔼":"1f53c","⏫":"23eb","🔽":"1f53d","⏬":"23ec","⏸":"23f8","⏹":"23f9","⏺":"23fa","⏏":"23cf","🎦":"1f3a6","🔅":"1f505","🔆":"1f506","📶":"1f4f6","📳":"1f4f3","📴":"1f4f4","♀":"2640","♂":"2642","⚕":"2695","♻":"267b","⚜":"269c","🔱":"1f531","📛":"1f4db","🔰":"1f530","⭕":"2b55","✅":"2705","☑":"2611","✔":"2714","✖":"2716","❌":"274c","❎":"274e","➕":"2795","➖":"2796","➗":"2797","➰":"27b0","➿":"27bf","〽":"303d","✳":"2733","✴":"2734","❇":"2747","‼":"203c","⁉":"2049","❓":"2753","❔":"2754","❕":"2755","❗":"2757","〰":"3030","©":"a9","®":"ae","™":"2122","🔟":"1f51f","💯":"1f4af","🔠":"1f520","🔡":"1f521","🔢":"1f522","🔣":"1f523","🔤":"1f524","🅰":"1f170","🆎":"1f18e","🅱":"1f171","🆑":"1f191","🆒":"1f192","🆓":"1f193","ℹ":"2139","🆔":"1f194","Ⓜ":"24c2","🆕":"1f195","🆖":"1f196","🅾":"1f17e","🆗":"1f197","🅿":"1f17f","🆘":"1f198","🆙":"1f199","🆚":"1f19a","🈁":"1f201","🈂":"1f202","🈷":"1f237","🈶":"1f236","🈯":"1f22f","🉐":"1f250","🈹":"1f239","🈚":"1f21a","🈲":"1f232","🉑":"1f251","🈸":"1f238","🈴":"1f234","🈳":"1f233","㊗":"3297","㊙":"3299","🈺":"1f23a","🈵":"1f235","▪":"25aa","▫":"25ab","◻":"25fb","◼":"25fc","◽":"25fd","◾":"25fe","⬛":"2b1b","⬜":"2b1c","🔶":"1f536","🔷":"1f537","🔸":"1f538","🔹":"1f539","🔺":"1f53a","🔻":"1f53b","💠":"1f4a0","🔘":"1f518","🔲":"1f532","🔳":"1f533","⚪":"26aa","⚫":"26ab","🔴":"1f534","🔵":"1f535","🏁":"1f3c1","🚩":"1f6a9","🎌":"1f38c","🏴":"1f3f4","🏳":"1f3f3","☺️":"263a","☹️":"2639","☠️":"2620","👶🏻":"1f476-1f3fb","👶🏼":"1f476-1f3fc","👶🏽":"1f476-1f3fd","👶🏾":"1f476-1f3fe","👶🏿":"1f476-1f3ff","🧒🏻":"1f9d2-1f3fb","🧒🏼":"1f9d2-1f3fc","🧒🏽":"1f9d2-1f3fd","🧒🏾":"1f9d2-1f3fe","🧒🏿":"1f9d2-1f3ff","👦🏻":"1f466-1f3fb","👦🏼":"1f466-1f3fc","👦🏽":"1f466-1f3fd","👦🏾":"1f466-1f3fe","👦🏿":"1f466-1f3ff","👧🏻":"1f467-1f3fb","👧🏼":"1f467-1f3fc","👧🏽":"1f467-1f3fd","👧🏾":"1f467-1f3fe","👧🏿":"1f467-1f3ff","🧑🏻":"1f9d1-1f3fb","🧑🏼":"1f9d1-1f3fc","🧑🏽":"1f9d1-1f3fd","🧑🏾":"1f9d1-1f3fe","🧑🏿":"1f9d1-1f3ff","👨🏻":"1f468-1f3fb","👨🏼":"1f468-1f3fc","👨🏽":"1f468-1f3fd","👨🏾":"1f468-1f3fe","👨🏿":"1f468-1f3ff","👩🏻":"1f469-1f3fb","👩🏼":"1f469-1f3fc","👩🏽":"1f469-1f3fd","👩🏾":"1f469-1f3fe","👩🏿":"1f469-1f3ff","🧓🏻":"1f9d3-1f3fb","🧓🏼":"1f9d3-1f3fc","🧓🏽":"1f9d3-1f3fd","🧓🏾":"1f9d3-1f3fe","🧓🏿":"1f9d3-1f3ff","👴🏻":"1f474-1f3fb","👴🏼":"1f474-1f3fc","👴🏽":"1f474-1f3fd","👴🏾":"1f474-1f3fe","👴🏿":"1f474-1f3ff","👵🏻":"1f475-1f3fb","👵🏼":"1f475-1f3fc","👵🏽":"1f475-1f3fd","👵🏾":"1f475-1f3fe","👵🏿":"1f475-1f3ff","👮🏻":"1f46e-1f3fb","👮🏼":"1f46e-1f3fc","👮🏽":"1f46e-1f3fd","👮🏾":"1f46e-1f3fe","👮🏿":"1f46e-1f3ff","🕵️":"1f575","🕵🏻":"1f575-1f3fb","🕵🏼":"1f575-1f3fc","🕵🏽":"1f575-1f3fd","🕵🏾":"1f575-1f3fe","🕵🏿":"1f575-1f3ff","💂🏻":"1f482-1f3fb","💂🏼":"1f482-1f3fc","💂🏽":"1f482-1f3fd","💂🏾":"1f482-1f3fe","💂🏿":"1f482-1f3ff","👷🏻":"1f477-1f3fb","👷🏼":"1f477-1f3fc","👷🏽":"1f477-1f3fd","👷🏾":"1f477-1f3fe","👷🏿":"1f477-1f3ff","🤴🏻":"1f934-1f3fb","🤴🏼":"1f934-1f3fc","🤴🏽":"1f934-1f3fd","🤴🏾":"1f934-1f3fe","🤴🏿":"1f934-1f3ff","👸🏻":"1f478-1f3fb","👸🏼":"1f478-1f3fc","👸🏽":"1f478-1f3fd","👸🏾":"1f478-1f3fe","👸🏿":"1f478-1f3ff","👳🏻":"1f473-1f3fb","👳🏼":"1f473-1f3fc","👳🏽":"1f473-1f3fd","👳🏾":"1f473-1f3fe","👳🏿":"1f473-1f3ff","👲🏻":"1f472-1f3fb","👲🏼":"1f472-1f3fc","👲🏽":"1f472-1f3fd","👲🏾":"1f472-1f3fe","👲🏿":"1f472-1f3ff","🧕🏻":"1f9d5-1f3fb","🧕🏼":"1f9d5-1f3fc","🧕🏽":"1f9d5-1f3fd","🧕🏾":"1f9d5-1f3fe","🧕🏿":"1f9d5-1f3ff","🧔🏻":"1f9d4-1f3fb","🧔🏼":"1f9d4-1f3fc","🧔🏽":"1f9d4-1f3fd","🧔🏾":"1f9d4-1f3fe","🧔🏿":"1f9d4-1f3ff","👱🏻":"1f471-1f3fb","👱🏼":"1f471-1f3fc","👱🏽":"1f471-1f3fd","👱🏾":"1f471-1f3fe","👱🏿":"1f471-1f3ff","🤵🏻":"1f935-1f3fb","🤵🏼":"1f935-1f3fc","🤵🏽":"1f935-1f3fd","🤵🏾":"1f935-1f3fe","🤵🏿":"1f935-1f3ff","👰🏻":"1f470-1f3fb","👰🏼":"1f470-1f3fc","👰🏽":"1f470-1f3fd","👰🏾":"1f470-1f3fe","👰🏿":"1f470-1f3ff","🤰🏻":"1f930-1f3fb","🤰🏼":"1f930-1f3fc","🤰🏽":"1f930-1f3fd","🤰🏾":"1f930-1f3fe","🤰🏿":"1f930-1f3ff","🤱🏻":"1f931-1f3fb","🤱🏼":"1f931-1f3fc","🤱🏽":"1f931-1f3fd","🤱🏾":"1f931-1f3fe","🤱🏿":"1f931-1f3ff","👼🏻":"1f47c-1f3fb","👼🏼":"1f47c-1f3fc","👼🏽":"1f47c-1f3fd","👼🏾":"1f47c-1f3fe","👼🏿":"1f47c-1f3ff","🎅🏻":"1f385-1f3fb","🎅🏼":"1f385-1f3fc","🎅🏽":"1f385-1f3fd","🎅🏾":"1f385-1f3fe","🎅🏿":"1f385-1f3ff","🤶🏻":"1f936-1f3fb","🤶🏼":"1f936-1f3fc","🤶🏽":"1f936-1f3fd","🤶🏾":"1f936-1f3fe","🤶🏿":"1f936-1f3ff","🧙🏻":"1f9d9-1f3fb","🧙🏼":"1f9d9-1f3fc","🧙🏽":"1f9d9-1f3fd","🧙🏾":"1f9d9-1f3fe","🧙🏿":"1f9d9-1f3ff","🧚🏻":"1f9da-1f3fb","🧚🏼":"1f9da-1f3fc","🧚🏽":"1f9da-1f3fd","🧚🏾":"1f9da-1f3fe","🧚🏿":"1f9da-1f3ff","🧛🏻":"1f9db-1f3fb","🧛🏼":"1f9db-1f3fc","🧛🏽":"1f9db-1f3fd","🧛🏾":"1f9db-1f3fe","🧛🏿":"1f9db-1f3ff","🧜🏻":"1f9dc-1f3fb","🧜🏼":"1f9dc-1f3fc","🧜🏽":"1f9dc-1f3fd","🧜🏾":"1f9dc-1f3fe","🧜🏿":"1f9dc-1f3ff","🧝🏻":"1f9dd-1f3fb","🧝🏼":"1f9dd-1f3fc","🧝🏽":"1f9dd-1f3fd","🧝🏾":"1f9dd-1f3fe","🧝🏿":"1f9dd-1f3ff","🙍🏻":"1f64d-1f3fb","🙍🏼":"1f64d-1f3fc","🙍🏽":"1f64d-1f3fd","🙍🏾":"1f64d-1f3fe","🙍🏿":"1f64d-1f3ff","🙎🏻":"1f64e-1f3fb","🙎🏼":"1f64e-1f3fc","🙎🏽":"1f64e-1f3fd","🙎🏾":"1f64e-1f3fe","🙎🏿":"1f64e-1f3ff","🙅🏻":"1f645-1f3fb","🙅🏼":"1f645-1f3fc","🙅🏽":"1f645-1f3fd","🙅🏾":"1f645-1f3fe","🙅🏿":"1f645-1f3ff","🙆🏻":"1f646-1f3fb","🙆🏼":"1f646-1f3fc","🙆🏽":"1f646-1f3fd","🙆🏾":"1f646-1f3fe","🙆🏿":"1f646-1f3ff","💁🏻":"1f481-1f3fb","💁🏼":"1f481-1f3fc","💁🏽":"1f481-1f3fd","💁🏾":"1f481-1f3fe","💁🏿":"1f481-1f3ff","🙋🏻":"1f64b-1f3fb","🙋🏼":"1f64b-1f3fc","🙋🏽":"1f64b-1f3fd","🙋🏾":"1f64b-1f3fe","🙋🏿":"1f64b-1f3ff","🙇🏻":"1f647-1f3fb","🙇🏼":"1f647-1f3fc","🙇🏽":"1f647-1f3fd","🙇🏾":"1f647-1f3fe","🙇🏿":"1f647-1f3ff","🤦🏻":"1f926-1f3fb","🤦🏼":"1f926-1f3fc","🤦🏽":"1f926-1f3fd","🤦🏾":"1f926-1f3fe","🤦🏿":"1f926-1f3ff","🤷🏻":"1f937-1f3fb","🤷🏼":"1f937-1f3fc","🤷🏽":"1f937-1f3fd","🤷🏾":"1f937-1f3fe","🤷🏿":"1f937-1f3ff","💆🏻":"1f486-1f3fb","💆🏼":"1f486-1f3fc","💆🏽":"1f486-1f3fd","💆🏾":"1f486-1f3fe","💆🏿":"1f486-1f3ff","💇🏻":"1f487-1f3fb","💇🏼":"1f487-1f3fc","💇🏽":"1f487-1f3fd","💇🏾":"1f487-1f3fe","💇🏿":"1f487-1f3ff","🚶🏻":"1f6b6-1f3fb","🚶🏼":"1f6b6-1f3fc","🚶🏽":"1f6b6-1f3fd","🚶🏾":"1f6b6-1f3fe","🚶🏿":"1f6b6-1f3ff","🏃🏻":"1f3c3-1f3fb","🏃🏼":"1f3c3-1f3fc","🏃🏽":"1f3c3-1f3fd","🏃🏾":"1f3c3-1f3fe","🏃🏿":"1f3c3-1f3ff","💃🏻":"1f483-1f3fb","💃🏼":"1f483-1f3fc","💃🏽":"1f483-1f3fd","💃🏾":"1f483-1f3fe","💃🏿":"1f483-1f3ff","🕺🏻":"1f57a-1f3fb","🕺🏼":"1f57a-1f3fc","🕺🏽":"1f57a-1f3fd","🕺🏾":"1f57a-1f3fe","🕺🏿":"1f57a-1f3ff","🧖🏻":"1f9d6-1f3fb","🧖🏼":"1f9d6-1f3fc","🧖🏽":"1f9d6-1f3fd","🧖🏾":"1f9d6-1f3fe","🧖🏿":"1f9d6-1f3ff","🧗🏻":"1f9d7-1f3fb","🧗🏼":"1f9d7-1f3fc","🧗🏽":"1f9d7-1f3fd","🧗🏾":"1f9d7-1f3fe","🧗🏿":"1f9d7-1f3ff","🧘🏻":"1f9d8-1f3fb","🧘🏼":"1f9d8-1f3fc","🧘🏽":"1f9d8-1f3fd","🧘🏾":"1f9d8-1f3fe","🧘🏿":"1f9d8-1f3ff","🛀🏻":"1f6c0-1f3fb","🛀🏼":"1f6c0-1f3fc","🛀🏽":"1f6c0-1f3fd","🛀🏾":"1f6c0-1f3fe","🛀🏿":"1f6c0-1f3ff","🛌🏻":"1f6cc-1f3fb","🛌🏼":"1f6cc-1f3fc","🛌🏽":"1f6cc-1f3fd","🛌🏾":"1f6cc-1f3fe","🛌🏿":"1f6cc-1f3ff","🕴️":"1f574","🕴🏻":"1f574-1f3fb","🕴🏼":"1f574-1f3fc","🕴🏽":"1f574-1f3fd","🕴🏾":"1f574-1f3fe","🕴🏿":"1f574-1f3ff","🗣️":"1f5e3","🏇🏻":"1f3c7-1f3fb","🏇🏼":"1f3c7-1f3fc","🏇🏽":"1f3c7-1f3fd","🏇🏾":"1f3c7-1f3fe","🏇🏿":"1f3c7-1f3ff","⛷️":"26f7","🏂🏻":"1f3c2-1f3fb","🏂🏼":"1f3c2-1f3fc","🏂🏽":"1f3c2-1f3fd","🏂🏾":"1f3c2-1f3fe","🏂🏿":"1f3c2-1f3ff","🏌️":"1f3cc","🏌🏻":"1f3cc-1f3fb","🏌🏼":"1f3cc-1f3fc","🏌🏽":"1f3cc-1f3fd","🏌🏾":"1f3cc-1f3fe","🏌🏿":"1f3cc-1f3ff","🏄🏻":"1f3c4-1f3fb","🏄🏼":"1f3c4-1f3fc","🏄🏽":"1f3c4-1f3fd","🏄🏾":"1f3c4-1f3fe","🏄🏿":"1f3c4-1f3ff","🚣🏻":"1f6a3-1f3fb","🚣🏼":"1f6a3-1f3fc","🚣🏽":"1f6a3-1f3fd","🚣🏾":"1f6a3-1f3fe","🚣🏿":"1f6a3-1f3ff","🏊🏻":"1f3ca-1f3fb","🏊🏼":"1f3ca-1f3fc","🏊🏽":"1f3ca-1f3fd","🏊🏾":"1f3ca-1f3fe","🏊🏿":"1f3ca-1f3ff","⛹️":"26f9","⛹🏻":"26f9-1f3fb","⛹🏼":"26f9-1f3fc","⛹🏽":"26f9-1f3fd","⛹🏾":"26f9-1f3fe","⛹🏿":"26f9-1f3ff","🏋️":"1f3cb","🏋🏻":"1f3cb-1f3fb","🏋🏼":"1f3cb-1f3fc","🏋🏽":"1f3cb-1f3fd","🏋🏾":"1f3cb-1f3fe","🏋🏿":"1f3cb-1f3ff","🚴🏻":"1f6b4-1f3fb","🚴🏼":"1f6b4-1f3fc","🚴🏽":"1f6b4-1f3fd","🚴🏾":"1f6b4-1f3fe","🚴🏿":"1f6b4-1f3ff","🚵🏻":"1f6b5-1f3fb","🚵🏼":"1f6b5-1f3fc","🚵🏽":"1f6b5-1f3fd","🚵🏾":"1f6b5-1f3fe","🚵🏿":"1f6b5-1f3ff","🏎️":"1f3ce","🏍️":"1f3cd","🤸🏻":"1f938-1f3fb","🤸🏼":"1f938-1f3fc","🤸🏽":"1f938-1f3fd","🤸🏾":"1f938-1f3fe","🤸🏿":"1f938-1f3ff","🤽🏻":"1f93d-1f3fb","🤽🏼":"1f93d-1f3fc","🤽🏽":"1f93d-1f3fd","🤽🏾":"1f93d-1f3fe","🤽🏿":"1f93d-1f3ff","🤾🏻":"1f93e-1f3fb","🤾🏼":"1f93e-1f3fc","🤾🏽":"1f93e-1f3fd","🤾🏾":"1f93e-1f3fe","🤾🏿":"1f93e-1f3ff","🤹🏻":"1f939-1f3fb","🤹🏼":"1f939-1f3fc","🤹🏽":"1f939-1f3fd","🤹🏾":"1f939-1f3fe","🤹🏿":"1f939-1f3ff","🤳🏻":"1f933-1f3fb","🤳🏼":"1f933-1f3fc","🤳🏽":"1f933-1f3fd","🤳🏾":"1f933-1f3fe","🤳🏿":"1f933-1f3ff","💪🏻":"1f4aa-1f3fb","💪🏼":"1f4aa-1f3fc","💪🏽":"1f4aa-1f3fd","💪🏾":"1f4aa-1f3fe","💪🏿":"1f4aa-1f3ff","👈🏻":"1f448-1f3fb","👈🏼":"1f448-1f3fc","👈🏽":"1f448-1f3fd","👈🏾":"1f448-1f3fe","👈🏿":"1f448-1f3ff","👉🏻":"1f449-1f3fb","👉🏼":"1f449-1f3fc","👉🏽":"1f449-1f3fd","👉🏾":"1f449-1f3fe","👉🏿":"1f449-1f3ff","☝️":"261d","☝🏻":"261d-1f3fb","☝🏼":"261d-1f3fc","☝🏽":"261d-1f3fd","☝🏾":"261d-1f3fe","☝🏿":"261d-1f3ff","👆🏻":"1f446-1f3fb","👆🏼":"1f446-1f3fc","👆🏽":"1f446-1f3fd","👆🏾":"1f446-1f3fe","👆🏿":"1f446-1f3ff","🖕🏻":"1f595-1f3fb","🖕🏼":"1f595-1f3fc","🖕🏽":"1f595-1f3fd","🖕🏾":"1f595-1f3fe","🖕🏿":"1f595-1f3ff","👇🏻":"1f447-1f3fb","👇🏼":"1f447-1f3fc","👇🏽":"1f447-1f3fd","👇🏾":"1f447-1f3fe","👇🏿":"1f447-1f3ff","✌️":"270c","✌🏻":"270c-1f3fb","✌🏼":"270c-1f3fc","✌🏽":"270c-1f3fd","✌🏾":"270c-1f3fe","✌🏿":"270c-1f3ff","🤞🏻":"1f91e-1f3fb","🤞🏼":"1f91e-1f3fc","🤞🏽":"1f91e-1f3fd","🤞🏾":"1f91e-1f3fe","🤞🏿":"1f91e-1f3ff","🖖🏻":"1f596-1f3fb","🖖🏼":"1f596-1f3fc","🖖🏽":"1f596-1f3fd","🖖🏾":"1f596-1f3fe","🖖🏿":"1f596-1f3ff","🤘🏻":"1f918-1f3fb","🤘🏼":"1f918-1f3fc","🤘🏽":"1f918-1f3fd","🤘🏾":"1f918-1f3fe","🤘🏿":"1f918-1f3ff","🤙🏻":"1f919-1f3fb","🤙🏼":"1f919-1f3fc","🤙🏽":"1f919-1f3fd","🤙🏾":"1f919-1f3fe","🤙🏿":"1f919-1f3ff","🖐️":"1f590","🖐🏻":"1f590-1f3fb","🖐🏼":"1f590-1f3fc","🖐🏽":"1f590-1f3fd","🖐🏾":"1f590-1f3fe","🖐🏿":"1f590-1f3ff","✋🏻":"270b-1f3fb","✋🏼":"270b-1f3fc","✋🏽":"270b-1f3fd","✋🏾":"270b-1f3fe","✋🏿":"270b-1f3ff","👌🏻":"1f44c-1f3fb","👌🏼":"1f44c-1f3fc","👌🏽":"1f44c-1f3fd","👌🏾":"1f44c-1f3fe","👌🏿":"1f44c-1f3ff","👍🏻":"1f44d-1f3fb","👍🏼":"1f44d-1f3fc","👍🏽":"1f44d-1f3fd","👍🏾":"1f44d-1f3fe","👍🏿":"1f44d-1f3ff","👎🏻":"1f44e-1f3fb","👎🏼":"1f44e-1f3fc","👎🏽":"1f44e-1f3fd","👎🏾":"1f44e-1f3fe","👎🏿":"1f44e-1f3ff","✊🏻":"270a-1f3fb","✊🏼":"270a-1f3fc","✊🏽":"270a-1f3fd","✊🏾":"270a-1f3fe","✊🏿":"270a-1f3ff","👊🏻":"1f44a-1f3fb","👊🏼":"1f44a-1f3fc","👊🏽":"1f44a-1f3fd","👊🏾":"1f44a-1f3fe","👊🏿":"1f44a-1f3ff","🤛🏻":"1f91b-1f3fb","🤛🏼":"1f91b-1f3fc","🤛🏽":"1f91b-1f3fd","🤛🏾":"1f91b-1f3fe","🤛🏿":"1f91b-1f3ff","🤜🏻":"1f91c-1f3fb","🤜🏼":"1f91c-1f3fc","🤜🏽":"1f91c-1f3fd","🤜🏾":"1f91c-1f3fe","🤜🏿":"1f91c-1f3ff","🤚🏻":"1f91a-1f3fb","🤚🏼":"1f91a-1f3fc","🤚🏽":"1f91a-1f3fd","🤚🏾":"1f91a-1f3fe","🤚🏿":"1f91a-1f3ff","👋🏻":"1f44b-1f3fb","👋🏼":"1f44b-1f3fc","👋🏽":"1f44b-1f3fd","👋🏾":"1f44b-1f3fe","👋🏿":"1f44b-1f3ff","🤟🏻":"1f91f-1f3fb","🤟🏼":"1f91f-1f3fc","🤟🏽":"1f91f-1f3fd","🤟🏾":"1f91f-1f3fe","🤟🏿":"1f91f-1f3ff","✍️":"270d","✍🏻":"270d-1f3fb","✍🏼":"270d-1f3fc","✍🏽":"270d-1f3fd","✍🏾":"270d-1f3fe","✍🏿":"270d-1f3ff","👏🏻":"1f44f-1f3fb","👏🏼":"1f44f-1f3fc","👏🏽":"1f44f-1f3fd","👏🏾":"1f44f-1f3fe","👏🏿":"1f44f-1f3ff","👐🏻":"1f450-1f3fb","👐🏼":"1f450-1f3fc","👐🏽":"1f450-1f3fd","👐🏾":"1f450-1f3fe","👐🏿":"1f450-1f3ff","🙌🏻":"1f64c-1f3fb","🙌🏼":"1f64c-1f3fc","🙌🏽":"1f64c-1f3fd","🙌🏾":"1f64c-1f3fe","🙌🏿":"1f64c-1f3ff","🤲🏻":"1f932-1f3fb","🤲🏼":"1f932-1f3fc","🤲🏽":"1f932-1f3fd","🤲🏾":"1f932-1f3fe","🤲🏿":"1f932-1f3ff","🙏🏻":"1f64f-1f3fb","🙏🏼":"1f64f-1f3fc","🙏🏽":"1f64f-1f3fd","🙏🏾":"1f64f-1f3fe","🙏🏿":"1f64f-1f3ff","💅🏻":"1f485-1f3fb","💅🏼":"1f485-1f3fc","💅🏽":"1f485-1f3fd","💅🏾":"1f485-1f3fe","💅🏿":"1f485-1f3ff","👂🏻":"1f442-1f3fb","👂🏼":"1f442-1f3fc","👂🏽":"1f442-1f3fd","👂🏾":"1f442-1f3fe","👂🏿":"1f442-1f3ff","👃🏻":"1f443-1f3fb","👃🏼":"1f443-1f3fc","👃🏽":"1f443-1f3fd","👃🏾":"1f443-1f3fe","👃🏿":"1f443-1f3ff","👁️":"1f441","❤️":"2764","❣️":"2763","🗨️":"1f5e8","🗯️":"1f5ef","🕳️":"1f573","🕶️":"1f576","🛍️":"1f6cd","⛑️":"26d1","🐿️":"1f43f","🕊️":"1f54a","🕷️":"1f577","🕸️":"1f578","🏵️":"1f3f5","☘️":"2618","🌶️":"1f336","🍽️":"1f37d","🗺️":"1f5fa","🏔️":"1f3d4","⛰️":"26f0","🏕️":"1f3d5","🏖️":"1f3d6","🏜️":"1f3dc","🏝️":"1f3dd","🏞️":"1f3de","🏟️":"1f3df","🏛️":"1f3db","🏗️":"1f3d7","🏘️":"1f3d8","🏙️":"1f3d9","🏚️":"1f3da","⛩️":"26e9","♨️":"2668","🖼️":"1f5bc","🛣️":"1f6e3","🛤️":"1f6e4","🛳️":"1f6f3","⛴️":"26f4","🛥️":"1f6e5","✈️":"2708","🛩️":"1f6e9","🛰️":"1f6f0","🛎️":"1f6ce","🛏️":"1f6cf","🛋️":"1f6cb","⏱️":"23f1","⏲️":"23f2","🕰️":"1f570","🌡️":"1f321","☀️":"2600","☁️":"2601","⛈️":"26c8","🌤️":"1f324","🌥️":"1f325","🌦️":"1f326","🌧️":"1f327","🌨️":"1f328","🌩️":"1f329","🌪️":"1f32a","🌫️":"1f32b","🌬️":"1f32c","☂️":"2602","⛱️":"26f1","❄️":"2744","☃️":"2603","☄️":"2604","🎗️":"1f397","🎟️":"1f39f","🎖️":"1f396","⛸️":"26f8","🕹️":"1f579","♠️":"2660","♥️":"2665","♦️":"2666","♣️":"2663","🎙️":"1f399","🎚️":"1f39a","🎛️":"1f39b","☎️":"260e","🖥️":"1f5a5","🖨️":"1f5a8","⌨️":"2328","🖱️":"1f5b1","🖲️":"1f5b2","🎞️":"1f39e","📽️":"1f4fd","🕯️":"1f56f","🗞️":"1f5de","🏷️":"1f3f7","✉️":"2709","🗳️":"1f5f3","✏️":"270f","✒️":"2712","🖋️":"1f58b","🖊️":"1f58a","🖌️":"1f58c","🖍️":"1f58d","🗂️":"1f5c2","🗒️":"1f5d2","🗓️":"1f5d3","🖇️":"1f587","✂️":"2702","🗃️":"1f5c3","🗄️":"1f5c4","🗑️":"1f5d1","🗝️":"1f5dd","⛏️":"26cf","⚒️":"2692","🛠️":"1f6e0","🗡️":"1f5e1","⚔️":"2694","🛡️":"1f6e1","⚙️":"2699","🗜️":"1f5dc","⚗️":"2697","⚖️":"2696","⛓️":"26d3","⚰️":"26b0","⚱️":"26b1","🛢️":"1f6e2","⚠️":"26a0","☢️":"2622","☣️":"2623","⬆️":"2b06","↗️":"2197","➡️":"27a1","↘️":"2198","⬇️":"2b07","↙️":"2199","⬅️":"2b05","↖️":"2196","↕️":"2195","↔️":"2194","↩️":"21a9","↪️":"21aa","⤴️":"2934","⤵️":"2935","⚛️":"269b","🕉️":"1f549","✡️":"2721","☸️":"2638","☯️":"262f","✝️":"271d","☦️":"2626","☪️":"262a","☮️":"262e","▶️":"25b6","⏭️":"23ed","⏯️":"23ef","◀️":"25c0","⏮️":"23ee","⏸️":"23f8","⏹️":"23f9","⏺️":"23fa","⏏️":"23cf","♀️":"2640","♂️":"2642","⚕️":"2695","♻️":"267b","⚜️":"269c","☑️":"2611","✔️":"2714","✖️":"2716","〽️":"303d","✳️":"2733","✴️":"2734","❇️":"2747","‼️":"203c","⁉️":"2049","〰️":"3030","©️":"a9","®️":"ae","™️":"2122","#⃣":"23-20e3","*⃣":"2a-20e3","0⃣":"30-20e3","1⃣":"31-20e3","2⃣":"32-20e3","3⃣":"33-20e3","4⃣":"34-20e3","5⃣":"35-20e3","6⃣":"36-20e3","7⃣":"37-20e3","8⃣":"38-20e3","9⃣":"39-20e3","🅰️":"1f170","🅱️":"1f171","ℹ️":"2139","Ⓜ️":"24c2","🅾️":"1f17e","🅿️":"1f17f","🈂️":"1f202","🈷️":"1f237","㊗️":"3297","㊙️":"3299","▪️":"25aa","▫️":"25ab","◻️":"25fb","◼️":"25fc","🏳️":"1f3f3","🇦🇨":"1f1e6-1f1e8","🇦🇩":"1f1e6-1f1e9","🇦🇪":"1f1e6-1f1ea","🇦🇫":"1f1e6-1f1eb","🇦🇬":"1f1e6-1f1ec","🇦🇮":"1f1e6-1f1ee","🇦🇱":"1f1e6-1f1f1","🇦🇲":"1f1e6-1f1f2","🇦🇴":"1f1e6-1f1f4","🇦🇶":"1f1e6-1f1f6","🇦🇷":"1f1e6-1f1f7","🇦🇸":"1f1e6-1f1f8","🇦🇹":"1f1e6-1f1f9","🇦🇺":"1f1e6-1f1fa","🇦🇼":"1f1e6-1f1fc","🇦🇽":"1f1e6-1f1fd","🇦🇿":"1f1e6-1f1ff","🇧🇦":"1f1e7-1f1e6","🇧🇧":"1f1e7-1f1e7","🇧🇩":"1f1e7-1f1e9","🇧🇪":"1f1e7-1f1ea","🇧🇫":"1f1e7-1f1eb","🇧🇬":"1f1e7-1f1ec","🇧🇭":"1f1e7-1f1ed","🇧🇮":"1f1e7-1f1ee","🇧🇯":"1f1e7-1f1ef","🇧🇱":"1f1e7-1f1f1","🇧🇲":"1f1e7-1f1f2","🇧🇳":"1f1e7-1f1f3","🇧🇴":"1f1e7-1f1f4","🇧🇶":"1f1e7-1f1f6","🇧🇷":"1f1e7-1f1f7","🇧🇸":"1f1e7-1f1f8","🇧🇹":"1f1e7-1f1f9","🇧🇻":"1f1e7-1f1fb","🇧🇼":"1f1e7-1f1fc","🇧🇾":"1f1e7-1f1fe","🇧🇿":"1f1e7-1f1ff","🇨🇦":"1f1e8-1f1e6","🇨🇨":"1f1e8-1f1e8","🇨🇩":"1f1e8-1f1e9","🇨🇫":"1f1e8-1f1eb","🇨🇬":"1f1e8-1f1ec","🇨🇭":"1f1e8-1f1ed","🇨🇮":"1f1e8-1f1ee","🇨🇰":"1f1e8-1f1f0","🇨🇱":"1f1e8-1f1f1","🇨🇲":"1f1e8-1f1f2","🇨🇳":"1f1e8-1f1f3","🇨🇴":"1f1e8-1f1f4","🇨🇵":"1f1e8-1f1f5","🇨🇷":"1f1e8-1f1f7","🇨🇺":"1f1e8-1f1fa","🇨🇻":"1f1e8-1f1fb","🇨🇼":"1f1e8-1f1fc","🇨🇽":"1f1e8-1f1fd","🇨🇾":"1f1e8-1f1fe","🇨🇿":"1f1e8-1f1ff","🇩🇪":"1f1e9-1f1ea","🇩🇬":"1f1e9-1f1ec","🇩🇯":"1f1e9-1f1ef","🇩🇰":"1f1e9-1f1f0","🇩🇲":"1f1e9-1f1f2","🇩🇴":"1f1e9-1f1f4","🇩🇿":"1f1e9-1f1ff","🇪🇦":"1f1ea-1f1e6","🇪🇨":"1f1ea-1f1e8","🇪🇪":"1f1ea-1f1ea","🇪🇬":"1f1ea-1f1ec","🇪🇭":"1f1ea-1f1ed","🇪🇷":"1f1ea-1f1f7","🇪🇸":"1f1ea-1f1f8","🇪🇹":"1f1ea-1f1f9","🇪🇺":"1f1ea-1f1fa","🇫🇮":"1f1eb-1f1ee","🇫🇯":"1f1eb-1f1ef","🇫🇰":"1f1eb-1f1f0","🇫🇲":"1f1eb-1f1f2","🇫🇴":"1f1eb-1f1f4","🇫🇷":"1f1eb-1f1f7","🇬🇦":"1f1ec-1f1e6","🇬🇧":"1f1ec-1f1e7","🇬🇩":"1f1ec-1f1e9","🇬🇪":"1f1ec-1f1ea","🇬🇫":"1f1ec-1f1eb","🇬🇬":"1f1ec-1f1ec","🇬🇭":"1f1ec-1f1ed","🇬🇮":"1f1ec-1f1ee","🇬🇱":"1f1ec-1f1f1","🇬🇲":"1f1ec-1f1f2","🇬🇳":"1f1ec-1f1f3","🇬🇵":"1f1ec-1f1f5","🇬🇶":"1f1ec-1f1f6","🇬🇷":"1f1ec-1f1f7","🇬🇸":"1f1ec-1f1f8","🇬🇹":"1f1ec-1f1f9","🇬🇺":"1f1ec-1f1fa","🇬🇼":"1f1ec-1f1fc","🇬🇾":"1f1ec-1f1fe","🇭🇰":"1f1ed-1f1f0","🇭🇲":"1f1ed-1f1f2","🇭🇳":"1f1ed-1f1f3","🇭🇷":"1f1ed-1f1f7","🇭🇹":"1f1ed-1f1f9","🇭🇺":"1f1ed-1f1fa","🇮🇨":"1f1ee-1f1e8","🇮🇩":"1f1ee-1f1e9","🇮🇪":"1f1ee-1f1ea","🇮🇱":"1f1ee-1f1f1","🇮🇲":"1f1ee-1f1f2","🇮🇳":"1f1ee-1f1f3","🇮🇴":"1f1ee-1f1f4","🇮🇶":"1f1ee-1f1f6","🇮🇷":"1f1ee-1f1f7","🇮🇸":"1f1ee-1f1f8","🇮🇹":"1f1ee-1f1f9","🇯🇪":"1f1ef-1f1ea","🇯🇲":"1f1ef-1f1f2","🇯🇴":"1f1ef-1f1f4","🇯🇵":"1f1ef-1f1f5","🇰🇪":"1f1f0-1f1ea","🇰🇬":"1f1f0-1f1ec","🇰🇭":"1f1f0-1f1ed","🇰🇮":"1f1f0-1f1ee","🇰🇲":"1f1f0-1f1f2","🇰🇳":"1f1f0-1f1f3","🇰🇵":"1f1f0-1f1f5","🇰🇷":"1f1f0-1f1f7","🇰🇼":"1f1f0-1f1fc","🇰🇾":"1f1f0-1f1fe","🇰🇿":"1f1f0-1f1ff","🇱🇦":"1f1f1-1f1e6","🇱🇧":"1f1f1-1f1e7","🇱🇨":"1f1f1-1f1e8","🇱🇮":"1f1f1-1f1ee","🇱🇰":"1f1f1-1f1f0","🇱🇷":"1f1f1-1f1f7","🇱🇸":"1f1f1-1f1f8","🇱🇹":"1f1f1-1f1f9","🇱🇺":"1f1f1-1f1fa","🇱🇻":"1f1f1-1f1fb","🇱🇾":"1f1f1-1f1fe","🇲🇦":"1f1f2-1f1e6","🇲🇨":"1f1f2-1f1e8","🇲🇩":"1f1f2-1f1e9","🇲🇪":"1f1f2-1f1ea","🇲🇫":"1f1f2-1f1eb","🇲🇬":"1f1f2-1f1ec","🇲🇭":"1f1f2-1f1ed","🇲🇰":"1f1f2-1f1f0","🇲🇱":"1f1f2-1f1f1","🇲🇲":"1f1f2-1f1f2","🇲🇳":"1f1f2-1f1f3","🇲🇴":"1f1f2-1f1f4","🇲🇵":"1f1f2-1f1f5","🇲🇶":"1f1f2-1f1f6","🇲🇷":"1f1f2-1f1f7","🇲🇸":"1f1f2-1f1f8","🇲🇹":"1f1f2-1f1f9","🇲🇺":"1f1f2-1f1fa","🇲🇻":"1f1f2-1f1fb","🇲🇼":"1f1f2-1f1fc","🇲🇽":"1f1f2-1f1fd","🇲🇾":"1f1f2-1f1fe","🇲🇿":"1f1f2-1f1ff","🇳🇦":"1f1f3-1f1e6","🇳🇨":"1f1f3-1f1e8","🇳🇪":"1f1f3-1f1ea","🇳🇫":"1f1f3-1f1eb","🇳🇬":"1f1f3-1f1ec","🇳🇮":"1f1f3-1f1ee","🇳🇱":"1f1f3-1f1f1","🇳🇴":"1f1f3-1f1f4","🇳🇵":"1f1f3-1f1f5","🇳🇷":"1f1f3-1f1f7","🇳🇺":"1f1f3-1f1fa","🇳🇿":"1f1f3-1f1ff","🇴🇲":"1f1f4-1f1f2","🇵🇦":"1f1f5-1f1e6","🇵🇪":"1f1f5-1f1ea","🇵🇫":"1f1f5-1f1eb","🇵🇬":"1f1f5-1f1ec","🇵🇭":"1f1f5-1f1ed","🇵🇰":"1f1f5-1f1f0","🇵🇱":"1f1f5-1f1f1","🇵🇲":"1f1f5-1f1f2","🇵🇳":"1f1f5-1f1f3","🇵🇷":"1f1f5-1f1f7","🇵🇸":"1f1f5-1f1f8","🇵🇹":"1f1f5-1f1f9","🇵🇼":"1f1f5-1f1fc","🇵🇾":"1f1f5-1f1fe","🇶🇦":"1f1f6-1f1e6","🇷🇪":"1f1f7-1f1ea","🇷🇴":"1f1f7-1f1f4","🇷🇸":"1f1f7-1f1f8","🇷🇺":"1f1f7-1f1fa","🇷🇼":"1f1f7-1f1fc","🇸🇦":"1f1f8-1f1e6","🇸🇧":"1f1f8-1f1e7","🇸🇨":"1f1f8-1f1e8","🇸🇩":"1f1f8-1f1e9","🇸🇪":"1f1f8-1f1ea","🇸🇬":"1f1f8-1f1ec","🇸🇭":"1f1f8-1f1ed","🇸🇮":"1f1f8-1f1ee","🇸🇯":"1f1f8-1f1ef","🇸🇰":"1f1f8-1f1f0","🇸🇱":"1f1f8-1f1f1","🇸🇲":"1f1f8-1f1f2","🇸🇳":"1f1f8-1f1f3","🇸🇴":"1f1f8-1f1f4","🇸🇷":"1f1f8-1f1f7","🇸🇸":"1f1f8-1f1f8","🇸🇹":"1f1f8-1f1f9","🇸🇻":"1f1f8-1f1fb","🇸🇽":"1f1f8-1f1fd","🇸🇾":"1f1f8-1f1fe","🇸🇿":"1f1f8-1f1ff","🇹🇦":"1f1f9-1f1e6","🇹🇨":"1f1f9-1f1e8","🇹🇩":"1f1f9-1f1e9","🇹🇫":"1f1f9-1f1eb","🇹🇬":"1f1f9-1f1ec","🇹🇭":"1f1f9-1f1ed","🇹🇯":"1f1f9-1f1ef","🇹🇰":"1f1f9-1f1f0","🇹🇱":"1f1f9-1f1f1","🇹🇲":"1f1f9-1f1f2","🇹🇳":"1f1f9-1f1f3","🇹🇴":"1f1f9-1f1f4","🇹🇷":"1f1f9-1f1f7","🇹🇹":"1f1f9-1f1f9","🇹🇻":"1f1f9-1f1fb","🇹🇼":"1f1f9-1f1fc","🇹🇿":"1f1f9-1f1ff","🇺🇦":"1f1fa-1f1e6","🇺🇬":"1f1fa-1f1ec","🇺🇲":"1f1fa-1f1f2","🇺🇳":"1f1fa-1f1f3","🇺🇸":"1f1fa-1f1f8","🇺🇾":"1f1fa-1f1fe","🇺🇿":"1f1fa-1f1ff","🇻🇦":"1f1fb-1f1e6","🇻🇨":"1f1fb-1f1e8","🇻🇪":"1f1fb-1f1ea","🇻🇬":"1f1fb-1f1ec","🇻🇮":"1f1fb-1f1ee","🇻🇳":"1f1fb-1f1f3","🇻🇺":"1f1fb-1f1fa","🇼🇫":"1f1fc-1f1eb","🇼🇸":"1f1fc-1f1f8","🇽🇰":"1f1fd-1f1f0","🇾🇪":"1f1fe-1f1ea","🇾🇹":"1f1fe-1f1f9","🇿🇦":"1f1ff-1f1e6","🇿🇲":"1f1ff-1f1f2","🇿🇼":"1f1ff-1f1fc","👨‍⚕":"1f468-200d-2695-fe0f","👩‍⚕":"1f469-200d-2695-fe0f","👨‍🎓":"1f468-200d-1f393","👩‍🎓":"1f469-200d-1f393","👨‍🏫":"1f468-200d-1f3eb","👩‍🏫":"1f469-200d-1f3eb","👨‍⚖":"1f468-200d-2696-fe0f","👩‍⚖":"1f469-200d-2696-fe0f","👨‍🌾":"1f468-200d-1f33e","👩‍🌾":"1f469-200d-1f33e","👨‍🍳":"1f468-200d-1f373","👩‍🍳":"1f469-200d-1f373","👨‍🔧":"1f468-200d-1f527","👩‍🔧":"1f469-200d-1f527","👨‍🏭":"1f468-200d-1f3ed","👩‍🏭":"1f469-200d-1f3ed","👨‍💼":"1f468-200d-1f4bc","👩‍💼":"1f469-200d-1f4bc","👨‍🔬":"1f468-200d-1f52c","👩‍🔬":"1f469-200d-1f52c","👨‍💻":"1f468-200d-1f4bb","👩‍💻":"1f469-200d-1f4bb","👨‍🎤":"1f468-200d-1f3a4","👩‍🎤":"1f469-200d-1f3a4","👨‍🎨":"1f468-200d-1f3a8","👩‍🎨":"1f469-200d-1f3a8","👨‍✈":"1f468-200d-2708-fe0f","👩‍✈":"1f469-200d-2708-fe0f","👨‍🚀":"1f468-200d-1f680","👩‍🚀":"1f469-200d-1f680","👨‍🚒":"1f468-200d-1f692","👩‍🚒":"1f469-200d-1f692","👮‍♂":"1f46e-200d-2642-fe0f","👮‍♀":"1f46e-200d-2640-fe0f","🕵‍♂":"1f575-fe0f-200d-2642-fe0f","🕵‍♀":"1f575-fe0f-200d-2640-fe0f","💂‍♂":"1f482-200d-2642-fe0f","💂‍♀":"1f482-200d-2640-fe0f","👷‍♂":"1f477-200d-2642-fe0f","👷‍♀":"1f477-200d-2640-fe0f","👳‍♂":"1f473-200d-2642-fe0f","👳‍♀":"1f473-200d-2640-fe0f","👱‍♂":"1f471-200d-2642-fe0f","👱‍♀":"1f471-200d-2640-fe0f","🧙‍♀":"1f9d9-200d-2640-fe0f","🧙‍♂":"1f9d9-200d-2642-fe0f","🧚‍♀":"1f9da-200d-2640-fe0f","🧚‍♂":"1f9da-200d-2642-fe0f","🧛‍♀":"1f9db-200d-2640-fe0f","🧛‍♂":"1f9db-200d-2642-fe0f","🧜‍♀":"1f9dc-200d-2640-fe0f","🧜‍♂":"1f9dc-200d-2642-fe0f","🧝‍♀":"1f9dd-200d-2640-fe0f","🧝‍♂":"1f9dd-200d-2642-fe0f","🧞‍♀":"1f9de-200d-2640-fe0f","🧞‍♂":"1f9de-200d-2642-fe0f","🧟‍♀":"1f9df-200d-2640-fe0f","🧟‍♂":"1f9df-200d-2642-fe0f","🙍‍♂":"1f64d-200d-2642-fe0f","🙍‍♀":"1f64d-200d-2640-fe0f","🙎‍♂":"1f64e-200d-2642-fe0f","🙎‍♀":"1f64e-200d-2640-fe0f","🙅‍♂":"1f645-200d-2642-fe0f","🙅‍♀":"1f645-200d-2640-fe0f","🙆‍♂":"1f646-200d-2642-fe0f","🙆‍♀":"1f646-200d-2640-fe0f","💁‍♂":"1f481-200d-2642-fe0f","💁‍♀":"1f481-200d-2640-fe0f","🙋‍♂":"1f64b-200d-2642-fe0f","🙋‍♀":"1f64b-200d-2640-fe0f","🙇‍♂":"1f647-200d-2642-fe0f","🙇‍♀":"1f647-200d-2640-fe0f","🤦‍♂":"1f926-200d-2642-fe0f","🤦‍♀":"1f926-200d-2640-fe0f","🤷‍♂":"1f937-200d-2642-fe0f","🤷‍♀":"1f937-200d-2640-fe0f","💆‍♂":"1f486-200d-2642-fe0f","💆‍♀":"1f486-200d-2640-fe0f","💇‍♂":"1f487-200d-2642-fe0f","💇‍♀":"1f487-200d-2640-fe0f","🚶‍♂":"1f6b6-200d-2642-fe0f","🚶‍♀":"1f6b6-200d-2640-fe0f","🏃‍♂":"1f3c3-200d-2642-fe0f","🏃‍♀":"1f3c3-200d-2640-fe0f","👯‍♂":"1f46f-200d-2642-fe0f","👯‍♀":"1f46f-200d-2640-fe0f","🧖‍♀":"1f9d6-200d-2640-fe0f","🧖‍♂":"1f9d6-200d-2642-fe0f","🧗‍♀":"1f9d7-200d-2640-fe0f","🧗‍♂":"1f9d7-200d-2642-fe0f","🧘‍♀":"1f9d8-200d-2640-fe0f","🧘‍♂":"1f9d8-200d-2642-fe0f","🏌‍♂":"1f3cc-fe0f-200d-2642-fe0f","🏌‍♀":"1f3cc-fe0f-200d-2640-fe0f","🏄‍♂":"1f3c4-200d-2642-fe0f","🏄‍♀":"1f3c4-200d-2640-fe0f","🚣‍♂":"1f6a3-200d-2642-fe0f","🚣‍♀":"1f6a3-200d-2640-fe0f","🏊‍♂":"1f3ca-200d-2642-fe0f","🏊‍♀":"1f3ca-200d-2640-fe0f","⛹‍♂":"26f9-fe0f-200d-2642-fe0f","⛹‍♀":"26f9-fe0f-200d-2640-fe0f","🏋‍♂":"1f3cb-fe0f-200d-2642-fe0f","🏋‍♀":"1f3cb-fe0f-200d-2640-fe0f","🚴‍♂":"1f6b4-200d-2642-fe0f","🚴‍♀":"1f6b4-200d-2640-fe0f","🚵‍♂":"1f6b5-200d-2642-fe0f","🚵‍♀":"1f6b5-200d-2640-fe0f","🤸‍♂":"1f938-200d-2642-fe0f","🤸‍♀":"1f938-200d-2640-fe0f","🤼‍♂":"1f93c-200d-2642-fe0f","🤼‍♀":"1f93c-200d-2640-fe0f","🤽‍♂":"1f93d-200d-2642-fe0f","🤽‍♀":"1f93d-200d-2640-fe0f","🤾‍♂":"1f93e-200d-2642-fe0f","🤾‍♀":"1f93e-200d-2640-fe0f","🤹‍♂":"1f939-200d-2642-fe0f","🤹‍♀":"1f939-200d-2640-fe0f","👨‍👦":"1f468-200d-1f466","👨‍👧":"1f468-200d-1f467","👩‍👦":"1f469-200d-1f466","👩‍👧":"1f469-200d-1f467","👁‍🗨":"1f441-200d-1f5e8","#️⃣":"23-20e3","*️⃣":"2a-20e3","0️⃣":"30-20e3","1️⃣":"31-20e3","2️⃣":"32-20e3","3️⃣":"33-20e3","4️⃣":"34-20e3","5️⃣":"35-20e3","6️⃣":"36-20e3","7️⃣":"37-20e3","8️⃣":"38-20e3","9️⃣":"39-20e3","🏳‍🌈":"1f3f3-fe0f-200d-1f308","👨‍⚕️":"1f468-200d-2695-fe0f","👨🏻‍⚕":"1f468-1f3fb-200d-2695-fe0f","👨🏼‍⚕":"1f468-1f3fc-200d-2695-fe0f","👨🏽‍⚕":"1f468-1f3fd-200d-2695-fe0f","👨🏾‍⚕":"1f468-1f3fe-200d-2695-fe0f","👨🏿‍⚕":"1f468-1f3ff-200d-2695-fe0f","👩‍⚕️":"1f469-200d-2695-fe0f","👩🏻‍⚕":"1f469-1f3fb-200d-2695-fe0f","👩🏼‍⚕":"1f469-1f3fc-200d-2695-fe0f","👩🏽‍⚕":"1f469-1f3fd-200d-2695-fe0f","👩🏾‍⚕":"1f469-1f3fe-200d-2695-fe0f","👩🏿‍⚕":"1f469-1f3ff-200d-2695-fe0f","👨🏻‍🎓":"1f468-1f3fb-200d-1f393","👨🏼‍🎓":"1f468-1f3fc-200d-1f393","👨🏽‍🎓":"1f468-1f3fd-200d-1f393","👨🏾‍🎓":"1f468-1f3fe-200d-1f393","👨🏿‍🎓":"1f468-1f3ff-200d-1f393","👩🏻‍🎓":"1f469-1f3fb-200d-1f393","👩🏼‍🎓":"1f469-1f3fc-200d-1f393","👩🏽‍🎓":"1f469-1f3fd-200d-1f393","👩🏾‍🎓":"1f469-1f3fe-200d-1f393","👩🏿‍🎓":"1f469-1f3ff-200d-1f393","👨🏻‍🏫":"1f468-1f3fb-200d-1f3eb","👨🏼‍🏫":"1f468-1f3fc-200d-1f3eb","👨🏽‍🏫":"1f468-1f3fd-200d-1f3eb","👨🏾‍🏫":"1f468-1f3fe-200d-1f3eb","👨🏿‍🏫":"1f468-1f3ff-200d-1f3eb","👩🏻‍🏫":"1f469-1f3fb-200d-1f3eb","👩🏼‍🏫":"1f469-1f3fc-200d-1f3eb","👩🏽‍🏫":"1f469-1f3fd-200d-1f3eb","👩🏾‍🏫":"1f469-1f3fe-200d-1f3eb","👩🏿‍🏫":"1f469-1f3ff-200d-1f3eb","👨‍⚖️":"1f468-200d-2696-fe0f","👨🏻‍⚖":"1f468-1f3fb-200d-2696-fe0f","👨🏼‍⚖":"1f468-1f3fc-200d-2696-fe0f","👨🏽‍⚖":"1f468-1f3fd-200d-2696-fe0f","👨🏾‍⚖":"1f468-1f3fe-200d-2696-fe0f","👨🏿‍⚖":"1f468-1f3ff-200d-2696-fe0f","👩‍⚖️":"1f469-200d-2696-fe0f","👩🏻‍⚖":"1f469-1f3fb-200d-2696-fe0f","👩🏼‍⚖":"1f469-1f3fc-200d-2696-fe0f","👩🏽‍⚖":"1f469-1f3fd-200d-2696-fe0f","👩🏾‍⚖":"1f469-1f3fe-200d-2696-fe0f","👩🏿‍⚖":"1f469-1f3ff-200d-2696-fe0f","👨🏻‍🌾":"1f468-1f3fb-200d-1f33e","👨🏼‍🌾":"1f468-1f3fc-200d-1f33e","👨🏽‍🌾":"1f468-1f3fd-200d-1f33e","👨🏾‍🌾":"1f468-1f3fe-200d-1f33e","👨🏿‍🌾":"1f468-1f3ff-200d-1f33e","👩🏻‍🌾":"1f469-1f3fb-200d-1f33e","👩🏼‍🌾":"1f469-1f3fc-200d-1f33e","👩🏽‍🌾":"1f469-1f3fd-200d-1f33e","👩🏾‍🌾":"1f469-1f3fe-200d-1f33e","👩🏿‍🌾":"1f469-1f3ff-200d-1f33e","👨🏻‍🍳":"1f468-1f3fb-200d-1f373","👨🏼‍🍳":"1f468-1f3fc-200d-1f373","👨🏽‍🍳":"1f468-1f3fd-200d-1f373","👨🏾‍🍳":"1f468-1f3fe-200d-1f373","👨🏿‍🍳":"1f468-1f3ff-200d-1f373","👩🏻‍🍳":"1f469-1f3fb-200d-1f373","👩🏼‍🍳":"1f469-1f3fc-200d-1f373","👩🏽‍🍳":"1f469-1f3fd-200d-1f373","👩🏾‍🍳":"1f469-1f3fe-200d-1f373","👩🏿‍🍳":"1f469-1f3ff-200d-1f373","👨🏻‍🔧":"1f468-1f3fb-200d-1f527","👨🏼‍🔧":"1f468-1f3fc-200d-1f527","👨🏽‍🔧":"1f468-1f3fd-200d-1f527","👨🏾‍🔧":"1f468-1f3fe-200d-1f527","👨🏿‍🔧":"1f468-1f3ff-200d-1f527","👩🏻‍🔧":"1f469-1f3fb-200d-1f527","👩🏼‍🔧":"1f469-1f3fc-200d-1f527","👩🏽‍🔧":"1f469-1f3fd-200d-1f527","👩🏾‍🔧":"1f469-1f3fe-200d-1f527","👩🏿‍🔧":"1f469-1f3ff-200d-1f527","👨🏻‍🏭":"1f468-1f3fb-200d-1f3ed","👨🏼‍🏭":"1f468-1f3fc-200d-1f3ed","👨🏽‍🏭":"1f468-1f3fd-200d-1f3ed","👨🏾‍🏭":"1f468-1f3fe-200d-1f3ed","👨🏿‍🏭":"1f468-1f3ff-200d-1f3ed","👩🏻‍🏭":"1f469-1f3fb-200d-1f3ed","👩🏼‍🏭":"1f469-1f3fc-200d-1f3ed","👩🏽‍🏭":"1f469-1f3fd-200d-1f3ed","👩🏾‍🏭":"1f469-1f3fe-200d-1f3ed","👩🏿‍🏭":"1f469-1f3ff-200d-1f3ed","👨🏻‍💼":"1f468-1f3fb-200d-1f4bc","👨🏼‍💼":"1f468-1f3fc-200d-1f4bc","👨🏽‍💼":"1f468-1f3fd-200d-1f4bc","👨🏾‍💼":"1f468-1f3fe-200d-1f4bc","👨🏿‍💼":"1f468-1f3ff-200d-1f4bc","👩🏻‍💼":"1f469-1f3fb-200d-1f4bc","👩🏼‍💼":"1f469-1f3fc-200d-1f4bc","👩🏽‍💼":"1f469-1f3fd-200d-1f4bc","👩🏾‍💼":"1f469-1f3fe-200d-1f4bc","👩🏿‍💼":"1f469-1f3ff-200d-1f4bc","👨🏻‍🔬":"1f468-1f3fb-200d-1f52c","👨🏼‍🔬":"1f468-1f3fc-200d-1f52c","👨🏽‍🔬":"1f468-1f3fd-200d-1f52c","👨🏾‍🔬":"1f468-1f3fe-200d-1f52c","👨🏿‍🔬":"1f468-1f3ff-200d-1f52c","👩🏻‍🔬":"1f469-1f3fb-200d-1f52c","👩🏼‍🔬":"1f469-1f3fc-200d-1f52c","👩🏽‍🔬":"1f469-1f3fd-200d-1f52c","👩🏾‍🔬":"1f469-1f3fe-200d-1f52c","👩🏿‍🔬":"1f469-1f3ff-200d-1f52c","👨🏻‍💻":"1f468-1f3fb-200d-1f4bb","👨🏼‍💻":"1f468-1f3fc-200d-1f4bb","👨🏽‍💻":"1f468-1f3fd-200d-1f4bb","👨🏾‍💻":"1f468-1f3fe-200d-1f4bb","👨🏿‍💻":"1f468-1f3ff-200d-1f4bb","👩🏻‍💻":"1f469-1f3fb-200d-1f4bb","👩🏼‍💻":"1f469-1f3fc-200d-1f4bb","👩🏽‍💻":"1f469-1f3fd-200d-1f4bb","👩🏾‍💻":"1f469-1f3fe-200d-1f4bb","👩🏿‍💻":"1f469-1f3ff-200d-1f4bb","👨🏻‍🎤":"1f468-1f3fb-200d-1f3a4","👨🏼‍🎤":"1f468-1f3fc-200d-1f3a4","👨🏽‍🎤":"1f468-1f3fd-200d-1f3a4","👨🏾‍🎤":"1f468-1f3fe-200d-1f3a4","👨🏿‍🎤":"1f468-1f3ff-200d-1f3a4","👩🏻‍🎤":"1f469-1f3fb-200d-1f3a4","👩🏼‍🎤":"1f469-1f3fc-200d-1f3a4","👩🏽‍🎤":"1f469-1f3fd-200d-1f3a4","👩🏾‍🎤":"1f469-1f3fe-200d-1f3a4","👩🏿‍🎤":"1f469-1f3ff-200d-1f3a4","👨🏻‍🎨":"1f468-1f3fb-200d-1f3a8","👨🏼‍🎨":"1f468-1f3fc-200d-1f3a8","👨🏽‍🎨":"1f468-1f3fd-200d-1f3a8","👨🏾‍🎨":"1f468-1f3fe-200d-1f3a8","👨🏿‍🎨":"1f468-1f3ff-200d-1f3a8","👩🏻‍🎨":"1f469-1f3fb-200d-1f3a8","👩🏼‍🎨":"1f469-1f3fc-200d-1f3a8","👩🏽‍🎨":"1f469-1f3fd-200d-1f3a8","👩🏾‍🎨":"1f469-1f3fe-200d-1f3a8","👩🏿‍🎨":"1f469-1f3ff-200d-1f3a8","👨‍✈️":"1f468-200d-2708-fe0f","👨🏻‍✈":"1f468-1f3fb-200d-2708-fe0f","👨🏼‍✈":"1f468-1f3fc-200d-2708-fe0f","👨🏽‍✈":"1f468-1f3fd-200d-2708-fe0f","👨🏾‍✈":"1f468-1f3fe-200d-2708-fe0f","👨🏿‍✈":"1f468-1f3ff-200d-2708-fe0f","👩‍✈️":"1f469-200d-2708-fe0f","👩🏻‍✈":"1f469-1f3fb-200d-2708-fe0f","👩🏼‍✈":"1f469-1f3fc-200d-2708-fe0f","👩🏽‍✈":"1f469-1f3fd-200d-2708-fe0f","👩🏾‍✈":"1f469-1f3fe-200d-2708-fe0f","👩🏿‍✈":"1f469-1f3ff-200d-2708-fe0f","👨🏻‍🚀":"1f468-1f3fb-200d-1f680","👨🏼‍🚀":"1f468-1f3fc-200d-1f680","👨🏽‍🚀":"1f468-1f3fd-200d-1f680","👨🏾‍🚀":"1f468-1f3fe-200d-1f680","👨🏿‍🚀":"1f468-1f3ff-200d-1f680","👩🏻‍🚀":"1f469-1f3fb-200d-1f680","👩🏼‍🚀":"1f469-1f3fc-200d-1f680","👩🏽‍🚀":"1f469-1f3fd-200d-1f680","👩🏾‍🚀":"1f469-1f3fe-200d-1f680","👩🏿‍🚀":"1f469-1f3ff-200d-1f680","👨🏻‍🚒":"1f468-1f3fb-200d-1f692","👨🏼‍🚒":"1f468-1f3fc-200d-1f692","👨🏽‍🚒":"1f468-1f3fd-200d-1f692","👨🏾‍🚒":"1f468-1f3fe-200d-1f692","👨🏿‍🚒":"1f468-1f3ff-200d-1f692","👩🏻‍🚒":"1f469-1f3fb-200d-1f692","👩🏼‍🚒":"1f469-1f3fc-200d-1f692","👩🏽‍🚒":"1f469-1f3fd-200d-1f692","👩🏾‍🚒":"1f469-1f3fe-200d-1f692","👩🏿‍🚒":"1f469-1f3ff-200d-1f692","👮‍♂️":"1f46e-200d-2642-fe0f","👮🏻‍♂":"1f46e-1f3fb-200d-2642-fe0f","👮🏼‍♂":"1f46e-1f3fc-200d-2642-fe0f","👮🏽‍♂":"1f46e-1f3fd-200d-2642-fe0f","👮🏾‍♂":"1f46e-1f3fe-200d-2642-fe0f","👮🏿‍♂":"1f46e-1f3ff-200d-2642-fe0f","👮‍♀️":"1f46e-200d-2640-fe0f","👮🏻‍♀":"1f46e-1f3fb-200d-2640-fe0f","👮🏼‍♀":"1f46e-1f3fc-200d-2640-fe0f","👮🏽‍♀":"1f46e-1f3fd-200d-2640-fe0f","👮🏾‍♀":"1f46e-1f3fe-200d-2640-fe0f","👮🏿‍♀":"1f46e-1f3ff-200d-2640-fe0f","🕵‍♂️":"1f575-fe0f-200d-2642-fe0f","🕵️‍♂":"1f575-fe0f-200d-2642-fe0f","🕵🏻‍♂":"1f575-1f3fb-200d-2642-fe0f","🕵🏼‍♂":"1f575-1f3fc-200d-2642-fe0f","🕵🏽‍♂":"1f575-1f3fd-200d-2642-fe0f","🕵🏾‍♂":"1f575-1f3fe-200d-2642-fe0f","🕵🏿‍♂":"1f575-1f3ff-200d-2642-fe0f","🕵‍♀️":"1f575-fe0f-200d-2640-fe0f","🕵️‍♀":"1f575-fe0f-200d-2640-fe0f","🕵🏻‍♀":"1f575-1f3fb-200d-2640-fe0f","🕵🏼‍♀":"1f575-1f3fc-200d-2640-fe0f","🕵🏽‍♀":"1f575-1f3fd-200d-2640-fe0f","🕵🏾‍♀":"1f575-1f3fe-200d-2640-fe0f","🕵🏿‍♀":"1f575-1f3ff-200d-2640-fe0f","💂‍♂️":"1f482-200d-2642-fe0f","💂🏻‍♂":"1f482-1f3fb-200d-2642-fe0f","💂🏼‍♂":"1f482-1f3fc-200d-2642-fe0f","💂🏽‍♂":"1f482-1f3fd-200d-2642-fe0f","💂🏾‍♂":"1f482-1f3fe-200d-2642-fe0f","💂🏿‍♂":"1f482-1f3ff-200d-2642-fe0f","💂‍♀️":"1f482-200d-2640-fe0f","💂🏻‍♀":"1f482-1f3fb-200d-2640-fe0f","💂🏼‍♀":"1f482-1f3fc-200d-2640-fe0f","💂🏽‍♀":"1f482-1f3fd-200d-2640-fe0f","💂🏾‍♀":"1f482-1f3fe-200d-2640-fe0f","💂🏿‍♀":"1f482-1f3ff-200d-2640-fe0f","👷‍♂️":"1f477-200d-2642-fe0f","👷🏻‍♂":"1f477-1f3fb-200d-2642-fe0f","👷🏼‍♂":"1f477-1f3fc-200d-2642-fe0f","👷🏽‍♂":"1f477-1f3fd-200d-2642-fe0f","👷🏾‍♂":"1f477-1f3fe-200d-2642-fe0f","👷🏿‍♂":"1f477-1f3ff-200d-2642-fe0f","👷‍♀️":"1f477-200d-2640-fe0f","👷🏻‍♀":"1f477-1f3fb-200d-2640-fe0f","👷🏼‍♀":"1f477-1f3fc-200d-2640-fe0f","👷🏽‍♀":"1f477-1f3fd-200d-2640-fe0f","👷🏾‍♀":"1f477-1f3fe-200d-2640-fe0f","👷🏿‍♀":"1f477-1f3ff-200d-2640-fe0f","👳‍♂️":"1f473-200d-2642-fe0f","👳🏻‍♂":"1f473-1f3fb-200d-2642-fe0f","👳🏼‍♂":"1f473-1f3fc-200d-2642-fe0f","👳🏽‍♂":"1f473-1f3fd-200d-2642-fe0f","👳🏾‍♂":"1f473-1f3fe-200d-2642-fe0f","👳🏿‍♂":"1f473-1f3ff-200d-2642-fe0f","👳‍♀️":"1f473-200d-2640-fe0f","👳🏻‍♀":"1f473-1f3fb-200d-2640-fe0f","👳🏼‍♀":"1f473-1f3fc-200d-2640-fe0f","👳🏽‍♀":"1f473-1f3fd-200d-2640-fe0f","👳🏾‍♀":"1f473-1f3fe-200d-2640-fe0f","👳🏿‍♀":"1f473-1f3ff-200d-2640-fe0f","👱‍♂️":"1f471-200d-2642-fe0f","👱🏻‍♂":"1f471-1f3fb-200d-2642-fe0f","👱🏼‍♂":"1f471-1f3fc-200d-2642-fe0f","👱🏽‍♂":"1f471-1f3fd-200d-2642-fe0f","👱🏾‍♂":"1f471-1f3fe-200d-2642-fe0f","👱🏿‍♂":"1f471-1f3ff-200d-2642-fe0f","👱‍♀️":"1f471-200d-2640-fe0f","👱🏻‍♀":"1f471-1f3fb-200d-2640-fe0f","👱🏼‍♀":"1f471-1f3fc-200d-2640-fe0f","👱🏽‍♀":"1f471-1f3fd-200d-2640-fe0f","👱🏾‍♀":"1f471-1f3fe-200d-2640-fe0f","👱🏿‍♀":"1f471-1f3ff-200d-2640-fe0f","🧙‍♀️":"1f9d9-200d-2640-fe0f","🧙🏻‍♀":"1f9d9-1f3fb-200d-2640-fe0f","🧙🏼‍♀":"1f9d9-1f3fc-200d-2640-fe0f","🧙🏽‍♀":"1f9d9-1f3fd-200d-2640-fe0f","🧙🏾‍♀":"1f9d9-1f3fe-200d-2640-fe0f","🧙🏿‍♀":"1f9d9-1f3ff-200d-2640-fe0f","🧙‍♂️":"1f9d9-200d-2642-fe0f","🧙🏻‍♂":"1f9d9-1f3fb-200d-2642-fe0f","🧙🏼‍♂":"1f9d9-1f3fc-200d-2642-fe0f","🧙🏽‍♂":"1f9d9-1f3fd-200d-2642-fe0f","🧙🏾‍♂":"1f9d9-1f3fe-200d-2642-fe0f","🧙🏿‍♂":"1f9d9-1f3ff-200d-2642-fe0f","🧚‍♀️":"1f9da-200d-2640-fe0f","🧚🏻‍♀":"1f9da-1f3fb-200d-2640-fe0f","🧚🏼‍♀":"1f9da-1f3fc-200d-2640-fe0f","🧚🏽‍♀":"1f9da-1f3fd-200d-2640-fe0f","🧚🏾‍♀":"1f9da-1f3fe-200d-2640-fe0f","🧚🏿‍♀":"1f9da-1f3ff-200d-2640-fe0f","🧚‍♂️":"1f9da-200d-2642-fe0f","🧚🏻‍♂":"1f9da-1f3fb-200d-2642-fe0f","🧚🏼‍♂":"1f9da-1f3fc-200d-2642-fe0f","🧚🏽‍♂":"1f9da-1f3fd-200d-2642-fe0f","🧚🏾‍♂":"1f9da-1f3fe-200d-2642-fe0f","🧚🏿‍♂":"1f9da-1f3ff-200d-2642-fe0f","🧛‍♀️":"1f9db-200d-2640-fe0f","🧛🏻‍♀":"1f9db-1f3fb-200d-2640-fe0f","🧛🏼‍♀":"1f9db-1f3fc-200d-2640-fe0f","🧛🏽‍♀":"1f9db-1f3fd-200d-2640-fe0f","🧛🏾‍♀":"1f9db-1f3fe-200d-2640-fe0f","🧛🏿‍♀":"1f9db-1f3ff-200d-2640-fe0f","🧛‍♂️":"1f9db-200d-2642-fe0f","🧛🏻‍♂":"1f9db-1f3fb-200d-2642-fe0f","🧛🏼‍♂":"1f9db-1f3fc-200d-2642-fe0f","🧛🏽‍♂":"1f9db-1f3fd-200d-2642-fe0f","🧛🏾‍♂":"1f9db-1f3fe-200d-2642-fe0f","🧛🏿‍♂":"1f9db-1f3ff-200d-2642-fe0f","🧜‍♀️":"1f9dc-200d-2640-fe0f","🧜🏻‍♀":"1f9dc-1f3fb-200d-2640-fe0f","🧜🏼‍♀":"1f9dc-1f3fc-200d-2640-fe0f","🧜🏽‍♀":"1f9dc-1f3fd-200d-2640-fe0f","🧜🏾‍♀":"1f9dc-1f3fe-200d-2640-fe0f","🧜🏿‍♀":"1f9dc-1f3ff-200d-2640-fe0f","🧜‍♂️":"1f9dc-200d-2642-fe0f","🧜🏻‍♂":"1f9dc-1f3fb-200d-2642-fe0f","🧜🏼‍♂":"1f9dc-1f3fc-200d-2642-fe0f","🧜🏽‍♂":"1f9dc-1f3fd-200d-2642-fe0f","🧜🏾‍♂":"1f9dc-1f3fe-200d-2642-fe0f","🧜🏿‍♂":"1f9dc-1f3ff-200d-2642-fe0f","🧝‍♀️":"1f9dd-200d-2640-fe0f","🧝🏻‍♀":"1f9dd-1f3fb-200d-2640-fe0f","🧝🏼‍♀":"1f9dd-1f3fc-200d-2640-fe0f","🧝🏽‍♀":"1f9dd-1f3fd-200d-2640-fe0f","🧝🏾‍♀":"1f9dd-1f3fe-200d-2640-fe0f","🧝🏿‍♀":"1f9dd-1f3ff-200d-2640-fe0f","🧝‍♂️":"1f9dd-200d-2642-fe0f","🧝🏻‍♂":"1f9dd-1f3fb-200d-2642-fe0f","🧝🏼‍♂":"1f9dd-1f3fc-200d-2642-fe0f","🧝🏽‍♂":"1f9dd-1f3fd-200d-2642-fe0f","🧝🏾‍♂":"1f9dd-1f3fe-200d-2642-fe0f","🧝🏿‍♂":"1f9dd-1f3ff-200d-2642-fe0f","🧞‍♀️":"1f9de-200d-2640-fe0f","🧞‍♂️":"1f9de-200d-2642-fe0f","🧟‍♀️":"1f9df-200d-2640-fe0f","🧟‍♂️":"1f9df-200d-2642-fe0f","🙍‍♂️":"1f64d-200d-2642-fe0f","🙍🏻‍♂":"1f64d-1f3fb-200d-2642-fe0f","🙍🏼‍♂":"1f64d-1f3fc-200d-2642-fe0f","🙍🏽‍♂":"1f64d-1f3fd-200d-2642-fe0f","🙍🏾‍♂":"1f64d-1f3fe-200d-2642-fe0f","🙍🏿‍♂":"1f64d-1f3ff-200d-2642-fe0f","🙍‍♀️":"1f64d-200d-2640-fe0f","🙍🏻‍♀":"1f64d-1f3fb-200d-2640-fe0f","🙍🏼‍♀":"1f64d-1f3fc-200d-2640-fe0f","🙍🏽‍♀":"1f64d-1f3fd-200d-2640-fe0f","🙍🏾‍♀":"1f64d-1f3fe-200d-2640-fe0f","🙍🏿‍♀":"1f64d-1f3ff-200d-2640-fe0f","🙎‍♂️":"1f64e-200d-2642-fe0f","🙎🏻‍♂":"1f64e-1f3fb-200d-2642-fe0f","🙎🏼‍♂":"1f64e-1f3fc-200d-2642-fe0f","🙎🏽‍♂":"1f64e-1f3fd-200d-2642-fe0f","🙎🏾‍♂":"1f64e-1f3fe-200d-2642-fe0f","🙎🏿‍♂":"1f64e-1f3ff-200d-2642-fe0f","🙎‍♀️":"1f64e-200d-2640-fe0f","🙎🏻‍♀":"1f64e-1f3fb-200d-2640-fe0f","🙎🏼‍♀":"1f64e-1f3fc-200d-2640-fe0f","🙎🏽‍♀":"1f64e-1f3fd-200d-2640-fe0f","🙎🏾‍♀":"1f64e-1f3fe-200d-2640-fe0f","🙎🏿‍♀":"1f64e-1f3ff-200d-2640-fe0f","🙅‍♂️":"1f645-200d-2642-fe0f","🙅🏻‍♂":"1f645-1f3fb-200d-2642-fe0f","🙅🏼‍♂":"1f645-1f3fc-200d-2642-fe0f","🙅🏽‍♂":"1f645-1f3fd-200d-2642-fe0f","🙅🏾‍♂":"1f645-1f3fe-200d-2642-fe0f","🙅🏿‍♂":"1f645-1f3ff-200d-2642-fe0f","🙅‍♀️":"1f645-200d-2640-fe0f","🙅🏻‍♀":"1f645-1f3fb-200d-2640-fe0f","🙅🏼‍♀":"1f645-1f3fc-200d-2640-fe0f","🙅🏽‍♀":"1f645-1f3fd-200d-2640-fe0f","🙅🏾‍♀":"1f645-1f3fe-200d-2640-fe0f","🙅🏿‍♀":"1f645-1f3ff-200d-2640-fe0f","🙆‍♂️":"1f646-200d-2642-fe0f","🙆🏻‍♂":"1f646-1f3fb-200d-2642-fe0f","🙆🏼‍♂":"1f646-1f3fc-200d-2642-fe0f","🙆🏽‍♂":"1f646-1f3fd-200d-2642-fe0f","🙆🏾‍♂":"1f646-1f3fe-200d-2642-fe0f","🙆🏿‍♂":"1f646-1f3ff-200d-2642-fe0f","🙆‍♀️":"1f646-200d-2640-fe0f","🙆🏻‍♀":"1f646-1f3fb-200d-2640-fe0f","🙆🏼‍♀":"1f646-1f3fc-200d-2640-fe0f","🙆🏽‍♀":"1f646-1f3fd-200d-2640-fe0f","🙆🏾‍♀":"1f646-1f3fe-200d-2640-fe0f","🙆🏿‍♀":"1f646-1f3ff-200d-2640-fe0f","💁‍♂️":"1f481-200d-2642-fe0f","💁🏻‍♂":"1f481-1f3fb-200d-2642-fe0f","💁🏼‍♂":"1f481-1f3fc-200d-2642-fe0f","💁🏽‍♂":"1f481-1f3fd-200d-2642-fe0f","💁🏾‍♂":"1f481-1f3fe-200d-2642-fe0f","💁🏿‍♂":"1f481-1f3ff-200d-2642-fe0f","💁‍♀️":"1f481-200d-2640-fe0f","💁🏻‍♀":"1f481-1f3fb-200d-2640-fe0f","💁🏼‍♀":"1f481-1f3fc-200d-2640-fe0f","💁🏽‍♀":"1f481-1f3fd-200d-2640-fe0f","💁🏾‍♀":"1f481-1f3fe-200d-2640-fe0f","💁🏿‍♀":"1f481-1f3ff-200d-2640-fe0f","🙋‍♂️":"1f64b-200d-2642-fe0f","🙋🏻‍♂":"1f64b-1f3fb-200d-2642-fe0f","🙋🏼‍♂":"1f64b-1f3fc-200d-2642-fe0f","🙋🏽‍♂":"1f64b-1f3fd-200d-2642-fe0f","🙋🏾‍♂":"1f64b-1f3fe-200d-2642-fe0f","🙋🏿‍♂":"1f64b-1f3ff-200d-2642-fe0f","🙋‍♀️":"1f64b-200d-2640-fe0f","🙋🏻‍♀":"1f64b-1f3fb-200d-2640-fe0f","🙋🏼‍♀":"1f64b-1f3fc-200d-2640-fe0f","🙋🏽‍♀":"1f64b-1f3fd-200d-2640-fe0f","🙋🏾‍♀":"1f64b-1f3fe-200d-2640-fe0f","🙋🏿‍♀":"1f64b-1f3ff-200d-2640-fe0f","🙇‍♂️":"1f647-200d-2642-fe0f","🙇🏻‍♂":"1f647-1f3fb-200d-2642-fe0f","🙇🏼‍♂":"1f647-1f3fc-200d-2642-fe0f","🙇🏽‍♂":"1f647-1f3fd-200d-2642-fe0f","🙇🏾‍♂":"1f647-1f3fe-200d-2642-fe0f","🙇🏿‍♂":"1f647-1f3ff-200d-2642-fe0f","🙇‍♀️":"1f647-200d-2640-fe0f","🙇🏻‍♀":"1f647-1f3fb-200d-2640-fe0f","🙇🏼‍♀":"1f647-1f3fc-200d-2640-fe0f","🙇🏽‍♀":"1f647-1f3fd-200d-2640-fe0f","🙇🏾‍♀":"1f647-1f3fe-200d-2640-fe0f","🙇🏿‍♀":"1f647-1f3ff-200d-2640-fe0f","🤦‍♂️":"1f926-200d-2642-fe0f","🤦🏻‍♂":"1f926-1f3fb-200d-2642-fe0f","🤦🏼‍♂":"1f926-1f3fc-200d-2642-fe0f","🤦🏽‍♂":"1f926-1f3fd-200d-2642-fe0f","🤦🏾‍♂":"1f926-1f3fe-200d-2642-fe0f","🤦🏿‍♂":"1f926-1f3ff-200d-2642-fe0f","🤦‍♀️":"1f926-200d-2640-fe0f","🤦🏻‍♀":"1f926-1f3fb-200d-2640-fe0f","🤦🏼‍♀":"1f926-1f3fc-200d-2640-fe0f","🤦🏽‍♀":"1f926-1f3fd-200d-2640-fe0f","🤦🏾‍♀":"1f926-1f3fe-200d-2640-fe0f","🤦🏿‍♀":"1f926-1f3ff-200d-2640-fe0f","🤷‍♂️":"1f937-200d-2642-fe0f","🤷🏻‍♂":"1f937-1f3fb-200d-2642-fe0f","🤷🏼‍♂":"1f937-1f3fc-200d-2642-fe0f","🤷🏽‍♂":"1f937-1f3fd-200d-2642-fe0f","🤷🏾‍♂":"1f937-1f3fe-200d-2642-fe0f","🤷🏿‍♂":"1f937-1f3ff-200d-2642-fe0f","🤷‍♀️":"1f937-200d-2640-fe0f","🤷🏻‍♀":"1f937-1f3fb-200d-2640-fe0f","🤷🏼‍♀":"1f937-1f3fc-200d-2640-fe0f","🤷🏽‍♀":"1f937-1f3fd-200d-2640-fe0f","🤷🏾‍♀":"1f937-1f3fe-200d-2640-fe0f","🤷🏿‍♀":"1f937-1f3ff-200d-2640-fe0f","💆‍♂️":"1f486-200d-2642-fe0f","💆🏻‍♂":"1f486-1f3fb-200d-2642-fe0f","💆🏼‍♂":"1f486-1f3fc-200d-2642-fe0f","💆🏽‍♂":"1f486-1f3fd-200d-2642-fe0f","💆🏾‍♂":"1f486-1f3fe-200d-2642-fe0f","💆🏿‍♂":"1f486-1f3ff-200d-2642-fe0f","💆‍♀️":"1f486-200d-2640-fe0f","💆🏻‍♀":"1f486-1f3fb-200d-2640-fe0f","💆🏼‍♀":"1f486-1f3fc-200d-2640-fe0f","💆🏽‍♀":"1f486-1f3fd-200d-2640-fe0f","💆🏾‍♀":"1f486-1f3fe-200d-2640-fe0f","💆🏿‍♀":"1f486-1f3ff-200d-2640-fe0f","💇‍♂️":"1f487-200d-2642-fe0f","💇🏻‍♂":"1f487-1f3fb-200d-2642-fe0f","💇🏼‍♂":"1f487-1f3fc-200d-2642-fe0f","💇🏽‍♂":"1f487-1f3fd-200d-2642-fe0f","💇🏾‍♂":"1f487-1f3fe-200d-2642-fe0f","💇🏿‍♂":"1f487-1f3ff-200d-2642-fe0f","💇‍♀️":"1f487-200d-2640-fe0f","💇🏻‍♀":"1f487-1f3fb-200d-2640-fe0f","💇🏼‍♀":"1f487-1f3fc-200d-2640-fe0f","💇🏽‍♀":"1f487-1f3fd-200d-2640-fe0f","💇🏾‍♀":"1f487-1f3fe-200d-2640-fe0f","💇🏿‍♀":"1f487-1f3ff-200d-2640-fe0f","🚶‍♂️":"1f6b6-200d-2642-fe0f","🚶🏻‍♂":"1f6b6-1f3fb-200d-2642-fe0f","🚶🏼‍♂":"1f6b6-1f3fc-200d-2642-fe0f","🚶🏽‍♂":"1f6b6-1f3fd-200d-2642-fe0f","🚶🏾‍♂":"1f6b6-1f3fe-200d-2642-fe0f","🚶🏿‍♂":"1f6b6-1f3ff-200d-2642-fe0f","🚶‍♀️":"1f6b6-200d-2640-fe0f","🚶🏻‍♀":"1f6b6-1f3fb-200d-2640-fe0f","🚶🏼‍♀":"1f6b6-1f3fc-200d-2640-fe0f","🚶🏽‍♀":"1f6b6-1f3fd-200d-2640-fe0f","🚶🏾‍♀":"1f6b6-1f3fe-200d-2640-fe0f","🚶🏿‍♀":"1f6b6-1f3ff-200d-2640-fe0f","🏃‍♂️":"1f3c3-200d-2642-fe0f","🏃🏻‍♂":"1f3c3-1f3fb-200d-2642-fe0f","🏃🏼‍♂":"1f3c3-1f3fc-200d-2642-fe0f","🏃🏽‍♂":"1f3c3-1f3fd-200d-2642-fe0f","🏃🏾‍♂":"1f3c3-1f3fe-200d-2642-fe0f","🏃🏿‍♂":"1f3c3-1f3ff-200d-2642-fe0f","🏃‍♀️":"1f3c3-200d-2640-fe0f","🏃🏻‍♀":"1f3c3-1f3fb-200d-2640-fe0f","🏃🏼‍♀":"1f3c3-1f3fc-200d-2640-fe0f","🏃🏽‍♀":"1f3c3-1f3fd-200d-2640-fe0f","🏃🏾‍♀":"1f3c3-1f3fe-200d-2640-fe0f","🏃🏿‍♀":"1f3c3-1f3ff-200d-2640-fe0f","👯‍♂️":"1f46f-200d-2642-fe0f","👯‍♀️":"1f46f-200d-2640-fe0f","🧖‍♀️":"1f9d6-200d-2640-fe0f","🧖🏻‍♀":"1f9d6-1f3fb-200d-2640-fe0f","🧖🏼‍♀":"1f9d6-1f3fc-200d-2640-fe0f","🧖🏽‍♀":"1f9d6-1f3fd-200d-2640-fe0f","🧖🏾‍♀":"1f9d6-1f3fe-200d-2640-fe0f","🧖🏿‍♀":"1f9d6-1f3ff-200d-2640-fe0f","🧖‍♂️":"1f9d6-200d-2642-fe0f","🧖🏻‍♂":"1f9d6-1f3fb-200d-2642-fe0f","🧖🏼‍♂":"1f9d6-1f3fc-200d-2642-fe0f","🧖🏽‍♂":"1f9d6-1f3fd-200d-2642-fe0f","🧖🏾‍♂":"1f9d6-1f3fe-200d-2642-fe0f","🧖🏿‍♂":"1f9d6-1f3ff-200d-2642-fe0f","🧗‍♀️":"1f9d7-200d-2640-fe0f","🧗🏻‍♀":"1f9d7-1f3fb-200d-2640-fe0f","🧗🏼‍♀":"1f9d7-1f3fc-200d-2640-fe0f","🧗🏽‍♀":"1f9d7-1f3fd-200d-2640-fe0f","🧗🏾‍♀":"1f9d7-1f3fe-200d-2640-fe0f","🧗🏿‍♀":"1f9d7-1f3ff-200d-2640-fe0f","🧗‍♂️":"1f9d7-200d-2642-fe0f","🧗🏻‍♂":"1f9d7-1f3fb-200d-2642-fe0f","🧗🏼‍♂":"1f9d7-1f3fc-200d-2642-fe0f","🧗🏽‍♂":"1f9d7-1f3fd-200d-2642-fe0f","🧗🏾‍♂":"1f9d7-1f3fe-200d-2642-fe0f","🧗🏿‍♂":"1f9d7-1f3ff-200d-2642-fe0f","🧘‍♀️":"1f9d8-200d-2640-fe0f","🧘🏻‍♀":"1f9d8-1f3fb-200d-2640-fe0f","🧘🏼‍♀":"1f9d8-1f3fc-200d-2640-fe0f","🧘🏽‍♀":"1f9d8-1f3fd-200d-2640-fe0f","🧘🏾‍♀":"1f9d8-1f3fe-200d-2640-fe0f","🧘🏿‍♀":"1f9d8-1f3ff-200d-2640-fe0f","🧘‍♂️":"1f9d8-200d-2642-fe0f","🧘🏻‍♂":"1f9d8-1f3fb-200d-2642-fe0f","🧘🏼‍♂":"1f9d8-1f3fc-200d-2642-fe0f","🧘🏽‍♂":"1f9d8-1f3fd-200d-2642-fe0f","🧘🏾‍♂":"1f9d8-1f3fe-200d-2642-fe0f","🧘🏿‍♂":"1f9d8-1f3ff-200d-2642-fe0f","🏌‍♂️":"1f3cc-fe0f-200d-2642-fe0f","🏌️‍♂":"1f3cc-fe0f-200d-2642-fe0f","🏌🏻‍♂":"1f3cc-1f3fb-200d-2642-fe0f","🏌🏼‍♂":"1f3cc-1f3fc-200d-2642-fe0f","🏌🏽‍♂":"1f3cc-1f3fd-200d-2642-fe0f","🏌🏾‍♂":"1f3cc-1f3fe-200d-2642-fe0f","🏌🏿‍♂":"1f3cc-1f3ff-200d-2642-fe0f","🏌‍♀️":"1f3cc-fe0f-200d-2640-fe0f","🏌️‍♀":"1f3cc-fe0f-200d-2640-fe0f","🏌🏻‍♀":"1f3cc-1f3fb-200d-2640-fe0f","🏌🏼‍♀":"1f3cc-1f3fc-200d-2640-fe0f","🏌🏽‍♀":"1f3cc-1f3fd-200d-2640-fe0f","🏌🏾‍♀":"1f3cc-1f3fe-200d-2640-fe0f","🏌🏿‍♀":"1f3cc-1f3ff-200d-2640-fe0f","🏄‍♂️":"1f3c4-200d-2642-fe0f","🏄🏻‍♂":"1f3c4-1f3fb-200d-2642-fe0f","🏄🏼‍♂":"1f3c4-1f3fc-200d-2642-fe0f","🏄🏽‍♂":"1f3c4-1f3fd-200d-2642-fe0f","🏄🏾‍♂":"1f3c4-1f3fe-200d-2642-fe0f","🏄🏿‍♂":"1f3c4-1f3ff-200d-2642-fe0f","🏄‍♀️":"1f3c4-200d-2640-fe0f","🏄🏻‍♀":"1f3c4-1f3fb-200d-2640-fe0f","🏄🏼‍♀":"1f3c4-1f3fc-200d-2640-fe0f","🏄🏽‍♀":"1f3c4-1f3fd-200d-2640-fe0f","🏄🏾‍♀":"1f3c4-1f3fe-200d-2640-fe0f","🏄🏿‍♀":"1f3c4-1f3ff-200d-2640-fe0f","🚣‍♂️":"1f6a3-200d-2642-fe0f","🚣🏻‍♂":"1f6a3-1f3fb-200d-2642-fe0f","🚣🏼‍♂":"1f6a3-1f3fc-200d-2642-fe0f","🚣🏽‍♂":"1f6a3-1f3fd-200d-2642-fe0f","🚣🏾‍♂":"1f6a3-1f3fe-200d-2642-fe0f","🚣🏿‍♂":"1f6a3-1f3ff-200d-2642-fe0f","🚣‍♀️":"1f6a3-200d-2640-fe0f","🚣🏻‍♀":"1f6a3-1f3fb-200d-2640-fe0f","🚣🏼‍♀":"1f6a3-1f3fc-200d-2640-fe0f","🚣🏽‍♀":"1f6a3-1f3fd-200d-2640-fe0f","🚣🏾‍♀":"1f6a3-1f3fe-200d-2640-fe0f","🚣🏿‍♀":"1f6a3-1f3ff-200d-2640-fe0f","🏊‍♂️":"1f3ca-200d-2642-fe0f","🏊🏻‍♂":"1f3ca-1f3fb-200d-2642-fe0f","🏊🏼‍♂":"1f3ca-1f3fc-200d-2642-fe0f","🏊🏽‍♂":"1f3ca-1f3fd-200d-2642-fe0f","🏊🏾‍♂":"1f3ca-1f3fe-200d-2642-fe0f","🏊🏿‍♂":"1f3ca-1f3ff-200d-2642-fe0f","🏊‍♀️":"1f3ca-200d-2640-fe0f","🏊🏻‍♀":"1f3ca-1f3fb-200d-2640-fe0f","🏊🏼‍♀":"1f3ca-1f3fc-200d-2640-fe0f","🏊🏽‍♀":"1f3ca-1f3fd-200d-2640-fe0f","🏊🏾‍♀":"1f3ca-1f3fe-200d-2640-fe0f","🏊🏿‍♀":"1f3ca-1f3ff-200d-2640-fe0f","⛹‍♂️":"26f9-fe0f-200d-2642-fe0f","⛹️‍♂":"26f9-fe0f-200d-2642-fe0f","⛹🏻‍♂":"26f9-1f3fb-200d-2642-fe0f","⛹🏼‍♂":"26f9-1f3fc-200d-2642-fe0f","⛹🏽‍♂":"26f9-1f3fd-200d-2642-fe0f","⛹🏾‍♂":"26f9-1f3fe-200d-2642-fe0f","⛹🏿‍♂":"26f9-1f3ff-200d-2642-fe0f","⛹‍♀️":"26f9-fe0f-200d-2640-fe0f","⛹️‍♀":"26f9-fe0f-200d-2640-fe0f","⛹🏻‍♀":"26f9-1f3fb-200d-2640-fe0f","⛹🏼‍♀":"26f9-1f3fc-200d-2640-fe0f","⛹🏽‍♀":"26f9-1f3fd-200d-2640-fe0f","⛹🏾‍♀":"26f9-1f3fe-200d-2640-fe0f","⛹🏿‍♀":"26f9-1f3ff-200d-2640-fe0f","🏋‍♂️":"1f3cb-fe0f-200d-2642-fe0f","🏋️‍♂":"1f3cb-fe0f-200d-2642-fe0f","🏋🏻‍♂":"1f3cb-1f3fb-200d-2642-fe0f","🏋🏼‍♂":"1f3cb-1f3fc-200d-2642-fe0f","🏋🏽‍♂":"1f3cb-1f3fd-200d-2642-fe0f","🏋🏾‍♂":"1f3cb-1f3fe-200d-2642-fe0f","🏋🏿‍♂":"1f3cb-1f3ff-200d-2642-fe0f","🏋‍♀️":"1f3cb-fe0f-200d-2640-fe0f","🏋️‍♀":"1f3cb-fe0f-200d-2640-fe0f","🏋🏻‍♀":"1f3cb-1f3fb-200d-2640-fe0f","🏋🏼‍♀":"1f3cb-1f3fc-200d-2640-fe0f","🏋🏽‍♀":"1f3cb-1f3fd-200d-2640-fe0f","🏋🏾‍♀":"1f3cb-1f3fe-200d-2640-fe0f","🏋🏿‍♀":"1f3cb-1f3ff-200d-2640-fe0f","🚴‍♂️":"1f6b4-200d-2642-fe0f","🚴🏻‍♂":"1f6b4-1f3fb-200d-2642-fe0f","🚴🏼‍♂":"1f6b4-1f3fc-200d-2642-fe0f","🚴🏽‍♂":"1f6b4-1f3fd-200d-2642-fe0f","🚴🏾‍♂":"1f6b4-1f3fe-200d-2642-fe0f","🚴🏿‍♂":"1f6b4-1f3ff-200d-2642-fe0f","🚴‍♀️":"1f6b4-200d-2640-fe0f","🚴🏻‍♀":"1f6b4-1f3fb-200d-2640-fe0f","🚴🏼‍♀":"1f6b4-1f3fc-200d-2640-fe0f","🚴🏽‍♀":"1f6b4-1f3fd-200d-2640-fe0f","🚴🏾‍♀":"1f6b4-1f3fe-200d-2640-fe0f","🚴🏿‍♀":"1f6b4-1f3ff-200d-2640-fe0f","🚵‍♂️":"1f6b5-200d-2642-fe0f","🚵🏻‍♂":"1f6b5-1f3fb-200d-2642-fe0f","🚵🏼‍♂":"1f6b5-1f3fc-200d-2642-fe0f","🚵🏽‍♂":"1f6b5-1f3fd-200d-2642-fe0f","🚵🏾‍♂":"1f6b5-1f3fe-200d-2642-fe0f","🚵🏿‍♂":"1f6b5-1f3ff-200d-2642-fe0f","🚵‍♀️":"1f6b5-200d-2640-fe0f","🚵🏻‍♀":"1f6b5-1f3fb-200d-2640-fe0f","🚵🏼‍♀":"1f6b5-1f3fc-200d-2640-fe0f","🚵🏽‍♀":"1f6b5-1f3fd-200d-2640-fe0f","🚵🏾‍♀":"1f6b5-1f3fe-200d-2640-fe0f","🚵🏿‍♀":"1f6b5-1f3ff-200d-2640-fe0f","🤸‍♂️":"1f938-200d-2642-fe0f","🤸🏻‍♂":"1f938-1f3fb-200d-2642-fe0f","🤸🏼‍♂":"1f938-1f3fc-200d-2642-fe0f","🤸🏽‍♂":"1f938-1f3fd-200d-2642-fe0f","🤸🏾‍♂":"1f938-1f3fe-200d-2642-fe0f","🤸🏿‍♂":"1f938-1f3ff-200d-2642-fe0f","🤸‍♀️":"1f938-200d-2640-fe0f","🤸🏻‍♀":"1f938-1f3fb-200d-2640-fe0f","🤸🏼‍♀":"1f938-1f3fc-200d-2640-fe0f","🤸🏽‍♀":"1f938-1f3fd-200d-2640-fe0f","🤸🏾‍♀":"1f938-1f3fe-200d-2640-fe0f","🤸🏿‍♀":"1f938-1f3ff-200d-2640-fe0f","🤼‍♂️":"1f93c-200d-2642-fe0f","🤼‍♀️":"1f93c-200d-2640-fe0f","🤽‍♂️":"1f93d-200d-2642-fe0f","🤽🏻‍♂":"1f93d-1f3fb-200d-2642-fe0f","🤽🏼‍♂":"1f93d-1f3fc-200d-2642-fe0f","🤽🏽‍♂":"1f93d-1f3fd-200d-2642-fe0f","🤽🏾‍♂":"1f93d-1f3fe-200d-2642-fe0f","🤽🏿‍♂":"1f93d-1f3ff-200d-2642-fe0f","🤽‍♀️":"1f93d-200d-2640-fe0f","🤽🏻‍♀":"1f93d-1f3fb-200d-2640-fe0f","🤽🏼‍♀":"1f93d-1f3fc-200d-2640-fe0f","🤽🏽‍♀":"1f93d-1f3fd-200d-2640-fe0f","🤽🏾‍♀":"1f93d-1f3fe-200d-2640-fe0f","🤽🏿‍♀":"1f93d-1f3ff-200d-2640-fe0f","🤾‍♂️":"1f93e-200d-2642-fe0f","🤾🏻‍♂":"1f93e-1f3fb-200d-2642-fe0f","🤾🏼‍♂":"1f93e-1f3fc-200d-2642-fe0f","🤾🏽‍♂":"1f93e-1f3fd-200d-2642-fe0f","🤾🏾‍♂":"1f93e-1f3fe-200d-2642-fe0f","🤾🏿‍♂":"1f93e-1f3ff-200d-2642-fe0f","🤾‍♀️":"1f93e-200d-2640-fe0f","🤾🏻‍♀":"1f93e-1f3fb-200d-2640-fe0f","🤾🏼‍♀":"1f93e-1f3fc-200d-2640-fe0f","🤾🏽‍♀":"1f93e-1f3fd-200d-2640-fe0f","🤾🏾‍♀":"1f93e-1f3fe-200d-2640-fe0f","🤾🏿‍♀":"1f93e-1f3ff-200d-2640-fe0f","🤹‍♂️":"1f939-200d-2642-fe0f","🤹🏻‍♂":"1f939-1f3fb-200d-2642-fe0f","🤹🏼‍♂":"1f939-1f3fc-200d-2642-fe0f","🤹🏽‍♂":"1f939-1f3fd-200d-2642-fe0f","🤹🏾‍♂":"1f939-1f3fe-200d-2642-fe0f","🤹🏿‍♂":"1f939-1f3ff-200d-2642-fe0f","🤹‍♀️":"1f939-200d-2640-fe0f","🤹🏻‍♀":"1f939-1f3fb-200d-2640-fe0f","🤹🏼‍♀":"1f939-1f3fc-200d-2640-fe0f","🤹🏽‍♀":"1f939-1f3fd-200d-2640-fe0f","🤹🏾‍♀":"1f939-1f3fe-200d-2640-fe0f","🤹🏿‍♀":"1f939-1f3ff-200d-2640-fe0f","👁‍🗨️":"1f441-200d-1f5e8","👁️‍🗨":"1f441-200d-1f5e8","🏳️‍🌈":"1f3f3-fe0f-200d-1f308","👨🏻‍⚕️":"1f468-1f3fb-200d-2695-fe0f","👨🏼‍⚕️":"1f468-1f3fc-200d-2695-fe0f","👨🏽‍⚕️":"1f468-1f3fd-200d-2695-fe0f","👨🏾‍⚕️":"1f468-1f3fe-200d-2695-fe0f","👨🏿‍⚕️":"1f468-1f3ff-200d-2695-fe0f","👩🏻‍⚕️":"1f469-1f3fb-200d-2695-fe0f","👩🏼‍⚕️":"1f469-1f3fc-200d-2695-fe0f","👩🏽‍⚕️":"1f469-1f3fd-200d-2695-fe0f","👩🏾‍⚕️":"1f469-1f3fe-200d-2695-fe0f","👩🏿‍⚕️":"1f469-1f3ff-200d-2695-fe0f","👨🏻‍⚖️":"1f468-1f3fb-200d-2696-fe0f","👨🏼‍⚖️":"1f468-1f3fc-200d-2696-fe0f","👨🏽‍⚖️":"1f468-1f3fd-200d-2696-fe0f","👨🏾‍⚖️":"1f468-1f3fe-200d-2696-fe0f","👨🏿‍⚖️":"1f468-1f3ff-200d-2696-fe0f","👩🏻‍⚖️":"1f469-1f3fb-200d-2696-fe0f","👩🏼‍⚖️":"1f469-1f3fc-200d-2696-fe0f","👩🏽‍⚖️":"1f469-1f3fd-200d-2696-fe0f","👩🏾‍⚖️":"1f469-1f3fe-200d-2696-fe0f","👩🏿‍⚖️":"1f469-1f3ff-200d-2696-fe0f","👨🏻‍✈️":"1f468-1f3fb-200d-2708-fe0f","👨🏼‍✈️":"1f468-1f3fc-200d-2708-fe0f","👨🏽‍✈️":"1f468-1f3fd-200d-2708-fe0f","👨🏾‍✈️":"1f468-1f3fe-200d-2708-fe0f","👨🏿‍✈️":"1f468-1f3ff-200d-2708-fe0f","👩🏻‍✈️":"1f469-1f3fb-200d-2708-fe0f","👩🏼‍✈️":"1f469-1f3fc-200d-2708-fe0f","👩🏽‍✈️":"1f469-1f3fd-200d-2708-fe0f","👩🏾‍✈️":"1f469-1f3fe-200d-2708-fe0f","👩🏿‍✈️":"1f469-1f3ff-200d-2708-fe0f","👮🏻‍♂️":"1f46e-1f3fb-200d-2642-fe0f","👮🏼‍♂️":"1f46e-1f3fc-200d-2642-fe0f","👮🏽‍♂️":"1f46e-1f3fd-200d-2642-fe0f","👮🏾‍♂️":"1f46e-1f3fe-200d-2642-fe0f","👮🏿‍♂️":"1f46e-1f3ff-200d-2642-fe0f","👮🏻‍♀️":"1f46e-1f3fb-200d-2640-fe0f","👮🏼‍♀️":"1f46e-1f3fc-200d-2640-fe0f","👮🏽‍♀️":"1f46e-1f3fd-200d-2640-fe0f","👮🏾‍♀️":"1f46e-1f3fe-200d-2640-fe0f","👮🏿‍♀️":"1f46e-1f3ff-200d-2640-fe0f","🕵️‍♂️":"1f575-fe0f-200d-2642-fe0f","🕵🏻‍♂️":"1f575-1f3fb-200d-2642-fe0f","🕵🏼‍♂️":"1f575-1f3fc-200d-2642-fe0f","🕵🏽‍♂️":"1f575-1f3fd-200d-2642-fe0f","🕵🏾‍♂️":"1f575-1f3fe-200d-2642-fe0f","🕵🏿‍♂️":"1f575-1f3ff-200d-2642-fe0f","🕵️‍♀️":"1f575-fe0f-200d-2640-fe0f","🕵🏻‍♀️":"1f575-1f3fb-200d-2640-fe0f","🕵🏼‍♀️":"1f575-1f3fc-200d-2640-fe0f","🕵🏽‍♀️":"1f575-1f3fd-200d-2640-fe0f","🕵🏾‍♀️":"1f575-1f3fe-200d-2640-fe0f","🕵🏿‍♀️":"1f575-1f3ff-200d-2640-fe0f","💂🏻‍♂️":"1f482-1f3fb-200d-2642-fe0f","💂🏼‍♂️":"1f482-1f3fc-200d-2642-fe0f","💂🏽‍♂️":"1f482-1f3fd-200d-2642-fe0f","💂🏾‍♂️":"1f482-1f3fe-200d-2642-fe0f","💂🏿‍♂️":"1f482-1f3ff-200d-2642-fe0f","💂🏻‍♀️":"1f482-1f3fb-200d-2640-fe0f","💂🏼‍♀️":"1f482-1f3fc-200d-2640-fe0f","💂🏽‍♀️":"1f482-1f3fd-200d-2640-fe0f","💂🏾‍♀️":"1f482-1f3fe-200d-2640-fe0f","💂🏿‍♀️":"1f482-1f3ff-200d-2640-fe0f","👷🏻‍♂️":"1f477-1f3fb-200d-2642-fe0f","👷🏼‍♂️":"1f477-1f3fc-200d-2642-fe0f","👷🏽‍♂️":"1f477-1f3fd-200d-2642-fe0f","👷🏾‍♂️":"1f477-1f3fe-200d-2642-fe0f","👷🏿‍♂️":"1f477-1f3ff-200d-2642-fe0f","👷🏻‍♀️":"1f477-1f3fb-200d-2640-fe0f","👷🏼‍♀️":"1f477-1f3fc-200d-2640-fe0f","👷🏽‍♀️":"1f477-1f3fd-200d-2640-fe0f","👷🏾‍♀️":"1f477-1f3fe-200d-2640-fe0f","👷🏿‍♀️":"1f477-1f3ff-200d-2640-fe0f","👳🏻‍♂️":"1f473-1f3fb-200d-2642-fe0f","👳🏼‍♂️":"1f473-1f3fc-200d-2642-fe0f","👳🏽‍♂️":"1f473-1f3fd-200d-2642-fe0f","👳🏾‍♂️":"1f473-1f3fe-200d-2642-fe0f","👳🏿‍♂️":"1f473-1f3ff-200d-2642-fe0f","👳🏻‍♀️":"1f473-1f3fb-200d-2640-fe0f","👳🏼‍♀️":"1f473-1f3fc-200d-2640-fe0f","👳🏽‍♀️":"1f473-1f3fd-200d-2640-fe0f","👳🏾‍♀️":"1f473-1f3fe-200d-2640-fe0f","👳🏿‍♀️":"1f473-1f3ff-200d-2640-fe0f","👱🏻‍♂️":"1f471-1f3fb-200d-2642-fe0f","👱🏼‍♂️":"1f471-1f3fc-200d-2642-fe0f","👱🏽‍♂️":"1f471-1f3fd-200d-2642-fe0f","👱🏾‍♂️":"1f471-1f3fe-200d-2642-fe0f","👱🏿‍♂️":"1f471-1f3ff-200d-2642-fe0f","👱🏻‍♀️":"1f471-1f3fb-200d-2640-fe0f","👱🏼‍♀️":"1f471-1f3fc-200d-2640-fe0f","👱🏽‍♀️":"1f471-1f3fd-200d-2640-fe0f","👱🏾‍♀️":"1f471-1f3fe-200d-2640-fe0f","👱🏿‍♀️":"1f471-1f3ff-200d-2640-fe0f","🧙🏻‍♀️":"1f9d9-1f3fb-200d-2640-fe0f","🧙🏼‍♀️":"1f9d9-1f3fc-200d-2640-fe0f","🧙🏽‍♀️":"1f9d9-1f3fd-200d-2640-fe0f","🧙🏾‍♀️":"1f9d9-1f3fe-200d-2640-fe0f","🧙🏿‍♀️":"1f9d9-1f3ff-200d-2640-fe0f","🧙🏻‍♂️":"1f9d9-1f3fb-200d-2642-fe0f","🧙🏼‍♂️":"1f9d9-1f3fc-200d-2642-fe0f","🧙🏽‍♂️":"1f9d9-1f3fd-200d-2642-fe0f","🧙🏾‍♂️":"1f9d9-1f3fe-200d-2642-fe0f","🧙🏿‍♂️":"1f9d9-1f3ff-200d-2642-fe0f","🧚🏻‍♀️":"1f9da-1f3fb-200d-2640-fe0f","🧚🏼‍♀️":"1f9da-1f3fc-200d-2640-fe0f","🧚🏽‍♀️":"1f9da-1f3fd-200d-2640-fe0f","🧚🏾‍♀️":"1f9da-1f3fe-200d-2640-fe0f","🧚🏿‍♀️":"1f9da-1f3ff-200d-2640-fe0f","🧚🏻‍♂️":"1f9da-1f3fb-200d-2642-fe0f","🧚🏼‍♂️":"1f9da-1f3fc-200d-2642-fe0f","🧚🏽‍♂️":"1f9da-1f3fd-200d-2642-fe0f","🧚🏾‍♂️":"1f9da-1f3fe-200d-2642-fe0f","🧚🏿‍♂️":"1f9da-1f3ff-200d-2642-fe0f","🧛🏻‍♀️":"1f9db-1f3fb-200d-2640-fe0f","🧛🏼‍♀️":"1f9db-1f3fc-200d-2640-fe0f","🧛🏽‍♀️":"1f9db-1f3fd-200d-2640-fe0f","🧛🏾‍♀️":"1f9db-1f3fe-200d-2640-fe0f","🧛🏿‍♀️":"1f9db-1f3ff-200d-2640-fe0f","🧛🏻‍♂️":"1f9db-1f3fb-200d-2642-fe0f","🧛🏼‍♂️":"1f9db-1f3fc-200d-2642-fe0f","🧛🏽‍♂️":"1f9db-1f3fd-200d-2642-fe0f","🧛🏾‍♂️":"1f9db-1f3fe-200d-2642-fe0f","🧛🏿‍♂️":"1f9db-1f3ff-200d-2642-fe0f","🧜🏻‍♀️":"1f9dc-1f3fb-200d-2640-fe0f","🧜🏼‍♀️":"1f9dc-1f3fc-200d-2640-fe0f","🧜🏽‍♀️":"1f9dc-1f3fd-200d-2640-fe0f","🧜🏾‍♀️":"1f9dc-1f3fe-200d-2640-fe0f","🧜🏿‍♀️":"1f9dc-1f3ff-200d-2640-fe0f","🧜🏻‍♂️":"1f9dc-1f3fb-200d-2642-fe0f","🧜🏼‍♂️":"1f9dc-1f3fc-200d-2642-fe0f","🧜🏽‍♂️":"1f9dc-1f3fd-200d-2642-fe0f","🧜🏾‍♂️":"1f9dc-1f3fe-200d-2642-fe0f","🧜🏿‍♂️":"1f9dc-1f3ff-200d-2642-fe0f","🧝🏻‍♀️":"1f9dd-1f3fb-200d-2640-fe0f","🧝🏼‍♀️":"1f9dd-1f3fc-200d-2640-fe0f","🧝🏽‍♀️":"1f9dd-1f3fd-200d-2640-fe0f","🧝🏾‍♀️":"1f9dd-1f3fe-200d-2640-fe0f","🧝🏿‍♀️":"1f9dd-1f3ff-200d-2640-fe0f","🧝🏻‍♂️":"1f9dd-1f3fb-200d-2642-fe0f","🧝🏼‍♂️":"1f9dd-1f3fc-200d-2642-fe0f","🧝🏽‍♂️":"1f9dd-1f3fd-200d-2642-fe0f","🧝🏾‍♂️":"1f9dd-1f3fe-200d-2642-fe0f","🧝🏿‍♂️":"1f9dd-1f3ff-200d-2642-fe0f","🙍🏻‍♂️":"1f64d-1f3fb-200d-2642-fe0f","🙍🏼‍♂️":"1f64d-1f3fc-200d-2642-fe0f","🙍🏽‍♂️":"1f64d-1f3fd-200d-2642-fe0f","🙍🏾‍♂️":"1f64d-1f3fe-200d-2642-fe0f","🙍🏿‍♂️":"1f64d-1f3ff-200d-2642-fe0f","🙍🏻‍♀️":"1f64d-1f3fb-200d-2640-fe0f","🙍🏼‍♀️":"1f64d-1f3fc-200d-2640-fe0f","🙍🏽‍♀️":"1f64d-1f3fd-200d-2640-fe0f","🙍🏾‍♀️":"1f64d-1f3fe-200d-2640-fe0f","🙍🏿‍♀️":"1f64d-1f3ff-200d-2640-fe0f","🙎🏻‍♂️":"1f64e-1f3fb-200d-2642-fe0f","🙎🏼‍♂️":"1f64e-1f3fc-200d-2642-fe0f","🙎🏽‍♂️":"1f64e-1f3fd-200d-2642-fe0f","🙎🏾‍♂️":"1f64e-1f3fe-200d-2642-fe0f","🙎🏿‍♂️":"1f64e-1f3ff-200d-2642-fe0f","🙎🏻‍♀️":"1f64e-1f3fb-200d-2640-fe0f","🙎🏼‍♀️":"1f64e-1f3fc-200d-2640-fe0f","🙎🏽‍♀️":"1f64e-1f3fd-200d-2640-fe0f","🙎🏾‍♀️":"1f64e-1f3fe-200d-2640-fe0f","🙎🏿‍♀️":"1f64e-1f3ff-200d-2640-fe0f","🙅🏻‍♂️":"1f645-1f3fb-200d-2642-fe0f","🙅🏼‍♂️":"1f645-1f3fc-200d-2642-fe0f","🙅🏽‍♂️":"1f645-1f3fd-200d-2642-fe0f","🙅🏾‍♂️":"1f645-1f3fe-200d-2642-fe0f","🙅🏿‍♂️":"1f645-1f3ff-200d-2642-fe0f","🙅🏻‍♀️":"1f645-1f3fb-200d-2640-fe0f","🙅🏼‍♀️":"1f645-1f3fc-200d-2640-fe0f","🙅🏽‍♀️":"1f645-1f3fd-200d-2640-fe0f","🙅🏾‍♀️":"1f645-1f3fe-200d-2640-fe0f","🙅🏿‍♀️":"1f645-1f3ff-200d-2640-fe0f","🙆🏻‍♂️":"1f646-1f3fb-200d-2642-fe0f","🙆🏼‍♂️":"1f646-1f3fc-200d-2642-fe0f","🙆🏽‍♂️":"1f646-1f3fd-200d-2642-fe0f","🙆🏾‍♂️":"1f646-1f3fe-200d-2642-fe0f","🙆🏿‍♂️":"1f646-1f3ff-200d-2642-fe0f","🙆🏻‍♀️":"1f646-1f3fb-200d-2640-fe0f","🙆🏼‍♀️":"1f646-1f3fc-200d-2640-fe0f","🙆🏽‍♀️":"1f646-1f3fd-200d-2640-fe0f","🙆🏾‍♀️":"1f646-1f3fe-200d-2640-fe0f","🙆🏿‍♀️":"1f646-1f3ff-200d-2640-fe0f","💁🏻‍♂️":"1f481-1f3fb-200d-2642-fe0f","💁🏼‍♂️":"1f481-1f3fc-200d-2642-fe0f","💁🏽‍♂️":"1f481-1f3fd-200d-2642-fe0f","💁🏾‍♂️":"1f481-1f3fe-200d-2642-fe0f","💁🏿‍♂️":"1f481-1f3ff-200d-2642-fe0f","💁🏻‍♀️":"1f481-1f3fb-200d-2640-fe0f","💁🏼‍♀️":"1f481-1f3fc-200d-2640-fe0f","💁🏽‍♀️":"1f481-1f3fd-200d-2640-fe0f","💁🏾‍♀️":"1f481-1f3fe-200d-2640-fe0f","💁🏿‍♀️":"1f481-1f3ff-200d-2640-fe0f","🙋🏻‍♂️":"1f64b-1f3fb-200d-2642-fe0f","🙋🏼‍♂️":"1f64b-1f3fc-200d-2642-fe0f","🙋🏽‍♂️":"1f64b-1f3fd-200d-2642-fe0f","🙋🏾‍♂️":"1f64b-1f3fe-200d-2642-fe0f","🙋🏿‍♂️":"1f64b-1f3ff-200d-2642-fe0f","🙋🏻‍♀️":"1f64b-1f3fb-200d-2640-fe0f","🙋🏼‍♀️":"1f64b-1f3fc-200d-2640-fe0f","🙋🏽‍♀️":"1f64b-1f3fd-200d-2640-fe0f","🙋🏾‍♀️":"1f64b-1f3fe-200d-2640-fe0f","🙋🏿‍♀️":"1f64b-1f3ff-200d-2640-fe0f","🙇🏻‍♂️":"1f647-1f3fb-200d-2642-fe0f","🙇🏼‍♂️":"1f647-1f3fc-200d-2642-fe0f","🙇🏽‍♂️":"1f647-1f3fd-200d-2642-fe0f","🙇🏾‍♂️":"1f647-1f3fe-200d-2642-fe0f","🙇🏿‍♂️":"1f647-1f3ff-200d-2642-fe0f","🙇🏻‍♀️":"1f647-1f3fb-200d-2640-fe0f","🙇🏼‍♀️":"1f647-1f3fc-200d-2640-fe0f","🙇🏽‍♀️":"1f647-1f3fd-200d-2640-fe0f","🙇🏾‍♀️":"1f647-1f3fe-200d-2640-fe0f","🙇🏿‍♀️":"1f647-1f3ff-200d-2640-fe0f","🤦🏻‍♂️":"1f926-1f3fb-200d-2642-fe0f","🤦🏼‍♂️":"1f926-1f3fc-200d-2642-fe0f","🤦🏽‍♂️":"1f926-1f3fd-200d-2642-fe0f","🤦🏾‍♂️":"1f926-1f3fe-200d-2642-fe0f","🤦🏿‍♂️":"1f926-1f3ff-200d-2642-fe0f","🤦🏻‍♀️":"1f926-1f3fb-200d-2640-fe0f","🤦🏼‍♀️":"1f926-1f3fc-200d-2640-fe0f","🤦🏽‍♀️":"1f926-1f3fd-200d-2640-fe0f","🤦🏾‍♀️":"1f926-1f3fe-200d-2640-fe0f","🤦🏿‍♀️":"1f926-1f3ff-200d-2640-fe0f","🤷🏻‍♂️":"1f937-1f3fb-200d-2642-fe0f","🤷🏼‍♂️":"1f937-1f3fc-200d-2642-fe0f","🤷🏽‍♂️":"1f937-1f3fd-200d-2642-fe0f","🤷🏾‍♂️":"1f937-1f3fe-200d-2642-fe0f","🤷🏿‍♂️":"1f937-1f3ff-200d-2642-fe0f","🤷🏻‍♀️":"1f937-1f3fb-200d-2640-fe0f","🤷🏼‍♀️":"1f937-1f3fc-200d-2640-fe0f","🤷🏽‍♀️":"1f937-1f3fd-200d-2640-fe0f","🤷🏾‍♀️":"1f937-1f3fe-200d-2640-fe0f","🤷🏿‍♀️":"1f937-1f3ff-200d-2640-fe0f","💆🏻‍♂️":"1f486-1f3fb-200d-2642-fe0f","💆🏼‍♂️":"1f486-1f3fc-200d-2642-fe0f","💆🏽‍♂️":"1f486-1f3fd-200d-2642-fe0f","💆🏾‍♂️":"1f486-1f3fe-200d-2642-fe0f","💆🏿‍♂️":"1f486-1f3ff-200d-2642-fe0f","💆🏻‍♀️":"1f486-1f3fb-200d-2640-fe0f","💆🏼‍♀️":"1f486-1f3fc-200d-2640-fe0f","💆🏽‍♀️":"1f486-1f3fd-200d-2640-fe0f","💆🏾‍♀️":"1f486-1f3fe-200d-2640-fe0f","💆🏿‍♀️":"1f486-1f3ff-200d-2640-fe0f","💇🏻‍♂️":"1f487-1f3fb-200d-2642-fe0f","💇🏼‍♂️":"1f487-1f3fc-200d-2642-fe0f","💇🏽‍♂️":"1f487-1f3fd-200d-2642-fe0f","💇🏾‍♂️":"1f487-1f3fe-200d-2642-fe0f","💇🏿‍♂️":"1f487-1f3ff-200d-2642-fe0f","💇🏻‍♀️":"1f487-1f3fb-200d-2640-fe0f","💇🏼‍♀️":"1f487-1f3fc-200d-2640-fe0f","💇🏽‍♀️":"1f487-1f3fd-200d-2640-fe0f","💇🏾‍♀️":"1f487-1f3fe-200d-2640-fe0f","💇🏿‍♀️":"1f487-1f3ff-200d-2640-fe0f","🚶🏻‍♂️":"1f6b6-1f3fb-200d-2642-fe0f","🚶🏼‍♂️":"1f6b6-1f3fc-200d-2642-fe0f","🚶🏽‍♂️":"1f6b6-1f3fd-200d-2642-fe0f","🚶🏾‍♂️":"1f6b6-1f3fe-200d-2642-fe0f","🚶🏿‍♂️":"1f6b6-1f3ff-200d-2642-fe0f","🚶🏻‍♀️":"1f6b6-1f3fb-200d-2640-fe0f","🚶🏼‍♀️":"1f6b6-1f3fc-200d-2640-fe0f","🚶🏽‍♀️":"1f6b6-1f3fd-200d-2640-fe0f","🚶🏾‍♀️":"1f6b6-1f3fe-200d-2640-fe0f","🚶🏿‍♀️":"1f6b6-1f3ff-200d-2640-fe0f","🏃🏻‍♂️":"1f3c3-1f3fb-200d-2642-fe0f","🏃🏼‍♂️":"1f3c3-1f3fc-200d-2642-fe0f","🏃🏽‍♂️":"1f3c3-1f3fd-200d-2642-fe0f","🏃🏾‍♂️":"1f3c3-1f3fe-200d-2642-fe0f","🏃🏿‍♂️":"1f3c3-1f3ff-200d-2642-fe0f","🏃🏻‍♀️":"1f3c3-1f3fb-200d-2640-fe0f","🏃🏼‍♀️":"1f3c3-1f3fc-200d-2640-fe0f","🏃🏽‍♀️":"1f3c3-1f3fd-200d-2640-fe0f","🏃🏾‍♀️":"1f3c3-1f3fe-200d-2640-fe0f","🏃🏿‍♀️":"1f3c3-1f3ff-200d-2640-fe0f","🧖🏻‍♀️":"1f9d6-1f3fb-200d-2640-fe0f","🧖🏼‍♀️":"1f9d6-1f3fc-200d-2640-fe0f","🧖🏽‍♀️":"1f9d6-1f3fd-200d-2640-fe0f","🧖🏾‍♀️":"1f9d6-1f3fe-200d-2640-fe0f","🧖🏿‍♀️":"1f9d6-1f3ff-200d-2640-fe0f","🧖🏻‍♂️":"1f9d6-1f3fb-200d-2642-fe0f","🧖🏼‍♂️":"1f9d6-1f3fc-200d-2642-fe0f","🧖🏽‍♂️":"1f9d6-1f3fd-200d-2642-fe0f","🧖🏾‍♂️":"1f9d6-1f3fe-200d-2642-fe0f","🧖🏿‍♂️":"1f9d6-1f3ff-200d-2642-fe0f","🧗🏻‍♀️":"1f9d7-1f3fb-200d-2640-fe0f","🧗🏼‍♀️":"1f9d7-1f3fc-200d-2640-fe0f","🧗🏽‍♀️":"1f9d7-1f3fd-200d-2640-fe0f","🧗🏾‍♀️":"1f9d7-1f3fe-200d-2640-fe0f","🧗🏿‍♀️":"1f9d7-1f3ff-200d-2640-fe0f","🧗🏻‍♂️":"1f9d7-1f3fb-200d-2642-fe0f","🧗🏼‍♂️":"1f9d7-1f3fc-200d-2642-fe0f","🧗🏽‍♂️":"1f9d7-1f3fd-200d-2642-fe0f","🧗🏾‍♂️":"1f9d7-1f3fe-200d-2642-fe0f","🧗🏿‍♂️":"1f9d7-1f3ff-200d-2642-fe0f","🧘🏻‍♀️":"1f9d8-1f3fb-200d-2640-fe0f","🧘🏼‍♀️":"1f9d8-1f3fc-200d-2640-fe0f","🧘🏽‍♀️":"1f9d8-1f3fd-200d-2640-fe0f","🧘🏾‍♀️":"1f9d8-1f3fe-200d-2640-fe0f","🧘🏿‍♀️":"1f9d8-1f3ff-200d-2640-fe0f","🧘🏻‍♂️":"1f9d8-1f3fb-200d-2642-fe0f","🧘🏼‍♂️":"1f9d8-1f3fc-200d-2642-fe0f","🧘🏽‍♂️":"1f9d8-1f3fd-200d-2642-fe0f","🧘🏾‍♂️":"1f9d8-1f3fe-200d-2642-fe0f","🧘🏿‍♂️":"1f9d8-1f3ff-200d-2642-fe0f","🏌️‍♂️":"1f3cc-fe0f-200d-2642-fe0f","🏌🏻‍♂️":"1f3cc-1f3fb-200d-2642-fe0f","🏌🏼‍♂️":"1f3cc-1f3fc-200d-2642-fe0f","🏌🏽‍♂️":"1f3cc-1f3fd-200d-2642-fe0f","🏌🏾‍♂️":"1f3cc-1f3fe-200d-2642-fe0f","🏌🏿‍♂️":"1f3cc-1f3ff-200d-2642-fe0f","🏌️‍♀️":"1f3cc-fe0f-200d-2640-fe0f","🏌🏻‍♀️":"1f3cc-1f3fb-200d-2640-fe0f","🏌🏼‍♀️":"1f3cc-1f3fc-200d-2640-fe0f","🏌🏽‍♀️":"1f3cc-1f3fd-200d-2640-fe0f","🏌🏾‍♀️":"1f3cc-1f3fe-200d-2640-fe0f","🏌🏿‍♀️":"1f3cc-1f3ff-200d-2640-fe0f","🏄🏻‍♂️":"1f3c4-1f3fb-200d-2642-fe0f","🏄🏼‍♂️":"1f3c4-1f3fc-200d-2642-fe0f","🏄🏽‍♂️":"1f3c4-1f3fd-200d-2642-fe0f","🏄🏾‍♂️":"1f3c4-1f3fe-200d-2642-fe0f","🏄🏿‍♂️":"1f3c4-1f3ff-200d-2642-fe0f","🏄🏻‍♀️":"1f3c4-1f3fb-200d-2640-fe0f","🏄🏼‍♀️":"1f3c4-1f3fc-200d-2640-fe0f","🏄🏽‍♀️":"1f3c4-1f3fd-200d-2640-fe0f","🏄🏾‍♀️":"1f3c4-1f3fe-200d-2640-fe0f","🏄🏿‍♀️":"1f3c4-1f3ff-200d-2640-fe0f","🚣🏻‍♂️":"1f6a3-1f3fb-200d-2642-fe0f","🚣🏼‍♂️":"1f6a3-1f3fc-200d-2642-fe0f","🚣🏽‍♂️":"1f6a3-1f3fd-200d-2642-fe0f","🚣🏾‍♂️":"1f6a3-1f3fe-200d-2642-fe0f","🚣🏿‍♂️":"1f6a3-1f3ff-200d-2642-fe0f","🚣🏻‍♀️":"1f6a3-1f3fb-200d-2640-fe0f","🚣🏼‍♀️":"1f6a3-1f3fc-200d-2640-fe0f","🚣🏽‍♀️":"1f6a3-1f3fd-200d-2640-fe0f","🚣🏾‍♀️":"1f6a3-1f3fe-200d-2640-fe0f","🚣🏿‍♀️":"1f6a3-1f3ff-200d-2640-fe0f","🏊🏻‍♂️":"1f3ca-1f3fb-200d-2642-fe0f","🏊🏼‍♂️":"1f3ca-1f3fc-200d-2642-fe0f","🏊🏽‍♂️":"1f3ca-1f3fd-200d-2642-fe0f","🏊🏾‍♂️":"1f3ca-1f3fe-200d-2642-fe0f","🏊🏿‍♂️":"1f3ca-1f3ff-200d-2642-fe0f","🏊🏻‍♀️":"1f3ca-1f3fb-200d-2640-fe0f","🏊🏼‍♀️":"1f3ca-1f3fc-200d-2640-fe0f","🏊🏽‍♀️":"1f3ca-1f3fd-200d-2640-fe0f","🏊🏾‍♀️":"1f3ca-1f3fe-200d-2640-fe0f","🏊🏿‍♀️":"1f3ca-1f3ff-200d-2640-fe0f","⛹️‍♂️":"26f9-fe0f-200d-2642-fe0f","⛹🏻‍♂️":"26f9-1f3fb-200d-2642-fe0f","⛹🏼‍♂️":"26f9-1f3fc-200d-2642-fe0f","⛹🏽‍♂️":"26f9-1f3fd-200d-2642-fe0f","⛹🏾‍♂️":"26f9-1f3fe-200d-2642-fe0f","⛹🏿‍♂️":"26f9-1f3ff-200d-2642-fe0f","⛹️‍♀️":"26f9-fe0f-200d-2640-fe0f","⛹🏻‍♀️":"26f9-1f3fb-200d-2640-fe0f","⛹🏼‍♀️":"26f9-1f3fc-200d-2640-fe0f","⛹🏽‍♀️":"26f9-1f3fd-200d-2640-fe0f","⛹🏾‍♀️":"26f9-1f3fe-200d-2640-fe0f","⛹🏿‍♀️":"26f9-1f3ff-200d-2640-fe0f","🏋️‍♂️":"1f3cb-fe0f-200d-2642-fe0f","🏋🏻‍♂️":"1f3cb-1f3fb-200d-2642-fe0f","🏋🏼‍♂️":"1f3cb-1f3fc-200d-2642-fe0f","🏋🏽‍♂️":"1f3cb-1f3fd-200d-2642-fe0f","🏋🏾‍♂️":"1f3cb-1f3fe-200d-2642-fe0f","🏋🏿‍♂️":"1f3cb-1f3ff-200d-2642-fe0f","🏋️‍♀️":"1f3cb-fe0f-200d-2640-fe0f","🏋🏻‍♀️":"1f3cb-1f3fb-200d-2640-fe0f","🏋🏼‍♀️":"1f3cb-1f3fc-200d-2640-fe0f","🏋🏽‍♀️":"1f3cb-1f3fd-200d-2640-fe0f","🏋🏾‍♀️":"1f3cb-1f3fe-200d-2640-fe0f","🏋🏿‍♀️":"1f3cb-1f3ff-200d-2640-fe0f","🚴🏻‍♂️":"1f6b4-1f3fb-200d-2642-fe0f","🚴🏼‍♂️":"1f6b4-1f3fc-200d-2642-fe0f","🚴🏽‍♂️":"1f6b4-1f3fd-200d-2642-fe0f","🚴🏾‍♂️":"1f6b4-1f3fe-200d-2642-fe0f","🚴🏿‍♂️":"1f6b4-1f3ff-200d-2642-fe0f","🚴🏻‍♀️":"1f6b4-1f3fb-200d-2640-fe0f","🚴🏼‍♀️":"1f6b4-1f3fc-200d-2640-fe0f","🚴🏽‍♀️":"1f6b4-1f3fd-200d-2640-fe0f","🚴🏾‍♀️":"1f6b4-1f3fe-200d-2640-fe0f","🚴🏿‍♀️":"1f6b4-1f3ff-200d-2640-fe0f","🚵🏻‍♂️":"1f6b5-1f3fb-200d-2642-fe0f","🚵🏼‍♂️":"1f6b5-1f3fc-200d-2642-fe0f","🚵🏽‍♂️":"1f6b5-1f3fd-200d-2642-fe0f","🚵🏾‍♂️":"1f6b5-1f3fe-200d-2642-fe0f","🚵🏿‍♂️":"1f6b5-1f3ff-200d-2642-fe0f","🚵🏻‍♀️":"1f6b5-1f3fb-200d-2640-fe0f","🚵🏼‍♀️":"1f6b5-1f3fc-200d-2640-fe0f","🚵🏽‍♀️":"1f6b5-1f3fd-200d-2640-fe0f","🚵🏾‍♀️":"1f6b5-1f3fe-200d-2640-fe0f","🚵🏿‍♀️":"1f6b5-1f3ff-200d-2640-fe0f","🤸🏻‍♂️":"1f938-1f3fb-200d-2642-fe0f","🤸🏼‍♂️":"1f938-1f3fc-200d-2642-fe0f","🤸🏽‍♂️":"1f938-1f3fd-200d-2642-fe0f","🤸🏾‍♂️":"1f938-1f3fe-200d-2642-fe0f","🤸🏿‍♂️":"1f938-1f3ff-200d-2642-fe0f","🤸🏻‍♀️":"1f938-1f3fb-200d-2640-fe0f","🤸🏼‍♀️":"1f938-1f3fc-200d-2640-fe0f","🤸🏽‍♀️":"1f938-1f3fd-200d-2640-fe0f","🤸🏾‍♀️":"1f938-1f3fe-200d-2640-fe0f","🤸🏿‍♀️":"1f938-1f3ff-200d-2640-fe0f","🤽🏻‍♂️":"1f93d-1f3fb-200d-2642-fe0f","🤽🏼‍♂️":"1f93d-1f3fc-200d-2642-fe0f","🤽🏽‍♂️":"1f93d-1f3fd-200d-2642-fe0f","🤽🏾‍♂️":"1f93d-1f3fe-200d-2642-fe0f","🤽🏿‍♂️":"1f93d-1f3ff-200d-2642-fe0f","🤽🏻‍♀️":"1f93d-1f3fb-200d-2640-fe0f","🤽🏼‍♀️":"1f93d-1f3fc-200d-2640-fe0f","🤽🏽‍♀️":"1f93d-1f3fd-200d-2640-fe0f","🤽🏾‍♀️":"1f93d-1f3fe-200d-2640-fe0f","🤽🏿‍♀️":"1f93d-1f3ff-200d-2640-fe0f","🤾🏻‍♂️":"1f93e-1f3fb-200d-2642-fe0f","🤾🏼‍♂️":"1f93e-1f3fc-200d-2642-fe0f","🤾🏽‍♂️":"1f93e-1f3fd-200d-2642-fe0f","🤾🏾‍♂️":"1f93e-1f3fe-200d-2642-fe0f","🤾🏿‍♂️":"1f93e-1f3ff-200d-2642-fe0f","🤾🏻‍♀️":"1f93e-1f3fb-200d-2640-fe0f","🤾🏼‍♀️":"1f93e-1f3fc-200d-2640-fe0f","🤾🏽‍♀️":"1f93e-1f3fd-200d-2640-fe0f","🤾🏾‍♀️":"1f93e-1f3fe-200d-2640-fe0f","🤾🏿‍♀️":"1f93e-1f3ff-200d-2640-fe0f","🤹🏻‍♂️":"1f939-1f3fb-200d-2642-fe0f","🤹🏼‍♂️":"1f939-1f3fc-200d-2642-fe0f","🤹🏽‍♂️":"1f939-1f3fd-200d-2642-fe0f","🤹🏾‍♂️":"1f939-1f3fe-200d-2642-fe0f","🤹🏿‍♂️":"1f939-1f3ff-200d-2642-fe0f","🤹🏻‍♀️":"1f939-1f3fb-200d-2640-fe0f","🤹🏼‍♀️":"1f939-1f3fc-200d-2640-fe0f","🤹🏽‍♀️":"1f939-1f3fd-200d-2640-fe0f","🤹🏾‍♀️":"1f939-1f3fe-200d-2640-fe0f","🤹🏿‍♀️":"1f939-1f3ff-200d-2640-fe0f","👩‍❤‍👨":"1f469-200d-2764-fe0f-200d-1f468","👨‍❤‍👨":"1f468-200d-2764-fe0f-200d-1f468","👩‍❤‍👩":"1f469-200d-2764-fe0f-200d-1f469","👨‍👩‍👦":"1f468-200d-1f469-200d-1f466","👨‍👩‍👧":"1f468-200d-1f469-200d-1f467","👨‍👨‍👦":"1f468-200d-1f468-200d-1f466","👨‍👨‍👧":"1f468-200d-1f468-200d-1f467","👩‍👩‍👦":"1f469-200d-1f469-200d-1f466","👩‍👩‍👧":"1f469-200d-1f469-200d-1f467","👨‍👦‍👦":"1f468-200d-1f466-200d-1f466","👨‍👧‍👦":"1f468-200d-1f467-200d-1f466","👨‍👧‍👧":"1f468-200d-1f467-200d-1f467","👩‍👦‍👦":"1f469-200d-1f466-200d-1f466","👩‍👧‍👦":"1f469-200d-1f467-200d-1f466","👩‍👧‍👧":"1f469-200d-1f467-200d-1f467","👁️‍🗨️":"1f441-200d-1f5e8","👩‍❤️‍👨":"1f469-200d-2764-fe0f-200d-1f468","👨‍❤️‍👨":"1f468-200d-2764-fe0f-200d-1f468","👩‍❤️‍👩":"1f469-200d-2764-fe0f-200d-1f469","👩‍❤‍💋‍👨":"1f469-200d-2764-fe0f-200d-1f48b-200d-1f468","👨‍❤‍💋‍👨":"1f468-200d-2764-fe0f-200d-1f48b-200d-1f468","👩‍❤‍💋‍👩":"1f469-200d-2764-fe0f-200d-1f48b-200d-1f469","👨‍👩‍👧‍👦":"1f468-200d-1f469-200d-1f467-200d-1f466","👨‍👩‍👦‍👦":"1f468-200d-1f469-200d-1f466-200d-1f466","👨‍👩‍👧‍👧":"1f468-200d-1f469-200d-1f467-200d-1f467","👨‍👨‍👧‍👦":"1f468-200d-1f468-200d-1f467-200d-1f466","👨‍👨‍👦‍👦":"1f468-200d-1f468-200d-1f466-200d-1f466","👨‍👨‍👧‍👧":"1f468-200d-1f468-200d-1f467-200d-1f467","👩‍👩‍👧‍👦":"1f469-200d-1f469-200d-1f467-200d-1f466","👩‍👩‍👦‍👦":"1f469-200d-1f469-200d-1f466-200d-1f466","👩‍👩‍👧‍👧":"1f469-200d-1f469-200d-1f467-200d-1f467","🏴󠁧󠁢󠁥󠁮󠁧󠁿":"1f3f4-e0067-e0062-e0065-e006e-e0067-e007f","🏴󠁧󠁢󠁳󠁣󠁴󠁿":"1f3f4-e0067-e0062-e0073-e0063-e0074-e007f","🏴󠁧󠁢󠁷󠁬󠁳󠁿":"1f3f4-e0067-e0062-e0077-e006c-e0073-e007f","👩‍❤️‍💋‍👨":"1f469-200d-2764-fe0f-200d-1f48b-200d-1f468","👨‍❤️‍💋‍👨":"1f468-200d-2764-fe0f-200d-1f48b-200d-1f468","👩‍❤️‍💋‍👩":"1f469-200d-2764-fe0f-200d-1f48b-200d-1f469"}
\ No newline at end of file
diff --git a/app/javascript/themes/glitch/util/emoji/emoji_mart_data_light.js b/app/javascript/themes/glitch/util/emoji/emoji_mart_data_light.js
deleted file mode 100644
index 45086fc4c..000000000
--- a/app/javascript/themes/glitch/util/emoji/emoji_mart_data_light.js
+++ /dev/null
@@ -1,41 +0,0 @@
-// The output of this module is designed to mimic emoji-mart's
-// "data" object, such that we can use it for a light version of emoji-mart's
-// emojiIndex.search functionality.
-const { unicodeToUnifiedName } = require('./unicode_to_unified_name');
-const [ shortCodesToEmojiData, skins, categories, short_names ] = require('./emoji_compressed');
-
-const emojis = {};
-
-// decompress
-Object.keys(shortCodesToEmojiData).forEach((shortCode) => {
-  let [
-    filenameData, // eslint-disable-line no-unused-vars
-    searchData,
-  ] = shortCodesToEmojiData[shortCode];
-  let [
-    native,
-    short_names,
-    search,
-    unified,
-  ] = searchData;
-
-  if (!unified) {
-    // unified name can be derived from unicodeToUnifiedName
-    unified = unicodeToUnifiedName(native);
-  }
-
-  short_names = [shortCode].concat(short_names);
-  emojis[shortCode] = {
-    native,
-    search,
-    short_names,
-    unified,
-  };
-});
-
-module.exports = {
-  emojis,
-  skins,
-  categories,
-  short_names,
-};
diff --git a/app/javascript/themes/glitch/util/emoji/emoji_mart_search_light.js b/app/javascript/themes/glitch/util/emoji/emoji_mart_search_light.js
deleted file mode 100644
index 5755bf1c4..000000000
--- a/app/javascript/themes/glitch/util/emoji/emoji_mart_search_light.js
+++ /dev/null
@@ -1,157 +0,0 @@
-// This code is largely borrowed from:
-// https://github.com/missive/emoji-mart/blob/5f2ffcc/src/utils/emoji-index.js
-
-import data from './emoji_mart_data_light';
-import { getData, getSanitizedData, intersect } from './emoji_utils';
-
-let originalPool = {};
-let index = {};
-let emojisList = {};
-let emoticonsList = {};
-
-for (let emoji in data.emojis) {
-  let emojiData = data.emojis[emoji];
-  let { short_names, emoticons } = emojiData;
-  let id = short_names[0];
-
-  if (emoticons) {
-    emoticons.forEach(emoticon => {
-      if (emoticonsList[emoticon]) {
-        return;
-      }
-
-      emoticonsList[emoticon] = id;
-    });
-  }
-
-  emojisList[id] = getSanitizedData(id);
-  originalPool[id] = emojiData;
-}
-
-function addCustomToPool(custom, pool) {
-  custom.forEach((emoji) => {
-    let emojiId = emoji.id || emoji.short_names[0];
-
-    if (emojiId && !pool[emojiId]) {
-      pool[emojiId] = getData(emoji);
-      emojisList[emojiId] = getSanitizedData(emoji);
-    }
-  });
-}
-
-function search(value, { emojisToShowFilter, maxResults, include, exclude, custom = [] } = {}) {
-  addCustomToPool(custom, originalPool);
-
-  maxResults = maxResults || 75;
-  include = include || [];
-  exclude = exclude || [];
-
-  let results = null,
-    pool = originalPool;
-
-  if (value.length) {
-    if (value === '-' || value === '-1') {
-      return [emojisList['-1']];
-    }
-
-    let values = value.toLowerCase().split(/[\s|,|\-|_]+/),
-      allResults = [];
-
-    if (values.length > 2) {
-      values = [values[0], values[1]];
-    }
-
-    if (include.length || exclude.length) {
-      pool = {};
-
-      data.categories.forEach(category => {
-        let isIncluded = include && include.length ? include.indexOf(category.name.toLowerCase()) > -1 : true;
-        let isExcluded = exclude && exclude.length ? exclude.indexOf(category.name.toLowerCase()) > -1 : false;
-        if (!isIncluded || isExcluded) {
-          return;
-        }
-
-        category.emojis.forEach(emojiId => pool[emojiId] = data.emojis[emojiId]);
-      });
-
-      if (custom.length) {
-        let customIsIncluded = include && include.length ? include.indexOf('custom') > -1 : true;
-        let customIsExcluded = exclude && exclude.length ? exclude.indexOf('custom') > -1 : false;
-        if (customIsIncluded && !customIsExcluded) {
-          addCustomToPool(custom, pool);
-        }
-      }
-    }
-
-    allResults = values.map((value) => {
-      let aPool = pool,
-        aIndex = index,
-        length = 0;
-
-      for (let charIndex = 0; charIndex < value.length; charIndex++) {
-        const char = value[charIndex];
-        length++;
-
-        aIndex[char] = aIndex[char] || {};
-        aIndex = aIndex[char];
-
-        if (!aIndex.results) {
-          let scores = {};
-
-          aIndex.results = [];
-          aIndex.pool = {};
-
-          for (let id in aPool) {
-            let emoji = aPool[id],
-              { search } = emoji,
-              sub = value.substr(0, length),
-              subIndex = search.indexOf(sub);
-
-            if (subIndex !== -1) {
-              let score = subIndex + 1;
-              if (sub === id) score = 0;
-
-              aIndex.results.push(emojisList[id]);
-              aIndex.pool[id] = emoji;
-
-              scores[id] = score;
-            }
-          }
-
-          aIndex.results.sort((a, b) => {
-            let aScore = scores[a.id],
-              bScore = scores[b.id];
-
-            return aScore - bScore;
-          });
-        }
-
-        aPool = aIndex.pool;
-      }
-
-      return aIndex.results;
-    }).filter(a => a);
-
-    if (allResults.length > 1) {
-      results = intersect.apply(null, allResults);
-    } else if (allResults.length) {
-      results = allResults[0];
-    } else {
-      results = [];
-    }
-  }
-
-  if (results) {
-    if (emojisToShowFilter) {
-      results = results.filter((result) => emojisToShowFilter(data.emojis[result.id].unified));
-    }
-
-    if (results && results.length > maxResults) {
-      results = results.slice(0, maxResults);
-    }
-  }
-
-  return results;
-}
-
-export { search };
diff --git a/app/javascript/themes/glitch/util/emoji/emoji_picker.js b/app/javascript/themes/glitch/util/emoji/emoji_picker.js
deleted file mode 100644
index 7e145381e..000000000
--- a/app/javascript/themes/glitch/util/emoji/emoji_picker.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import Picker from 'emoji-mart/dist-es/components/picker';
-import Emoji from 'emoji-mart/dist-es/components/emoji';
-
-export {
-  Picker,
-  Emoji,
-};
diff --git a/app/javascript/themes/glitch/util/emoji/emoji_unicode_mapping_light.js b/app/javascript/themes/glitch/util/emoji/emoji_unicode_mapping_light.js
deleted file mode 100644
index 918684c31..000000000
--- a/app/javascript/themes/glitch/util/emoji/emoji_unicode_mapping_light.js
+++ /dev/null
@@ -1,35 +0,0 @@
-// A mapping of unicode strings to an object containing the filename
-// (i.e. the svg filename) and a shortCode intended to be shown
-// as a "title" attribute in an HTML element (aka tooltip).
-
-const [
-  shortCodesToEmojiData,
-  skins, // eslint-disable-line no-unused-vars
-  categories, // eslint-disable-line no-unused-vars
-  short_names, // eslint-disable-line no-unused-vars
-  emojisWithoutShortCodes,
-] = require('./emoji_compressed');
-const { unicodeToFilename } = require('./unicode_to_filename');
-
-// decompress
-const unicodeMapping = {};
-
-function processEmojiMapData(emojiMapData, shortCode) {
-  let [ native, filename ] = emojiMapData;
-  if (!filename) {
-    // filename name can be derived from unicodeToFilename
-    filename = unicodeToFilename(native);
-  }
-  unicodeMapping[native] = {
-    shortCode: shortCode,
-    filename: filename,
-  };
-}
-
-Object.keys(shortCodesToEmojiData).forEach((shortCode) => {
-  let [ filenameData ] = shortCodesToEmojiData[shortCode];
-  filenameData.forEach(emojiMapData => processEmojiMapData(emojiMapData, shortCode));
-});
-emojisWithoutShortCodes.forEach(emojiMapData => processEmojiMapData(emojiMapData));
-
-module.exports = unicodeMapping;
diff --git a/app/javascript/themes/glitch/util/emoji/emoji_utils.js b/app/javascript/themes/glitch/util/emoji/emoji_utils.js
deleted file mode 100644
index dbf725c1f..000000000
--- a/app/javascript/themes/glitch/util/emoji/emoji_utils.js
+++ /dev/null
@@ -1,258 +0,0 @@
-// This code is largely borrowed from:
-// https://github.com/missive/emoji-mart/blob/5f2ffcc/src/utils/index.js
-
-import data from './emoji_mart_data_light';
-
-const buildSearch = (data) => {
-  const search = [];
-
-  let addToSearch = (strings, split) => {
-    if (!strings) {
-      return;
-    }
-
-    (Array.isArray(strings) ? strings : [strings]).forEach((string) => {
-      (split ? string.split(/[-|_|\s]+/) : [string]).forEach((s) => {
-        s = s.toLowerCase();
-
-        if (search.indexOf(s) === -1) {
-          search.push(s);
-        }
-      });
-    });
-  };
-
-  addToSearch(data.short_names, true);
-  addToSearch(data.name, true);
-  addToSearch(data.keywords, false);
-  addToSearch(data.emoticons, false);
-
-  return search.join(',');
-};
-
-const _String = String;
-
-const stringFromCodePoint = _String.fromCodePoint || function () {
-  let MAX_SIZE = 0x4000;
-  let codeUnits = [];
-  let highSurrogate;
-  let lowSurrogate;
-  let index = -1;
-  let length = arguments.length;
-  if (!length) {
-    return '';
-  }
-  let result = '';
-  while (++index < length) {
-    let codePoint = Number(arguments[index]);
-    if (
-      !isFinite(codePoint) ||       // `NaN`, `+Infinity`, or `-Infinity`
-      codePoint < 0 ||              // not a valid Unicode code point
-      codePoint > 0x10FFFF ||       // not a valid Unicode code point
-      Math.floor(codePoint) !== codePoint // not an integer
-    ) {
-      throw RangeError('Invalid code point: ' + codePoint);
-    }
-    if (codePoint <= 0xFFFF) { // BMP code point
-      codeUnits.push(codePoint);
-    } else { // Astral code point; split in surrogate halves
-      // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
-      codePoint -= 0x10000;
-      highSurrogate = (codePoint >> 10) + 0xD800;
-      lowSurrogate = (codePoint % 0x400) + 0xDC00;
-      codeUnits.push(highSurrogate, lowSurrogate);
-    }
-    if (index + 1 === length || codeUnits.length > MAX_SIZE) {
-      result += String.fromCharCode.apply(null, codeUnits);
-      codeUnits.length = 0;
-    }
-  }
-  return result;
-};
-
-
-const _JSON = JSON;
-
-const COLONS_REGEX = /^(?:\:([^\:]+)\:)(?:\:skin-tone-(\d)\:)?$/;
-const SKINS = [
-  '1F3FA', '1F3FB', '1F3FC',
-  '1F3FD', '1F3FE', '1F3FF',
-];
-
-function unifiedToNative(unified) {
-  let unicodes = unified.split('-'),
-    codePoints = unicodes.map((u) => `0x${u}`);
-
-  return stringFromCodePoint.apply(null, codePoints);
-}
-
-function sanitize(emoji) {
-  let { name, short_names, skin_tone, skin_variations, emoticons, unified, custom, imageUrl } = emoji,
-    id = emoji.id || short_names[0],
-    colons = `:${id}:`;
-
-  if (custom) {
-    return {
-      id,
-      name,
-      colons,
-      emoticons,
-      custom,
-      imageUrl,
-    };
-  }
-
-  if (skin_tone) {
-    colons += `:skin-tone-${skin_tone}:`;
-  }
-
-  return {
-    id,
-    name,
-    colons,
-    emoticons,
-    unified: unified.toLowerCase(),
-    skin: skin_tone || (skin_variations ? 1 : null),
-    native: unifiedToNative(unified),
-  };
-}
-
-function getSanitizedData() {
-  return sanitize(getData(...arguments));
-}
-
-function getData(emoji, skin, set) {
-  let emojiData = {};
-
-  if (typeof emoji === 'string') {
-    let matches = emoji.match(COLONS_REGEX);
-
-    if (matches) {
-      emoji = matches[1];
-
-      if (matches[2]) {
-        skin = parseInt(matches[2]);
-      }
-    }
-
-    if (data.short_names.hasOwnProperty(emoji)) {
-      emoji = data.short_names[emoji];
-    }
-
-    if (data.emojis.hasOwnProperty(emoji)) {
-      emojiData = data.emojis[emoji];
-    }
-  } else if (emoji.id) {
-    if (data.short_names.hasOwnProperty(emoji.id)) {
-      emoji.id = data.short_names[emoji.id];
-    }
-
-    if (data.emojis.hasOwnProperty(emoji.id)) {
-      emojiData = data.emojis[emoji.id];
-      skin = skin || emoji.skin;
-    }
-  }
-
-  if (!Object.keys(emojiData).length) {
-    emojiData = emoji;
-    emojiData.custom = true;
-
-    if (!emojiData.search) {
-      emojiData.search = buildSearch(emoji);
-    }
-  }
-
-  emojiData.emoticons = emojiData.emoticons || [];
-  emojiData.variations = emojiData.variations || [];
-
-  if (emojiData.skin_variations && skin > 1 && set) {
-    emojiData = JSON.parse(_JSON.stringify(emojiData));
-
-    let skinKey = SKINS[skin - 1],
-      variationData = emojiData.skin_variations[skinKey];
-
-    if (!variationData.variations && emojiData.variations) {
-      delete emojiData.variations;
-    }
-
-    if (variationData[`has_img_${set}`]) {
-      emojiData.skin_tone = skin;
-
-      for (let k in variationData) {
-        let v = variationData[k];
-        emojiData[k] = v;
-      }
-    }
-  }
-
-  if (emojiData.variations && emojiData.variations.length) {
-    emojiData = JSON.parse(_JSON.stringify(emojiData));
-    emojiData.unified = emojiData.variations.shift();
-  }
-
-  return emojiData;
-}
-
-function uniq(arr) {
-  return arr.reduce((acc, item) => {
-    if (acc.indexOf(item) === -1) {
-      acc.push(item);
-    }
-    return acc;
-  }, []);
-}
-
-function intersect(a, b) {
-  const uniqA = uniq(a);
-  const uniqB = uniq(b);
-
-  return uniqA.filter(item => uniqB.indexOf(item) >= 0);
-}
-
-function deepMerge(a, b) {
-  let o = {};
-
-  for (let key in a) {
-    let originalValue = a[key],
-      value = originalValue;
-
-    if (b.hasOwnProperty(key)) {
-      value = b[key];
-    }
-
-    if (typeof value === 'object') {
-      value = deepMerge(originalValue, value);
-    }
-
-    o[key] = value;
-  }
-
-  return o;
-}
-
-// https://github.com/sonicdoe/measure-scrollbar
-function measureScrollbar() {
-  const div = document.createElement('div');
-
-  div.style.width = '100px';
-  div.style.height = '100px';
-  div.style.overflow = 'scroll';
-  div.style.position = 'absolute';
-  div.style.top = '-9999px';
-
-  document.body.appendChild(div);
-  const scrollbarWidth = div.offsetWidth - div.clientWidth;
-  document.body.removeChild(div);
-
-  return scrollbarWidth;
-}
-
-export {
-  getData,
-  getSanitizedData,
-  uniq,
-  intersect,
-  deepMerge,
-  unifiedToNative,
-  measureScrollbar,
-};
diff --git a/app/javascript/themes/glitch/util/emoji/index.js b/app/javascript/themes/glitch/util/emoji/index.js
deleted file mode 100644
index 8c45a58fe..000000000
--- a/app/javascript/themes/glitch/util/emoji/index.js
+++ /dev/null
@@ -1,95 +0,0 @@
-import { autoPlayGif } from 'themes/glitch/util/initial_state';
-import unicodeMapping from './emoji_unicode_mapping_light';
-import Trie from 'substring-trie';
-
-const trie = new Trie(Object.keys(unicodeMapping));
-
-const assetHost = process.env.CDN_HOST || '';
-
-const emojify = (str, customEmojis = {}) => {
-  const tagCharsWithoutEmojis = '<&';
-  const tagCharsWithEmojis = Object.keys(customEmojis).length ? '<&:' : '<&';
-  let rtn = '', tagChars = tagCharsWithEmojis, invisible = 0;
-  for (;;) {
-    let match, i = 0, tag;
-    while (i < str.length && (tag = tagChars.indexOf(str[i])) === -1 && (invisible || !(match = trie.search(str.slice(i))))) {
-      i += str.codePointAt(i) < 65536 ? 1 : 2;
-    }
-    let rend, replacement = '';
-    if (i === str.length) {
-      break;
-    } else if (str[i] === ':') {
-      if (!(() => {
-        rend = str.indexOf(':', i + 1) + 1;
-        if (!rend) return false; // no pair of ':'
-        const lt = str.indexOf('<', i + 1);
-        if (!(lt === -1 || lt >= rend)) return false; // tag appeared before closing ':'
-        const shortname = str.slice(i, rend);
-        // now got a replacee as ':shortname:'
-        // if you want additional emoji handler, add statements below which set replacement and return true.
-        if (shortname in customEmojis) {
-          const filename = autoPlayGif ? customEmojis[shortname].url : customEmojis[shortname].static_url;
-          replacement = `<img draggable="false" class="emojione" alt="${shortname}" title="${shortname}" src="${filename}" />`;
-          return true;
-        }
-        return false;
-      })()) rend = ++i;
-    } else if (tag >= 0) { // <, &
-      rend = str.indexOf('>;'[tag], i + 1) + 1;
-      if (!rend) {
-        break;
-      }
-      if (tag === 0) {
-        if (invisible) {
-          if (str[i + 1] === '/') { // closing tag
-            if (!--invisible) {
-              tagChars = tagCharsWithEmojis;
-            }
-          } else if (str[rend - 2] !== '/') { // opening tag
-            invisible++;
-          }
-        } else {
-          if (str.startsWith('<span class="invisible">', i)) {
-            // avoid emojifying on invisible text
-            invisible = 1;
-            tagChars = tagCharsWithoutEmojis;
-          }
-        }
-      }
-      i = rend;
-    } else { // matched to unicode emoji
-      const { filename, shortCode } = unicodeMapping[match];
-      const title = shortCode ? `:${shortCode}:` : '';
-      replacement = `<img draggable="false" class="emojione" alt="${match}" title="${title}" src="${assetHost}/emoji/${filename}.svg" />`;
-      rend = i + match.length;
-    }
-    rtn += str.slice(0, i) + replacement;
-    str = str.slice(rend);
-  }
-  return rtn + str;
-};
-
-export default emojify;
-
-export const buildCustomEmojis = (customEmojis) => {
-  const emojis = [];
-
-  customEmojis.forEach(emoji => {
-    const shortcode = emoji.get('shortcode');
-    const url       = autoPlayGif ? emoji.get('url') : emoji.get('static_url');
-    const name      = shortcode.replace(':', '');
-
-    emojis.push({
-      id: name,
-      name,
-      short_names: [name],
-      text: '',
-      emoticons: [],
-      keywords: [name],
-      imageUrl: url,
-      custom: true,
-    });
-  });
-
-  return emojis;
-};
diff --git a/app/javascript/themes/glitch/util/emoji/unicode_to_filename.js b/app/javascript/themes/glitch/util/emoji/unicode_to_filename.js
deleted file mode 100644
index c75c4cd7d..000000000
--- a/app/javascript/themes/glitch/util/emoji/unicode_to_filename.js
+++ /dev/null
@@ -1,26 +0,0 @@
-// taken from:
-// https://github.com/twitter/twemoji/blob/47732c7/twemoji-generator.js#L848-L866
-exports.unicodeToFilename = (str) => {
-  let result = '';
-  let charCode = 0;
-  let p = 0;
-  let i = 0;
-  while (i < str.length) {
-    charCode = str.charCodeAt(i++);
-    if (p) {
-      if (result.length > 0) {
-        result += '-';
-      }
-      result += (0x10000 + ((p - 0xD800) << 10) + (charCode - 0xDC00)).toString(16);
-      p = 0;
-    } else if (0xD800 <= charCode && charCode <= 0xDBFF) {
-      p = charCode;
-    } else {
-      if (result.length > 0) {
-        result += '-';
-      }
-      result += charCode.toString(16);
-    }
-  }
-  return result;
-};
diff --git a/app/javascript/themes/glitch/util/emoji/unicode_to_unified_name.js b/app/javascript/themes/glitch/util/emoji/unicode_to_unified_name.js
deleted file mode 100644
index 808ac197e..000000000
--- a/app/javascript/themes/glitch/util/emoji/unicode_to_unified_name.js
+++ /dev/null
@@ -1,17 +0,0 @@
-function padLeft(str, num) {
-  while (str.length < num) {
-    str = '0' + str;
-  }
-  return str;
-}
-
-exports.unicodeToUnifiedName = (str) => {
-  let output = '';
-  for (let i = 0; i < str.length; i += 2) {
-    if (i > 0) {
-      output += '-';
-    }
-    output += padLeft(str.codePointAt(i).toString(16).toUpperCase(), 4);
-  }
-  return output;
-};
diff --git a/app/javascript/themes/glitch/util/extra_polyfills.js b/app/javascript/themes/glitch/util/extra_polyfills.js
deleted file mode 100644
index 3acc55abd..000000000
--- a/app/javascript/themes/glitch/util/extra_polyfills.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import 'intersection-observer';
-import 'requestidlecallback';
-import objectFitImages  from 'object-fit-images';
-
-objectFitImages();
diff --git a/app/javascript/themes/glitch/util/fullscreen.js b/app/javascript/themes/glitch/util/fullscreen.js
deleted file mode 100644
index cf5d0cf98..000000000
--- a/app/javascript/themes/glitch/util/fullscreen.js
+++ /dev/null
@@ -1,46 +0,0 @@
-// APIs for normalizing fullscreen operations. Note that Edge uses
-// the WebKit-prefixed APIs currently (as of Edge 16).
-
-export const isFullscreen = () => document.fullscreenElement ||
-  document.webkitFullscreenElement ||
-  document.mozFullScreenElement;
-
-export const exitFullscreen = () => {
-  if (document.exitFullscreen) {
-    document.exitFullscreen();
-  } else if (document.webkitExitFullscreen) {
-    document.webkitExitFullscreen();
-  } else if (document.mozCancelFullScreen) {
-    document.mozCancelFullScreen();
-  }
-};
-
-export const requestFullscreen = el => {
-  if (el.requestFullscreen) {
-    el.requestFullscreen();
-  } else if (el.webkitRequestFullscreen) {
-    el.webkitRequestFullscreen();
-  } else if (el.mozRequestFullScreen) {
-    el.mozRequestFullScreen();
-  }
-};
-
-export const attachFullscreenListener = (listener) => {
-  if ('onfullscreenchange' in document) {
-    document.addEventListener('fullscreenchange', listener);
-  } else if ('onwebkitfullscreenchange' in document) {
-    document.addEventListener('webkitfullscreenchange', listener);
-  } else if ('onmozfullscreenchange' in document) {
-    document.addEventListener('mozfullscreenchange', listener);
-  }
-};
-
-export const detachFullscreenListener = (listener) => {
-  if ('onfullscreenchange' in document) {
-    document.removeEventListener('fullscreenchange', listener);
-  } else if ('onwebkitfullscreenchange' in document) {
-    document.removeEventListener('webkitfullscreenchange', listener);
-  } else if ('onmozfullscreenchange' in document) {
-    document.removeEventListener('mozfullscreenchange', listener);
-  }
-};
diff --git a/app/javascript/themes/glitch/util/get_rect_from_entry.js b/app/javascript/themes/glitch/util/get_rect_from_entry.js
deleted file mode 100644
index c266cd7dc..000000000
--- a/app/javascript/themes/glitch/util/get_rect_from_entry.js
+++ /dev/null
@@ -1,21 +0,0 @@
-
-// Get the bounding client rect from an IntersectionObserver entry.
-// This is to work around a bug in Chrome: https://crbug.com/737228
-
-let hasBoundingRectBug;
-
-function getRectFromEntry(entry) {
-  if (typeof hasBoundingRectBug !== 'boolean') {
-    const boundingRect = entry.target.getBoundingClientRect();
-    const observerRect = entry.boundingClientRect;
-    hasBoundingRectBug = boundingRect.height !== observerRect.height ||
-      boundingRect.top !== observerRect.top ||
-      boundingRect.width !== observerRect.width ||
-      boundingRect.bottom !== observerRect.bottom ||
-      boundingRect.left !== observerRect.left ||
-      boundingRect.right !== observerRect.right;
-  }
-  return hasBoundingRectBug ? entry.target.getBoundingClientRect() : entry.boundingClientRect;
-}
-
-export default getRectFromEntry;
diff --git a/app/javascript/themes/glitch/util/initial_state.js b/app/javascript/themes/glitch/util/initial_state.js
deleted file mode 100644
index ef5d8b0ef..000000000
--- a/app/javascript/themes/glitch/util/initial_state.js
+++ /dev/null
@@ -1,21 +0,0 @@
-const element = document.getElementById('initial-state');
-const initialState = element && function () {
-  const result = JSON.parse(element.textContent);
-  try {
-    result.local_settings = JSON.parse(localStorage.getItem('mastodon-settings'));
-  } catch (e) {
-    result.local_settings = {};
-  }
-  return result;
-}();
-
-const getMeta = (prop) => initialState && initialState.meta && initialState.meta[prop];
-
-export const reduceMotion = getMeta('reduce_motion');
-export const autoPlayGif = getMeta('auto_play_gif');
-export const unfollowModal = getMeta('unfollow_modal');
-export const boostModal = getMeta('boost_modal');
-export const deleteModal = getMeta('delete_modal');
-export const me = getMeta('me');
-
-export default initialState;
diff --git a/app/javascript/themes/glitch/util/intersection_observer_wrapper.js b/app/javascript/themes/glitch/util/intersection_observer_wrapper.js
deleted file mode 100644
index 2b24c6583..000000000
--- a/app/javascript/themes/glitch/util/intersection_observer_wrapper.js
+++ /dev/null
@@ -1,57 +0,0 @@
-// Wrapper for IntersectionObserver in order to make working with it
-// a bit easier. We also follow this performance advice:
-// "If you need to observe multiple elements, it is both possible and
-// advised to observe multiple elements using the same IntersectionObserver
-// instance by calling observe() multiple times."
-// https://developers.google.com/web/updates/2016/04/intersectionobserver
-
-class IntersectionObserverWrapper {
-
-  callbacks = {};
-  observerBacklog = [];
-  observer = null;
-
-  connect (options) {
-    const onIntersection = (entries) => {
-      entries.forEach(entry => {
-        const id = entry.target.getAttribute('data-id');
-        if (this.callbacks[id]) {
-          this.callbacks[id](entry);
-        }
-      });
-    };
-
-    this.observer = new IntersectionObserver(onIntersection, options);
-    this.observerBacklog.forEach(([ id, node, callback ]) => {
-      this.observe(id, node, callback);
-    });
-    this.observerBacklog = null;
-  }
-
-  observe (id, node, callback) {
-    if (!this.observer) {
-      this.observerBacklog.push([ id, node, callback ]);
-    } else {
-      this.callbacks[id] = callback;
-      this.observer.observe(node);
-    }
-  }
-
-  unobserve (id, node) {
-    if (this.observer) {
-      delete this.callbacks[id];
-      this.observer.unobserve(node);
-    }
-  }
-
-  disconnect () {
-    if (this.observer) {
-      this.callbacks = {};
-      this.observer.disconnect();
-      this.observer = null;
-    }
-  }
-
-}
-
-export default IntersectionObserverWrapper;
diff --git a/app/javascript/themes/glitch/util/is_mobile.js b/app/javascript/themes/glitch/util/is_mobile.js
deleted file mode 100644
index 80e8e0a8a..000000000
--- a/app/javascript/themes/glitch/util/is_mobile.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import detectPassiveEvents from 'detect-passive-events';
-
-const LAYOUT_BREAKPOINT = 630;
-
-export function isMobile(width, columns) {
-  switch (columns) {
-  case 'multiple':
-    return false;
-  case 'single':
-    return true;
-  default:
-    return width <= LAYOUT_BREAKPOINT;
-  }
-};
-
-const iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
-
-let userTouching = false;
-let listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false;
-
-function touchListener() {
-  userTouching = true;
-  window.removeEventListener('touchstart', touchListener, listenerOptions);
-}
-
-window.addEventListener('touchstart', touchListener, listenerOptions);
-
-export function isUserTouching() {
-  return userTouching;
-}
-
-export function isIOS() {
-  return iOS;
-};
diff --git a/app/javascript/themes/glitch/util/link_header.js b/app/javascript/themes/glitch/util/link_header.js
deleted file mode 100644
index a3e7ccf1c..000000000
--- a/app/javascript/themes/glitch/util/link_header.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import Link from 'http-link-header';
-import querystring from 'querystring';
-
-Link.parseAttrs = (link, parts) => {
-  let match = null;
-  let attr  = '';
-  let value = '';
-  let attrs = '';
-
-  let uriAttrs = /<(.*)>;\s*(.*)/gi.exec(parts);
-
-  if(uriAttrs) {
-    attrs = uriAttrs[2];
-    link  = Link.parseParams(link, uriAttrs[1]);
-  }
-
-  while(match = Link.attrPattern.exec(attrs)) { // eslint-disable-line no-cond-assign
-    attr  = match[1].toLowerCase();
-    value = match[4] || match[3] || match[2];
-
-    if( /\*$/.test(attr)) {
-      Link.setAttr(link, attr, Link.parseExtendedValue(value));
-    } else if(/%/.test(value)) {
-      Link.setAttr(link, attr, querystring.decode(value));
-    } else {
-      Link.setAttr(link, attr, value);
-    }
-  }
-
-  return link;
-};
-
-export default Link;
diff --git a/app/javascript/themes/glitch/util/load_polyfills.js b/app/javascript/themes/glitch/util/load_polyfills.js
deleted file mode 100644
index 8927b7358..000000000
--- a/app/javascript/themes/glitch/util/load_polyfills.js
+++ /dev/null
@@ -1,39 +0,0 @@
-// Convenience function to load polyfills and return a promise when it's done.
-// If there are no polyfills, then this is just Promise.resolve() which means
-// it will execute in the same tick of the event loop (i.e. near-instant).
-
-function importBasePolyfills() {
-  return import(/* webpackChunkName: "base_polyfills" */ './base_polyfills');
-}
-
-function importExtraPolyfills() {
-  return import(/* webpackChunkName: "extra_polyfills" */ './extra_polyfills');
-}
-
-function loadPolyfills() {
-  const needsBasePolyfills = !(
-    window.Intl &&
-    Object.assign &&
-    Number.isNaN &&
-    window.Symbol &&
-    Array.prototype.includes
-  );
-
-  // Latest version of Firefox and Safari do not have IntersectionObserver.
-  // Edge does not have requestIdleCallback and object-fit CSS property.
-  // This avoids shipping them all the polyfills.
-  const needsExtraPolyfills = !(
-    window.IntersectionObserver &&
-    window.IntersectionObserverEntry &&
-    'isIntersecting' in IntersectionObserverEntry.prototype &&
-    window.requestIdleCallback &&
-    'object-fit' in (new Image()).style
-  );
-
-  return Promise.all([
-    needsBasePolyfills && importBasePolyfills(),
-    needsExtraPolyfills && importExtraPolyfills(),
-  ]);
-}
-
-export default loadPolyfills;
diff --git a/app/javascript/themes/glitch/util/main.js b/app/javascript/themes/glitch/util/main.js
deleted file mode 100644
index c10a64ded..000000000
--- a/app/javascript/themes/glitch/util/main.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import * as WebPushSubscription from './web_push_subscription';
-import Mastodon from 'themes/glitch/containers/mastodon';
-import React from 'react';
-import ReactDOM from 'react-dom';
-import ready from './ready';
-
-const perf = require('./performance');
-
-function main() {
-  perf.start('main()');
-
-  if (window.history && history.replaceState) {
-    const { pathname, search, hash } = window.location;
-    const path = pathname + search + hash;
-    if (!(/^\/web[$/]/).test(path)) {
-      history.replaceState(null, document.title, `/web${path}`);
-    }
-  }
-
-  ready(() => {
-    const mountNode = document.getElementById('mastodon');
-    const props = JSON.parse(mountNode.getAttribute('data-props'));
-
-    ReactDOM.render(<Mastodon {...props} />, mountNode);
-    if (process.env.NODE_ENV === 'production') {
-      // avoid offline in dev mode because it's harder to debug
-      require('offline-plugin/runtime').install();
-      WebPushSubscription.register();
-    }
-    perf.stop('main()');
-
-    // remember the initial URL
-    if (window.history && typeof window._mastoInitialHistoryLen === 'undefined') {
-      window._mastoInitialHistoryLen = window.history.length;
-    }
-  });
-}
-
-export default main;
diff --git a/app/javascript/themes/glitch/util/optional_motion.js b/app/javascript/themes/glitch/util/optional_motion.js
deleted file mode 100644
index b8a57b22f..000000000
--- a/app/javascript/themes/glitch/util/optional_motion.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import { reduceMotion } from 'themes/glitch/util/initial_state';
-import ReducedMotion from './reduced_motion';
-import Motion from 'react-motion/lib/Motion';
-
-export default reduceMotion ? ReducedMotion : Motion;
diff --git a/app/javascript/themes/glitch/util/performance.js b/app/javascript/themes/glitch/util/performance.js
deleted file mode 100644
index 450a90626..000000000
--- a/app/javascript/themes/glitch/util/performance.js
+++ /dev/null
@@ -1,31 +0,0 @@
-//
-// Tools for performance debugging, only enabled in development mode.
-// Open up Chrome Dev Tools, then Timeline, then User Timing to see output.
-// Also see config/webpack/loaders/mark.js for the webpack loader marks.
-//
-
-let marky;
-
-if (process.env.NODE_ENV === 'development') {
-  if (typeof performance !== 'undefined' && performance.setResourceTimingBufferSize) {
-    // Increase Firefox's performance entry limit; otherwise it's capped to 150.
-    // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1331135
-    performance.setResourceTimingBufferSize(Infinity);
-  }
-  marky = require('marky');
-  // allows us to easily do e.g. ReactPerf.printWasted() while debugging
-  //window.ReactPerf = require('react-addons-perf');
-  //window.ReactPerf.start();
-}
-
-export function start(name) {
-  if (process.env.NODE_ENV === 'development') {
-    marky.mark(name);
-  }
-}
-
-export function stop(name) {
-  if (process.env.NODE_ENV === 'development') {
-    marky.stop(name);
-  }
-}
diff --git a/app/javascript/themes/glitch/util/react_router_helpers.js b/app/javascript/themes/glitch/util/react_router_helpers.js
deleted file mode 100644
index c02fb5247..000000000
--- a/app/javascript/themes/glitch/util/react_router_helpers.js
+++ /dev/null
@@ -1,64 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { Switch, Route } from 'react-router-dom';
-
-import ColumnLoading from 'themes/glitch/features/ui/components/column_loading';
-import BundleColumnError from 'themes/glitch/features/ui/components/bundle_column_error';
-import BundleContainer from 'themes/glitch/features/ui/containers/bundle_container';
-
-// Small wrapper to pass multiColumn to the route components
-export class WrappedSwitch extends React.PureComponent {
-
-  render () {
-    const { multiColumn, children } = this.props;
-
-    return (
-      <Switch>
-        {React.Children.map(children, child => React.cloneElement(child, { multiColumn }))}
-      </Switch>
-    );
-  }
-
-}
-
-WrappedSwitch.propTypes = {
-  multiColumn: PropTypes.bool,
-  children: PropTypes.node,
-};
-
-// Small Wraper to extract the params from the route and pass
-// them to the rendered component, together with the content to
-// be rendered inside (the children)
-export class WrappedRoute extends React.Component {
-
-  static propTypes = {
-    component: PropTypes.func.isRequired,
-    content: PropTypes.node,
-    multiColumn: PropTypes.bool,
-  }
-
-  renderComponent = ({ match }) => {
-    const { component, content, multiColumn } = this.props;
-
-    return (
-      <BundleContainer fetchComponent={component} loading={this.renderLoading} error={this.renderError}>
-        {Component => <Component params={match.params} multiColumn={multiColumn}>{content}</Component>}
-      </BundleContainer>
-    );
-  }
-
-  renderLoading = () => {
-    return <ColumnLoading />;
-  }
-
-  renderError = (props) => {
-    return <BundleColumnError {...props} />;
-  }
-
-  render () {
-    const { component: Component, content, ...rest } = this.props;
-
-    return <Route {...rest} render={this.renderComponent} />;
-  }
-
-}
diff --git a/app/javascript/themes/glitch/util/ready.js b/app/javascript/themes/glitch/util/ready.js
deleted file mode 100644
index dd543910b..000000000
--- a/app/javascript/themes/glitch/util/ready.js
+++ /dev/null
@@ -1,7 +0,0 @@
-export default function ready(loaded) {
-  if (['interactive', 'complete'].includes(document.readyState)) {
-    loaded();
-  } else {
-    document.addEventListener('DOMContentLoaded', loaded);
-  }
-}
diff --git a/app/javascript/themes/glitch/util/reduced_motion.js b/app/javascript/themes/glitch/util/reduced_motion.js
deleted file mode 100644
index 95519042b..000000000
--- a/app/javascript/themes/glitch/util/reduced_motion.js
+++ /dev/null
@@ -1,44 +0,0 @@
-// Like react-motion's Motion, but reduces all animations to cross-fades
-// for the benefit of users with motion sickness.
-import React from 'react';
-import Motion from 'react-motion/lib/Motion';
-import PropTypes from 'prop-types';
-
-const stylesToKeep = ['opacity', 'backgroundOpacity'];
-
-const extractValue = (value) => {
-  // This is either an object with a "val" property or it's a number
-  return (typeof value === 'object' && value && 'val' in value) ? value.val : value;
-};
-
-class ReducedMotion extends React.Component {
-
-  static propTypes = {
-    defaultStyle: PropTypes.object,
-    style: PropTypes.object,
-    children: PropTypes.func,
-  }
-
-  render() {
-
-    const { style, defaultStyle, children } = this.props;
-
-    Object.keys(style).forEach(key => {
-      if (stylesToKeep.includes(key)) {
-        return;
-      }
-      // If it's setting an x or height or scale or some other value, we need
-      // to preserve the end-state value without actually animating it
-      style[key] = defaultStyle[key] = extractValue(style[key]);
-    });
-
-    return (
-      <Motion style={style} defaultStyle={defaultStyle}>
-        {children}
-      </Motion>
-    );
-  }
-
-}
-
-export default ReducedMotion;
diff --git a/app/javascript/themes/glitch/util/rtl.js b/app/javascript/themes/glitch/util/rtl.js
deleted file mode 100644
index 00870a15d..000000000
--- a/app/javascript/themes/glitch/util/rtl.js
+++ /dev/null
@@ -1,31 +0,0 @@
-// U+0590  to U+05FF  - Hebrew
-// U+0600  to U+06FF  - Arabic
-// U+0700  to U+074F  - Syriac
-// U+0750  to U+077F  - Arabic Supplement
-// U+0780  to U+07BF  - Thaana
-// U+07C0  to U+07FF  - N'Ko
-// U+0800  to U+083F  - Samaritan
-// U+08A0  to U+08FF  - Arabic Extended-A
-// U+FB1D  to U+FB4F  - Hebrew presentation forms
-// U+FB50  to U+FDFF  - Arabic presentation forms A
-// U+FE70  to U+FEFF  - Arabic presentation forms B
-
-const rtlChars = /[\u0590-\u083F]|[\u08A0-\u08FF]|[\uFB1D-\uFDFF]|[\uFE70-\uFEFF]/mg;
-
-export function isRtl(text) {
-  if (text.length === 0) {
-    return false;
-  }
-
-  text = text.replace(/(?:^|[^\/\w])@([a-z0-9_]+(@[a-z0-9\.\-]+)?)/ig, '');
-  text = text.replace(/(?:^|[^\/\w])#([\S]+)/ig, '');
-  text = text.replace(/\s+/g, '');
-
-  const matches = text.match(rtlChars);
-
-  if (!matches) {
-    return false;
-  }
-
-  return matches.length / text.length > 0.3;
-};
diff --git a/app/javascript/themes/glitch/util/schedule_idle_task.js b/app/javascript/themes/glitch/util/schedule_idle_task.js
deleted file mode 100644
index b04d4a8ee..000000000
--- a/app/javascript/themes/glitch/util/schedule_idle_task.js
+++ /dev/null
@@ -1,29 +0,0 @@
-// Wrapper to call requestIdleCallback() to schedule low-priority work.
-// See https://developer.mozilla.org/en-US/docs/Web/API/Background_Tasks_API
-// for a good breakdown of the concepts behind this.
-
-import Queue from 'tiny-queue';
-
-const taskQueue = new Queue();
-let runningRequestIdleCallback = false;
-
-function runTasks(deadline) {
-  while (taskQueue.length && deadline.timeRemaining() > 0) {
-    taskQueue.shift()();
-  }
-  if (taskQueue.length) {
-    requestIdleCallback(runTasks);
-  } else {
-    runningRequestIdleCallback = false;
-  }
-}
-
-function scheduleIdleTask(task) {
-  taskQueue.push(task);
-  if (!runningRequestIdleCallback) {
-    runningRequestIdleCallback = true;
-    requestIdleCallback(runTasks);
-  }
-}
-
-export default scheduleIdleTask;
diff --git a/app/javascript/themes/glitch/util/scroll.js b/app/javascript/themes/glitch/util/scroll.js
deleted file mode 100644
index 2af07e0fb..000000000
--- a/app/javascript/themes/glitch/util/scroll.js
+++ /dev/null
@@ -1,30 +0,0 @@
-const easingOutQuint = (x, t, b, c, d) => c * ((t = t / d - 1) * t * t * t * t + 1) + b;
-
-const scroll = (node, key, target) => {
-  const startTime = Date.now();
-  const offset    = node[key];
-  const gap       = target - offset;
-  const duration  = 1000;
-  let interrupt   = false;
-
-  const step = () => {
-    const elapsed    = Date.now() - startTime;
-    const percentage = elapsed / duration;
-
-    if (percentage > 1 || interrupt) {
-      return;
-    }
-
-    node[key] = easingOutQuint(0, elapsed, offset, gap, duration);
-    requestAnimationFrame(step);
-  };
-
-  step();
-
-  return () => {
-    interrupt = true;
-  };
-};
-
-export const scrollRight = (node, position) => scroll(node, 'scrollLeft', position);
-export const scrollTop = (node) => scroll(node, 'scrollTop', 0);
diff --git a/app/javascript/themes/glitch/util/stream.js b/app/javascript/themes/glitch/util/stream.js
deleted file mode 100644
index 36c68ffc5..000000000
--- a/app/javascript/themes/glitch/util/stream.js
+++ /dev/null
@@ -1,73 +0,0 @@
-import WebSocketClient from 'websocket.js';
-
-export function connectStream(path, pollingRefresh = null, callbacks = () => ({ onConnect() {}, onDisconnect() {}, onReceive() {} })) {
-  return (dispatch, getState) => {
-    const streamingAPIBaseURL = getState().getIn(['meta', 'streaming_api_base_url']);
-    const accessToken = getState().getIn(['meta', 'access_token']);
-    const { onConnect, onDisconnect, onReceive } = callbacks(dispatch, getState);
-    let polling = null;
-
-    const setupPolling = () => {
-      polling = setInterval(() => {
-        pollingRefresh(dispatch);
-      }, 20000);
-    };
-
-    const clearPolling = () => {
-      if (polling) {
-        clearInterval(polling);
-        polling = null;
-      }
-    };
-
-    const subscription = getStream(streamingAPIBaseURL, accessToken, path, {
-      connected () {
-        if (pollingRefresh) {
-          clearPolling();
-        }
-        onConnect();
-      },
-
-      disconnected () {
-        if (pollingRefresh) {
-          setupPolling();
-        }
-        onDisconnect();
-      },
-
-      received (data) {
-        onReceive(data);
-      },
-
-      reconnected () {
-        if (pollingRefresh) {
-          clearPolling();
-          pollingRefresh(dispatch);
-        }
-        onConnect();
-      },
-
-    });
-
-    const disconnect = () => {
-      if (subscription) {
-        subscription.close();
-      }
-      clearPolling();
-    };
-
-    return disconnect;
-  };
-}
-
-
-export default function getStream(streamingAPIBaseURL, accessToken, stream, { connected, received, disconnected, reconnected }) {
-  const ws = new WebSocketClient(`${streamingAPIBaseURL}/api/v1/streaming/?access_token=${accessToken}&stream=${stream}`);
-
-  ws.onopen      = connected;
-  ws.onmessage   = e => received(JSON.parse(e.data));
-  ws.onclose     = disconnected;
-  ws.onreconnect = reconnected;
-
-  return ws;
-};
diff --git a/app/javascript/themes/glitch/util/url_regex.js b/app/javascript/themes/glitch/util/url_regex.js
deleted file mode 100644
index e676d1879..000000000
--- a/app/javascript/themes/glitch/util/url_regex.js
+++ /dev/null
@@ -1,196 +0,0 @@
-const regexen = {};
-
-const regexSupplant = function(regex, flags) {
-  flags = flags || '';
-  if (typeof regex !== 'string') {
-    if (regex.global && flags.indexOf('g') < 0) {
-      flags += 'g';
-    }
-    if (regex.ignoreCase && flags.indexOf('i') < 0) {
-      flags += 'i';
-    }
-    if (regex.multiline && flags.indexOf('m') < 0) {
-      flags += 'm';
-    }
-
-    regex = regex.source;
-  }
-  return new RegExp(regex.replace(/#\{(\w+)\}/g, function(match, name) {
-    var newRegex = regexen[name] || '';
-    if (typeof newRegex !== 'string') {
-      newRegex = newRegex.source;
-    }
-    return newRegex;
-  }), flags);
-};
-
-const stringSupplant = function(str, values) {
-  return str.replace(/#\{(\w+)\}/g, function(match, name) {
-    return values[name] || '';
-  });
-};
-
-export const urlRegex = (function() {
-  regexen.spaces_group = /\x09-\x0D\x20\x85\xA0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000/;
-  regexen.invalid_chars_group = /\uFFFE\uFEFF\uFFFF\u202A-\u202E/;
-  regexen.punct = /\!'#%&'\(\)*\+,\\\-\.\/:;<=>\?@\[\]\^_{|}~\$/;
-  regexen.validUrlPrecedingChars = regexSupplant(/(?:[^A-Za-z0-9@@$###{invalid_chars_group}]|^)/);
-  regexen.invalidDomainChars = stringSupplant('#{punct}#{spaces_group}#{invalid_chars_group}', regexen);
-  regexen.validDomainChars = regexSupplant(/[^#{invalidDomainChars}]/);
-  regexen.validSubdomain = regexSupplant(/(?:(?:#{validDomainChars}(?:[_-]|#{validDomainChars})*)?#{validDomainChars}\.)/);
-  regexen.validDomainName = regexSupplant(/(?:(?:#{validDomainChars}(?:-|#{validDomainChars})*)?#{validDomainChars}\.)/);
-  regexen.validGTLD = regexSupplant(RegExp(
-  '(?:(?:' +
-    '삼성|닷컴|닷넷|香格里拉|餐厅|食品|飞利浦|電訊盈科|集团|通販|购物|谷歌|诺基亚|联通|网络|网站|网店|网址|组织机构|移动|珠宝|点看|游戏|淡马锡|机构|書籍|时尚|新闻|政府|' +
-    '政务|手表|手机|我爱你|慈善|微博|广东|工行|家電|娱乐|天主教|大拿|大众汽车|在线|嘉里大酒店|嘉里|商标|商店|商城|公益|公司|八卦|健康|信息|佛山|企业|中文网|中信|世界|' +
-    'ポイント|ファッション|セール|ストア|コム|グーグル|クラウド|みんな|คอม|संगठन|नेट|कॉम|همراه|موقع|موبايلي|كوم|كاثوليك|عرب|شبكة|' +
-    'بيتك|بازار|العليان|ارامكو|اتصالات|ابوظبي|קום|сайт|рус|орг|онлайн|москва|ком|католик|дети|' +
-    'zuerich|zone|zippo|zip|zero|zara|zappos|yun|youtube|you|yokohama|yoga|yodobashi|yandex|yamaxun|' +
-    'yahoo|yachts|xyz|xxx|xperia|xin|xihuan|xfinity|xerox|xbox|wtf|wtc|wow|world|works|work|woodside|' +
-    'wolterskluwer|wme|winners|wine|windows|win|williamhill|wiki|wien|whoswho|weir|weibo|wedding|wed|' +
-    'website|weber|webcam|weatherchannel|weather|watches|watch|warman|wanggou|wang|walter|walmart|' +
-    'wales|vuelos|voyage|voto|voting|vote|volvo|volkswagen|vodka|vlaanderen|vivo|viva|vistaprint|' +
-    'vista|vision|visa|virgin|vip|vin|villas|viking|vig|video|viajes|vet|versicherung|' +
-    'vermögensberatung|vermögensberater|verisign|ventures|vegas|vanguard|vana|vacations|ups|uol|uno|' +
-    'university|unicom|uconnect|ubs|ubank|tvs|tushu|tunes|tui|tube|trv|trust|travelersinsurance|' +
-    'travelers|travelchannel|travel|training|trading|trade|toys|toyota|town|tours|total|toshiba|' +
-    'toray|top|tools|tokyo|today|tmall|tkmaxx|tjx|tjmaxx|tirol|tires|tips|tiffany|tienda|tickets|' +
-    'tiaa|theatre|theater|thd|teva|tennis|temasek|telefonica|telecity|tel|technology|tech|team|tdk|' +
-    'tci|taxi|tax|tattoo|tatar|tatamotors|target|taobao|talk|taipei|tab|systems|symantec|sydney|' +
-    'swiss|swiftcover|swatch|suzuki|surgery|surf|support|supply|supplies|sucks|style|study|studio|' +
-    'stream|store|storage|stockholm|stcgroup|stc|statoil|statefarm|statebank|starhub|star|staples|' +
-    'stada|srt|srl|spreadbetting|spot|spiegel|space|soy|sony|song|solutions|solar|sohu|software|' +
-    'softbank|social|soccer|sncf|smile|smart|sling|skype|sky|skin|ski|site|singles|sina|silk|shriram|' +
-    'showtime|show|shouji|shopping|shop|shoes|shiksha|shia|shell|shaw|sharp|shangrila|sfr|sexy|sex|' +
-    'sew|seven|ses|services|sener|select|seek|security|secure|seat|search|scot|scor|scjohnson|' +
-    'science|schwarz|schule|school|scholarships|schmidt|schaeffler|scb|sca|sbs|sbi|saxo|save|sas|' +
-    'sarl|sapo|sap|sanofi|sandvikcoromant|sandvik|samsung|samsclub|salon|sale|sakura|safety|safe|' +
-    'saarland|ryukyu|rwe|run|ruhr|rugby|rsvp|room|rogers|rodeo|rocks|rocher|rmit|rip|rio|ril|' +
-    'rightathome|ricoh|richardli|rich|rexroth|reviews|review|restaurant|rest|republican|report|' +
-    'repair|rentals|rent|ren|reliance|reit|reisen|reise|rehab|redumbrella|redstone|red|recipes|' +
-    'realty|realtor|realestate|read|raid|radio|racing|qvc|quest|quebec|qpon|pwc|pub|prudential|pru|' +
-    'protection|property|properties|promo|progressive|prof|productions|prod|pro|prime|press|praxi|' +
-    'pramerica|post|porn|politie|poker|pohl|pnc|plus|plumbing|playstation|play|place|pizza|pioneer|' +
-    'pink|ping|pin|pid|pictures|pictet|pics|piaget|physio|photos|photography|photo|phone|philips|phd|' +
-    'pharmacy|pfizer|pet|pccw|pay|passagens|party|parts|partners|pars|paris|panerai|panasonic|' +
-    'pamperedchef|page|ovh|ott|otsuka|osaka|origins|orientexpress|organic|org|orange|oracle|open|ooo|' +
-    'onyourside|online|onl|ong|one|omega|ollo|oldnavy|olayangroup|olayan|okinawa|office|off|observer|' +
-    'obi|nyc|ntt|nrw|nra|nowtv|nowruz|now|norton|northwesternmutual|nokia|nissay|nissan|ninja|nikon|' +
-    'nike|nico|nhk|ngo|nfl|nexus|nextdirect|next|news|newholland|new|neustar|network|netflix|netbank|' +
-    'net|nec|nba|navy|natura|nationwide|name|nagoya|nadex|nab|mutuelle|mutual|museum|mtr|mtpc|mtn|' +
-    'msd|movistar|movie|mov|motorcycles|moto|moscow|mortgage|mormon|mopar|montblanc|monster|money|' +
-    'monash|mom|moi|moe|moda|mobily|mobile|mobi|mma|mls|mlb|mitsubishi|mit|mint|mini|mil|microsoft|' +
-    'miami|metlife|merckmsd|meo|menu|men|memorial|meme|melbourne|meet|media|med|mckinsey|mcdonalds|' +
-    'mcd|mba|mattel|maserati|marshalls|marriott|markets|marketing|market|map|mango|management|man|' +
-    'makeup|maison|maif|madrid|macys|luxury|luxe|lupin|lundbeck|ltda|ltd|lplfinancial|lpl|love|lotto|' +
-    'lotte|london|lol|loft|locus|locker|loans|loan|lixil|living|live|lipsy|link|linde|lincoln|limo|' +
-    'limited|lilly|like|lighting|lifestyle|lifeinsurance|life|lidl|liaison|lgbt|lexus|lego|legal|' +
-    'lefrak|leclerc|lease|lds|lawyer|law|latrobe|latino|lat|lasalle|lanxess|landrover|land|lancome|' +
-    'lancia|lancaster|lamer|lamborghini|ladbrokes|lacaixa|kyoto|kuokgroup|kred|krd|kpn|kpmg|kosher|' +
-    'komatsu|koeln|kiwi|kitchen|kindle|kinder|kim|kia|kfh|kerryproperties|kerrylogistics|kerryhotels|' +
-    'kddi|kaufen|juniper|juegos|jprs|jpmorgan|joy|jot|joburg|jobs|jnj|jmp|jll|jlc|jio|jewelry|jetzt|' +
-    'jeep|jcp|jcb|java|jaguar|iwc|iveco|itv|itau|istanbul|ist|ismaili|iselect|irish|ipiranga|' +
-    'investments|intuit|international|intel|int|insure|insurance|institute|ink|ing|info|infiniti|' +
-    'industries|immobilien|immo|imdb|imamat|ikano|iinet|ifm|ieee|icu|ice|icbc|ibm|hyundai|hyatt|' +
-    'hughes|htc|hsbc|how|house|hotmail|hotels|hoteles|hot|hosting|host|hospital|horse|honeywell|' +
-    'honda|homesense|homes|homegoods|homedepot|holiday|holdings|hockey|hkt|hiv|hitachi|hisamitsu|' +
-    'hiphop|hgtv|hermes|here|helsinki|help|healthcare|health|hdfcbank|hdfc|hbo|haus|hangout|hamburg|' +
-    'hair|guru|guitars|guide|guge|gucci|guardian|group|grocery|gripe|green|gratis|graphics|grainger|' +
-    'gov|got|gop|google|goog|goodyear|goodhands|goo|golf|goldpoint|gold|godaddy|gmx|gmo|gmbh|gmail|' +
-    'globo|global|gle|glass|glade|giving|gives|gifts|gift|ggee|george|genting|gent|gea|gdn|gbiz|' +
-    'garden|gap|games|game|gallup|gallo|gallery|gal|fyi|futbol|furniture|fund|fun|fujixerox|fujitsu|' +
-    'ftr|frontier|frontdoor|frogans|frl|fresenius|free|fox|foundation|forum|forsale|forex|ford|' +
-    'football|foodnetwork|food|foo|fly|flsmidth|flowers|florist|flir|flights|flickr|fitness|fit|' +
-    'fishing|fish|firmdale|firestone|fire|financial|finance|final|film|fido|fidelity|fiat|ferrero|' +
-    'ferrari|feedback|fedex|fast|fashion|farmers|farm|fans|fan|family|faith|fairwinds|fail|fage|' +
-    'extraspace|express|exposed|expert|exchange|everbank|events|eus|eurovision|etisalat|esurance|' +
-    'estate|esq|erni|ericsson|equipment|epson|epost|enterprises|engineering|engineer|energy|emerck|' +
-    'email|education|edu|edeka|eco|eat|earth|dvr|dvag|durban|dupont|duns|dunlop|duck|dubai|dtv|drive|' +
-    'download|dot|doosan|domains|doha|dog|dodge|doctor|docs|dnp|diy|dish|discover|discount|directory|' +
-    'direct|digital|diet|diamonds|dhl|dev|design|desi|dentist|dental|democrat|delta|deloitte|dell|' +
-    'delivery|degree|deals|dealer|deal|dds|dclk|day|datsun|dating|date|data|dance|dad|dabur|cyou|' +
-    'cymru|cuisinella|csc|cruises|cruise|crs|crown|cricket|creditunion|creditcard|credit|courses|' +
-    'coupons|coupon|country|corsica|coop|cool|cookingchannel|cooking|contractors|contact|consulting|' +
-    'construction|condos|comsec|computer|compare|company|community|commbank|comcast|com|cologne|' +
-    'college|coffee|codes|coach|clubmed|club|cloud|clothing|clinique|clinic|click|cleaning|claims|' +
-    'cityeats|city|citic|citi|citadel|cisco|circle|cipriani|church|chrysler|chrome|christmas|chloe|' +
-    'chintai|cheap|chat|chase|channel|chanel|cfd|cfa|cern|ceo|center|ceb|cbs|cbre|cbn|cba|catholic|' +
-    'catering|cat|casino|cash|caseih|case|casa|cartier|cars|careers|career|care|cards|caravan|car|' +
-    'capitalone|capital|capetown|canon|cancerresearch|camp|camera|cam|calvinklein|call|cal|cafe|cab|' +
-    'bzh|buzz|buy|business|builders|build|bugatti|budapest|brussels|brother|broker|broadway|' +
-    'bridgestone|bradesco|box|boutique|bot|boston|bostik|bosch|boots|booking|book|boo|bond|bom|bofa|' +
-    'boehringer|boats|bnpparibas|bnl|bmw|bms|blue|bloomberg|blog|blockbuster|blanco|blackfriday|' +
-    'black|biz|bio|bingo|bing|bike|bid|bible|bharti|bet|bestbuy|best|berlin|bentley|beer|beauty|' +
-    'beats|bcn|bcg|bbva|bbt|bbc|bayern|bauhaus|basketball|baseball|bargains|barefoot|barclays|' +
-    'barclaycard|barcelona|bar|bank|band|bananarepublic|banamex|baidu|baby|azure|axa|aws|avianca|' +
-    'autos|auto|author|auspost|audio|audible|audi|auction|attorney|athleta|associates|asia|asda|arte|' +
-    'art|arpa|army|archi|aramco|arab|aquarelle|apple|app|apartments|aol|anz|anquan|android|analytics|' +
-    'amsterdam|amica|amfam|amex|americanfamily|americanexpress|alstom|alsace|ally|allstate|allfinanz|' +
-    'alipay|alibaba|alfaromeo|akdn|airtel|airforce|airbus|aigo|aig|agency|agakhan|africa|afl|' +
-    'afamilycompany|aetna|aero|aeg|adult|ads|adac|actor|active|aco|accountants|accountant|accenture|' +
-    'academy|abudhabi|abogado|able|abc|abbvie|abbott|abb|abarth|aarp|aaa|onion' +
-  ')(?=[^0-9a-zA-Z@]|$))'));
-  regexen.validCCTLD = regexSupplant(RegExp(
-  '(?:(?:' +
-      '한국|香港|澳門|新加坡|台灣|台湾|中國|中国|გე|ไทย|ලංකා|ഭാരതം|ಭಾರತ|భారత్|சிங்கப்பூர்|இலங்கை|இந்தியா|ଭାରତ|ભારત|ਭਾਰਤ|' +
-      'ভাৰত|ভারত|বাংলা|भारोत|भारतम्|भारत|ڀارت|پاکستان|مليسيا|مصر|قطر|فلسطين|عمان|عراق|سورية|سودان|تونس|' +
-      'بھارت|بارت|ایران|امارات|المغرب|السعودية|الجزائر|الاردن|հայ|қаз|укр|срб|рф|мон|мкд|ею|бел|бг|ελ|' +
-      'zw|zm|za|yt|ye|ws|wf|vu|vn|vi|vg|ve|vc|va|uz|uy|us|um|uk|ug|ua|tz|tw|tv|tt|tr|tp|to|tn|tm|tl|tk|' +
-      'tj|th|tg|tf|td|tc|sz|sy|sx|sv|su|st|ss|sr|so|sn|sm|sl|sk|sj|si|sh|sg|se|sd|sc|sb|sa|rw|ru|rs|ro|' +
-      're|qa|py|pw|pt|ps|pr|pn|pm|pl|pk|ph|pg|pf|pe|pa|om|nz|nu|nr|np|no|nl|ni|ng|nf|ne|nc|na|mz|my|mx|' +
-      'mw|mv|mu|mt|ms|mr|mq|mp|mo|mn|mm|ml|mk|mh|mg|mf|me|md|mc|ma|ly|lv|lu|lt|ls|lr|lk|li|lc|lb|la|kz|' +
-      'ky|kw|kr|kp|kn|km|ki|kh|kg|ke|jp|jo|jm|je|it|is|ir|iq|io|in|im|il|ie|id|hu|ht|hr|hn|hm|hk|gy|gw|' +
-      'gu|gt|gs|gr|gq|gp|gn|gm|gl|gi|gh|gg|gf|ge|gd|gb|ga|fr|fo|fm|fk|fj|fi|eu|et|es|er|eh|eg|ee|ec|dz|' +
-      'do|dm|dk|dj|de|cz|cy|cx|cw|cv|cu|cr|co|cn|cm|cl|ck|ci|ch|cg|cf|cd|cc|ca|bz|by|bw|bv|bt|bs|br|bq|' +
-      'bo|bn|bm|bl|bj|bi|bh|bg|bf|be|bd|bb|ba|az|ax|aw|au|at|as|ar|aq|ao|an|am|al|ai|ag|af|ae|ad|ac' +
-  ')(?=[^0-9a-zA-Z@]|$))'));
-  regexen.validPunycode = /(?:xn--[0-9a-z]+)/;
-  regexen.validSpecialCCTLD = /(?:(?:co|tv)(?=[^0-9a-zA-Z@]|$))/;
-  regexen.validDomain = regexSupplant(/(?:#{validSubdomain}*#{validDomainName}(?:#{validGTLD}|#{validCCTLD}|#{validPunycode}))/);
-  regexen.validPortNumber = /[0-9]+/;
-  regexen.pd = /\u002d\u058a\u05be\u1400\u1806\u2010-\u2015\u2e17\u2e1a\u2e3a\u2e40\u301c\u3030\u30a0\ufe31\ufe58\ufe63\uff0d/;
-  regexen.validGeneralUrlPathChars = regexSupplant(/[^#{spaces_group}\(\)\?]/i);
-  // Allow URL paths to contain up to two nested levels of balanced parens
-  //  1. Used in Wikipedia URLs like /Primer_(film)
-  //  2. Used in IIS sessions like /S(dfd346)/
-  //  3. Used in Rdio URLs like /track/We_Up_(Album_Version_(Edited))/
-  regexen.validUrlBalancedParens = regexSupplant(
-    '\\('                                   +
-      '(?:'                                 +
-        '#{validGeneralUrlPathChars}+'      +
-        '|'                                 +
-        // allow one nested level of balanced parentheses
-        '(?:'                               +
-          '#{validGeneralUrlPathChars}*'    +
-          '\\('                             +
-            '#{validGeneralUrlPathChars}+'  +
-          '\\)'                             +
-          '#{validGeneralUrlPathChars}*'    +
-        ')'                                 +
-      ')'                                   +
-    '\\)'
-  , 'i');
-  // Valid end-of-path chracters (so /foo. does not gobble the period).
-  // 1. Allow =&# for empty URL parameters and other URL-join artifacts
-  regexen.validUrlPathEndingChars = regexSupplant(/[^#{spaces_group}\(\)\?!\*';:=\,\.\$%\[\]#{pd}~&\|@]|(?:#{validUrlBalancedParens})/i);
-  // Allow @ in a url, but only in the middle. Catch things like http://example.com/@user/
-  regexen.validUrlPath = regexSupplant('(?:' +
-    '(?:' +
-      '#{validGeneralUrlPathChars}*' +
-        '(?:#{validUrlBalancedParens}#{validGeneralUrlPathChars}*)*' +
-        '#{validUrlPathEndingChars}'+
-      ')|(?:@#{validGeneralUrlPathChars}+\/)'+
-    ')', 'i');
-  regexen.validUrlQueryChars = /[a-z0-9!?\*'@\(\);:&=\+\$\/%#\[\]\-_\.,~|]/i;
-  regexen.validUrlQueryEndingChars = /[a-z0-9_&=#\/]/i;
-  regexen.validUrl = regexSupplant(
-    '('                                                          + // $1 URL
-      '(https?:\\/\\/)'                                          + // $2 Protocol
-      '(#{validDomain})'                                         + // $3 Domain(s)
-      '(?::(#{validPortNumber}))?'                               + // $4 Port number (optional)
-      '(\\/#{validUrlPath}*)?'                                   + // $5 URL Path
-      '(\\?#{validUrlQueryChars}*#{validUrlQueryEndingChars})?'  + // $6 Query String
-    ')'
-  , 'gi');
-  return regexen.validUrl;
-}());
diff --git a/app/javascript/themes/glitch/util/uuid.js b/app/javascript/themes/glitch/util/uuid.js
deleted file mode 100644
index be1899305..000000000
--- a/app/javascript/themes/glitch/util/uuid.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export default function uuid(a) {
-  return a ? (a^Math.random() * 16 >> a / 4).toString(16) : ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, uuid);
-};
diff --git a/app/javascript/themes/glitch/util/web_push_subscription.js b/app/javascript/themes/glitch/util/web_push_subscription.js
deleted file mode 100644
index 70b26105b..000000000
--- a/app/javascript/themes/glitch/util/web_push_subscription.js
+++ /dev/null
@@ -1,105 +0,0 @@
-import axios from 'axios';
-import { store } from 'themes/glitch/containers/mastodon';
-import { setBrowserSupport, setSubscription, clearSubscription } from 'themes/glitch/actions/push_notifications';
-
-// Taken from https://www.npmjs.com/package/web-push
-const urlBase64ToUint8Array = (base64String) => {
-  const padding = '='.repeat((4 - base64String.length % 4) % 4);
-  const base64 = (base64String + padding)
-    .replace(/\-/g, '+')
-    .replace(/_/g, '/');
-
-  const rawData = window.atob(base64);
-  const outputArray = new Uint8Array(rawData.length);
-
-  for (let i = 0; i < rawData.length; ++i) {
-    outputArray[i] = rawData.charCodeAt(i);
-  }
-  return outputArray;
-};
-
-const getApplicationServerKey = () => document.querySelector('[name="applicationServerKey"]').getAttribute('content');
-
-const getRegistration = () => navigator.serviceWorker.ready;
-
-const getPushSubscription = (registration) =>
-  registration.pushManager.getSubscription()
-    .then(subscription => ({ registration, subscription }));
-
-const subscribe = (registration) =>
-  registration.pushManager.subscribe({
-    userVisibleOnly: true,
-    applicationServerKey: urlBase64ToUint8Array(getApplicationServerKey()),
-  });
-
-const unsubscribe = ({ registration, subscription }) =>
-  subscription ? subscription.unsubscribe().then(() => registration) : registration;
-
-const sendSubscriptionToBackend = (subscription) =>
-  axios.post('/api/web/push_subscriptions', {
-    subscription,
-  }).then(response => response.data);
-
-// Last one checks for payload support: https://web-push-book.gauntface.com/chapter-06/01-non-standards-browsers/#no-payload
-const supportsPushNotifications = ('serviceWorker' in navigator && 'PushManager' in window && 'getKey' in PushSubscription.prototype);
-
-export function register () {
-  store.dispatch(setBrowserSupport(supportsPushNotifications));
-
-  if (supportsPushNotifications) {
-    if (!getApplicationServerKey()) {
-      console.error('The VAPID public key is not set. You will not be able to receive Web Push Notifications.');
-      return;
-    }
-
-    getRegistration()
-      .then(getPushSubscription)
-      .then(({ registration, subscription }) => {
-        if (subscription !== null) {
-          // We have a subscription, check if it is still valid
-          const currentServerKey = (new Uint8Array(subscription.options.applicationServerKey)).toString();
-          const subscriptionServerKey = urlBase64ToUint8Array(getApplicationServerKey()).toString();
-          const serverEndpoint = store.getState().getIn(['push_notifications', 'subscription', 'endpoint']);
-
-          // If the VAPID public key did not change and the endpoint corresponds
-          // to the endpoint saved in the backend, the subscription is valid
-          if (subscriptionServerKey === currentServerKey && subscription.endpoint === serverEndpoint) {
-            return subscription;
-          } else {
-            // Something went wrong, try to subscribe again
-            return unsubscribe({ registration, subscription }).then(subscribe).then(sendSubscriptionToBackend);
-          }
-        }
-
-        // No subscription, try to subscribe
-        return subscribe(registration).then(sendSubscriptionToBackend);
-      })
-      .then(subscription => {
-        // If we got a PushSubscription (and not a subscription object from the backend)
-        // it means that the backend subscription is valid (and was set during hydration)
-        if (!(subscription instanceof PushSubscription)) {
-          store.dispatch(setSubscription(subscription));
-        }
-      })
-      .catch(error => {
-        if (error.code === 20 && error.name === 'AbortError') {
-          console.warn('Your browser supports Web Push Notifications, but does not seem to implement the VAPID protocol.');
-        } else if (error.code === 5 && error.name === 'InvalidCharacterError') {
-          console.error('The VAPID public key seems to be invalid:', getApplicationServerKey());
-        }
-
-        // Clear alerts and hide UI settings
-        store.dispatch(clearSubscription());
-
-        try {
-          getRegistration()
-            .then(getPushSubscription)
-            .then(unsubscribe);
-        } catch (e) {
-
-        }
-      });
-  } else {
-    console.warn('Your browser does not support Web Push Notifications.');
-  }
-}