logo

pleroma-fe

My custom branche(s) on git.pleroma.social/pleroma/pleroma-fe git clone https://hacktivis.me/git/pleroma-fe.git
commit: 3a507ba9b2fde594950a09c9d7934d54561a187c
parent b13d8f7e6339e877a38a28008630dc8ec64abcdf
Author: HJ <30-hj@users.noreply.git.pleroma.social>
Date:   Fri, 23 Dec 2022 13:31:18 +0000

Merge branch 'develop' into 'master'

Update stable - 2.5.0 release

See merge request pleroma/pleroma-fe!1711

Diffstat:

M.babelrc6+++---
M.eslintrc.js5+++--
M.gitignore1+
M.gitlab-ci.yml2+-
A.gitlab/issue_templates/Bug.md25+++++++++++++++++++++++++
A.gitlab/issue_templates/Suggestion.md11+++++++++++
A.gitlab/issue_templates/default.md7+++++++
A.gitlab/merge_request_templates/default.md30++++++++++++++++++++++++++++++
M.node-version2+-
MCHANGELOG.md80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
MCONTRIBUTORS.md2++
MREADME.md27+++++++++++----------------
Mbuild/build.js7+++++++
Mbuild/dev-server.js27++++++++++++++++-----------
Abuild/update-emoji.js27+++++++++++++++++++++++++++
Mbuild/webpack.base.conf.js82+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mbuild/webpack.dev.conf.js6++++--
Mbuild/webpack.prod.conf.js17+++++++++++------
Mconfig/index.js5++++-
Aimage-1.png0
Aimage.png0
Mindex.html1+
Mpackage.json210++++++++++++++++++++++++++++++++++++++++---------------------------------------
Arenovate.json6++++++
Msrc/App.js69+++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/App.scss886+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/App.vue64++++++++++++++++++++++++++++++++++++++--------------------------
Asrc/_mixins.scss17+++++++++++++++++
Msrc/_variables.scss2++
Asrc/assets/pleromatan_apology.png0
Asrc/assets/pleromatan_apology_fox.png0
Asrc/assets/pleromatan_apology_fox_mask.png0
Asrc/assets/pleromatan_apology_mask.png0
Msrc/boot/after_store.js62++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/boot/routes.js29++++++++++++++++++++++-------
Msrc/components/about/about.vue4++--
Msrc/components/account_actions/account_actions.js9+++++++--
Msrc/components/account_actions/account_actions.vue16++++++++++------
Asrc/components/announcement/announcement.js105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/announcement/announcement.vue136+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/announcement_editor/announcement_editor.js13+++++++++++++
Asrc/components/announcement_editor/announcement_editor.vue60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/announcements_page/announcements_page.js55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/announcements_page/announcements_page.vue79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/async_component_error/async_component_error.vue1+
Msrc/components/attachment/attachment.js115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Asrc/components/attachment/attachment.scss268+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/attachment/attachment.vue534+++++++++++++++++++++++++++++++++++--------------------------------------------
Msrc/components/auth_form/auth_form.js5+++--
Msrc/components/avatar_list/avatar_list.vue2+-
Msrc/components/basic_user_card/basic_user_card.js16+++++-----------
Msrc/components/basic_user_card/basic_user_card.vue40+++++++++++++++++++---------------------
Msrc/components/bookmark_timeline/bookmark_timeline.js2+-
Msrc/components/chat/chat.js107+++++++++++++++++--------------------------------------------------------------
Msrc/components/chat/chat.scss88++++++++++++-------------------------------------------------------------------
Msrc/components/chat/chat.vue125++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msrc/components/chat/chat_layout_utils.js27+++++++++------------------
Msrc/components/chat_list/chat_list.vue4++--
Msrc/components/chat_list_item/chat_list_item.scss4++--
Msrc/components/chat_message/chat_message.js9++++-----
Msrc/components/chat_message/chat_message.scss11+++++++----
Msrc/components/chat_message/chat_message.vue16++++++++--------
Msrc/components/chat_new/chat_new.scss8++++----
Msrc/components/chat_new/chat_new.vue7+++----
Msrc/components/chat_title/chat_title.js17+++++++----------
Msrc/components/chat_title/chat_title.vue40+++++++++++++++++++---------------------
Msrc/components/checkbox/checkbox.vue15++++++---------
Msrc/components/color_input/color_input.scss8++++----
Msrc/components/color_input/color_input.vue25+++++++++++++------------
Msrc/components/conversation/conversation.js396++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/components/conversation/conversation.vue277+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Msrc/components/desktop_nav/desktop_nav.js24++++++++++++++----------
Msrc/components/desktop_nav/desktop_nav.scss41++++++++++++++++++++++++++++++++++-------
Msrc/components/desktop_nav/desktop_nav.vue6++++--
Msrc/components/dialog_modal/dialog_modal.vue9---------
Msrc/components/domain_mute_card/domain_mute_card.vue4++--
Asrc/components/edit_status_modal/edit_status_modal.js75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/edit_status_modal/edit_status_modal.vue48++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/emoji_input/emoji_input.js219+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/components/emoji_input/emoji_input.vue214++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/components/emoji_input/suggestor.js45+++++++++++++++++++++++----------------------
Msrc/components/emoji_picker/emoji_picker.js314++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/components/emoji_picker/emoji_picker.scss65++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/components/emoji_picker/emoji_picker.vue196++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Msrc/components/emoji_reactions/emoji_reactions.vue84++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/components/exporter/exporter.js14++------------
Msrc/components/exporter/exporter.vue4++--
Msrc/components/extra_buttons/extra_buttons.js54++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/components/extra_buttons/extra_buttons.vue90++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/components/favorite_button/favorite_button.js17++++++++++++++---
Msrc/components/favorite_button/favorite_button.vue63+++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Msrc/components/features_panel/features_panel.vue2+-
Msrc/components/flash/flash.js7++++---
Msrc/components/flash/flash.vue28++++++++++++----------------
Msrc/components/follow_button/follow_button.js5++++-
Msrc/components/follow_button/follow_button.vue2+-
Msrc/components/follow_card/follow_card.js4+++-
Msrc/components/follow_card/follow_card.vue12++++++++++++
Msrc/components/font_control/font_control.js11++++++-----
Msrc/components/font_control/font_control.vue5+++--
Msrc/components/gallery/gallery.js97++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Msrc/components/gallery/gallery.vue201+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/components/global_notice_list/global_notice_list.vue15+++++++--------
Msrc/components/hashtag_link/hashtag_link.vue4++--
Msrc/components/image_cropper/image_cropper.js4++--
Msrc/components/importer/importer.js21+++------------------
Msrc/components/importer/importer.vue28+++++++++++++++++++---------
Msrc/components/instance_specific_panel/instance_specific_panel.vue2+-
Msrc/components/interactions/interactions.js9+++++++--
Msrc/components/interactions/interactions.vue9+++++++++
Msrc/components/interface_language_switcher/interface_language_switcher.vue40+++++++++++++++++++++++-----------------
Msrc/components/link-preview/link-preview.vue2+-
Asrc/components/lists/lists.js27+++++++++++++++++++++++++++
Asrc/components/lists/lists.vue33+++++++++++++++++++++++++++++++++
Asrc/components/lists_card/lists_card.js16++++++++++++++++
Asrc/components/lists_card/lists_card.vue51+++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/lists_edit/lists_edit.js145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/lists_edit/lists_edit.vue228+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/lists_menu/lists_menu_content.js22++++++++++++++++++++++
Asrc/components/lists_menu/lists_menu_content.vue12++++++++++++
Asrc/components/lists_timeline/lists_timeline.js36++++++++++++++++++++++++++++++++++++
Asrc/components/lists_timeline/lists_timeline.vue10++++++++++
Asrc/components/lists_user_search/lists_user_search.js51+++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/lists_user_search/lists_user_search.vue47+++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/login_form/login_form.js2+-
Msrc/components/login_form/login_form.vue16++++++++++------
Msrc/components/media_modal/media_modal.js100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Msrc/components/media_modal/media_modal.vue271+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/components/media_upload/media_upload.js5+++--
Msrc/components/media_upload/media_upload.vue10+++++++---
Msrc/components/mention_link/mention_link.js63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/components/mention_link/mention_link.scss55++++++++++++++++++++++++++++++++++++++++---------------
Msrc/components/mention_link/mention_link.vue93+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/components/mentions_line/mentions_line.scss12+++++++-----
Msrc/components/mentions_line/mentions_line.vue19++++++++-----------
Msrc/components/mfa_form/recovery_form.vue14+++++++++-----
Msrc/components/mfa_form/totp_form.vue14+++++++++-----
Msrc/components/mobile_nav/mobile_nav.js35++++++++++++++++++++++++++---------
Msrc/components/mobile_nav/mobile_nav.vue94++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Msrc/components/mobile_post_status_button/mobile_post_status_button.js7++++---
Msrc/components/mobile_post_status_button/mobile_post_status_button.vue58++++++++++++++++++++++++++++++----------------------------
Msrc/components/modal/modal.vue7+++++--
Msrc/components/moderation_tools/moderation_tools.js16++++++++++++++--
Msrc/components/moderation_tools/moderation_tools.vue21++++++++++++---------
Msrc/components/mrf_transparency_panel/mrf_transparency_panel.js6+++---
Msrc/components/nav_panel/nav_panel.js87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Msrc/components/nav_panel/nav_panel.vue221+++++++++++++++++++++++++++++++++++--------------------------------------------
Asrc/components/navigation/filter.js19+++++++++++++++++++
Asrc/components/navigation/navigation.js82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/navigation/navigation_entry.js51+++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/navigation/navigation_entry.vue133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/navigation/navigation_pins.js94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/navigation/navigation_pins.vue74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/notification/notification.js24+++++++++++++++++-------
Msrc/components/notification/notification.scss13++++++++++++-
Msrc/components/notification/notification.vue141++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Msrc/components/notifications/notification_filters.vue35++++++++++++-----------------------
Msrc/components/notifications/notifications.js74+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/components/notifications/notifications.scss44++++++++++++++++++++++++--------------------
Msrc/components/notifications/notifications.vue141++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/components/opacity_input/opacity_input.vue13+++++++------
Asrc/components/optional_router_link/optional_router_link.vue23+++++++++++++++++++++++
Msrc/components/password_reset/password_reset.vue10+++++++---
Asrc/components/pinch_zoom/pinch_zoom.js13+++++++++++++
Asrc/components/pinch_zoom/pinch_zoom.vue11+++++++++++
Msrc/components/poll/poll.js2+-
Msrc/components/poll/poll.vue19++++++++++++-------
Msrc/components/poll/poll_form.vue3++-
Msrc/components/popover/popover.js290++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Msrc/components/popover/popover.vue120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/components/post_status_form/post_status_form.js100++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Msrc/components/post_status_form/post_status_form.vue181++++++++++++++++++++++++++++++++-----------------------------------------------
Msrc/components/public_and_external_timeline/public_and_external_timeline.js2+-
Msrc/components/public_timeline/public_timeline.js2+-
Asrc/components/quick_filter_settings/quick_filter_settings.js70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/quick_filter_settings/quick_filter_settings.vue88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/quick_view_settings/quick_view_settings.js69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/quick_view_settings/quick_view_settings.vue75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/range_input/range_input.vue15++++++++-------
Msrc/components/react_button/react_button.js95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/components/react_button/react_button.vue59++++++++++++++++++++++++++++++++++++++++++++++-------------
Msrc/components/registration/registration.js26++++++++++++++++++--------
Msrc/components/registration/registration.vue72++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/components/remote_follow/remote_follow.js2+-
Msrc/components/remote_follow/remote_follow.vue2+-
Asrc/components/remove_follower_button/remove_follower_button.js25+++++++++++++++++++++++++
Asrc/components/remove_follower_button/remove_follower_button.vue13+++++++++++++
Msrc/components/reply_button/reply_button.js15+++++++++++++--
Msrc/components/reply_button/reply_button.vue45+++++++++++++++++++++++++++++++++++++++------
Asrc/components/report/report.js34++++++++++++++++++++++++++++++++++
Asrc/components/report/report.scss43+++++++++++++++++++++++++++++++++++++++++++
Asrc/components/report/report.vue74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/retweet_button/retweet_button.js17+++++++++++++++--
Msrc/components/retweet_button/retweet_button.vue63+++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Msrc/components/rich_content/rich_content.jsx40++++++++++++++++++++++++----------------
Msrc/components/scope_selector/scope_selector.vue3+++
Msrc/components/search/search.js43+++++++++++++++++++++++++++++++++----------
Msrc/components/search/search.vue42++++++++++++++++++++++++++++++++++--------
Msrc/components/search_bar/search_bar.js2+-
Msrc/components/search_bar/search_bar.vue2++
Msrc/components/select/select.js7++-----
Msrc/components/select/select.vue16+++++++++-------
Msrc/components/selectable_list/selectable_list.vue12++++++------
Msrc/components/settings_modal/helpers/boolean_setting.js24+++++++++++++++++++++---
Msrc/components/settings_modal/helpers/boolean_setting.vue12+++++++++---
Msrc/components/settings_modal/helpers/choice_setting.js18+++++++++++++++---
Msrc/components/settings_modal/helpers/choice_setting.vue12+++++++++---
Asrc/components/settings_modal/helpers/integer_setting.js44++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/settings_modal/helpers/integer_setting.vue27+++++++++++++++++++++++++++
Msrc/components/settings_modal/helpers/modified_indicator.vue14+++++++-------
Asrc/components/settings_modal/helpers/server_side_indicator.vue51+++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/settings_modal/helpers/shared_computed_object.js9+++++++++
Asrc/components/settings_modal/helpers/size_setting.js67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/settings_modal/helpers/size_setting.vue54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/settings_modal/settings_modal.js15+++++++++++++--
Msrc/components/settings_modal/settings_modal.scss26+++++++++++++++++++++++++-
Msrc/components/settings_modal/settings_modal.vue43+++++++++++++++++++++++--------------------
Msrc/components/settings_modal/settings_modal_content.js9++++++---
Msrc/components/settings_modal/settings_modal_content.vue1+
Msrc/components/settings_modal/tabs/data_import_export_tab.js29++++++++++++++++++++++++++++-
Msrc/components/settings_modal/tabs/data_import_export_tab.vue61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/settings_modal/tabs/filtering_tab.js13+++----------
Msrc/components/settings_modal/tabs/filtering_tab.vue169++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/components/settings_modal/tabs/general_tab.js60+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/components/settings_modal/tabs/general_tab.vue422+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/components/settings_modal/tabs/mutes_and_blocks_tab.js2+-
Msrc/components/settings_modal/tabs/mutes_and_blocks_tab.scss2+-
Msrc/components/settings_modal/tabs/mutes_and_blocks_tab.vue36++++++++++++++++++------------------
Msrc/components/settings_modal/tabs/notifications_tab.js8+++++---
Msrc/components/settings_modal/tabs/notifications_tab.vue86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/components/settings_modal/tabs/profile_tab.js79++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/components/settings_modal/tabs/profile_tab.scss8++++++--
Msrc/components/settings_modal/tabs/profile_tab.vue170++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/components/settings_modal/tabs/security_tab/mfa.js6+++---
Msrc/components/settings_modal/tabs/security_tab/mfa_totp.js2+-
Msrc/components/settings_modal/tabs/security_tab/security_tab.js57+++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/components/settings_modal/tabs/security_tab/security_tab.vue110++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/components/settings_modal/tabs/theme_tab/preview.vue14+++++++++-----
Msrc/components/settings_modal/tabs/theme_tab/theme_tab.js31+++++++++++++++++++------------
Msrc/components/settings_modal/tabs/theme_tab/theme_tab.scss49+++++++++++++++++--------------------------------
Msrc/components/settings_modal/tabs/theme_tab/theme_tab.vue58++++++++++++++++++++++++++++++++++------------------------
Msrc/components/settings_modal/tabs/version_tab.vue2+-
Msrc/components/shadow_control/shadow_control.js19+++++++++++--------
Msrc/components/shadow_control/shadow_control.vue9+++++----
Msrc/components/shout_panel/shout_panel.js2+-
Msrc/components/shout_panel/shout_panel.vue23++++++++++++-----------
Msrc/components/side_drawer/side_drawer.js28++++++++++++++++++++--------
Msrc/components/side_drawer/side_drawer.vue50+++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/components/staff_panel/staff_panel.js8++++----
Msrc/components/staff_panel/staff_panel.vue2+-
Msrc/components/status/status.js187++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/components/status/status.scss64+++++++++++++++++++++++++++++++++-------------------------------
Msrc/components/status/status.vue130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/components/status_body/status_body.js18+++++++++++-------
Msrc/components/status_body/status_body.scss58+++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/components/status_body/status_body.vue21++++++++++++---------
Msrc/components/status_content/status_content.js76+++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/components/status_content/status_content.vue58+++++++++++++++++++++++++++++-----------------------------
Asrc/components/status_history_modal/status_history_modal.js60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/status_history_modal/status_history_modal.vue46++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/status_popover/status_popover.js12++++++++++--
Msrc/components/status_popover/status_popover.vue10+++++-----
Msrc/components/sticker_picker/sticker_picker.js6+++---
Msrc/components/still-image/still-image.js36++++++++++++++++++++++++++++++++++--
Msrc/components/still-image/still-image.vue15+++++++++------
Asrc/components/swipe_click/swipe_click.js84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/swipe_click/swipe_click.vue14++++++++++++++
Dsrc/components/tab_switcher/tab_switcher.js156-------------------------------------------------------------------------------
Asrc/components/tab_switcher/tab_switcher.jsx183+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/tab_switcher/tab_switcher.scss11+++--------
Msrc/components/tag_timeline/tag_timeline.js2+-
Msrc/components/terms_of_service_panel/terms_of_service_panel.vue2+-
Asrc/components/thread_tree/thread_tree.js90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/thread_tree/thread_tree.vue135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/timeago/timeago.vue23++++++++++++++++++++---
Msrc/components/timeline/timeline.js74+++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Msrc/components/timeline/timeline.scss59+++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/components/timeline/timeline.vue201+++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Dsrc/components/timeline/timeline_quick_settings.js61-------------------------------------------------------------
Dsrc/components/timeline/timeline_quick_settings.vue102-------------------------------------------------------------------------------
Msrc/components/timeline_menu/timeline_menu.js40+++++++++++++++++++++++++++++++++++-----
Msrc/components/timeline_menu/timeline_menu.vue71++++++++++++++++++++++++++++++++++++-----------------------------------
Dsrc/components/timeline_menu/timeline_menu_content.js29-----------------------------
Dsrc/components/timeline_menu/timeline_menu_content.vue66------------------------------------------------------------------
Asrc/components/unicode_domain_indicator/unicode_domain_indicator.vue26++++++++++++++++++++++++++
Asrc/components/update_notification/update_notification.js69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/update_notification/update_notification.scss113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/update_notification/update_notification.vue103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/user_avatar/user_avatar.js13++++++++++++-
Msrc/components/user_avatar/user_avatar.vue98+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/components/user_card/user_card.js51++++++++++++++++++++++++++++++++++++++++++---------
Asrc/components/user_card/user_card.scss352+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/user_card/user_card.vue399+++++++++++++------------------------------------------------------------------
Asrc/components/user_link/user_link.vue38++++++++++++++++++++++++++++++++++++++
Asrc/components/user_list_menu/user_list_menu.js93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/user_list_menu/user_list_menu.vue38++++++++++++++++++++++++++++++++++++++
Msrc/components/user_list_popover/user_list_popover.js10++++++++--
Msrc/components/user_list_popover/user_list_popover.vue19+++++++++++++------
Asrc/components/user_note/user_note.js45+++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/user_note/user_note.vue88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/user_panel/user_panel.vue7++++---
Asrc/components/user_popover/user_popover.js23+++++++++++++++++++++++
Asrc/components/user_popover/user_popover.vue33+++++++++++++++++++++++++++++++++
Msrc/components/user_profile/user_profile.js25+++++++++++++++++--------
Msrc/components/user_profile/user_profile.vue37+++++++++++++++----------------------
Msrc/components/user_reporting_modal/user_reporting_modal.js16++++++++++------
Msrc/components/user_reporting_modal/user_reporting_modal.vue29+++++++++++------------------
Msrc/components/who_to_follow/who_to_follow.js2+-
Msrc/components/who_to_follow_panel/who_to_follow_panel.js10+++++-----
Msrc/components/who_to_follow_panel/who_to_follow_panel.vue2+-
Msrc/directives/body_scroll_lock.js6+++---
Dsrc/hocs/with_load_more/with_load_more.js112-------------------------------------------------------------------------------
Asrc/hocs/with_load_more/with_load_more.jsx109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/hocs/with_load_more/with_load_more.scss2+-
Dsrc/hocs/with_subscription/with_subscription.js94-------------------------------------------------------------------------------
Asrc/hocs/with_subscription/with_subscription.jsx91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/hocs/with_subscription/with_subscription.scss2+-
Msrc/i18n/ar.json17+++++++++++++++++
Msrc/i18n/ca.json45+++++++++++++++++++++++++++++++++------------
Msrc/i18n/de.json1-
Msrc/i18n/en.json322+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Msrc/i18n/eo.json278+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/i18n/es.json13+++++++++----
Msrc/i18n/eu.json3+--
Msrc/i18n/fi.json3+--
Msrc/i18n/fr.json271++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/i18n/he.json3+--
Msrc/i18n/id.json19++++++++++++++-----
Msrc/i18n/it.json15++++++++++-----
Msrc/i18n/ja_easy.json3+--
Msrc/i18n/ja_pedantic.json28++++++++++++++++++++--------
Msrc/i18n/ko.json830++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Asrc/i18n/languages.js53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/i18n/messages.js49+++++++++++++++----------------------------------
Msrc/i18n/nb.json3+--
Msrc/i18n/nl.json417+++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Msrc/i18n/oc.json3+--
Msrc/i18n/pl.json3+--
Msrc/i18n/pt.json1-
Msrc/i18n/ru.json12++++++++++--
Msrc/i18n/service_worker_messages.js1+
Asrc/i18n/sk.json512+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/i18n/te.json2+-
Msrc/i18n/uk.json73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Msrc/i18n/vi.json444++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/i18n/zh.json107++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/i18n/zh_Hant.json1-
Msrc/lib/notification-i18n-loader.js4++--
Msrc/lib/persisted_state.js17++++++++++-------
Msrc/main.js47++++++++++++++++++++++-------------------------
Asrc/modules/announcements.js135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/modules/api.js33++++++++++++++++++++++++++++-----
Msrc/modules/chats.js22+++++++++++-----------
Msrc/modules/config.js69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Asrc/modules/editStatus.js25+++++++++++++++++++++++++
Msrc/modules/errors.js4++--
Msrc/modules/instance.js187+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Msrc/modules/interface.js50++++++++++++++++++++++++++++++++++----------------
Asrc/modules/lists.js130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/modules/media_viewer.js9+++++----
Msrc/modules/oauth.js4+---
Msrc/modules/polls.js13++++++-------
Msrc/modules/reports.js44+++++++++++++++++++++++++++++++++++---------
Asrc/modules/serverSideConfig.js140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/modules/serverSideStorage.js436+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/modules/shout.js15++++++++++++++-
Asrc/modules/statusHistory.js25+++++++++++++++++++++++++
Msrc/modules/statuses.js50++++++++++++++++++++++++++++++++------------------
Msrc/modules/users.js101++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Asrc/panel.scss240+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/services/api/api.service.js507++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/services/backend_interactor_service/backend_interactor_service.js9+++++++--
Msrc/services/chat_service/chat_service.js12++++++------
Msrc/services/chat_utils/chat_utils.js2+-
Msrc/services/color_convert/color_convert.js12+++++++-----
Msrc/services/completion/completion.js2+-
Msrc/services/date_utils/date_utils.js16+++++++---------
Msrc/services/entity_normalizer/entity_normalizer.service.js77++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/services/errors/errors.js1+
Msrc/services/export_import/export_import.js4+++-
Msrc/services/file_size_format/file_size_format.js13++++++-------
Msrc/services/gesture_service/gesture_service.js137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/services/html_converter/html_line_converter.service.js4++--
Msrc/services/html_converter/html_tree_converter.service.js3++-
Msrc/services/html_converter/utility.service.js14+++++++++++---
Asrc/services/lists_fetcher/lists_fetcher.service.js22++++++++++++++++++++++
Msrc/services/locale/locale.service.js29++++++++++++++++++++++++++---
Msrc/services/new_api/password_reset.js2+-
Msrc/services/notification_utils/notification_utils.js12++++++++++--
Msrc/services/notifications_fetcher/notifications_fetcher.service.js29++++++++++++++++++++---------
Msrc/services/offset_finder/offset_finder.service.js2+-
Msrc/services/push/push.js6+++---
Msrc/services/resettable_async_component.js22++++++++++------------
Msrc/services/status_poster/status_poster.service.js42++++++++++++++++++++++++++++++++++++++++++
Msrc/services/style_setter/style_setter.js39+++++++++++++++++++++++++++++++++++----
Msrc/services/theme_data/pleromafe.js8++++++++
Msrc/services/theme_data/theme_data.service.js3++-
Msrc/services/timeline_fetcher/timeline_fetcher.service.js30+++++++++++++++++-------------
Msrc/services/user_highlighter/user_highlighter.js2+-
Msrc/sw.js10++++------
Mstatic/config.json1+
Dstatic/emoji.json1432-------------------------------------------------------------------------------
Mtest/e2e/custom-assertions/elementCount.js2+-
Mtest/e2e/nightwatch.conf.js54+++++++++++++++++++++++++++---------------------------
Mtest/e2e/runner.js8++++----
Mtest/unit/karma.conf.js37++++++++-----------------------------
Mtest/unit/specs/boot/routes.spec.js66++++++++++++++++++++++++++++++++++++++++++++----------------------
Mtest/unit/specs/components/emoji_input.spec.js78+++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mtest/unit/specs/components/rich_content.spec.js212++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Dtest/unit/specs/components/timeline.spec.js27---------------------------
Mtest/unit/specs/components/user_profile.spec.js53++++++++++++++++++++++++++++-------------------------
Atest/unit/specs/modules/lists.spec.js83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/unit/specs/modules/serverSideStorage.spec.js338+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/unit/specs/modules/statuses.spec.js4++--
Mtest/unit/specs/modules/users.spec.js44+++++++++++++++++++++++++++++++++++++++-----
Mtest/unit/specs/services/chat_service/chat_service.spec.js24++++++++++++------------
Mtest/unit/specs/services/date_utils/date_utils.spec.js10+++++-----
Mtest/unit/specs/services/entity_normalizer/entity_normalizer.spec.js27++++++++++++++-------------
Mtest/unit/specs/services/file_size_format/file_size_format.spec.js4++--
Mtest/unit/specs/services/theme_data/sanity_checks.spec.js2+-
Myarn.lock11721+++++++++++++++++++++++++++++++++++++------------------------------------------
421 files changed, 24387 insertions(+), 13779 deletions(-)

diff --git a/.babelrc b/.babelrc @@ -1,5 +1,5 @@ { - "presets": ["@babel/preset-env", "@vue/babel-preset-jsx"], - "plugins": ["@babel/plugin-transform-runtime", "lodash"], - "comments": false + "presets": ["@babel/preset-env"], + "plugins": ["@babel/plugin-transform-runtime", "lodash", "@vue/babel-plugin-jsx"], + "comments": true } diff --git a/.eslintrc.js b/.eslintrc.js @@ -1,7 +1,7 @@ module.exports = { root: true, parserOptions: { - parser: 'babel-eslint', + parser: '@babel/eslint-parser', sourceType: 'module' }, // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style @@ -21,6 +21,7 @@ module.exports = { 'generator-star-spacing': 0, // allow debugger during development 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, - 'vue/require-prop-types': 0 + 'vue/require-prop-types': 0, + 'vue/multi-word-component-names': 0 } } diff --git a/.gitignore b/.gitignore @@ -7,3 +7,4 @@ test/e2e/reports selenium-debug.log .idea/ config/local.json +static/emoji.json diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml @@ -1,7 +1,7 @@ # This file is a template, and might need editing before it works on your project. # Official framework image. Look for the different tagged releases at: # https://hub.docker.com/r/library/node/tags/ -image: node:10 +image: node:16 stages: - lint diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md @@ -0,0 +1,25 @@ +# Environment info + +<!-- Everything is optional and where applicable but the more information the better. --> +* Browser, version, OS, platform: +* Instance URL: +* Frontend version (see settings -> about): +* Backend version (see settings -> about): +* Browser extensions (ublock, rikaichamp etc): +* Known instance/user customizations (i.e. pleromafe mods/forks, instance styles etc) + +# Bug description & reproduction steps + +<!-- Type out here how to reproduce the bug, what goes wrong and what should go right --> +<!-- Screenshots and videos help a lot ;) any observations might also help --> +<!-- Also mention if there any errors in browser's console if relevant --> + +# Bug seriousness + +<!-- Everything is optional and free-form --> +* How annoying it is: +* How often does it happen: +* How many people does it affect: +* Is there a workaround for it: + +/label ~Bug diff --git a/.gitlab/issue_templates/Suggestion.md b/.gitlab/issue_templates/Suggestion.md @@ -0,0 +1,11 @@ +# Behavior suggestion/Feature request +<!-- +Type out what you want to see changed or what feature you want to see added to +PleormaFE. Please also explain how it would benefit users (or admins/moderators) +and what intended usecase is. Any background information (i.e. porting behavior +from other frontends/services, specific situations, personal preferences etc.) +as well as examples would be greatly appreciated. +--> + +/label ~suggestion + diff --git a/.gitlab/issue_templates/default.md b/.gitlab/issue_templates/default.md @@ -0,0 +1,7 @@ +<!-- +please use one of the templates if applicable, otherwise - type out here +in free-form +--> + +/label ~needs-triage + diff --git a/.gitlab/merge_request_templates/default.md b/.gitlab/merge_request_templates/default.md @@ -0,0 +1,30 @@ +<!-- +Feel free to submit merge requests that are work-in-progress, but mark them as +Draft: or WIP:. +Merge requests that have Draft or WIP status will not be merged and have less chances +of being reviewed, but you can still ask people to take a look if you need advice. +--> +# Changes + +* +* +* + +<!-- List what your merge request changes and how --> +<!-- +Try to not to break existing behavior, if your changes do break existing behavior +make it configurable to toggle between old behavior and new. Which one should be +default is up to discussion. +--> +<!-- If your merge request resolves some issue link it like so: "Closes #99999" --> +<!-- +If merge request adds some new feature that depends on backend: + +1. Make sure it gracefully degrades if backend hasn't been updated to support the feature, + we try to make PleromaFE compatible with older versions of BE so that people can still + update frontend safely without updating backend since it's costly and much riskier. +2. Link related BE merge request here +--> +<!-- Screenshots are welcome --> + +/label ~needs-review diff --git a/.node-version b/.node-version @@ -1 +1 @@ -7.2.1 +16.18.1 diff --git a/CHANGELOG.md b/CHANGELOG.md @@ -3,18 +3,90 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## 2.5.0 - 23.12.2022 +### Fixed +- UI no longer lags when switching between mobile and desktop mode +- Popovers no longer constrained by DOM hierarchy, shouldn't be cut off by anything +- Emoji autocomplete popover and picker popover stick to the text cursor. +- Attachments are ALWAYS in same order as user uploaded, no more "videos first" +- Pinned statuses no longer appear at bottom of user timeline (still appear as part of the timeline when fetched deep enough) +- Fixed many many bugs related to new mentions, including spacing and alignment issues +- Links in profile bios now properly open in new tabs +- "Always show mobile button" is working now +- Inline images now respect their intended width/height attributes +- Links with `&` in them work properly now +- Attachment description is prefilled with backend-provided default when uploading +- Proper visual feedback that next image is loading when browsing +- Additional HTML sanitization on frontend side in case backend sanitization fails +- Interaction list popovers now properly emojify names +- AdminFE button no longer scrolls page to top when clicked +- User handles with non-ascii domains now have less intrusive indicator for the domain name +- Completely hidden posts still no longer have 1px border +- A lot of accessibility improvements + +### Changed +- Using Vue 3 now +- A lot of internal dependencies updated +- "(You)s" are optional (opt-in) now, bolding your nickname is also optional (opt-out) +- User highlight background now also covers the `@` +- Reverted back to textual `@`, svg version is opt-in. +- Settings window has been thoroughly rearranged to make more sense and make navigation settings easier. +- Uploaded attachments are uniform with displayed attachments +- Flash is watchable in media-modal (takes up nearly full screen though due to sizing issues) +- Notifications about likes/repeats/emoji reacts are now minimized so they always take up same amount of space irrelevant to size of post. (You can expand them to full if need be) +- Slight width/spacing adjustments +- More sizing stuff is font-size dependent now +- Scrollbars are styled/colorized now +- Scrollbars are toggleable (for stuff that didn't have visible scrollbars before) (opt-in) +- Updated localization files +- Top bar is more useful in mobile mode now. +- "Show new" button is way more compact in mobile mode +- Slightly adjusted placement and spacing of the topbar buttons so it's less easy to accidentally log yourself out + +### Added +- 3 column mode: only enables when there's space for it (opt-out, customizable) +- Apologetic pleroma-tan +- New button on timeline header to change some of the new and often-used settings +- Support for lists +- Added ability to edit posts and view post edit history etc. +- Added ability to add personal note to users +- Added initial support for admin announcements +- Added ui for account migration +- Added ui for backups +- Added ability to force-unfollow a user from you +- Emoji are now grouped by pack +- Ability to pin navigation items and collapse the navigation menu +- Ability to rearrange order of attachments when uploading +- Ability to scroll column (or page) to top via panel header button +- Options to show domains in mentions +- Option to show user avatars in mention links (opt-in) +- Option to disable the tooltip for mentions +- Option to completely hide muted threads +- Option to customize what clicking user avatar does in user popover +- Notifications for poll results +- "Favorites" link in navigation +- Very early and somewhat experimental system for automatic settings sync (used only for pinned navigation and apologetic pleroma-tan) +- Implemented remote interaction with statuses for anon visitors +- Ability to open videos in modal even if you disabled that feature, via an icon button +- New button on attachment that indicates that attachment has a description and shows a bar filled with description +- Attachments are truncated just like post contents +- Media modal now also displays description and counter position in gallery (i.e. 1/5) +- Enabled users to zoom and pan images in media viewer with mouse and touch +- Timelines/panels and conversations have sticky headers now (a bit glitchy on some browsers like safari) (opt-out) + + ## [2.4.2] - 2022-01-09 -### Added +### Added - Added Apply and Reset buttons to the bottom of theme tab to minimize UI travel - Implemented user option to always show floating New Post button (normally mobile-only) -- Display reasons for instance specific policies +- Display reasons for instance specific policies - Added functionality to cancel follow request ### Fixed - Fixed link to external profile not working on user profiles -- Fixed mobile shoutbox display +- Fixed mobile shoutbox display - Fixed favicon badge not working in Chrome -- Escape html more properly in subject/display name +- Escape html more properly in subject/display name ## [2.4.0] - 2021-08-08 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md @@ -10,3 +10,5 @@ Contributors of this project. - shpuld (shpuld@shitposter.club): CSS and styling - Vincent Guth (https://unsplash.com/photos/XrwVIFy6rTw): Background images. - hj (hj@shigusegubu.club): Code +- Sean King (seanking@kazv.moe): Code +- tusooa (tusooa@kazv.moe): Code diff --git a/README.md b/README.md @@ -1,18 +1,19 @@ # Pleroma-FE -> A single column frontend designed for Pleroma. +> Highly-customizable frontend designed for Pleroma. -![screenshot](/uploads/796c5ecf985ed1e2b0943ee0df131ed0/DJVqSJ0.png) +![screenshot](./image-1.png) # For Translators -To translate Pleroma-FE, add your language to [src/i18n/messages.js](https://git.pleroma.social/pleroma/pleroma-fe/blob/develop/src/i18n/messages.js). Pleroma-FE will set your language by your browser locale, but you can temporarily force it in the code by changing the locale in main.js. +To translate Pleroma-FE, use our weblate server: https://translate.pleroma.social/. If you need to add your language it should be added as a json file in [src/i18n/](https://git.pleroma.social/pleroma/pleroma-fe/blob/develop/src/i18n/) folder and added in a list within [src/i18n/languages.js](https://git.pleroma.social/pleroma/pleroma-fe/blob/develop/src/i18n/languages.js). -# FOR ADMINS +Pleroma-FE will set your language by your browser locale, but you can change language in settings. -You don't need to build Pleroma-FE yourself. Those using the Pleroma backend will be able to use it out of the box. +# For instance admins +You don't need to build Pleroma-FE yourself. Those using the Pleroma backend will be able to use it out of the box. Information of customizing PleromaFE settings/defaults is in our [guide](https://docs-develop.pleroma.social/frontend/CONFIGURATION/) and in case you want to build your own custom version there's [another](https://docs-develop.pleroma.social/frontend/HACKING/) -## Build Setup +# Build Setup ``` bash # install dependencies @@ -20,13 +21,13 @@ npm install -g yarn yarn # serve with hot reload at localhost:8080 -npm run dev +yarn dev # build for production with minification -npm run build +yarn build # run unit tests -npm run unit +yarn unit ``` # For Contributors: @@ -40,10 +41,4 @@ FE Build process also leaves current commit hash in global variable `___pleromaf # Configuration -Edit config.json for configuration. - -## Options - -### Login methods - -```loginMethod``` can be set to either ```password``` (the default) or ```token```, which will use the full oauth redirection flow, which is useful for SSO situations. +Set configuration settings in AdminFE, additionally you can edit config.json. For more details see [documentation](https://docs-develop.pleroma.social/frontend/CONFIGURATION/). diff --git a/build/build.js b/build/build.js @@ -18,6 +18,9 @@ console.log( var spinner = ora('building for production...') spinner.start() +var updateEmoji = require('./update-emoji').updateEmoji +updateEmoji() + var assetsPath = path.join(config.build.assetsRoot, config.build.assetsSubDirectory) rm('-rf', assetsPath) mkdir('-p', assetsPath) @@ -33,4 +36,8 @@ webpack(webpackConfig, function (err, stats) { chunks: false, chunkModules: false }) + '\n') + if (stats.hasErrors()) { + console.error('See above for errors.') + process.exit(1) + } }) diff --git a/build/dev-server.js b/build/dev-server.js @@ -10,6 +10,9 @@ var webpackConfig = process.env.NODE_ENV === 'testing' ? require('./webpack.prod.conf') : require('./webpack.dev.conf') +var updateEmoji = require('./update-emoji').updateEmoji +updateEmoji() + // default port where dev server listens for incoming traffic var port = process.env.PORT || config.dev.port // Define HTTP proxies to your custom API backend @@ -29,18 +32,20 @@ var devMiddleware = require('webpack-dev-middleware')(compiler, { }) var hotMiddleware = require('webpack-hot-middleware')(compiler) + +// FIXME: The statement below gives error about hooks being required in webpack 5. // force page reload when html-webpack-plugin template changes -compiler.plugin('compilation', function (compilation) { - compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { - // FIXME: This supposed to reload whole page when index.html is changed, - // however now it reloads entire page on every breath, i suppose the order - // of plugins changed or something. It's a minor thing and douesn't hurt - // disabling it, constant reloads hurt much more +// compiler.plugin('compilation', function (compilation) { +// compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { +// // FIXME: This supposed to reload whole page when index.html is changed, +// // however now it reloads entire page on every breath, i suppose the order +// // of plugins changed or something. It's a minor thing and douesn't hurt +// // disabling it, constant reloads hurt much more - // hotMiddleware.publish({ action: 'reload' }) - // cb() - }) -}) +// // hotMiddleware.publish({ action: 'reload' }) +// // cb() +// }) +// }) // proxy api requests Object.keys(proxyTable).forEach(function (context) { @@ -48,7 +53,7 @@ Object.keys(proxyTable).forEach(function (context) { if (typeof options === 'string') { options = { target: options } } - app.use(proxyMiddleware(context, options)) + app.use(proxyMiddleware.createProxyMiddleware(context, options)) }) // handle fallback for HTML5 history API diff --git a/build/update-emoji.js b/build/update-emoji.js @@ -0,0 +1,27 @@ + +module.exports = { + updateEmoji () { + const emojis = require('@kazvmoe-infra/unicode-emoji-json/data-by-group') + const fs = require('fs') + + Object.keys(emojis) + .map(k => { + emojis[k].map(e => { + delete e.unicode_version + delete e.emoji_version + delete e.skin_tone_support_unicode_version + }) + }) + + const res = {} + Object.keys(emojis) + .map(k => { + const groupId = k.replace('&', 'and').replace(/ /g, '-').toLowerCase() + res[groupId] = emojis[k] + }) + + console.info('Updating emojis...') + fs.writeFileSync('static/emoji.json', JSON.stringify(res)) + console.info('Done.') + } +} diff --git a/build/webpack.base.conf.js b/build/webpack.base.conf.js @@ -2,8 +2,11 @@ var path = require('path') var config = require('../config') var utils = require('./utils') var projectRoot = path.resolve(__dirname, '../') -var ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin') +var ServiceWorkerWebpackPlugin = require('serviceworker-webpack5-plugin') var CopyPlugin = require('copy-webpack-plugin'); +var { VueLoaderPlugin } = require('vue-loader') +var ESLintPlugin = require('eslint-webpack-plugin'); + var env = process.env.NODE_ENV // check env & config/index.js to decide weither to enable CSS Sourcemaps for the @@ -21,7 +24,8 @@ module.exports = { output: { path: config.build.assetsRoot, publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath, - filename: '[name].js' + filename: '[name].js', + chunkFilename: '[name].js' }, optimization: { splitChunks: { @@ -29,38 +33,47 @@ module.exports = { } }, resolve: { - extensions: ['.js', '.vue'], + extensions: ['.mjs', '.js', '.jsx', '.vue'], modules: [ path.join(__dirname, '../node_modules') ], alias: { - 'vue$': 'vue/dist/vue.runtime.common', 'static': path.resolve(__dirname, '../static'), 'src': path.resolve(__dirname, '../src'), 'assets': path.resolve(__dirname, '../src/assets'), - 'components': path.resolve(__dirname, '../src/components') + 'components': path.resolve(__dirname, '../src/components'), + 'vue-i18n': 'vue-i18n/dist/vue-i18n.runtime.esm-bundler.js' + }, + fallback: { + 'querystring': require.resolve('querystring-es3'), + 'url': require.resolve('url/') } }, module: { noParse: /node_modules\/localforage\/dist\/localforage.js/, rules: [ { - enforce: 'pre', - test: /\.(js|vue)$/, - include: projectRoot, - exclude: /node_modules/, - use: { - loader: 'eslint-loader', - options: { - formatter: require('eslint-friendly-formatter'), - sourceMap: config.build.productionSourceMap, - extract: true - } - } + enforce: 'post', + test: /\.(json5?|ya?ml)$/, // target json, json5, yaml and yml files + type: 'javascript/auto', + loader: '@intlify/vue-i18n-loader', + include: [ // Use `Rule.include` to specify the files of locale messages to be pre-compiled + path.resolve(__dirname, '../src/i18n') + ] }, { test: /\.vue$/, - use: 'vue-loader' + loader: 'vue-loader', + options: { + compilerOptions: { + isCustomElement(tag) { + if (tag === 'pinch-zoom') { + return true + } + return false + } + } + } }, { test: /\.jsx?$/, @@ -70,24 +83,23 @@ module.exports = { }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, - use: { - loader: 'url-loader', - options: { - limit: 10000, - name: utils.assetsPath('img/[name].[hash:7].[ext]') - } + type: 'asset', + generator: { + filename: utils.assetsPath('img/[name].[hash:7][ext]') } }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, - use: { - loader: 'url-loader', - options: { - limit: 10000, - name: utils.assetsPath('fonts/[name].[hash:7].[ext]') - } + type: 'asset', + generator: { + filename: utils.assetsPath('fonts/[name].[hash:7][ext]') } }, + { + test: /\.mjs$/, + include: /node_modules/, + type: 'javascript/auto' + } ] }, plugins: [ @@ -95,13 +107,17 @@ module.exports = { entry: path.join(__dirname, '..', 'src/sw.js'), filename: 'sw-pleroma.js' }), + new ESLintPlugin({ + extensions: ['js', 'vue'], + formatter: require('eslint-formatter-friendly') + }), + new VueLoaderPlugin(), // This copies Ruffle's WASM to a directory so that JS side can access it new CopyPlugin({ patterns: [ { - from: "node_modules/ruffle-mirror/*", - to: "static/ruffle", - flatten: true + from: "node_modules/@ruffle-rs/ruffle/**/*", + to: "static/ruffle/[name][ext]" }, ], options: { diff --git a/build/webpack.dev.conf.js b/build/webpack.dev.conf.js @@ -16,12 +16,14 @@ module.exports = merge(baseWebpackConfig, { }, mode: 'development', // eval-source-map is faster for development - devtool: '#eval-source-map', + devtool: 'eval-source-map', plugins: [ new webpack.DefinePlugin({ 'process.env': config.dev.env, 'COMMIT_HASH': JSON.stringify('DEV'), - 'DEV_OVERRIDES': JSON.stringify(config.dev.settings) + 'DEV_OVERRIDES': JSON.stringify(config.dev.settings), + '__VUE_OPTIONS_API__': true, + '__VUE_PROD_DEVTOOLS__': false }), // https://github.com/glenjamin/webpack-hot-middleware#installation--usage new webpack.HotModuleReplacementPlugin(), diff --git a/build/webpack.prod.conf.js b/build/webpack.prod.conf.js @@ -5,6 +5,7 @@ var webpack = require('webpack') var merge = require('webpack-merge') var baseWebpackConfig = require('./webpack.base.conf') var MiniCssExtractPlugin = require('mini-css-extract-plugin') +const CssMinimizerPlugin = require("css-minimizer-webpack-plugin") var HtmlWebpackPlugin = require('html-webpack-plugin') var env = process.env.NODE_ENV === 'testing' ? require('../config/test.env') @@ -19,12 +20,16 @@ var webpackConfig = merge(baseWebpackConfig, { module: { rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, extract: true }) }, - devtool: config.build.productionSourceMap ? '#source-map' : false, + devtool: config.build.productionSourceMap ? 'source-map' : false, optimization: { minimize: true, splitChunks: { chunks: 'all' - } + }, + minimizer: [ + `...`, + new CssMinimizerPlugin() + ] }, output: { path: config.build.assetsRoot, @@ -36,7 +41,9 @@ var webpackConfig = merge(baseWebpackConfig, { new webpack.DefinePlugin({ 'process.env': env, 'COMMIT_HASH': JSON.stringify(commitHash), - 'DEV_OVERRIDES': JSON.stringify(undefined) + 'DEV_OVERRIDES': JSON.stringify(undefined), + '__VUE_OPTIONS_API__': true, + '__VUE_PROD_DEVTOOLS__': false }), // extract css into its own file new MiniCssExtractPlugin({ @@ -58,9 +65,7 @@ var webpackConfig = merge(baseWebpackConfig, { ignoreCustomComments: [/server-generated-meta/] // more options: // https://github.com/kangax/html-minifier#options-quick-reference - }, - // necessary to consistently work with multiple chunks via CommonsChunkPlugin - chunksSortMode: 'dependency' + } }), // split vendor js into its own file // extract webpack runtime and module manifest to its own file in order to diff --git a/config/index.js b/config/index.js @@ -52,7 +52,10 @@ module.exports = { target, changeOrigin: true, cookieDomainRewrite: 'localhost', - ws: true + ws: true, + headers: { + 'Origin': target + } }, '/oauth/revoke': { target, diff --git a/image-1.png b/image-1.png Binary files differ. diff --git a/image.png b/image.png Binary files differ. diff --git a/index.html b/index.html @@ -10,5 +10,6 @@ <noscript>To use Pleroma, please enable JavaScript.</noscript> <div id="app"></div> <!-- built files will be auto injected --> + <div id="popovers" /> </body> </html> diff --git a/package.json b/package.json @@ -1,9 +1,9 @@ { "name": "pleroma_fe", - "version": "1.0.0", - "description": "A Qvitter-style frontend for certain GS servers.", - "author": "Roger Braun <roger@rogerbraun.net>", - "private": true, + "version": "2.5.0", + "description": "Pleroma frontend, the default frontend of Pleroma social network server", + "author": "Pleroma contributors <https://git.pleroma.social/pleroma/pleroma-fe/-/blob/develop/CONTRIBUTORS.md>", + "private": false, "scripts": { "dev": "node build/dev-server.js", "build": "node build/build.js", @@ -16,110 +16,114 @@ "lint-fix": "eslint --fix --ext .js,.vue src test/unit/specs test/e2e/specs" }, "dependencies": { - "@babel/runtime": "^7.7.6", - "@chenfengyuan/vue-qrcode": "^1.0.0", - "@fortawesome/fontawesome-svg-core": "^1.2.32", - "@fortawesome/free-regular-svg-icons": "^5.15.1", - "@fortawesome/free-solid-svg-icons": "^5.15.1", - "@fortawesome/vue-fontawesome": "^2.0.0", - "body-scroll-lock": "^2.6.4", - "chromatism": "^3.0.0", - "cropperjs": "^1.4.3", - "diff": "^3.0.1", - "escape-html": "^1.0.3", - "localforage": "^1.5.0", - "parse-link-header": "^1.0.1", - "phoenix": "^1.3.0", - "portal-vue": "^2.1.4", - "punycode.js": "^2.1.0", - "ruffle-mirror": "^2021.4.10", - "v-click-outside": "^2.1.1", - "vue": "^2.6.11", - "vue-i18n": "^7.3.2", - "vue-router": "^3.0.1", - "vue-template-compiler": "^2.6.11", - "vuelidate": "^0.7.4", - "vuex": "^3.0.1" + "@babel/runtime": "7.20.0", + "@chenfengyuan/vue-qrcode": "2.0.0", + "@fortawesome/fontawesome-svg-core": "6.2.0", + "@fortawesome/free-regular-svg-icons": "6.2.0", + "@fortawesome/free-solid-svg-icons": "6.2.0", + "@fortawesome/vue-fontawesome": "3.0.1", + "@kazvmoe-infra/pinch-zoom-element": "1.2.0", + "@kazvmoe-infra/unicode-emoji-json": "0.4.0", + "@ruffle-rs/ruffle": "0.1.0-nightly.2022.7.12", + "@vuelidate/core": "2.0.0", + "@vuelidate/validators": "2.0.0", + "body-scroll-lock": "3.1.5", + "chromatism": "3.0.0", + "click-outside-vue3": "4.0.1", + "cropperjs": "1.5.12", + "escape-html": "1.0.3", + "js-cookie": "3.0.1", + "localforage": "1.10.0", + "lozad": "1.16.0", + "parse-link-header": "2.0.0", + "phoenix": "1.6.2", + "punycode.js": "2.1.0", + "qrcode": "1.5.0", + "querystring-es3": "0.2.1", + "url": "0.11.0", + "utf8": "3.0.0", + "vue": "3.2.41", + "vue-i18n": "9.2.2", + "vue-router": "4.1.6", + "vue-template-compiler": "2.7.13", + "vuex": "4.1.0" }, "devDependencies": { - "@babel/core": "^7.7.5", - "@babel/plugin-transform-runtime": "^7.7.6", - "@babel/preset-env": "^7.7.6", - "@babel/register": "^7.7.4", - "@ungap/event-target": "^0.1.0", - "@vue/babel-helper-vue-jsx-merge-props": "^1.2.1", - "@vue/babel-preset-jsx": "^1.2.4", - "@vue/test-utils": "^1.0.0-beta.26", - "autoprefixer": "^6.4.0", - "babel-eslint": "^7.0.0", - "babel-loader": "^8.0.6", - "babel-plugin-lodash": "^3.3.4", - "chai": "^3.5.0", - "chalk": "^1.1.3", - "chromedriver": "^87.0.1", - "connect-history-api-fallback": "^1.1.0", - "copy-webpack-plugin": "^6.4.1", - "cross-spawn": "^4.0.2", - "css-loader": "^0.28.0", - "custom-event-polyfill": "^1.0.7", - "eslint": "^5.16.0", - "eslint-config-standard": "^12.0.0", - "eslint-friendly-formatter": "^2.0.5", - "eslint-loader": "^2.1.0", - "eslint-plugin-import": "^2.13.0", - "eslint-plugin-node": "^7.0.0", - "eslint-plugin-promise": "^4.0.0", - "eslint-plugin-standard": "^4.0.0", - "eslint-plugin-vue": "^5.2.2", - "eventsource-polyfill": "^0.9.6", - "express": "^4.13.3", - "file-loader": "^3.0.1", - "function-bind": "^1.0.2", - "html-webpack-plugin": "^3.0.0", - "http-proxy-middleware": "^0.17.2", - "inject-loader": "^2.0.1", - "iso-639-1": "^2.0.3", - "isparta-loader": "^2.0.0", - "json-loader": "^0.5.4", - "karma": "^3.0.0", - "karma-coverage": "^1.1.1", - "karma-firefox-launcher": "^1.1.0", - "karma-mocha": "^1.2.0", - "karma-mocha-reporter": "^2.2.1", - "karma-sinon-chai": "^2.0.2", - "karma-sourcemap-loader": "^0.3.7", - "karma-spec-reporter": "0.0.26", - "karma-webpack": "^4.0.0-rc.3", - "lodash": "^4.16.4", - "lolex": "^1.4.0", - "mini-css-extract-plugin": "^0.5.0", - "mocha": "^3.1.0", - "nightwatch": "^0.9.8", - "opn": "^4.0.2", - "ora": "^0.3.0", - "postcss-loader": "^3.0.0", - "raw-loader": "^0.5.1", - "sass": "^1.17.3", - "sass-loader": "git://github.com/webpack-contrib/sass-loader", + "@babel/core": "7.19.6", + "@babel/eslint-parser": "7.19.1", + "@babel/plugin-transform-runtime": "7.19.6", + "@babel/preset-env": "7.19.4", + "@babel/register": "7.18.9", + "@intlify/vue-i18n-loader": "5.0.0", + "@ungap/event-target": "0.2.3", + "@vue/babel-helper-vue-jsx-merge-props": "1.4.0", + "@vue/babel-plugin-jsx": "1.1.1", + "@vue/compiler-sfc": "3.2.41", + "@vue/test-utils": "2.2.6", + "autoprefixer": "10.4.12", + "babel-loader": "8.2.5", + "babel-plugin-lodash": "3.3.4", + "chai": "4.3.7", + "chalk": "1.1.3", + "chromedriver": "104.0.0", + "connect-history-api-fallback": "2.0.0", + "copy-webpack-plugin": "11.0.0", + "cross-spawn": "7.0.3", + "css-loader": "6.7.1", + "css-minimizer-webpack-plugin": "4.2.2", + "custom-event-polyfill": "1.0.7", + "eslint": "8.29.0", + "eslint-config-standard": "17.0.0", + "eslint-formatter-friendly": "7.0.0", + "eslint-plugin-import": "2.26.0", + "eslint-plugin-n": "15.6.0", + "eslint-plugin-promise": "6.1.1", + "eslint-plugin-vue": "9.7.0", + "eslint-webpack-plugin": "3.2.0", + "eventsource-polyfill": "0.9.6", + "express": "4.18.2", + "function-bind": "1.1.1", + "html-webpack-plugin": "5.5.0", + "http-proxy-middleware": "2.0.6", + "iso-639-1": "2.1.15", + "json-loader": "0.5.7", + "karma": "6.4.1", + "karma-coverage": "2.2.0", + "karma-firefox-launcher": "2.1.2", + "karma-mocha": "2.0.1", + "karma-mocha-reporter": "2.2.5", + "karma-sinon-chai": "2.0.2", + "karma-sourcemap-loader": "0.3.8", + "karma-spec-reporter": "0.0.34", + "karma-webpack": "5.0.0", + "lodash": "4.17.21", + "mini-css-extract-plugin": "2.6.1", + "mocha": "10.0.0", + "nightwatch": "2.3.3", + "opn": "5.5.0", + "ora": "0.4.1", + "postcss": "8.4.16", + "postcss-loader": "7.0.1", + "sass": "1.55.0", + "sass-loader": "13.0.2", "selenium-server": "2.53.1", - "semver": "^5.3.0", - "serviceworker-webpack-plugin": "^1.0.0", - "shelljs": "^0.8.4", - "sinon": "^2.1.0", - "sinon-chai": "^2.8.0", - "stylelint": "^13.6.1", - "stylelint-config-standard": "^20.0.0", - "stylelint-rscss": "^0.4.0", - "url-loader": "^1.1.2", - "vue-loader": "^14.0.0", - "vue-style-loader": "^4.0.0", - "webpack": "^4.44.0", - "webpack-dev-middleware": "^3.6.0", - "webpack-hot-middleware": "^2.12.2", - "webpack-merge": "^0.14.1" + "semver": "7.3.8", + "serviceworker-webpack5-plugin": "2.0.0", + "shelljs": "0.8.5", + "sinon": "14.0.2", + "sinon-chai": "3.7.0", + "stylelint": "13.13.1", + "stylelint-config-standard": "20.0.0", + "stylelint-rscss": "0.4.0", + "vue-loader": "17.0.1", + "vue-style-loader": "4.1.3", + "webpack": "5.74.0", + "webpack-dev-middleware": "3.7.3", + "webpack-hot-middleware": "2.25.2", + "webpack-merge": "0.20.0" }, "engines": { - "node": ">= 4.0.0", + "node": ">= 16.0.0", "npm": ">= 3.0.0" } } diff --git a/renovate.json b/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:base" + ] +} diff --git a/src/App.js b/src/App.js @@ -1,28 +1,29 @@ import UserPanel from './components/user_panel/user_panel.vue' import NavPanel from './components/nav_panel/nav_panel.vue' -import Notifications from './components/notifications/notifications.vue' import InstanceSpecificPanel from './components/instance_specific_panel/instance_specific_panel.vue' import FeaturesPanel from './components/features_panel/features_panel.vue' import WhoToFollowPanel from './components/who_to_follow_panel/who_to_follow_panel.vue' import ShoutPanel from './components/shout_panel/shout_panel.vue' -import SettingsModal from './components/settings_modal/settings_modal.vue' import MediaModal from './components/media_modal/media_modal.vue' import SideDrawer from './components/side_drawer/side_drawer.vue' import MobilePostStatusButton from './components/mobile_post_status_button/mobile_post_status_button.vue' import MobileNav from './components/mobile_nav/mobile_nav.vue' import DesktopNav from './components/desktop_nav/desktop_nav.vue' import UserReportingModal from './components/user_reporting_modal/user_reporting_modal.vue' +import EditStatusModal from './components/edit_status_modal/edit_status_modal.vue' import PostStatusModal from './components/post_status_modal/post_status_modal.vue' +import StatusHistoryModal from './components/status_history_modal/status_history_modal.vue' import GlobalNoticeList from './components/global_notice_list/global_notice_list.vue' import { windowWidth, windowHeight } from './services/window_utils/window_utils' import { mapGetters } from 'vuex' +import { defineAsyncComponent } from 'vue' export default { name: 'app', components: { UserPanel, NavPanel, - Notifications, + Notifications: defineAsyncComponent(() => import('./components/notifications/notifications.vue')), InstanceSpecificPanel, FeaturesPanel, WhoToFollowPanel, @@ -32,9 +33,12 @@ export default { MobilePostStatusButton, MobileNav, DesktopNav, - SettingsModal, + SettingsModal: defineAsyncComponent(() => import('./components/settings_modal/settings_modal.vue')), + UpdateNotification: defineAsyncComponent(() => import('./components/update_notification/update_notification.vue')), UserReportingModal, PostStatusModal, + EditStatusModal, + StatusHistoryModal, GlobalNoticeList }, data: () => ({ @@ -46,10 +50,27 @@ export default { this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val }) window.addEventListener('resize', this.updateMobileState) }, - destroyed () { + unmounted () { window.removeEventListener('resize', this.updateMobileState) }, computed: { + classes () { + return [ + { + '-reverse': this.reverseLayout, + '-no-sticky-headers': this.noSticky, + '-has-new-post-button': this.newPostButtonShown + }, + '-' + this.layoutType + ] + }, + navClasses () { + const { navbarColumnStretch } = this.$store.getters.mergedConfig + return [ + '-' + this.layoutType, + ...(navbarColumnStretch ? ['-column-stretch'] : []) + ] + }, currentUser () { return this.$store.state.users.currentUser }, userBackground () { return this.currentUser.background_image }, instanceBackground () { @@ -65,38 +86,50 @@ export default { } } }, - shout () { return this.$store.state.shout.channel.state === 'joined' }, + shout () { return this.$store.state.shout.joined }, suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled }, showInstanceSpecificPanel () { return this.$store.state.instance.showInstanceSpecificPanel && !this.$store.getters.mergedConfig.hideISP && this.$store.state.instance.instanceSpecificPanelContent }, + isChats () { + return this.$route.name === 'chat' || this.$route.name === 'chats' + }, + isListEdit () { + return this.$route.name === 'lists-edit' + }, + newPostButtonShown () { + if (this.isChats) return false + if (this.isListEdit) return false + return this.$store.getters.mergedConfig.alwaysShowNewPostButton || this.layoutType === 'mobile' + }, showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel }, + editingAvailable () { return this.$store.state.instance.editingAvailable }, shoutboxPosition () { - return this.$store.getters.mergedConfig.showNewPostButton || false + return this.$store.getters.mergedConfig.alwaysShowNewPostButton || false }, hideShoutbox () { return this.$store.getters.mergedConfig.hideShoutbox }, - isMobileLayout () { return this.$store.state.interface.mobileLayout }, + layoutType () { return this.$store.state.interface.layoutType }, privateMode () { return this.$store.state.instance.private }, - sidebarAlign () { - return { - 'order': this.$store.getters.mergedConfig.sidebarRight ? 99 : 0 + reverseLayout () { + const { thirdColumnMode, sidebarRight: reverseSetting } = this.$store.getters.mergedConfig + if (this.layoutType !== 'wide') { + return reverseSetting + } else { + return thirdColumnMode === 'notifications' ? reverseSetting : !reverseSetting } }, + noSticky () { return this.$store.getters.mergedConfig.disableStickyHeaders }, + showScrollbars () { return this.$store.getters.mergedConfig.showScrollbars }, ...mapGetters(['mergedConfig']) }, methods: { updateMobileState () { - const mobileLayout = windowWidth() <= 800 - const layoutHeight = windowHeight() - const changed = mobileLayout !== this.isMobileLayout - if (changed) { - this.$store.dispatch('setMobileLayout', mobileLayout) - } - this.$store.dispatch('setLayoutHeight', layoutHeight) + this.$store.dispatch('setLayoutWidth', windowWidth()) + this.$store.dispatch('setLayoutHeight', windowHeight()) } } } diff --git a/src/App.scss b/src/App.scss @@ -1,77 +1,374 @@ +// stylelint-disable rscss/class-format @import './_variables.scss'; -#app { - min-height: 100vh; - max-width: 100%; - overflow: hidden; -} - -.app-bg-wrapper { - position: fixed; - z-index: -1; - height: 100%; - left: 0; - right: -20px; - background-size: cover; - background-repeat: no-repeat; - background-color: var(--wallpaper); - background-image: var(--body-background-image); - background-position: 50% 50px; -} - -i[class^='icon-'] { - user-select: none; -} - -h4 { - margin: 0; -} - -#content { - box-sizing: border-box; - padding-top: 60px; - margin: auto; - min-height: 100vh; - max-width: 980px; - align-content: flex-start; -} - -.underlay { - background-color: rgba(0,0,0,0.15); - background-color: var(--underlay, rgba(0,0,0,0.15)); -} - -.text-center { - text-align: center; +:root { + --navbar-height: 3.5rem; + --post-line-height: 1.4; + // Z-Index stuff + --ZI_media_modal: 9000; + --ZI_modals_popovers: 8500; + --ZI_modals: 8000; + --ZI_navbar_popovers: 7500; + --ZI_navbar: 7000; + --ZI_popovers: 6000; } html { font-size: 14px; + // overflow-x: clip causes my browser's tab to crash with SIGILL lul } body { - overscroll-behavior-y: none; font-family: sans-serif; font-family: var(--interfaceFont, sans-serif); margin: 0; color: $fallback--text; color: var(--text, $fallback--text); - max-width: 100vw; - overflow-x: hidden; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + overscroll-behavior-y: none; + overflow-x: clip; + overflow-y: scroll; &.hidden { display: none; } } +// ## Custom scrollbars +// Only show custom scrollbars on devices which +// have a cursor/pointer to operate them +@media (any-pointer: fine) { + * { + scrollbar-color: var(--btn) transparent; + + &::-webkit-scrollbar { + background: transparent; + } + + &::-webkit-scrollbar-button, + &::-webkit-scrollbar-thumb { + background-color: var(--btn); + box-shadow: var(--buttonShadow); + border-radius: var(--btnRadius); + } + + // horizontal/vertical/increment/decrement are webkit-specific stuff + // that indicates whether we're affecting vertical scrollbar, increase button etc + // stylelint-disable selector-pseudo-class-no-unknown + &::-webkit-scrollbar-button { + --___bgPadding: 2px; + + color: var(--btnText); + background-repeat: no-repeat, no-repeat; + + &:horizontal { + background-size: 50% calc(50% - var(--___bgPadding)), 50% calc(50% - var(--___bgPadding)); + + &:increment { + background-image: + linear-gradient(45deg, var(--btnText) 50%, transparent 51%), + linear-gradient(-45deg, transparent 50%, var(--btnText) 51%); + background-position: top var(--___bgPadding) left 50%, right 50% bottom var(--___bgPadding); + } + + &:decrement { + background-image: + linear-gradient(45deg, transparent 50%, var(--btnText) 51%), + linear-gradient(-45deg, var(--btnText) 50%, transparent 51%); + background-position: bottom var(--___bgPadding) right 50%, left 50% top var(--___bgPadding); + } + } + + &:vertical { + background-size: calc(50% - var(--___bgPadding)) 50%, calc(50% - var(--___bgPadding)) 50%; + + &:increment { + background-image: + linear-gradient(-45deg, transparent 50%, var(--btnText) 51%), + linear-gradient(45deg, transparent 50%, var(--btnText) 51%); + background-position: right var(--___bgPadding) top 50%, left var(--___bgPadding) top 50%; + } + + &:decrement { + background-image: + linear-gradient(-45deg, var(--btnText) 50%, transparent 51%), + linear-gradient(45deg, var(--btnText) 50%, transparent 51%); + background-position: left var(--___bgPadding) top 50%, right var(--___bgPadding) top 50%; + } + } + } + // stylelint-enable selector-pseudo-class-no-unknown + } + // Body should have background to scrollbar otherwise it will use white (body color?) + html { + scrollbar-color: var(--selectedMenu) var(--wallpaper); + background: var(--wallpaper); + } +} + a { text-decoration: none; color: $fallback--link; color: var(--link, $fallback--link); } +h4 { + margin: 0; +} + +.iconLetter { + display: inline-block; + text-align: center; + font-weight: 1000; +} + +i[class*=icon-], +.svg-inline--fa, +.iconLetter { + color: $fallback--icon; + color: var(--icon, $fallback--icon); +} + +.button-unstyled:hover, +a:hover { + > i[class*=icon-], + > .svg-inline--fa, + > .iconLetter { + color: var(--text); + } +} + +nav { + z-index: var(--ZI_navbar); + color: var(--topBarText); + background-color: $fallback--fg; + background-color: var(--topBar, $fallback--fg); + color: $fallback--faint; + color: var(--faint, $fallback--faint); + box-shadow: 0 0 4px rgba(0, 0, 0, 0.6); + box-shadow: var(--topBarShadow); + box-sizing: border-box; + height: var(--navbar-height); + position: fixed; +} + +#sidebar { + grid-area: sidebar; +} + +#modal { + position: absolute; + z-index: var(--ZI_modals); +} + +.column.-scrollable { + top: var(--navbar-height); + position: sticky; +} + +#main-scroller { + grid-area: content; + position: relative; +} + +#notifs-column { + grid-area: notifs; +} + +.app-bg-wrapper { + position: fixed; + height: 100%; + top: var(--navbar-height); + z-index: -1000; + left: 0; + right: -20px; + background-size: cover; + background-repeat: no-repeat; + background-color: var(--wallpaper); + background-image: var(--body-background-image); + background-position: 50%; +} + +.underlay { + grid-column-start: 1; + grid-column-end: span 3; + grid-row-start: 1; + grid-row-end: 1; + pointer-events: none; + background-color: rgba(0, 0, 0, 0.15); + background-color: var(--underlay, rgba(0, 0, 0, 0.15)); + z-index: -1000; +} + +.app-layout { + --miniColumn: 25rem; + --maxiColumn: 45rem; + --columnGap: 1em; + --status-margin: 0.75em; + --effectiveSidebarColumnWidth: minmax(var(--miniColumn), var(--sidebarColumnWidth, var(--miniColumn))); + --effectiveNotifsColumnWidth: minmax(var(--miniColumn), var(--notifsColumnWidth, var(--miniColumn))); + --effectiveContentColumnWidth: minmax(var(--miniColumn), var(--contentColumnWidth, var(--maxiColumn))); + + position: relative; + display: grid; + grid-template-columns: + var(--effectiveSidebarColumnWidth) + var(--effectiveContentColumnWidth); + grid-template-areas: "sidebar content"; + grid-template-rows: 1fr; + box-sizing: border-box; + margin: 0 auto; + align-content: flex-start; + flex-wrap: wrap; + justify-content: center; + min-height: 100vh; + overflow-x: clip; + + .column { + --___columnMargin: var(--columnGap); + + display: grid; + grid-template-columns: 100%; + box-sizing: border-box; + grid-row-start: 1; + grid-row-end: 1; + margin: 0 calc(var(--___columnMargin) / 2); + padding: calc(var(--___columnMargin)) 0; + row-gap: var(--___columnMargin); + align-content: start; + + &:not(.-scrollable) { + margin-top: var(--navbar-height); + } + + &:hover { + z-index: 2; + } + + &.-full-height { + margin-bottom: 0; + padding-top: 0; + padding-bottom: 0; + } + + &.-scrollable { + --___paddingIncrease: calc(var(--columnGap) / 2); + + position: sticky; + top: var(--navbar-height); + max-height: calc(100vh - var(--navbar-height)); + overflow-y: auto; + overflow-x: hidden; + margin-left: calc(var(--___paddingIncrease) * -1); + padding-left: calc(var(--___paddingIncrease) + var(--___columnMargin) / 2); + + // On browsers that don't support hiding scrollbars we enforce "show scrolbars" mode + // might implement old style of hiding scrollbars later if there's demand + @supports (scrollbar-width: none) or (-webkit-text-fill-color: initial) { + &:not(.-show-scrollbar) { + scrollbar-width: none; + margin-right: calc(var(--___paddingIncrease) * -1); + padding-right: calc(var(--___paddingIncrease) + var(--___columnMargin) / 2); + + &::-webkit-scrollbar { + display: block; + width: 0; + } + } + } + + .panel-heading.-sticky { + top: calc(var(--columnGap) / -1); + } + } + } + + &.-has-new-post-button { + .column { + padding-bottom: 10rem; + } + } + + &.-no-sticky-headers { + .column { + .panel-heading.-sticky { + position: relative; + top: 0; + } + } + } + + .column-inner { + display: grid; + grid-template-columns: 100%; + box-sizing: border-box; + row-gap: 1em; + align-content: start; + } + + &.-reverse:not(.-wide):not(.-mobile) { + grid-template-columns: + var(--effectiveContentColumnWidth) + var(--effectiveSidebarColumnWidth); + grid-template-areas: "content sidebar"; + } + + &.-wide { + grid-template-columns: + var(--effectiveSidebarColumnWidth) + var(--effectiveContentColumnWidth) + var(--effectiveNotifsColumnWidth); + grid-template-areas: "sidebar content notifs"; + + &.-reverse { + grid-template-columns: + var(--effectiveNotifsColumnWidth) + var(--effectiveContentColumnWidth) + var(--effectiveSidebarColumnWidth); + grid-template-areas: "notifs content sidebar"; + } + } + + &.-mobile { + grid-template-columns: 100vw; + grid-template-areas: "content"; + padding: 0; + + .column { + margin-left: 0; + margin-right: 0; + padding-top: 0; + margin-top: var(--navbar-height); + margin-bottom: 0; + } + + .panel-heading, + .panel-heading::after, + .panel-heading::before, + .panel, + .panel::after { + border-top-left-radius: 0; + border-top-right-radius: 0; + } + + #sidebar, + #notifs-column { + display: none; + } + } + + &.-normal { + #notifs-column { + display: none; + } + } +} + +.text-center { + text-align: center; +} + .button-default { user-select: none; color: $fallback--text; @@ -84,7 +381,7 @@ a { cursor: pointer; box-shadow: $fallback--buttonShadow; box-shadow: var(--buttonShadow); - font-size: 14px; + font-size: 1em; font-family: sans-serif; font-family: var(--interfaceFont, sans-serif); @@ -103,12 +400,12 @@ a { } &:hover { - box-shadow: 0px 0px 4px rgba(255, 255, 255, 0.3); + box-shadow: 0 0 4px rgba(255, 255, 255, 0.3); box-shadow: var(--buttonHoverShadow); } &:active { - box-shadow: 0px 0px 4px 0px rgba(255, 255, 255, 0.3), 0px 1px 0px 0px rgba(0, 0, 0, 0.2) inset, 0px -1px 0px 0px rgba(255, 255, 255, 0.2) inset; + box-shadow: 0 0 4px 0 rgba(255, 255, 255, 0.3), 0 1px 0 0 rgba(0, 0, 0, 0.2) inset, 0 -1px 0 0 rgba(255, 255, 255, 0.2) inset; box-shadow: var(--buttonPressedShadow); color: $fallback--text; color: var(--btnPressedText, $fallback--text); @@ -141,7 +438,7 @@ a { color: var(--btnToggledText, $fallback--text); background-color: $fallback--fg; background-color: var(--btnToggled, $fallback--fg); - box-shadow: 0px 0px 4px 0px rgba(255, 255, 255, 0.3), 0px 1px 0px 0px rgba(0, 0, 0, 0.2) inset, 0px -1px 0px 0px rgba(255, 255, 255, 0.2) inset; + box-shadow: 0 0 4px 0 rgba(255, 255, 255, 0.3), 0 1px 0 0 rgba(0, 0, 0, 0.2) inset, 0 -1px 0 0 rgba(255, 255, 255, 0.2) inset; box-shadow: var(--buttonPressedShadow); svg, @@ -191,8 +488,9 @@ a { } } -input, textarea, .input { - +input, +textarea, +.input { &.unstyled { border-radius: 0; background: none; @@ -200,10 +498,12 @@ input, textarea, .input { height: unset; } + --_padding: 0.5em; + border: none; border-radius: $fallback--inputRadius; border-radius: var(--inputRadius, $fallback--inputRadius); - box-shadow: 0px 1px 0px 0px rgba(0, 0, 0, 0.2) inset, 0px -1px 0px 0px rgba(255, 255, 255, 0.2) inset, 0px 0px 2px 0px rgba(0, 0, 0, 1) inset; + box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.2) inset, 0 -1px 0 0 rgba(255, 255, 255, 0.2) inset, 0 0 2px 0 rgba(0, 0, 0, 1) inset; box-shadow: var(--inputShadow); background-color: $fallback--fg; background-color: var(--input, $fallback--fg); @@ -211,17 +511,18 @@ input, textarea, .input { color: var(--inputText, $fallback--lightText); font-family: sans-serif; font-family: var(--inputFont, sans-serif); - font-size: 14px; + font-size: 1em; margin: 0; box-sizing: border-box; display: inline-block; position: relative; - height: 28px; - line-height: 16px; + line-height: 2; hyphens: none; - padding: 8px .5em; + padding: 0 var(--_padding); - &:disabled, &[disabled=disabled], &.disabled { + &:disabled, + &[disabled=disabled], + &.disabled { cursor: not-allowed; opacity: 0.5; } @@ -236,18 +537,21 @@ input, textarea, .input { &[type=radio] { display: none; + &:checked + label::before { - box-shadow: 0px 0px 2px black inset, 0px 0px 0px 4px $fallback--fg inset; - box-shadow: var(--inputShadow), 0px 0px 0px 4px var(--fg, $fallback--fg) inset; + box-shadow: 0 0 2px black inset, 0 0 0 4px $fallback--fg inset; + box-shadow: var(--inputShadow), 0 0 0 4px var(--fg, $fallback--fg) inset; background-color: var(--accent, $fallback--link); } + &:disabled { &, & + label, & + label::before { - opacity: .5; + opacity: 0.5; } } + + label::before { flex-shrink: 0; display: inline-block; @@ -256,35 +560,37 @@ input, textarea, .input { width: 1.1em; height: 1.1em; border-radius: 100%; // Radio buttons should always be circle - box-shadow: 0px 0px 2px black inset; + box-shadow: 0 0 2px black inset; box-shadow: var(--inputShadow); - margin-right: .5em; + margin-right: 0.5em; background-color: $fallback--fg; background-color: var(--input, $fallback--fg); vertical-align: top; text-align: center; - line-height: 1.1em; + line-height: 1.1; font-size: 1.1em; box-sizing: border-box; color: transparent; overflow: hidden; - box-sizing: border-box; } } &[type=checkbox] { display: none; + &:checked + label::before { color: $fallback--text; color: var(--inputText, $fallback--text); } + &:disabled { &, & + label, & + label::before { - opacity: .5; + opacity: 0.5; } } + + label::before { flex-shrink: 0; display: inline-block; @@ -294,19 +600,18 @@ input, textarea, .input { height: 1.1em; border-radius: $fallback--checkboxRadius; border-radius: var(--checkboxRadius, $fallback--checkboxRadius); - box-shadow: 0px 0px 2px black inset; + box-shadow: 0 0 2px black inset; box-shadow: var(--inputShadow); - margin-right: .5em; + margin-right: 0.5em; background-color: $fallback--fg; background-color: var(--input, $fallback--fg); vertical-align: top; text-align: center; - line-height: 1.1em; + line-height: 1.1; font-size: 1.1em; box-sizing: border-box; color: transparent; overflow: hidden; - box-sizing: border-box; } } @@ -315,6 +620,12 @@ input, textarea, .input { } } +// Textareas should have stock line-height + vertical padding instead of huge line-height +textarea { + padding: var(--_padding); + line-height: var(--post-line-height); +} + option { color: $fallback--text; color: var(--text, $fallback--text); @@ -324,6 +635,7 @@ option { .hide-number-spinner { -moz-appearance: textfield; + &[type=number]::-webkit-inner-spin-button, &[type=number]::-webkit-outer-spin-button { opacity: 0; @@ -331,11 +643,6 @@ option { } } -i[class*=icon-], .svg-inline--fa { - color: $fallback--icon; - color: var(--icon, $fallback--icon); -} - .btn-block { display: block; width: 100%; @@ -362,273 +669,16 @@ i[class*=icon-], .svg-inline--fa { } } -.container { - display: flex; - flex-wrap: wrap; - margin: 0; - padding: 0 10px 0 10px; -} - -.auto-size { - flex: 1 -} - -main-router { - flex: 1; -} - -.status.compact { - color: rgba(0, 0, 0, 0.42); - font-weight: 300; - - p { - margin: 0; - font-size: 0.8em - } -} - -/* Panel */ - -.panel { - display: flex; - position: relative; - - flex-direction: column; - margin: 0.5em; - - background-color: $fallback--bg; - background-color: var(--bg, $fallback--bg); - - &::after, & { - border-radius: $fallback--panelRadius; - border-radius: var(--panelRadius, $fallback--panelRadius); - } - - &::after { - content: ''; - position: absolute; - - top: 0; - bottom: 0; - left: 0; - right: 0; - - pointer-events: none; - - box-shadow: 1px 1px 4px rgba(0,0,0,.6); - box-shadow: var(--panelShadow); - } -} - -.panel-body:empty::before { - content: "¯\\_(ツ)_/¯"; // Could use words but it'd require translations - display: block; - margin: 1em; - text-align: center; -} - -.panel-heading { - display: flex; - flex: none; - border-radius: $fallback--panelRadius $fallback--panelRadius 0 0; - border-radius: var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius) 0 0; - background-size: cover; - padding: .6em .6em; - text-align: left; - line-height: 28px; - color: var(--panelText); - background-color: $fallback--fg; - background-color: var(--panel, $fallback--fg); - align-items: baseline; - box-shadow: var(--panelHeaderShadow); - - .title { - flex: 1 0 auto; - font-size: 1.3em; - } - - .faint { - background-color: transparent; - color: $fallback--faint; - color: var(--panelFaint, $fallback--faint); - } - - .faint-link { - color: $fallback--faint; - color: var(--faintLink, $fallback--faint); - } - - .alert { - white-space: nowrap; - text-overflow: ellipsis; - overflow-x: hidden; - } - - .button-default, - .alert { - // height: 100%; - line-height: 21px; - min-height: 0; - box-sizing: border-box; - margin: 0; - margin-left: .5em; - min-width: 1px; - align-self: stretch; - } - - .button-default { - flex-shrink: 0; - - &, - i[class*=icon-] { - color: $fallback--text; - color: var(--btnPanelText, $fallback--text); - } - - &:active { - background-color: $fallback--fg; - background-color: var(--btnPressedPanel, $fallback--fg); - color: $fallback--text; - color: var(--btnPressedPanelText, $fallback--text); - } - - &:disabled { - color: $fallback--text; - color: var(--btnDisabledPanelText, $fallback--text); - } - - &.toggled { - color: $fallback--text; - color: var(--btnToggledPanelText, $fallback--text); - } - } - - a, - .-link { - color: $fallback--link; - color: var(--panelLink, $fallback--link) - } -} - -.panel-heading.stub { - border-radius: $fallback--panelRadius; - border-radius: var(--panelRadius, $fallback--panelRadius); -} - -/* TODO Should remove timeline-footer from here when we refactor panels into - * separate component and utilize slots - */ -.panel-footer, .timeline-footer { - display: flex; - border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius; - border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius); - flex: none; - padding: 0.6em 0.6em; - text-align: left; - line-height: 28px; - align-items: baseline; - border-width: 1px 0 0 0; - border-style: solid; - border-color: var(--border, $fallback--border); - - .faint { - color: $fallback--faint; - color: var(--panelFaint, $fallback--faint); - } - - a, - .-link { - color: $fallback--link; - color: var(--panelLink, $fallback--link); - } -} - -.panel-body > p { - line-height: 18px; - padding: 1em; - margin: 0; -} - -.container > * { - min-width: 0px; -} +@import './panel.scss'; .fa { color: grey; } -nav { - z-index: 1000; - color: var(--topBarText); - background-color: $fallback--fg; - background-color: var(--topBar, $fallback--fg); - color: $fallback--faint; - color: var(--faint, $fallback--faint); - box-shadow: 0px 0px 4px rgba(0,0,0,.6); - box-shadow: var(--topBarShadow); - box-sizing: border-box; -} - -.fade-enter-active, .fade-leave-active { - transition: opacity .2s -} -.fade-enter, .fade-leave-active { - opacity: 0 -} - -.main { - flex-basis: 50%; - flex-grow: 1; - flex-shrink: 1; -} - -.sidebar-bounds { - flex: 0; - flex-basis: 35%; -} - -.sidebar-flexer { - flex: 1; - flex-basis: 345px; - width: 365px; -} - .mobile-shown { display: none; } -@media all and (min-width: 800px) { - body { - overflow-y: scroll; - } - - .sidebar-bounds { - overflow: hidden; - max-height: 100vh; - width: 345px; - position: fixed; - margin-top: -10px; - - .sidebar-scroller { - height: 96vh; - width: 365px; - padding-top: 10px; - padding-right: 50px; - overflow-x: hidden; - overflow-y: scroll; - } - - .sidebar { - width: 345px; - } - } - .sidebar-flexer { - max-height: 96vh; - flex-shrink: 0; - flex-grow: 0; - } -} - .badge { box-sizing: border-box; display: inline-block; @@ -656,12 +706,10 @@ nav { } .alert { - margin: 0.35em; - padding: 0.25em; + margin: 0 0.35em; + padding: 0 0.25em; border-radius: $fallback--tooltipRadius; border-radius: var(--tooltipRadius, $fallback--tooltipRadius); - min-height: 28px; - line-height: 28px; &.error { background-color: $fallback--alertError; @@ -712,7 +760,7 @@ nav { } .visibility-notice { - padding: .5em; + padding: 0.5em; border: 1px solid $fallback--faint; border: 1px solid var(--faint, $fallback--faint); border-radius: $fallback--inputRadius; @@ -727,87 +775,27 @@ nav { position: absolute; top: 0; right: 0; - padding: .5em; + padding: 0.5em; color: inherit; } } .fa-scale-110 { - &.svg-inline--fa { + &.svg-inline--fa, + &.iconLetter { font-size: 1.1em; } } .fa-old-padding { - &.svg-inline--fa { + &.iconLetter, + &.svg-inline--fa, &-layer { padding: 0 0.3em; } } -@keyframes shakeError { - 0% { - transform: translateX(0); - } - 15% { - transform: translateX(0.375rem); - } - 30% { - transform: translateX(-0.375rem); - } - 45% { - transform: translateX(0.375rem); - } - 60% { - transform: translateX(-0.375rem); - } - 75% { - transform: translateX(0.375rem); - } - 90% { - transform: translateX(-0.375rem); - } - 100% { - transform: translateX(0); - } -} - -@media all and (max-width: 800px) { - .mobile-hidden { - display: none; - } - - .panel-switcher { - display: flex; - } - - .container { - padding: 0; - } - - .panel { - margin: 0.5em 0 0.5em 0; - } - - .menu-button { - display: block; - margin-right: 0.8em; - } - - .main { - margin-bottom: 7em; - } -} - -.setting-list, -.option-list{ - list-style-type: none; - padding-left: 2em; - li { - margin-bottom: 0.5em; - } - .suboptions { - margin-top: 0.3em - } +.veryfaint { + opacity: 0.25; } .login-hint { @@ -819,18 +807,26 @@ nav { a { display: inline-block; - padding: 1em 0px; + padding: 1em 0; width: 100%; } } .btn.button-default { - min-height: 28px; + min-height: 2em; } -.animate-spin { - animation: spin 2s infinite linear; - display: inline-block; +.new-status-notification { + position: relative; + font-size: 1.1em; + z-index: 1; + flex: 1; +} + +@media all and (max-width: 800px) { + .mobile-hidden { + display: none; + } } @keyframes spin { @@ -843,49 +839,47 @@ nav { } } -.new-status-notification { - position: relative; - font-size: 1.1em; - z-index: 1; - flex: 1; -} +@keyframes shakeError { + 0% { + transform: translateX(0); + } -.chat-layout { - // Needed for smoother chat navigation in the desktop Safari (otherwise the chat layout "jumps" as the chat opens). - overflow: hidden; - height: 100%; + 15% { + transform: translateX(0.375rem); + } - // Get rid of scrollbar on body as scrolling happens on different element - body { - overflow: hidden; + 30% { + transform: translateX(-0.375rem); } - // Ensures the fixed position of the mobile browser bars on scroll up / down events. - // Prevents the mobile browser bars from overlapping or hiding the message posting form. - @media all and (max-width: 800px) { - body { - height: 100%; - } + 45% { + transform: translateX(0.375rem); + } - #app { - height: 100%; - overflow: hidden; - min-height: auto; - } + 60% { + transform: translateX(-0.375rem); + } - #app_bg_wrapper { - overflow: hidden; - } + 75% { + transform: translateX(0.375rem); + } - .main { - overflow: hidden; - height: 100%; - } + 90% { + transform: translateX(-0.375rem); + } - #content { - padding-top: 0; - height: 100%; - overflow: visible; - } + 100% { + transform: translateX(0); } } + +// Vue transitions +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.3s; +} + +.fade-enter-from, +.fade-leave-active { + opacity: 0; +} diff --git a/src/App.vue b/src/App.vue @@ -1,39 +1,43 @@ <template> <div - id="app" + id="app-loaded" :style="bgStyle" > <div id="app_bg_wrapper" class="app-bg-wrapper" /> - <MobileNav v-if="isMobileLayout" /> - <DesktopNav v-else /> - <div class="app-bg-wrapper app-container-wrapper" /> + <MobileNav v-if="layoutType === 'mobile'" /> + <DesktopNav + v-else + :class="navClasses" + /> + <Notifications v-if="currentUser" /> <div id="content" - class="container underlay" + class="app-layout container" + :class="classes" > + <div class="underlay" /> <div - class="sidebar-flexer mobile-hidden" - :style="sidebarAlign" + id="sidebar" + class="column -scrollable" + :class="{ '-show-scrollbar': showScrollbars }" > - <div class="sidebar-bounds"> - <div class="sidebar-scroller"> - <div class="sidebar"> - <user-panel /> - <div v-if="!isMobileLayout"> - <nav-panel /> - <instance-specific-panel v-if="showInstanceSpecificPanel" /> - <features-panel v-if="!currentUser && showFeaturesPanel" /> - <who-to-follow-panel v-if="currentUser && suggestionsEnabled" /> - <notifications v-if="currentUser" /> - </div> - </div> - </div> - </div> + <user-panel /> + <template v-if="layoutType !== 'mobile'"> + <nav-panel /> + <instance-specific-panel v-if="showInstanceSpecificPanel" /> + <features-panel v-if="!currentUser && showFeaturesPanel" /> + <who-to-follow-panel v-if="currentUser && suggestionsEnabled" /> + <div id="notifs-sidebar" /> + </template> </div> - <div class="main"> + <main + id="main-scroller" + class="column main" + :class="{ '-full-height': isChats || isListEdit }" + > <div v-if="!currentUser" class="login-hint panel panel-default" @@ -46,20 +50,28 @@ </router-link> </div> <router-view /> - </div> - <media-modal /> + </main> + <div + id="notifs-column" + class="column -scrollable" + :class="{ '-show-scrollbar': showScrollbars }" + /> </div> + <MediaModal /> <shout-panel v-if="currentUser && shout && !hideShoutbox" :floating="true" class="floating-shout mobile-hidden" - :class="{ 'left': shoutboxPosition }" + :class="{ '-left': shoutboxPosition }" /> <MobilePostStatusButton /> <UserReportingModal /> <PostStatusModal /> + <EditStatusModal v-if="editingAvailable" /> + <StatusHistoryModal v-if="editingAvailable" /> <SettingsModal /> - <portal-target name="modal" /> + <UpdateNotification /> + <div id="modal" /> <GlobalNoticeList /> </div> </template> diff --git a/src/_mixins.scss b/src/_mixins.scss @@ -0,0 +1,17 @@ +@mixin unfocused-style { + @content; + + &:focus:not(:focus-visible):not(:hover) { + @content; + } +} + +@mixin focused-style { + &:hover, &:focus { + @content; + } + + &:focus-visible { + @content; + } +} diff --git a/src/_variables.scss b/src/_variables.scss @@ -30,3 +30,5 @@ $fallback--attachmentRadius: 10px; $fallback--chatMessageRadius: 10px; $fallback--buttonShadow: 0px 0px 2px 0px rgba(0, 0, 0, 1), 0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset, 0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset; + +$status-margin: 0.75em; diff --git a/src/assets/pleromatan_apology.png b/src/assets/pleromatan_apology.png Binary files differ. diff --git a/src/assets/pleromatan_apology_fox.png b/src/assets/pleromatan_apology_fox.png Binary files differ. diff --git a/src/assets/pleromatan_apology_fox_mask.png b/src/assets/pleromatan_apology_fox_mask.png Binary files differ. diff --git a/src/assets/pleromatan_apology_mask.png b/src/assets/pleromatan_apology_mask.png Binary files differ. diff --git a/src/boot/after_store.js b/src/boot/after_store.js @@ -1,12 +1,18 @@ -import Vue from 'vue' -import VueRouter from 'vue-router' -import routes from './routes' +import { createApp } from 'vue' +import { createRouter, createWebHistory } from 'vue-router' +import vClickOutside from 'click-outside-vue3' + +import { FontAwesomeIcon, FontAwesomeLayers } from '@fortawesome/vue-fontawesome' + import App from '../App.vue' -import { windowWidth } from '../services/window_utils/window_utils' +import routes from './routes' +import VBodyScrollLock from 'src/directives/body_scroll_lock' + +import { windowWidth, windowHeight } from '../services/window_utils/window_utils' import { getOrCreateApp, getClientToken } from '../services/new_api/oauth.js' import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js' import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js' -import { applyTheme } from '../services/style_setter/style_setter.js' +import { applyTheme, applyConfig } from '../services/style_setter/style_setter.js' import FaviconService from '../services/favicon_service/favicon_service.js' let staticInitialResults = null @@ -115,6 +121,7 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => { copyInstanceOption('nsfwCensorImage') copyInstanceOption('background') copyInstanceOption('hidePostStats') + copyInstanceOption('hideBotIndication') copyInstanceOption('hideUserStats') copyInstanceOption('hideFilteredStatuses') copyInstanceOption('logo') @@ -149,7 +156,7 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => { copyInstanceOption('hideSitename') copyInstanceOption('sidebarRight') - return store.dispatch('setTheme', config['theme']) + return store.dispatch('setTheme', config.theme) } const getTOS = async ({ store }) => { @@ -190,7 +197,7 @@ const getStickers = async ({ store }) => { const stickers = (await Promise.all( Object.entries(values).map(async ([name, path]) => { const resPack = await window.fetch(path + 'pack.json') - var meta = {} + let meta = {} if (resPack.ok) { meta = await resPack.json() } @@ -244,6 +251,7 @@ const getNodeInfo = async ({ store }) => { store.dispatch('setInstanceOption', { name: 'pleromaChatMessagesAvailable', value: features.includes('pleroma_chat_messages') }) store.dispatch('setInstanceOption', { name: 'gopherAvailable', value: features.includes('gopher') }) store.dispatch('setInstanceOption', { name: 'pollsAvailable', value: features.includes('polls') }) + store.dispatch('setInstanceOption', { name: 'editingAvailable', value: features.includes('editing') }) store.dispatch('setInstanceOption', { name: 'pollLimits', value: metadata.pollLimits }) store.dispatch('setInstanceOption', { name: 'mailerEnabled', value: metadata.mailerEnabled }) @@ -312,6 +320,7 @@ const setConfig = async ({ store }) => { } const checkOAuthToken = async ({ store }) => { + // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve, reject) => { if (store.getters.getUserToken()) { try { @@ -325,8 +334,8 @@ const checkOAuthToken = async ({ store }) => { } const afterStoreSetup = async ({ store, i18n }) => { - const width = windowWidth() - store.dispatch('setMobileLayout', width <= 800) + store.dispatch('setLayoutWidth', windowWidth()) + store.dispatch('setLayoutHeight', windowHeight()) FaviconService.initFaviconService() @@ -352,6 +361,8 @@ const afterStoreSetup = async ({ store, i18n }) => { console.error('Failed to load any theme!') } + applyConfig(store.state.config) + // Now we can try getting the server settings and logging in // Most of these are preloaded into the index.html so blocking is minimized await Promise.all([ @@ -363,28 +374,39 @@ const afterStoreSetup = async ({ store, i18n }) => { // Start fetching things that don't need to block the UI store.dispatch('fetchMutes') + store.dispatch('startFetchingAnnouncements') getTOS({ store }) getStickers({ store }) - const router = new VueRouter({ - mode: 'history', + const router = createRouter({ + history: createWebHistory(), routes: routes(store), scrollBehavior: (to, _from, savedPosition) => { if (to.matched.some(m => m.meta.dontScroll)) { return false } - return savedPosition || { x: 0, y: 0 } + return savedPosition || { left: 0, top: 0 } } }) - /* eslint-disable no-new */ - return new Vue({ - router, - store, - i18n, - el: '#app', - render: h => h(App) - }) + const app = createApp(App) + + app.use(router) + app.use(store) + app.use(i18n) + + app.use(vClickOutside) + app.use(VBodyScrollLock) + + app.component('FAIcon', FontAwesomeIcon) + app.component('FALayers', FontAwesomeLayers) + + // remove after vue 3.3 + app.config.unwrapInjectedRef = true + + app.mount('#app') + + return app } export default afterStoreSetup diff --git a/src/boot/routes.js b/src/boot/routes.js @@ -20,6 +20,11 @@ import ShoutPanel from 'components/shout_panel/shout_panel.vue' import WhoToFollow from 'components/who_to_follow/who_to_follow.vue' import About from 'components/about/about.vue' import RemoteUserResolver from 'components/remote_user_resolver/remote_user_resolver.vue' +import Lists from 'components/lists/lists.vue' +import ListsTimeline from 'components/lists_timeline/lists_timeline.vue' +import ListsEdit from 'components/lists_edit/lists_edit.vue' +import NavPanel from 'src/components/nav_panel/nav_panel.vue' +import AnnouncementsPage from 'components/announcements_page/announcements_page.vue' export default (store) => { const validateAuthenticatedRoute = (to, from, next) => { @@ -31,7 +36,8 @@ export default (store) => { } let routes = [ - { name: 'root', + { + name: 'root', path: '/', redirect: _to => { return (store.state.users.currentUser @@ -45,31 +51,40 @@ export default (store) => { { name: 'tag-timeline', path: '/tag/:tag', component: TagTimeline }, { name: 'bookmarks', path: '/bookmarks', component: BookmarkTimeline }, { name: 'conversation', path: '/notice/:id', component: ConversationPage, meta: { dontScroll: true } }, - { name: 'remote-user-profile-acct', - path: '/remote-users/(@?):username([^/@]+)@:hostname([^/@]+)', + { + name: 'remote-user-profile-acct', + path: '/remote-users/:_(@)?:username([^/@]+)@:hostname([^/@]+)', component: RemoteUserResolver, beforeEnter: validateAuthenticatedRoute }, - { name: 'remote-user-profile', + { + name: 'remote-user-profile', path: '/remote-users/:hostname/:username', component: RemoteUserResolver, beforeEnter: validateAuthenticatedRoute }, - { name: 'external-user-profile', path: '/users/:id', component: UserProfile }, + { name: 'external-user-profile', path: '/users/$:id', component: UserProfile }, { name: 'interactions', path: '/users/:username/interactions', component: Interactions, beforeEnter: validateAuthenticatedRoute }, { name: 'dms', path: '/users/:username/dms', component: DMs, beforeEnter: validateAuthenticatedRoute }, { name: 'registration', path: '/registration', component: Registration }, { name: 'password-reset', path: '/password-reset', component: PasswordReset, props: true }, { name: 'registration-token', path: '/registration/:token', component: Registration }, { name: 'friend-requests', path: '/friend-requests', component: FollowRequests, beforeEnter: validateAuthenticatedRoute }, - { name: 'notifications', path: '/:username/notifications', component: Notifications, beforeEnter: validateAuthenticatedRoute }, + { name: 'notifications', path: '/:username/notifications', component: Notifications, props: () => ({ disableTeleport: true }), beforeEnter: validateAuthenticatedRoute }, { name: 'login', path: '/login', component: AuthForm }, { name: 'shout-panel', path: '/shout-panel', component: ShoutPanel, props: () => ({ floating: false }) }, { name: 'oauth-callback', path: '/oauth-callback', component: OAuthCallback, props: (route) => ({ code: route.query.code }) }, { name: 'search', path: '/search', component: Search, props: (route) => ({ query: route.query.query }) }, { name: 'who-to-follow', path: '/who-to-follow', component: WhoToFollow, beforeEnter: validateAuthenticatedRoute }, { name: 'about', path: '/about', component: About }, - { name: 'user-profile', path: '/(users/)?:name', component: UserProfile } + { name: 'announcements', path: '/announcements', component: AnnouncementsPage }, + { name: 'user-profile', path: '/users/:name', component: UserProfile }, + { name: 'legacy-user-profile', path: '/:name', component: UserProfile }, + { name: 'lists', path: '/lists', component: Lists }, + { name: 'lists-timeline', path: '/lists/:id', component: ListsTimeline }, + { name: 'lists-edit', path: '/lists/:id/edit', component: ListsEdit }, + { name: 'lists-new', path: '/lists/new', component: ListsEdit }, + { name: 'edit-navigation', path: '/nav-edit', component: NavPanel, props: () => ({ forceExpand: true, forceEditMode: true }), beforeEnter: validateAuthenticatedRoute } ] if (store.state.instance.pleromaChatMessagesAvailable) { diff --git a/src/components/about/about.vue b/src/components/about/about.vue @@ -1,5 +1,5 @@ <template> - <div class="sidebar"> + <div class="column-inner"> <instance-specific-panel v-if="showInstanceSpecificPanel" /> <staff-panel /> <terms-of-service-panel /> @@ -8,7 +8,7 @@ </div> </template> -<script src="./about.js" ></script> +<script src="./about.js"></script> <style lang="scss"> </style> diff --git a/src/components/account_actions/account_actions.js b/src/components/account_actions/account_actions.js @@ -1,6 +1,7 @@ import { mapState } from 'vuex' import ProgressButton from '../progress_button/progress_button.vue' import Popover from '../popover/popover.vue' +import UserListMenu from 'src/components/user_list_menu/user_list_menu.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faEllipsisV @@ -19,7 +20,8 @@ const AccountActions = { }, components: { ProgressButton, - Popover + Popover, + UserListMenu }, methods: { showRepeats () { @@ -34,13 +36,16 @@ const AccountActions = { unblockUser () { this.$store.dispatch('unblockUser', this.user.id) }, + removeUserFromFollowers () { + this.$store.dispatch('removeUserFromFollowers', this.user.id) + }, reportUser () { this.$store.dispatch('openUserReportingModal', { userId: this.user.id }) }, openChat () { this.$router.push({ name: 'chat', - params: { recipient_id: this.user.id } + params: { username: this.$store.state.users.currentUser.screen_name, recipient_id: this.user.id } }) } }, diff --git a/src/components/account_actions/account_actions.vue b/src/components/account_actions/account_actions.vue @@ -6,7 +6,7 @@ :bound-to="{ x: 'container' }" remove-padding > - <template v-slot:content> + <template #content> <div class="dropdown-menu"> <template v-if="relationship.following"> <button @@ -28,6 +28,14 @@ class="dropdown-divider" /> </template> + <UserListMenu :user="user" /> + <button + v-if="relationship.followed_by" + class="btn button-default btn-block dropdown-item" + @click="removeUserFromFollowers" + > + {{ $t('user_card.remove_follower') }} + </button> <button v-if="relationship.blocking" class="btn button-default btn-block dropdown-item" @@ -57,7 +65,7 @@ </button> </div> </template> - <template v-slot:trigger> + <template #trigger> <button class="button-unstyled ellipsis-button"> <FAIcon class="icon" @@ -74,10 +82,6 @@ <style lang="scss"> @import '../../_variables.scss'; .AccountActions { - button.dropdown-item { - margin-left: 0; - } - .ellipsis-button { width: 2.5em; margin: -0.5em 0; diff --git a/src/components/announcement/announcement.js b/src/components/announcement/announcement.js @@ -0,0 +1,105 @@ +import { mapState } from 'vuex' +import AnnouncementEditor from '../announcement_editor/announcement_editor.vue' +import RichContent from '../rich_content/rich_content.jsx' +import localeService from '../../services/locale/locale.service.js' + +const Announcement = { + components: { + AnnouncementEditor, + RichContent + }, + data () { + return { + editing: false, + editedAnnouncement: { + content: '', + startsAt: undefined, + endsAt: undefined, + allDay: undefined + }, + editError: '' + } + }, + props: { + announcement: Object + }, + computed: { + ...mapState({ + currentUser: state => state.users.currentUser + }), + content () { + return this.announcement.content + }, + isRead () { + return this.announcement.read + }, + publishedAt () { + const time = this.announcement.published_at + if (!time) { + return + } + + return this.formatTimeOrDate(time, localeService.internalToBrowserLocale(this.$i18n.locale)) + }, + startsAt () { + const time = this.announcement.starts_at + if (!time) { + return + } + + return this.formatTimeOrDate(time, localeService.internalToBrowserLocale(this.$i18n.locale)) + }, + endsAt () { + const time = this.announcement.ends_at + if (!time) { + return + } + + return this.formatTimeOrDate(time, localeService.internalToBrowserLocale(this.$i18n.locale)) + }, + inactive () { + return this.announcement.inactive + } + }, + methods: { + markAsRead () { + if (!this.isRead) { + return this.$store.dispatch('markAnnouncementAsRead', this.announcement.id) + } + }, + deleteAnnouncement () { + return this.$store.dispatch('deleteAnnouncement', this.announcement.id) + }, + formatTimeOrDate (time, locale) { + const d = new Date(time) + return this.announcement.all_day ? d.toLocaleDateString(locale) : d.toLocaleString(locale) + }, + enterEditMode () { + this.editedAnnouncement.content = this.announcement.pleroma.raw_content + this.editedAnnouncement.startsAt = this.announcement.starts_at + this.editedAnnouncement.endsAt = this.announcement.ends_at + this.editedAnnouncement.allDay = this.announcement.all_day + this.editing = true + }, + submitEdit () { + this.$store.dispatch('editAnnouncement', { + id: this.announcement.id, + ...this.editedAnnouncement + }) + .then(() => { + this.editing = false + }) + .catch(error => { + this.editError = error.error + }) + }, + cancelEdit () { + this.editing = false + }, + clearError () { + this.editError = undefined + } + } +} + +export default Announcement diff --git a/src/components/announcement/announcement.vue b/src/components/announcement/announcement.vue @@ -0,0 +1,136 @@ +<template> + <div class="announcement"> + <div class="heading"> + <h4>{{ $t('announcements.title') }}</h4> + </div> + <div class="body"> + <rich-content + v-if="!editing" + :html="content" + :emoji="announcement.emojis" + :handle-links="true" + /> + <announcement-editor + v-else + :announcement="editedAnnouncement" + /> + </div> + <div class="footer"> + <div + v-if="!editing" + class="times" + > + <span v-if="publishedAt"> + {{ $t('announcements.published_time_display', { time: publishedAt }) }} + </span> + <span v-if="startsAt"> + {{ $t('announcements.start_time_display', { time: startsAt }) }} + </span> + <span v-if="endsAt"> + {{ $t('announcements.end_time_display', { time: endsAt }) }} + </span> + </div> + <div + v-if="!editing" + class="actions" + > + <button + v-if="currentUser" + class="btn button-default" + :class="{ toggled: isRead }" + :disabled="inactive" + :title="inactive ? $t('announcements.inactive_message') : ''" + @click="markAsRead" + > + {{ $t('announcements.mark_as_read_action') }} + </button> + <button + v-if="currentUser && currentUser.role === 'admin'" + class="btn button-default" + @click="enterEditMode" + > + {{ $t('announcements.edit_action') }} + </button> + <button + v-if="currentUser && currentUser.role === 'admin'" + class="btn button-default" + @click="deleteAnnouncement" + > + {{ $t('announcements.delete_action') }} + </button> + </div> + <div + v-else + class="actions" + > + <button + class="btn button-default" + @click="submitEdit" + > + {{ $t('announcements.submit_edit_action') }} + </button> + <button + class="btn button-default" + @click="cancelEdit" + > + {{ $t('announcements.cancel_edit_action') }} + </button> + <div + v-if="editing && editError" + class="alert error" + > + {{ $t('announcements.edit_error', { error }) }} + <button + class="button-unstyled" + @click="clearError" + > + <FAIcon + class="fa-scale-110 fa-old-padding" + icon="times" + :title="$t('announcements.close_error')" + /> + </button> + </div> + </div> + </div> + </div> +</template> + +<script src="./announcement.js"></script> + +<style lang="scss"> +@import "../../variables"; + +.announcement { + border-bottom-width: 1px; + border-bottom-style: solid; + border-bottom-color: var(--border, $fallback--border); + border-radius: 0; + padding: var(--status-margin, $status-margin); + + .heading, .body { + margin-bottom: var(--status-margin, $status-margin); + } + + .footer { + display: flex; + flex-direction: column; + .times { + display: flex; + flex-direction: column; + } + } + + .footer .actions { + display: flex; + flex-direction: row; + justify-content: space-evenly; + + .btn { + flex: 1; + margin: 1em; + max-width: 10em; + } + } +} +</style> diff --git a/src/components/announcement_editor/announcement_editor.js b/src/components/announcement_editor/announcement_editor.js @@ -0,0 +1,13 @@ +import Checkbox from '../checkbox/checkbox.vue' + +const AnnouncementEditor = { + components: { + Checkbox + }, + props: { + announcement: Object, + disabled: Boolean + } +} + +export default AnnouncementEditor diff --git a/src/components/announcement_editor/announcement_editor.vue b/src/components/announcement_editor/announcement_editor.vue @@ -0,0 +1,60 @@ +<template> + <div class="announcement-editor"> + <textarea + ref="textarea" + v-model="announcement.content" + class="post-textarea" + rows="1" + cols="1" + :placeholder="$t('announcements.post_placeholder')" + :disabled="disabled" + /> + <span class="announcement-metadata"> + <label for="announcement-start-time">{{ $t('announcements.start_time_prompt') }}</label> + <input + id="announcement-start-time" + v-model="announcement.startsAt" + :type="announcement.allDay ? 'date' : 'datetime-local'" + :disabled="disabled" + > + </span> + <span class="announcement-metadata"> + <label for="announcement-end-time">{{ $t('announcements.end_time_prompt') }}</label> + <input + id="announcement-end-time" + v-model="announcement.endsAt" + :type="announcement.allDay ? 'date' : 'datetime-local'" + :disabled="disabled" + > + </span> + <span class="announcement-metadata"> + <Checkbox + id="announcement-all-day" + v-model="announcement.allDay" + :disabled="disabled" + /> + <label for="announcement-all-day">{{ $t('announcements.all_day_prompt') }}</label> + </span> + </div> +</template> + +<script src="./announcement_editor.js"></script> + +<style lang="scss"> +.announcement-editor { + display: flex; + align-items: stretch; + flex-direction: column; + + .announcement-metadata { + margin-top: 0.5em; + } + + .post-textarea { + resize: vertical; + height: 10em; + overflow: none; + box-sizing: content-box; + } +} +</style> diff --git a/src/components/announcements_page/announcements_page.js b/src/components/announcements_page/announcements_page.js @@ -0,0 +1,55 @@ +import { mapState } from 'vuex' +import Announcement from '../announcement/announcement.vue' +import AnnouncementEditor from '../announcement_editor/announcement_editor.vue' + +const AnnouncementsPage = { + components: { + Announcement, + AnnouncementEditor + }, + data () { + return { + newAnnouncement: { + content: '', + startsAt: undefined, + endsAt: undefined, + allDay: false + }, + posting: false, + error: undefined + } + }, + mounted () { + this.$store.dispatch('fetchAnnouncements') + }, + computed: { + ...mapState({ + currentUser: state => state.users.currentUser + }), + announcements () { + return this.$store.state.announcements.announcements + } + }, + methods: { + postAnnouncement () { + this.posting = true + this.$store.dispatch('postAnnouncement', this.newAnnouncement) + .then(() => { + this.newAnnouncement.content = '' + this.startsAt = undefined + this.endsAt = undefined + }) + .catch(error => { + this.error = error.error + }) + .finally(() => { + this.posting = false + }) + }, + clearError () { + this.error = undefined + } + } +} + +export default AnnouncementsPage diff --git a/src/components/announcements_page/announcements_page.vue b/src/components/announcements_page/announcements_page.vue @@ -0,0 +1,79 @@ +<template> + <div class="panel panel-default announcements-page"> + <div class="panel-heading"> + <span> + {{ $t('announcements.page_header') }} + </span> + </div> + <div class="panel-body"> + <section + v-if="currentUser && currentUser.role === 'admin'" + > + <div class="post-form"> + <div class="heading"> + <h4>{{ $t('announcements.post_form_header') }}</h4> + </div> + <div class="body"> + <announcement-editor + :announcement="newAnnouncement" + :disabled="posting" + /> + </div> + <div class="footer"> + <button + class="btn button-default post-button" + :disabled="posting" + @click.prevent="postAnnouncement" + > + {{ $t('announcements.post_action') }} + </button> + <div + v-if="error" + class="alert error" + > + {{ $t('announcements.post_error', { error }) }} + <button + class="button-unstyled" + @click="clearError" + > + <FAIcon + class="fa-scale-110 fa-old-padding" + icon="times" + :title="$t('announcements.close_error')" + /> + </button> + </div> + </div> + </div> + </section> + <section + v-for="announcement in announcements" + :key="announcement.id" + > + <announcement + :announcement="announcement" + /> + </section> + </div> + </div> +</template> + +<script src="./announcements_page.js"></script> + +<style lang="scss"> +@import "../../variables"; + +.announcements-page { + .post-form { + padding: var(--status-margin, $status-margin); + + .heading, .body { + margin-bottom: var(--status-margin, $status-margin); + } + + .post-button { + min-width: 10em; + } + } +} +</style> diff --git a/src/components/async_component_error/async_component_error.vue b/src/components/async_component_error/async_component_error.vue @@ -19,6 +19,7 @@ <script> export default { + emits: ['resetAsyncComponent'], methods: { retry () { this.$emit('resetAsyncComponent') diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js @@ -11,7 +11,12 @@ import { faImage, faVideo, faPlayCircle, - faTimes + faTimes, + faStop, + faSearchPlus, + faTrashAlt, + faPencilAlt, + faAlignRight } from '@fortawesome/free-solid-svg-icons' library.add( @@ -20,27 +25,39 @@ library.add( faImage, faVideo, faPlayCircle, - faTimes + faTimes, + faStop, + faSearchPlus, + faTrashAlt, + faPencilAlt, + faAlignRight ) const Attachment = { props: [ 'attachment', + 'description', + 'hideDescription', 'nsfw', 'size', - 'allowPlay', 'setMedia', - 'naturalSizeLoad' + 'remove', + 'shiftUp', + 'shiftDn', + 'edit' ], data () { return { + localDescription: this.description || this.attachment.description, nsfwImage: this.$store.state.instance.nsfwCensorImage || nsfwImage, hideNsfwLocal: this.$store.getters.mergedConfig.hideNsfw, preloadImage: this.$store.getters.mergedConfig.preloadImage, loading: false, img: fileTypeService.fileType(this.attachment.mimetype) === 'image' && document.createElement('img'), modalOpen: false, - showHidden: false + showHidden: false, + flashLoaded: false, + showDescription: false } }, components: { @@ -49,8 +66,23 @@ const Attachment = { VideoAttachment }, computed: { + classNames () { + return [ + { + '-loading': this.loading, + '-nsfw-placeholder': this.hidden, + '-editable': this.edit !== undefined + }, + '-type-' + this.type, + this.size && '-size-' + this.size, + `-${this.useContainFit ? 'contain' : 'cover'}-fit` + ] + }, usePlaceholder () { - return this.size === 'hide' || this.type === 'unknown' + return this.size === 'hide' + }, + useContainFit () { + return this.$store.getters.mergedConfig.useContainFit }, placeholderName () { if (this.attachment.description === '' || !this.attachment.description) { @@ -74,24 +106,36 @@ const Attachment = { return this.nsfw && this.hideNsfwLocal && !this.showHidden }, isEmpty () { - return (this.type === 'html' && !this.attachment.oembed) || this.type === 'unknown' - }, - isSmall () { - return this.size === 'small' - }, - fullwidth () { - if (this.size === 'hide') return false - return this.type === 'html' || this.type === 'audio' || this.type === 'unknown' + return (this.type === 'html' && !this.attachment.oembed) }, useModal () { - const modalTypes = this.size === 'hide' ? ['image', 'video', 'audio'] - : this.mergedConfig.playVideosInModal - ? ['image', 'video'] - : ['image'] + let modalTypes = [] + switch (this.size) { + case 'hide': + case 'small': + modalTypes = ['image', 'video', 'audio', 'flash'] + break + default: + modalTypes = this.mergedConfig.playVideosInModal + ? ['image', 'video', 'flash'] + : ['image'] + break + } return modalTypes.includes(this.type) }, + videoTag () { + return this.useModal ? 'button' : 'span' + }, ...mapGetters(['mergedConfig']) }, + watch: { + 'attachment.description' (newVal) { + this.localDescription = newVal + }, + localDescription (newVal) { + this.onEdit(newVal) + } + }, methods: { linkClicked ({ target }) { if (target.tagName === 'A') { @@ -100,12 +144,37 @@ const Attachment = { }, openModal (event) { if (this.useModal) { - event.stopPropagation() - event.preventDefault() - this.setMedia() - this.$store.dispatch('setCurrent', this.attachment) + this.$emit('setMedia') + this.$store.dispatch('setCurrentMedia', this.attachment) + } else if (this.type === 'unknown') { + window.open(this.attachment.url) } }, + openModalForce (event) { + this.$emit('setMedia') + this.$store.dispatch('setCurrentMedia', this.attachment) + }, + onEdit (event) { + this.edit && this.edit(this.attachment, event) + }, + onRemove () { + this.remove && this.remove(this.attachment) + }, + onShiftUp () { + this.shiftUp && this.shiftUp(this.attachment) + }, + onShiftDn () { + this.shiftDn && this.shiftDn(this.attachment) + }, + stopFlash () { + this.$refs.flash.closePlayer() + }, + setFlashLoaded (event) { + this.flashLoaded = event + }, + toggleDescription () { + this.showDescription = !this.showDescription + }, toggleHidden (event) { if ( (this.mergedConfig.useOneClickNsfw && !this.showHidden) && @@ -132,7 +201,7 @@ const Attachment = { onImageLoad (image) { const width = image.naturalWidth const height = image.naturalHeight - this.naturalSizeLoad && this.naturalSizeLoad({ width, height }) + this.$emit('naturalSizeLoad', { id: this.attachment.id, width, height }) } } } diff --git a/src/components/attachment/attachment.scss b/src/components/attachment/attachment.scss @@ -0,0 +1,268 @@ +@import '../../_variables.scss'; + +.Attachment { + display: inline-flex; + flex-direction: column; + position: relative; + align-self: flex-start; + line-height: 0; + height: 100%; + border-style: solid; + border-width: 1px; + border-radius: $fallback--attachmentRadius; + border-radius: var(--attachmentRadius, $fallback--attachmentRadius); + border-color: $fallback--border; + border-color: var(--border, $fallback--border); + + .attachment-wrapper { + flex: 1 1 auto; + height: 100%; + position: relative; + overflow: hidden; + } + + .description-container { + flex: 0 1 0; + display: flex; + padding-top: 0.5em; + z-index: 1; + + p { + flex: 1; + text-align: center; + line-height: 1.5; + padding: 0.5em; + margin: 0; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } + + &.-static { + position: absolute; + left: 0; + right: 0; + bottom: 0; + padding-top: 0; + background: var(--popover); + box-shadow: var(--popupShadow); + } + } + + .description-field { + flex: 1; + min-width: 0; + } + + & .placeholder-container, + & .image-container, + & .audio-container, + & .video-container, + & .flash-container, + & .oembed-container { + display: flex; + justify-content: center; + width: 100%; + height: 100%; + } + + .image-container { + .image { + width: 100%; + height: 100%; + } + } + + & .flash-container, + & .video-container { + & .flash, + & video { + width: 100%; + height: 100%; + object-fit: contain; + align-self: center; + } + } + + .audio-container { + display: flex; + align-items: flex-end; + + audio { + width: 100%; + height: 100%; + } + } + + .placeholder-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding-top: 0.5em; + } + + + .play-icon { + position: absolute; + font-size: 64px; + top: calc(50% - 32px); + left: calc(50% - 32px); + color: rgba(255, 255, 255, 0.75); + text-shadow: 0 0 2px rgba(0, 0, 0, 0.4); + + &::before { + margin: 0; + } + } + + .attachment-buttons { + display: flex; + position: absolute; + right: 0; + top: 0; + margin-top: 0.5em; + margin-right: 0.5em; + z-index: 1; + + .attachment-button { + padding: 0; + border-radius: $fallback--tooltipRadius; + border-radius: var(--tooltipRadius, $fallback--tooltipRadius); + text-align: center; + width: 2em; + height: 2em; + margin-left: 0.5em; + font-size: 1.25em; + // TODO: theming? hard to theme with unknown background image color + background: rgba(230, 230, 230, 0.7); + + .svg-inline--fa { + color: rgba(0, 0, 0, 0.6); + } + + &:hover .svg-inline--fa { + color: rgba(0, 0, 0, 0.9); + } + } + } + + .oembed-container { + line-height: 1.2em; + flex: 1 0 100%; + width: 100%; + margin-right: 15px; + display: flex; + + img { + width: 100%; + } + + .image { + flex: 1; + img { + border: 0px; + border-radius: 5px; + height: 100%; + object-fit: cover; + } + } + + .text { + flex: 2; + margin: 8px; + word-break: break-all; + h1 { + font-size: 1rem; + margin: 0px; + } + } + } + + &.-size-small { + .play-icon { + zoom: 0.5; + opacity: 0.7; + } + + .attachment-buttons { + zoom: 0.7; + opacity: 0.5; + } + } + + &.-editable { + padding: 0.5em; + + & .description-container, + & .attachment-buttons { + margin: 0; + } + } + + &.-placeholder { + display: inline-block; + color: $fallback--link; + color: var(--postLink, $fallback--link); + overflow: hidden; + white-space: nowrap; + height: auto; + line-height: 1.5; + + &:not(.-editable) { + border: none; + } + + &.-editable { + display: flex; + flex-direction: row; + align-items: baseline; + + & .description-container, + & .attachment-buttons { + margin: 0; + padding: 0; + position: relative; + } + + .description-container { + flex: 1; + padding-left: 0.5em; + } + + .attachment-buttons { + order: 99; + align-self: center; + } + } + + a { + display: inline-block; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + } + + svg { + color: inherit; + } + } + + &.-loading { + cursor: progress; + } + + &.-contain-fit { + img, + canvas { + object-fit: contain; + } + } + + &.-cover-fit { + img, + canvas { + object-fit: cover; + } + } +} diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue @@ -1,7 +1,8 @@ <template> - <div + <button v-if="usePlaceholder" - :class="{ 'fullwidth': fullwidth }" + class="Attachment -placeholder button-unstyled" + :class="classNames" @click="openModal" > <a @@ -11,318 +12,257 @@ :href="attachment.url" :alt="attachment.description" :title="attachment.description" + @click.prevent > <FAIcon :icon="placeholderIconClass" /> - <b>{{ nsfw ? "NSFW / " : "" }}</b>{{ placeholderName }} + <b>{{ nsfw ? "NSFW / " : "" }}</b>{{ edit ? '' : placeholderName }} </a> - </div> - <div - v-else - v-show="!isEmpty" - class="attachment" - :class="{[type]: true, loading, 'fullwidth': fullwidth, 'nsfw-placeholder': hidden}" - > - <a - v-if="hidden" - class="image-attachment" - :href="attachment.url" - :alt="attachment.description" - :title="attachment.description" - @click.prevent.stop="toggleHidden" + <div + v-if="edit || remove" + class="attachment-buttons" > - <img - :key="nsfwImage" - class="nsfw" - :src="nsfwImage" - :class="{'small': isSmall}" + <button + v-if="remove" + class="button-unstyled attachment-button" + @click.prevent="onRemove" > - <FAIcon - v-if="type === 'video'" - class="play-icon" - icon="play-circle" - /> - </a> - <button - v-if="nsfw && hideNsfwLocal && !hidden" - class="button-unstyled hider" - @click.prevent="toggleHidden" + <FAIcon icon="trash-alt" /> + </button> + </div> + <div + v-if="size !== 'hide' && !hideDescription && (edit || localDescription || showDescription)" + class="description-container" + :class="{ '-static': !edit }" > - <FAIcon icon="times" /> - </button> - - <a - v-if="type === 'image' && (!hidden || preloadImage)" - class="image-attachment" - :class="{'hidden': hidden && preloadImage }" - :href="attachment.url" - target="_blank" - @click="openModal" + <input + v-if="edit" + v-model="localDescription" + type="text" + class="description-field" + :placeholder="$t('post_status.media_description')" + @keydown.enter.prevent="" + > + <p v-else> + {{ localDescription }} + </p> + </div> + </button> + <div + v-else + class="Attachment" + :class="classNames" + > + <div + v-show="!isEmpty" + class="attachment-wrapper" > - <StillImage - class="image" - :referrerpolicy="referrerpolicy" - :mimetype="attachment.mimetype" - :src="attachment.large_thumb_url || attachment.url" - :image-load-handler="onImageLoad" + <a + v-if="hidden" + class="image-container" + :href="attachment.url" :alt="attachment.description" - /> - </a> + :title="attachment.description" + @click.prevent.stop="toggleHidden" + > + <img + :key="nsfwImage" + class="nsfw" + :src="nsfwImage" + > + <FAIcon + v-if="type === 'video'" + class="play-icon" + icon="play-circle" + /> + </a> + <div + v-if="!hidden" + class="attachment-buttons" + > + <button + v-if="type === 'flash' && flashLoaded" + class="button-unstyled attachment-button" + :title="$t('status.attachment_stop_flash')" + @click.prevent="stopFlash" + > + <FAIcon icon="stop" /> + </button> + <button + v-if="attachment.description && size !== 'small' && !edit && type !== 'unknown'" + class="button-unstyled attachment-button" + :title="$t('status.show_attachment_description')" + @click.prevent="toggleDescription" + > + <FAIcon icon="align-right" /> + </button> + <button + v-if="!useModal && type !== 'unknown'" + class="button-unstyled attachment-button" + :title="$t('status.show_attachment_in_modal')" + @click.prevent="openModalForce" + > + <FAIcon icon="search-plus" /> + </button> + <button + v-if="nsfw && hideNsfwLocal" + class="button-unstyled attachment-button" + :title="$t('status.hide_attachment')" + @click.prevent="toggleHidden" + > + <FAIcon icon="times" /> + </button> + <button + v-if="shiftUp" + class="button-unstyled attachment-button" + :title="$t('status.move_up')" + @click.prevent="onShiftUp" + > + <FAIcon icon="chevron-left" /> + </button> + <button + v-if="shiftDn" + class="button-unstyled attachment-button" + :title="$t('status.move_down')" + @click.prevent="onShiftDn" + > + <FAIcon icon="chevron-right" /> + </button> + <button + v-if="remove" + class="button-unstyled attachment-button" + :title="$t('status.remove_attachment')" + @click.prevent="onRemove" + > + <FAIcon icon="trash-alt" /> + </button> + </div> - <a - v-if="type === 'video' && !hidden" - class="video-container" - :class="{'small': isSmall}" - :href="allowPlay ? undefined : attachment.url" - @click="openModal" - > - <VideoAttachment - class="video" - :attachment="attachment" - :controls="allowPlay" - @play="$emit('play')" - @pause="$emit('pause')" - /> - <FAIcon - v-if="!allowPlay" - class="play-icon" - icon="play-circle" - /> - </a> + <a + v-if="type === 'image' && (!hidden || preloadImage)" + class="image-container" + :class="{'-hidden': hidden && preloadImage }" + :href="attachment.url" + target="_blank" + @click.stop.prevent="openModal" + > + <StillImage + class="image" + :referrerpolicy="referrerpolicy" + :mimetype="attachment.mimetype" + :src="attachment.large_thumb_url || attachment.url" + :image-load-handler="onImageLoad" + :alt="attachment.description" + /> + </a> + + <a + v-if="type === 'unknown' && !hidden" + class="placeholder-container" + :href="attachment.url" + target="_blank" + > + <FAIcon + size="5x" + :icon="placeholderIconClass" + /> + <p> + {{ localDescription }} + </p> + </a> + + <component + :is="videoTag" + v-if="type === 'video' && !hidden" + class="video-container" + :class="{ 'button-unstyled': 'isModal' }" + :href="attachment.url" + @click.stop.prevent="openModal" + > + <VideoAttachment + class="video" + :attachment="attachment" + :controls="!useModal" + @play="$emit('play')" + @pause="$emit('pause')" + /> + <FAIcon + v-if="useModal" + class="play-icon" + icon="play-circle" + /> + </component> + + <span + v-if="type === 'audio' && !hidden" + class="audio-container" + :href="attachment.url" + @click.stop.prevent="openModal" + > + <audio + v-if="type === 'audio'" + :src="attachment.url" + :alt="attachment.description" + :title="attachment.description" + controls + @play="$emit('play')" + @pause="$emit('pause')" + /> + </span> - <audio - v-if="type === 'audio'" - :src="attachment.url" - :alt="attachment.description" - :title="attachment.description" - controls - @play="$emit('play')" - @pause="$emit('pause')" - /> + <div + v-if="type === 'html' && attachment.oembed" + class="oembed-container" + @click.prevent="linkClicked" + > + <div + v-if="attachment.thumb_url" + class="image" + > + <img :src="attachment.thumb_url"> + </div> + <div class="text"> + <!-- eslint-disable vue/no-v-html --> + <h1><a :href="attachment.url">{{ attachment.oembed.title }}</a></h1> + <div v-html="attachment.oembed.oembedHTML" /> + <!-- eslint-enable vue/no-v-html --> + </div> + </div> + <span + v-if="type === 'flash' && !hidden" + class="flash-container" + :href="attachment.url" + @click.stop.prevent="openModal" + > + <Flash + ref="flash" + class="flash" + :src="attachment.large_thumb_url || attachment.url" + @playerOpened="setFlashLoaded(true)" + @playerClosed="setFlashLoaded(false)" + /> + </span> + </div> <div - v-if="type === 'html' && attachment.oembed" - class="oembed" - @click.prevent="linkClicked" + v-if="size !== 'hide' && !hideDescription && (edit || (localDescription && showDescription))" + class="description-container" + :class="{ '-static': !edit }" > - <div - v-if="attachment.thumb_url" - class="image" + <input + v-if="edit" + v-model="localDescription" + type="text" + class="description-field" + :placeholder="$t('post_status.media_description')" + @keydown.enter.prevent="" > - <img :src="attachment.thumb_url"> - </div> - <div class="text"> - <!-- eslint-disable vue/no-v-html --> - <h1><a :href="attachment.url">{{ attachment.oembed.title }}</a></h1> - <div v-html="attachment.oembed.oembedHTML" /> - <!-- eslint-enable vue/no-v-html --> - </div> + <p v-else> + {{ localDescription }} + </p> </div> - - <Flash - v-if="type === 'flash'" - :src="attachment.large_thumb_url || attachment.url" - /> </div> </template> <script src="./attachment.js"></script> -<style lang="scss"> -@import '../../_variables.scss'; - -.attachments { - display: flex; - flex-wrap: wrap; - - .non-gallery { - max-width: 100%; - } - - .placeholder { - display: inline-block; - padding: 0.3em 1em 0.3em 0; - color: $fallback--link; - color: var(--postLink, $fallback--link); - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - max-width: 100%; - - svg { - color: inherit; - } - } - - .nsfw-placeholder { - cursor: pointer; - - &.loading { - cursor: progress; - } - } - - .attachment { - position: relative; - margin-top: 0.5em; - align-self: flex-start; - line-height: 0; - - border-style: solid; - border-width: 1px; - border-radius: $fallback--attachmentRadius; - border-radius: var(--attachmentRadius, $fallback--attachmentRadius); - border-color: $fallback--border; - border-color: var(--border, $fallback--border); - overflow: hidden; - } - - .non-gallery.attachment { - &.flash, - &.video { - flex: 1 0 40%; - } - .nsfw { - height: 260px; - } - .small { - height: 120px; - flex-grow: 0; - } - .video { - height: 260px; - display: flex; - } - video { - max-height: 100%; - object-fit: contain; - } - } - - .fullwidth { - flex-basis: 100%; - } - // fixes small gap below video - &.video { - line-height: 0; - } - - .video-container { - display: flex; - max-height: 100%; - } - - .video { - width: 100%; - height: 100%; - } - - .play-icon { - position: absolute; - font-size: 64px; - top: calc(50% - 32px); - left: calc(50% - 32px); - color: rgba(255, 255, 255, 0.75); - text-shadow: 0 0 2px rgba(0, 0, 0, 0.4); - } - - .play-icon::before { - margin: 0; - } - - &.html { - flex-basis: 90%; - width: 100%; - display: flex; - } - - .hider { - position: absolute; - right: 0; - margin: 10px; - padding: 0; - z-index: 4; - border-radius: $fallback--tooltipRadius; - border-radius: var(--tooltipRadius, $fallback--tooltipRadius); - text-align: center; - width: 2em; - height: 2em; - font-size: 1.25em; - // TODO: theming? hard to theme with unknown background image color - background: rgba(230, 230, 230, 0.7); - .svg-inline--fa { - color: rgba(0, 0, 0, 0.6); - } - &:hover .svg-inline--fa { - color: rgba(0, 0, 0, 0.9); - } - } - - video { - z-index: 0; - } - - audio { - width: 100%; - } - - img.media-upload { - line-height: 0; - max-height: 200px; - max-width: 100%; - } - - .oembed { - line-height: 1.2em; - flex: 1 0 100%; - width: 100%; - margin-right: 15px; - display: flex; - - img { - width: 100%; - } - - .image { - flex: 1; - img { - border: 0px; - border-radius: 5px; - height: 100%; - object-fit: cover; - } - } - - .text { - flex: 2; - margin: 8px; - word-break: break-all; - h1 { - font-size: 14px; - margin: 0px; - } - } - } - - .image-attachment { - &, - & .image { - width: 100%; - height: 100%; - } - - &.hidden { - display: none; - } - - .nsfw { - object-fit: cover; - width: 100%; - height: 100%; - } - - img { - image-orientation: from-image; // NOTE: only FF supports this - } - } -} -</style> +<style src="./attachment.scss" lang="scss"></style> diff --git a/src/components/auth_form/auth_form.js b/src/components/auth_form/auth_form.js @@ -1,3 +1,4 @@ +import { h, resolveComponent } from 'vue' import LoginForm from '../login_form/login_form.vue' import MFARecoveryForm from '../mfa_form/recovery_form.vue' import MFATOTPForm from '../mfa_form/totp_form.vue' @@ -5,8 +6,8 @@ import { mapGetters } from 'vuex' const AuthForm = { name: 'AuthForm', - render (createElement) { - return createElement('component', { is: this.authForm }) + render () { + return h(resolveComponent(this.authForm)) }, computed: { authForm () { diff --git a/src/components/avatar_list/avatar_list.vue b/src/components/avatar_list/avatar_list.vue @@ -14,7 +14,7 @@ </div> </template> -<script src="./avatar_list.js" ></script> +<script src="./avatar_list.js"></script> <style lang="scss"> @import '../../_variables.scss'; diff --git a/src/components/basic_user_card/basic_user_card.js b/src/components/basic_user_card/basic_user_card.js @@ -1,5 +1,6 @@ -import UserCard from '../user_card/user_card.vue' +import UserPopover from '../user_popover/user_popover.vue' import UserAvatar from '../user_avatar/user_avatar.vue' +import UserLink from '../user_link/user_link.vue' import RichContent from 'src/components/rich_content/rich_content.jsx' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' @@ -7,20 +8,13 @@ const BasicUserCard = { props: [ 'user' ], - data () { - return { - userExpanded: false - } - }, components: { - UserCard, + UserPopover, UserAvatar, - RichContent + RichContent, + UserLink }, methods: { - toggleUserExpanded () { - this.userExpanded = !this.userExpanded - }, userProfileLink (user) { return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames) } diff --git a/src/components/basic_user_card/basic_user_card.vue b/src/components/basic_user_card/basic_user_card.vue @@ -1,24 +1,22 @@ <template> <div class="basic-user-card"> - <router-link :to="userProfileLink(user)"> - <UserAvatar - class="avatar" - :user="user" - @click.prevent.native="toggleUserExpanded" - /> - </router-link> - <div - v-if="userExpanded" - class="basic-user-card-expanded-content" + <router-link + :to="userProfileLink(user)" + @click.prevent > - <UserCard + <UserPopover :user-id="user.id" - :rounded="true" - :bordered="true" - /> - </div> + :overlay-centers="true" + overlay-centers-selector=".avatar" + > + <UserAvatar + class="user-avatar avatar" + :user="user" + @click.prevent + /> + </UserPopover> + </router-link> <div - v-else class="basic-user-card-collapsed-content" > <div @@ -32,12 +30,10 @@ /> </div> <div> - <router-link + <user-link class="basic-user-card-screen-name" - :to="userProfileLink(user)" - > - @{{ user.screen_name_ui }} - </router-link> + :user="user" + /> </div> <slot /> </div> @@ -53,6 +49,8 @@ margin: 0; padding: 0.6em 1em; + --emoji-size: 14px; + &-collapsed-content { margin-left: 0.7em; text-align: left; diff --git a/src/components/bookmark_timeline/bookmark_timeline.js b/src/components/bookmark_timeline/bookmark_timeline.js @@ -9,7 +9,7 @@ const Bookmarks = { components: { Timeline }, - destroyed () { + unmounted () { this.$store.commit('clearTimeline', { timeline: 'bookmarks' }) } } diff --git a/src/components/chat/chat.js b/src/components/chat/chat.js @@ -6,7 +6,7 @@ import PostStatusForm from '../post_status_form/post_status_form.vue' import ChatTitle from '../chat_title/chat_title.vue' import chatService from '../../services/chat_service/chat_service.js' import { promiseInterval } from '../../services/promise_interval/promise_interval.js' -import { getScrollPosition, getNewTopPosition, isBottomedOut, scrollableContainerHeight, isScrollable } from './chat_layout_utils.js' +import { getScrollPosition, getNewTopPosition, isBottomedOut, isScrollable } from './chat_layout_utils.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faChevronDown, @@ -20,7 +20,7 @@ library.add( ) const BOTTOMED_OUT_OFFSET = 10 -const JUMP_TO_BOTTOM_BUTTON_VISIBILITY_OFFSET = 150 +const JUMP_TO_BOTTOM_BUTTON_VISIBILITY_OFFSET = 10 const SAFE_RESIZE_TIME_OFFSET = 100 const MARK_AS_READ_DELAY = 1500 const MAX_RETRIES = 10 @@ -43,7 +43,7 @@ const Chat = { }, created () { this.startFetching() - window.addEventListener('resize', this.handleLayoutChange) + window.addEventListener('resize', this.handleResize) }, mounted () { window.addEventListener('scroll', this.handleScroll) @@ -52,15 +52,12 @@ const Chat = { } this.$nextTick(() => { - this.updateScrollableContainerHeight() this.handleResize() }) - this.setChatLayout() }, - destroyed () { + unmounted () { window.removeEventListener('scroll', this.handleScroll) - window.removeEventListener('resize', this.handleLayoutChange) - this.unsetChatLayout() + window.removeEventListener('resize', this.handleResize) if (typeof document.hidden !== 'undefined') document.removeEventListener('visibilitychange', this.handleVisibilityChange, false) this.$store.dispatch('clearCurrentChat') }, @@ -96,8 +93,7 @@ const Chat = { ...mapState({ backendInteractor: state => state.api.backendInteractor, mastoUserSocketStatus: state => state.api.mastoUserSocketStatus, - mobileLayout: state => state.interface.mobileLayout, - layoutHeight: state => state.interface.layoutHeight, + mobileLayout: state => state.interface.layoutType === 'mobile', currentUser: state => state.users.currentUser }) }, @@ -112,12 +108,9 @@ const Chat = { } }) }, - '$route': function () { + $route: function () { this.startFetching() }, - layoutHeight () { - this.handleResize({ expand: true }) - }, mastoUserSocketStatus (newValue) { if (newValue === WSConnectionStatus.JOINED) { this.fetchChat({ isFirstFetch: true }) @@ -132,7 +125,6 @@ const Chat = { onFilesDropped () { this.$nextTick(() => { this.handleResize() - this.updateScrollableContainerHeight() }) }, handleVisibilityChange () { @@ -142,45 +134,9 @@ const Chat = { } }) }, - setChatLayout () { - // This is a hacky way to adjust the global layout to the mobile chat (without modifying the rest of the app). - // This layout prevents empty spaces from being visible at the bottom - // of the chat on iOS Safari (`safe-area-inset`) when - // - the on-screen keyboard appears and the user starts typing - // - the user selects the text inside the input area - // - the user selects and deletes the text that is multiple lines long - // TODO: unify the chat layout with the global layout. - let html = document.querySelector('html') - if (html) { - html.classList.add('chat-layout') - } - - this.$nextTick(() => { - this.updateScrollableContainerHeight() - }) - }, - unsetChatLayout () { - let html = document.querySelector('html') - if (html) { - html.classList.remove('chat-layout') - } - }, - handleLayoutChange () { - this.$nextTick(() => { - this.updateScrollableContainerHeight() - this.scrollDown() - }) - }, - // Ensures the proper position of the posting form in the mobile layout (the mobile browser panel does not overlap or hide it) - updateScrollableContainerHeight () { - const header = this.$refs.header - const footer = this.$refs.footer - const inner = this.mobileLayout ? window.document.body : this.$refs.inner - this.scrollableContainerHeight = scrollableContainerHeight(inner, header, footer) + 'px' - }, - // Preserves the scroll position when OSK appears or the posting form changes its height. + // "Sticks" scroll to bottom instead of top, helps with OSK resizing the viewport handleResize (opts = {}) { - const { expand = false, delayed = false } = opts + const { delayed = false } = opts if (delayed) { setTimeout(() => { @@ -190,29 +146,20 @@ const Chat = { } this.$nextTick(() => { - this.updateScrollableContainerHeight() - - const { offsetHeight = undefined } = this.lastScrollPosition - this.lastScrollPosition = getScrollPosition(this.$refs.scrollable) - - const diff = this.lastScrollPosition.offsetHeight - offsetHeight - if (diff < 0 || (!this.bottomedOut() && expand)) { + const { offsetHeight = undefined } = getScrollPosition() + const diff = offsetHeight - this.lastScrollPosition.offsetHeight + if (diff !== 0 && !this.bottomedOut()) { this.$nextTick(() => { - this.updateScrollableContainerHeight() - this.$refs.scrollable.scrollTo({ - top: this.$refs.scrollable.scrollTop - diff, - left: 0 - }) + window.scrollBy({ top: -Math.trunc(diff) }) }) } + this.lastScrollPosition = getScrollPosition() }) }, scrollDown (options = {}) { const { behavior = 'auto', forceRead = false } = options - const scrollable = this.$refs.scrollable - if (!scrollable) { return } this.$nextTick(() => { - scrollable.scrollTo({ top: scrollable.scrollHeight, left: 0, behavior }) + window.scrollTo({ top: document.documentElement.scrollHeight, behavior }) }) if (forceRead) { this.readChat() @@ -228,11 +175,10 @@ const Chat = { }) }, bottomedOut (offset) { - return isBottomedOut(this.$refs.scrollable, offset) + return isBottomedOut(offset) }, reachedTop () { - const scrollable = this.$refs.scrollable - return scrollable && scrollable.scrollTop <= 0 + return window.scrollY <= 0 }, cullOlderCheck () { window.setTimeout(() => { @@ -242,6 +188,7 @@ const Chat = { }, 5000) }, handleScroll: _.throttle(function () { + this.lastScrollPosition = getScrollPosition() if (!this.currentChat) { return } if (this.reachedTop()) { @@ -263,10 +210,9 @@ const Chat = { } }, 200), handleScrollUp (positionBeforeLoading) { - const positionAfterLoading = getScrollPosition(this.$refs.scrollable) - this.$refs.scrollable.scrollTo({ - top: getNewTopPosition(positionBeforeLoading, positionAfterLoading), - left: 0 + const positionAfterLoading = getScrollPosition() + window.scrollTo({ + top: getNewTopPosition(positionBeforeLoading, positionAfterLoading) }) }, fetchChat ({ isFirstFetch = false, fetchLatest = false, maxId }) { @@ -285,22 +231,18 @@ const Chat = { chatService.clear(chatMessageService) } - const positionBeforeUpdate = getScrollPosition(this.$refs.scrollable) + const positionBeforeUpdate = getScrollPosition() this.$store.dispatch('addChatMessages', { chatId, messages }).then(() => { this.$nextTick(() => { if (fetchOlderMessages) { this.handleScrollUp(positionBeforeUpdate) } - if (isFirstFetch) { - this.updateScrollableContainerHeight() - } - // In vertical screens, the first batch of fetched messages may not always take the // full height of the scrollable container. // If this is the case, we want to fetch the messages until the scrollable container // is fully populated so that the user has the ability to scroll up and load the history. - if (!isScrollable(this.$refs.scrollable) && messages.length > 0) { + if (!isScrollable() && messages.length > 0) { this.fetchChat({ maxId: this.currentChatMessageService.minId }) } }) @@ -336,9 +278,6 @@ const Chat = { this.handleResize() // When the posting form size changes because of a media attachment, we need an extra resize // to account for the potential delay in the DOM update. - setTimeout(() => { - this.updateScrollableContainerHeight() - }, SAFE_RESIZE_TIME_OFFSET) this.scrollDown({ forceRead: true }) }) }, diff --git a/src/components/chat/chat.scss b/src/components/chat/chat.scss @@ -1,28 +1,22 @@ .chat-view { display: flex; - height: calc(100vh - 60px); - width: 100%; - - .chat-title { - // prevents chat header jumping on when the user avatar loads - height: 28px; - } + height: 100%; .chat-view-inner { height: auto; width: 100%; overflow: visible; display: flex; - margin: 0.5em 0.5em 0 0.5em; } .chat-view-body { + box-sizing: border-box; background-color: var(--chatBg, $fallback--bg); display: flex; flex-direction: column; width: 100%; overflow: visible; - min-height: 100%; + min-height: calc(100vh - var(--navbar-height)); margin: 0 0 0 0; border-radius: 10px 10px 0 0; border-radius: var(--panelRadius, 10px) var(--panelRadius, 10px) 0 0; @@ -32,36 +26,32 @@ } } - .scrollable-message-list { + .message-list { padding: 0 0.8em; height: 100%; - overflow-y: scroll; - overflow-x: hidden; display: flex; flex-direction: column; + justify-content: end; } .footer { position: sticky; bottom: 0; + background-color: $fallback--bg; + background-color: var(--bg, $fallback--bg); + z-index: 1; } .chat-view-heading { - align-items: center; - justify-content: space-between; - top: 50px; - display: flex; - z-index: 2; - position: sticky; - overflow: hidden; + grid-template-columns: auto minmax(50%, 1fr); } .go-back-button { - cursor: pointer; - width: 28px; text-align: center; - padding: 0.6em; - margin: -0.6em 0.6em -0.6em -0.6em; + line-height: 1; + height: 100%; + align-self: start; + width: var(--__panel-heading-height-inner); } .jump-to-bottom-button { @@ -115,56 +105,4 @@ } } } - - @media all and (max-width: 800px) { - height: 100%; - overflow: hidden; - - .chat-view-inner { - overflow: hidden; - height: 100%; - margin-top: 0; - margin-left: 0; - margin-right: 0; - } - - .chat-view-body { - display: flex; - min-height: auto; - overflow: hidden; - height: 100%; - margin: 0; - border-radius: 0; - } - - .chat-view-heading { - box-sizing: border-box; - position: static; - z-index: 9999; - top: 0; - margin-top: 0; - border-radius: 0; - - /* This practically overlays the panel heading color over panel background - * color. This is needed because we allow transparent panel background and - * it doesn't work well in this "disjointed panel header" case - */ - background: - linear-gradient(to top, var(--panel), var(--panel)), - linear-gradient(to top, var(--bg), var(--bg)); - height: 50px; - } - - .scrollable-message-list { - display: unset; - overflow-y: scroll; - overflow-x: hidden; - -webkit-overflow-scrolling: touch; - } - - .footer { - position: sticky; - bottom: auto; - } - } } diff --git a/src/components/chat/chat.vue b/src/components/chat/chat.vue @@ -2,23 +2,22 @@ <div class="chat-view"> <div class="chat-view-inner"> <div - id="nav" ref="inner" class="panel-default panel chat-view-body" > <div ref="header" - class="panel-heading chat-view-heading mobile-hidden" + class="panel-heading -sticky chat-view-heading" > - <a - class="go-back-button" + <button + class="button-unstyled go-back-button" @click="goBack" > <FAIcon size="lg" icon="chevron-left" /> - </a> + </button> <div class="title text-center"> <ChatTitle :user="recipient" @@ -26,73 +25,69 @@ /> </div> </div> - <template> + <div + class="message-list" + :style="{ height: scrollableContainerHeight }" + > + <template v-if="!errorLoadingChat"> + <ChatMessage + v-for="chatViewItem in chatViewItems" + :key="chatViewItem.id" + :author="recipient" + :chat-view-item="chatViewItem" + :hovered-message-chain="chatViewItem.messageChainId === hoveredMessageChainId" + @hover="onMessageHover" + /> + </template> <div - ref="scrollable" - class="scrollable-message-list" - :style="{ height: scrollableContainerHeight }" - @scroll="handleScroll" + v-else + class="chat-loading-error" > - <template v-if="!errorLoadingChat"> - <ChatMessage - v-for="chatViewItem in chatViewItems" - :key="chatViewItem.id" - :author="recipient" - :chat-view-item="chatViewItem" - :hovered-message-chain="chatViewItem.messageChainId === hoveredMessageChainId" - @hover="onMessageHover" - /> - </template> - <div - v-else - class="chat-loading-error" - > - <div class="alert error"> - {{ $t('chats.error_loading_chat') }} - </div> + <div class="alert error"> + {{ $t('chats.error_loading_chat') }} </div> </div> + </div> + <div + ref="footer" + class="panel-body footer" + > <div - ref="footer" - class="panel-body footer" + class="jump-to-bottom-button" + :class="{ 'visible': jumpToBottomButtonVisible }" + @click="scrollDown({ behavior: 'smooth' })" > - <div - class="jump-to-bottom-button" - :class="{ 'visible': jumpToBottomButtonVisible }" - @click="scrollDown({ behavior: 'smooth' })" - > - <span> - <FAIcon icon="chevron-down" /> - <div - v-if="newMessageCount" - class="badge badge-notification unread-chat-count unread-message-count" - > - {{ newMessageCount }} - </div> - </span> - </div> - <PostStatusForm - :disable-subject="true" - :disable-scope-selector="true" - :disable-notice="true" - :disable-lock-warning="true" - :disable-polls="true" - :disable-sensitivity-checkbox="true" - :disable-submit="errorLoadingChat || !currentChat" - :disable-preview="true" - :optimistic-posting="true" - :post-handler="sendMessage" - :submit-on-enter="!mobileLayout" - :preserve-focus="!mobileLayout" - :auto-focus="!mobileLayout" - :placeholder="formPlaceholder" - :file-limit="1" - max-height="160" - emoji-picker-placement="top" - @resize="handleResize" - /> + <span> + <FAIcon icon="chevron-down" /> + <div + v-if="newMessageCount" + class="badge badge-notification unread-chat-count unread-message-count" + > + {{ newMessageCount }} + </div> + </span> </div> - </template> + <PostStatusForm + :disable-subject="true" + :disable-scope-selector="true" + :disable-notice="true" + :disable-lock-warning="true" + :disable-polls="true" + :disable-sensitivity-checkbox="true" + :disable-submit="errorLoadingChat || !currentChat" + :disable-preview="true" + :optimistic-posting="true" + :post-handler="sendMessage" + :submit-on-enter="!mobileLayout" + :preserve-focus="!mobileLayout" + :auto-focus="!mobileLayout" + :placeholder="formPlaceholder" + :file-limit="1" + max-height="160" + emoji-picker-placement="top" + @resize="handleResize" + /> + </div> </div> </div> </div> diff --git a/src/components/chat/chat_layout_utils.js b/src/components/chat/chat_layout_utils.js @@ -1,9 +1,9 @@ // Captures a scroll position -export const getScrollPosition = (el) => { +export const getScrollPosition = () => { return { - scrollTop: el.scrollTop, - scrollHeight: el.scrollHeight, - offsetHeight: el.offsetHeight + scrollTop: window.scrollY, + scrollHeight: document.documentElement.scrollHeight, + offsetHeight: window.innerHeight } } @@ -13,21 +13,12 @@ export const getNewTopPosition = (previousPosition, newPosition) => { return previousPosition.scrollTop + (newPosition.scrollHeight - previousPosition.scrollHeight) } -export const isBottomedOut = (el, offset = 0) => { - if (!el) { return } - const scrollHeight = el.scrollTop + offset - const totalHeight = el.scrollHeight - el.offsetHeight +export const isBottomedOut = (offset = 0) => { + const scrollHeight = window.scrollY + offset + const totalHeight = document.documentElement.scrollHeight - window.innerHeight return totalHeight <= scrollHeight } - -// Height of the scrollable container. The dynamic height is needed to ensure the mobile browser panel doesn't overlap or hide the posting form. -export const scrollableContainerHeight = (inner, header, footer) => { - return inner.offsetHeight - header.clientHeight - footer.clientHeight -} - // Returns whether or not the scrollbar is visible. -export const isScrollable = (el) => { - if (!el) return - - return el.scrollHeight > el.clientHeight +export const isScrollable = () => { + return document.documentElement.scrollHeight > window.innerHeight } diff --git a/src/components/chat_list/chat_list.vue b/src/components/chat_list/chat_list.vue @@ -6,7 +6,7 @@ v-else class="chat-list panel panel-default" > - <div class="panel-heading"> + <div class="panel-heading -sticky"> <span class="title"> {{ $t("chats.chats") }} </span> @@ -23,7 +23,7 @@ class="timeline" > <List :items="sortedChatList"> - <template v-slot:item="{item}"> + <template #item="{item}"> <ChatListItem :key="item.id" :compact="false" diff --git a/src/components/chat_list_item/chat_list_item.scss b/src/components/chat_list_item/chat_list_item.scss @@ -43,7 +43,7 @@ white-space: nowrap; overflow: hidden; flex-shrink: 1; - line-height: 1.4em; + line-height: var(--post-line-height); } .chat-preview { @@ -82,7 +82,7 @@ } .time-wrapper { - line-height: 1.4em; + line-height: var(--post-line-height); } .chat-preview-body { diff --git a/src/components/chat_message/chat_message.js b/src/components/chat_message/chat_message.js @@ -6,7 +6,7 @@ import Gallery from '../gallery/gallery.vue' import LinkPreview from '../link-preview/link-preview.vue' import StatusContent from '../status_content/status_content.vue' import ChatMessageDate from '../chat_message_date/chat_message_date.vue' -import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' +import { defineAsyncComponent } from 'vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faTimes, @@ -27,6 +27,7 @@ const ChatMessage = { 'chatViewItem', 'hoveredMessageChain' ], + emits: ['hover'], components: { Popover, Attachment, @@ -34,7 +35,8 @@ const ChatMessage = { UserAvatar, Gallery, LinkPreview, - ChatMessageDate + ChatMessageDate, + UserPopover: defineAsyncComponent(() => import('../user_popover/user_popover.vue')) }, computed: { // Returns HH:MM (hours and minutes) in local time. @@ -48,9 +50,6 @@ const ChatMessage = { message () { return this.chatViewItem.data }, - userProfileLink () { - return generateProfileLink(this.author.id, this.author.screen_name, this.$store.state.instance.restrictedNicknames) - }, isMessage () { return this.chatViewItem.type === 'message' }, diff --git a/src/components/chat_message/chat_message.scss b/src/components/chat_message/chat_message.scss @@ -1,6 +1,7 @@ @import '../../_variables.scss'; .chat-message-wrapper { + &.hovered-message-chain { .animated.Avatar { canvas { @@ -40,6 +41,12 @@ .chat-message { display: flex; padding-bottom: 0.5em; + + .status-body:hover { + --_still-image-img-visibility: visible; + --_still-image-canvas-visibility: hidden; + --_still-image-label-visibility: hidden; + } } .avatar-wrapper { @@ -62,10 +69,6 @@ &.with-media { width: 100%; - .gallery-row { - overflow: hidden; - } - .status { width: 100%; } diff --git a/src/components/chat_message/chat_message.vue b/src/components/chat_message/chat_message.vue @@ -14,16 +14,16 @@ v-if="!isCurrentUser" class="avatar-wrapper" > - <router-link + <UserPopover v-if="chatViewItem.isHead" - :to="userProfileLink" + :user-id="author.id" > <UserAvatar :compact="true" :better-shadow="betterShadow" :user="author" /> - </router-link> + </UserPopover> </div> <div class="chat-message-inner"> <div @@ -44,13 +44,13 @@ <Popover trigger="click" placement="top" - :bound-to-selector="isCurrentUser ? '' : '.scrollable-message-list'" + bound-to-selector=".chat-view-inner" :bound-to="{ x: 'container' }" :margin="popoverMarginStyle" @show="menuOpened = true" @close="menuOpened = false" > - <template v-slot:content> + <template #content> <div class="dropdown-menu"> <button class="button-default dropdown-item dropdown-item-icon" @@ -60,7 +60,7 @@ </button> </div> </template> - <template v-slot:trigger> + <template #trigger> <button class="button-default menu-icon" :title="$t('chats.more')" @@ -75,7 +75,7 @@ :status="messageForStatusContent" :full-content="true" > - <template v-slot:footer> + <template #footer> <span class="created-at" > @@ -96,7 +96,7 @@ </div> </template> -<script src="./chat_message.js" ></script> +<script src="./chat_message.js"></script> <style lang="scss"> @import './chat_message.scss'; diff --git a/src/components/chat_new/chat_new.scss b/src/components/chat_new/chat_new.scss @@ -22,10 +22,10 @@ } .go-back-button { - cursor: pointer; - width: 28px; text-align: center; - padding: 0.6em; - margin: -0.6em 0.6em -0.6em -0.6em; + line-height: 1; + height: 100%; + align-self: start; + width: var(--__panel-heading-height-inner); } } diff --git a/src/components/chat_new/chat_new.vue b/src/components/chat_new/chat_new.vue @@ -1,21 +1,20 @@ <template> <div - id="nav" class="panel-default panel chat-new" > <div ref="header" class="panel-heading" > - <a - class="go-back-button" + <button + class="button-unstyled go-back-button" @click="goBack" > <FAIcon size="lg" icon="chevron-left" /> - </a> + </button> </div> <div class="input-wrap"> <div class="input-search"> diff --git a/src/components/chat_title/chat_title.js b/src/components/chat_title/chat_title.js @@ -1,11 +1,13 @@ -import Vue from 'vue' -import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import UserAvatar from '../user_avatar/user_avatar.vue' +import RichContent from 'src/components/rich_content/rich_content.jsx' +import { defineAsyncComponent } from 'vue' -export default Vue.component('chat-title', { +export default { name: 'ChatTitle', components: { - UserAvatar + UserAvatar, + RichContent, + UserPopover: defineAsyncComponent(() => import('../user_popover/user_popover.vue')) }, props: [ 'user', 'withAvatar' @@ -17,10 +19,5 @@ export default Vue.component('chat-title', { htmlTitle () { return this.user ? this.user.name_html : '' } - }, - methods: { - getUserProfileLink (user) { - return generateProfileLink(user.id, user.screen_name) - } } -}) +} diff --git a/src/components/chat_title/chat_title.vue b/src/components/chat_title/chat_title.vue @@ -1,25 +1,26 @@ <template> - <!-- eslint-disable vue/no-v-html --> <div class="chat-title" :title="title" > - <router-link + <UserPopover v-if="withAvatar && user" - :to="getUserProfileLink(user)" + class="avatar-container" + :user-id="user.id" > <UserAvatar + class="titlebar-avatar" :user="user" - width="23px" - height="23px" /> - </router-link> - <span + </UserPopover> + <RichContent + v-if="user" class="username" - v-html="htmlTitle" + :title="'@'+(user && user.screen_name_ui)" + :html="htmlTitle" + :emoji="user.emoji || []" /> </div> - <!-- eslint-enable vue/no-v-html --> </template> <script src="./chat_title.js"></script> @@ -32,7 +33,8 @@ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - align-items: center; + + --emoji-size: 14px; .username { max-width: 100%; @@ -41,21 +43,17 @@ display: inline; word-wrap: break-word; overflow: hidden; - text-overflow: ellipsis; + } - .emoji { - width: 14px; - height: 14px; - vertical-align: middle; - object-fit: contain - } + .avatar-container { + align-self: center; + line-height: 1; } - .Avatar { - width: 23px; - height: 23px; + .titlebar-avatar { margin-right: 0.5em; - + height: 1.5em; + width: 1.5em; border-radius: $fallback--avatarAltRadius; border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); diff --git a/src/components/checkbox/checkbox.vue b/src/components/checkbox/checkbox.vue @@ -6,9 +6,9 @@ <input type="checkbox" :disabled="disabled" - :checked="checked" - :indeterminate.prop="indeterminate" - @change="$emit('change', $event.target.checked)" + :checked="modelValue" + :indeterminate="indeterminate" + @change="$emit('update:modelValue', $event.target.checked)" > <i class="checkbox-indicator" /> <span @@ -22,15 +22,12 @@ <script> export default { - model: { - prop: 'checked', - event: 'change' - }, props: [ - 'checked', + 'modelValue', 'indeterminate', 'disabled' - ] + ], + emits: ['update:modelValue'] } </script> diff --git a/src/components/color_input/color_input.scss b/src/components/color_input/color_input.scss @@ -27,16 +27,16 @@ &.nativeColor { flex: 0 0 2em; min-width: 2em; - align-self: center; - height: 100%; + align-self: stretch; + min-height: 100%; } } .computedIndicator, .transparentIndicator { flex: 0 0 2em; min-width: 2em; - align-self: center; - height: 100%; + align-self: stretch; + min-height: 100%; } .transparentIndicator { // forgot to install counter-strike source, ooops diff --git a/src/components/color_input/color_input.vue b/src/components/color_input/color_input.vue @@ -11,28 +11,28 @@ </label> <Checkbox v-if="typeof fallback !== 'undefined' && showOptionalTickbox" - :checked="present" + :model-value="present" :disabled="disabled" class="opt" - @change="$emit('input', typeof value === 'undefined' ? fallback : undefined)" + @update:modelValue="$emit('update:modelValue', typeof modelValue === 'undefined' ? fallback : undefined)" /> <div class="input color-input-field"> <input :id="name + '-t'" class="textColor unstyled" type="text" - :value="value || fallback" + :value="modelValue || fallback" :disabled="!present || disabled" - @input="$emit('input', $event.target.value)" + @input="$emit('update:modelValue', $event.target.value)" > <input v-if="validColor" :id="name" class="nativeColor unstyled" type="color" - :value="value || fallback" + :value="modelValue || fallback" :disabled="!present || disabled" - @input="$emit('input', $event.target.value)" + @input="$emit('update:modelValue', $event.target.value)" > <div v-if="transparentColor" @@ -46,7 +46,6 @@ </div> </div> </template> -<style lang="scss" src="./color_input.scss"></style> <script> import Checkbox from '../checkbox/checkbox.vue' import { hex2rgb } from '../../services/color_convert/color_convert.js' @@ -67,7 +66,7 @@ export default { }, // Color value, should be required but vue cannot tell the difference // between "property missing" and "property set to undefined" - value: { + modelValue: { required: false, type: String, default: undefined @@ -91,22 +90,24 @@ export default { default: true } }, + emits: ['update:modelValue'], computed: { present () { - return typeof this.value !== 'undefined' + return typeof this.modelValue !== 'undefined' }, validColor () { - return hex2rgb(this.value || this.fallback) + return hex2rgb(this.modelValue || this.fallback) }, transparentColor () { - return this.value === 'transparent' + return this.modelValue === 'transparent' }, computedColor () { - return this.value && this.value.startsWith('--') + return this.modelValue && this.modelValue.startsWith('--') } } } </script> +<style lang="scss" src="./color_input.scss"></style> <style lang="scss"> .color-control { diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js @@ -1,5 +1,23 @@ import { reduce, filter, findIndex, clone, get } from 'lodash' import Status from '../status/status.vue' +import ThreadTree from '../thread_tree/thread_tree.vue' +import { WSConnectionStatus } from '../../services/api/api.service.js' +import { mapGetters, mapState } from 'vuex' +import QuickFilterSettings from '../quick_filter_settings/quick_filter_settings.vue' +import QuickViewSettings from '../quick_view_settings/quick_view_settings.vue' + +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faAngleDoubleDown, + faAngleDoubleLeft, + faChevronLeft +} from '@fortawesome/free-solid-svg-icons' + +library.add( + faAngleDoubleDown, + faAngleDoubleLeft, + faChevronLeft +) const sortById = (a, b) => { const idA = a.type === 'retweet' ? a.retweeted_status.id : a.id @@ -35,7 +53,10 @@ const conversation = { data () { return { highlight: null, - expanded: false + expanded: false, + threadDisplayStatusObject: {}, // id => 'showing' | 'hidden' + statusContentPropertiesObject: {}, + inlineDivePosition: null } }, props: [ @@ -53,13 +74,54 @@ const conversation = { } }, computed: { - hideStatus () { + maxDepthToShowByDefault () { + // maxDepthInThread = max number of depths that is *visible* + // since our depth starts with 0 and "showing" means "showing children" + // there is a -2 here + const maxDepth = this.$store.getters.mergedConfig.maxDepthInThread - 2 + return maxDepth >= 1 ? maxDepth : 1 + }, + streamingEnabled () { + return this.mergedConfig.useStreamingApi && this.mastoUserSocketStatus === WSConnectionStatus.JOINED + }, + displayStyle () { + return this.$store.getters.mergedConfig.conversationDisplay + }, + isTreeView () { + return !this.isLinearView + }, + treeViewIsSimple () { + return !this.$store.getters.mergedConfig.conversationTreeAdvanced + }, + isLinearView () { + return this.displayStyle === 'linear' + }, + shouldFadeAncestors () { + return this.$store.getters.mergedConfig.conversationTreeFadeAncestors + }, + otherRepliesButtonPosition () { + return this.$store.getters.mergedConfig.conversationOtherRepliesButton + }, + showOtherRepliesButtonBelowStatus () { + return this.otherRepliesButtonPosition === 'below' + }, + showOtherRepliesButtonInsideStatus () { + return this.otherRepliesButtonPosition === 'inside' + }, + suspendable () { + if (this.isTreeView) { + return Object.entries(this.statusContentProperties) + .every(([k, prop]) => !prop.replying && prop.mediaPlaying.length === 0) + } if (this.$refs.statusComponent && this.$refs.statusComponent[0]) { - return this.virtualHidden && this.$refs.statusComponent[0].suspendable + return this.$refs.statusComponent.every(s => s.suspendable) } else { - return this.virtualHidden + return true } }, + hideStatus () { + return this.virtualHidden && this.suspendable + }, status () { return this.$store.state.statuses.allStatusesObject[this.statusId] }, @@ -90,6 +152,121 @@ const conversation = { return sortAndFilterConversation(conversation, this.status) }, + statusMap () { + return this.conversation.reduce((res, s) => { + res[s.id] = s + return res + }, {}) + }, + threadTree () { + const reverseLookupTable = this.conversation.reduce((table, status, index) => { + table[status.id] = index + return table + }, {}) + + const threads = this.conversation.reduce((a, cur) => { + const id = cur.id + a.forest[id] = this.getReplies(id) + .map(s => s.id) + + return a + }, { + forest: {} + }) + + const walk = (forest, topLevel, depth = 0, processed = {}) => topLevel.map(id => { + if (processed[id]) { + return [] + } + + processed[id] = true + return [{ + status: this.conversation[reverseLookupTable[id]], + id, + depth + }, walk(forest, forest[id], depth + 1, processed)].reduce((a, b) => a.concat(b), []) + }).reduce((a, b) => a.concat(b), []) + + const linearized = walk(threads.forest, this.topLevel.map(k => k.id)) + + return linearized + }, + replyIds () { + return this.conversation.map(k => k.id) + .reduce((res, id) => { + res[id] = (this.replies[id] || []).map(k => k.id) + return res + }, {}) + }, + totalReplyCount () { + const sizes = {} + const subTreeSizeFor = (id) => { + if (sizes[id]) { + return sizes[id] + } + sizes[id] = 1 + this.replyIds[id].map(cid => subTreeSizeFor(cid)).reduce((a, b) => a + b, 0) + return sizes[id] + } + this.conversation.map(k => k.id).map(subTreeSizeFor) + return Object.keys(sizes).reduce((res, id) => { + res[id] = sizes[id] - 1 // exclude itself + return res + }, {}) + }, + totalReplyDepth () { + const depths = {} + const subTreeDepthFor = (id) => { + if (depths[id]) { + return depths[id] + } + depths[id] = 1 + this.replyIds[id].map(cid => subTreeDepthFor(cid)).reduce((a, b) => a > b ? a : b, 0) + return depths[id] + } + this.conversation.map(k => k.id).map(subTreeDepthFor) + return Object.keys(depths).reduce((res, id) => { + res[id] = depths[id] - 1 // exclude itself + return res + }, {}) + }, + depths () { + return this.threadTree.reduce((a, k) => { + a[k.id] = k.depth + return a + }, {}) + }, + topLevel () { + const topLevel = this.conversation.reduce((tl, cur) => + tl.filter(k => this.getReplies(cur.id).map(v => v.id).indexOf(k.id) === -1), this.conversation) + return topLevel + }, + otherTopLevelCount () { + return this.topLevel.length - 1 + }, + showingTopLevel () { + if (this.canDive && this.diveRoot) { + return [this.statusMap[this.diveRoot]] + } + return this.topLevel + }, + diveRoot () { + const statusId = this.inlineDivePosition || this.statusId + const isTopLevel = !this.parentOf(statusId) + return isTopLevel ? null : statusId + }, + diveDepth () { + return this.canDive && this.diveRoot ? this.depths[this.diveRoot] : 0 + }, + diveMode () { + return this.canDive && !!this.diveRoot + }, + shouldShowAllConversationButton () { + // The "show all conversation" button tells the user that there exist + // other toplevel statuses, so do not show it if there is only a single root + return this.isTreeView && this.isExpanded && this.diveMode && this.topLevel.length > 1 + }, + shouldShowAncestors () { + return this.isTreeView && this.isExpanded && this.ancestorsOf(this.diveRoot).length + }, replies () { let i = 1 // eslint-disable-next-line camelcase @@ -101,7 +278,7 @@ const conversation = { result[irid] = result[irid] || [] result[irid].push({ name: `#${i}`, - id: id + id }) } i++ @@ -109,15 +286,77 @@ const conversation = { }, {}) }, isExpanded () { - return this.expanded || this.isPage + return !!(this.expanded || this.isPage) }, hiddenStyle () { const height = (this.status && this.status.virtualHeight) || '120px' return this.virtualHidden ? { height } : {} - } + }, + threadDisplayStatus () { + return this.conversation.reduce((a, k) => { + const id = k.id + const depth = this.depths[id] + const status = (() => { + if (this.threadDisplayStatusObject[id]) { + return this.threadDisplayStatusObject[id] + } + if ((depth - this.diveDepth) <= this.maxDepthToShowByDefault) { + return 'showing' + } else { + return 'hidden' + } + })() + + a[id] = status + return a + }, {}) + }, + statusContentProperties () { + return this.conversation.reduce((a, k) => { + const id = k.id + const props = (() => { + const def = { + showingTall: false, + expandingSubject: false, + showingLongSubject: false, + isReplying: false, + mediaPlaying: [] + } + + if (this.statusContentPropertiesObject[id]) { + return { + ...def, + ...this.statusContentPropertiesObject[id] + } + } + return def + })() + + a[id] = props + return a + }, {}) + }, + canDive () { + return this.isTreeView && this.isExpanded + }, + focused () { + return (id) => { + return (this.isExpanded) && id === this.highlight + } + }, + maybeHighlight () { + return this.isExpanded ? this.highlight : null + }, + ...mapGetters(['mergedConfig']), + ...mapState({ + mastoUserSocketStatus: state => state.api.mastoUserSocketStatus + }) }, components: { - Status + Status, + ThreadTree, + QuickFilterSettings, + QuickViewSettings }, watch: { statusId (newVal, oldVal) { @@ -132,6 +371,8 @@ const conversation = { expanded (value) { if (value) { this.fetchConversation() + } else { + this.resetDisplayState() } }, virtualHidden (value) { @@ -161,24 +402,153 @@ const conversation = { getReplies (id) { return this.replies[id] || [] }, - focused (id) { - return (this.isExpanded) && id === this.statusId + getHighlight () { + return this.isExpanded ? this.highlight : null }, setHighlight (id) { if (!id) return this.highlight = id + + if (!this.streamingEnabled) { + this.$store.dispatch('fetchStatus', id) + } + this.$store.dispatch('fetchFavsAndRepeats', id) this.$store.dispatch('fetchEmojiReactionsBy', id) }, - getHighlight () { - return this.isExpanded ? this.highlight : null - }, toggleExpanded () { this.expanded = !this.expanded }, getConversationId (statusId) { const status = this.$store.state.statuses.allStatusesObject[statusId] return get(status, 'retweeted_status.statusnet_conversation_id', get(status, 'statusnet_conversation_id')) + }, + setThreadDisplay (id, nextStatus) { + this.threadDisplayStatusObject = { + ...this.threadDisplayStatusObject, + [id]: nextStatus + } + }, + toggleThreadDisplay (id) { + const curStatus = this.threadDisplayStatus[id] + const nextStatus = curStatus === 'showing' ? 'hidden' : 'showing' + this.setThreadDisplay(id, nextStatus) + }, + setThreadDisplayRecursively (id, nextStatus) { + this.setThreadDisplay(id, nextStatus) + this.getReplies(id).map(k => k.id).map(id => this.setThreadDisplayRecursively(id, nextStatus)) + }, + showThreadRecursively (id) { + this.setThreadDisplayRecursively(id, 'showing') + }, + setStatusContentProperty (id, name, value) { + this.statusContentPropertiesObject = { + ...this.statusContentPropertiesObject, + [id]: { + ...this.statusContentPropertiesObject[id], + [name]: value + } + } + }, + toggleStatusContentProperty (id, name) { + this.setStatusContentProperty(id, name, !this.statusContentProperties[id][name]) + }, + leastVisibleAncestor (id) { + let cur = id + let parent = this.parentOf(cur) + while (cur) { + // if the parent is showing it means cur is visible + if (this.threadDisplayStatus[parent] === 'showing') { + return cur + } + parent = this.parentOf(parent) + cur = this.parentOf(cur) + } + // nothing found, fall back to toplevel + return this.topLevel[0] ? this.topLevel[0].id : undefined + }, + diveIntoStatus (id, preventScroll) { + this.tryScrollTo(id) + }, + diveToTopLevel () { + this.tryScrollTo(this.topLevelAncestorOrSelfId(this.diveRoot) || this.topLevel[0].id) + }, + // only used when we are not on a page + undive () { + this.inlineDivePosition = null + this.setHighlight(this.statusId) + }, + tryScrollTo (id) { + if (!id) { + return + } + if (this.isPage) { + // set statusId + this.$router.push({ name: 'conversation', params: { id } }) + } else { + this.inlineDivePosition = id + } + // Because the conversation can be unmounted when out of sight + // and mounted again when it comes into sight, + // the `mounted` or `created` function in `status` should not + // contain scrolling calls, as we do not want the page to jump + // when we scroll with an expanded conversation. + // + // Now the method is to rely solely on the `highlight` watcher + // in `status` components. + // In linear views, all statuses are rendered at all times, but + // in tree views, it is possible that a change in active status + // removes and adds status components (e.g. an originally child + // status becomes an ancestor status, and thus they will be + // different). + // Here, let the components be rendered first, in order to trigger + // the `highlight` watcher. + this.$nextTick(() => { + this.setHighlight(id) + }) + }, + goToCurrent () { + this.tryScrollTo(this.diveRoot || this.topLevel[0].id) + }, + statusById (id) { + return this.statusMap[id] + }, + parentOf (id) { + const status = this.statusById(id) + if (!status) { + return undefined + } + const { in_reply_to_status_id: parentId } = status + if (!this.statusMap[parentId]) { + return undefined + } + return parentId + }, + parentOrSelf (id) { + return this.parentOf(id) || id + }, + // Ancestors of some status, from top to bottom + ancestorsOf (id) { + const ancestors = [] + let cur = this.parentOf(id) + while (cur) { + ancestors.unshift(this.statusMap[cur]) + cur = this.parentOf(cur) + } + return ancestors + }, + topLevelAncestorOrSelfId (id) { + let cur = id + let parent = this.parentOf(id) + while (parent) { + cur = this.parentOf(cur) + parent = this.parentOf(parent) + } + return cur + }, + resetDisplayState () { + this.undive() + this.threadDisplayStatusObject = {} } } } diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue @@ -7,7 +7,7 @@ > <div v-if="isExpanded" - class="panel-heading conversation-heading" + class="panel-heading conversation-heading -sticky" > <span class="title"> {{ $t('timeline.conversation') }} </span> <button @@ -17,25 +17,189 @@ > {{ $t('timeline.collapse') }} </button> + <QuickFilterSettings + v-if="!collapsable" + :conversation="true" + class="rightside-button" + /> + <QuickViewSettings + v-if="!collapsable" + :conversation="true" + class="rightside-button" + /> + </div> + <div class="conversation-body panel-body"> + <div + v-if="isTreeView" + class="thread-body" + > + <div + v-if="shouldShowAllConversationButton" + class="conversation-dive-to-top-level-box" + > + <i18n-t + keypath="status.show_all_conversation_with_icon" + tag="button" + class="button-unstyled -link" + scope="global" + @click.prevent="diveToTopLevel" + > + <template #icon> + <FAIcon + icon="angle-double-left" + /> + </template> + <template #text> + <span> + {{ $tc('status.show_all_conversation', otherTopLevelCount, { numStatus: otherTopLevelCount }) }} + </span> + </template> + </i18n-t> + </div> + <div + v-if="shouldShowAncestors" + class="thread-ancestors" + > + <article + v-for="status in ancestorsOf(diveRoot)" + :key="status.id" + class="thread-ancestor" + :class="{'thread-ancestor-has-other-replies': getReplies(status.id).length > 1, '-faded': shouldFadeAncestors}" + > + <status + ref="statusComponent" + :inline-expanded="collapsable && isExpanded" + :statusoid="status" + :expandable="!isExpanded" + :show-pinned="pinnedStatusIdsObject && pinnedStatusIdsObject[status.id]" + :focused="focused(status.id)" + :in-conversation="isExpanded" + :highlight="getHighlight()" + :replies="getReplies(status.id)" + :in-profile="inProfile" + :profile-user-id="profileUserId" + class="conversation-status status-fadein panel-body" + + :simple-tree="treeViewIsSimple" + :toggle-thread-display="toggleThreadDisplay" + :thread-display-status="threadDisplayStatus" + :show-thread-recursively="showThreadRecursively" + :total-reply-count="totalReplyCount" + :total-reply-depth="totalReplyDepth" + :show-other-replies-as-button="showOtherRepliesButtonInsideStatus" + :dive="() => diveIntoStatus(status.id)" + + :controlled-showing-tall="statusContentProperties[status.id].showingTall" + :controlled-expanding-subject="statusContentProperties[status.id].expandingSubject" + :controlled-showing-long-subject="statusContentProperties[status.id].showingLongSubject" + :controlled-replying="statusContentProperties[status.id].replying" + :controlled-media-playing="statusContentProperties[status.id].mediaPlaying" + :controlled-toggle-showing-tall="() => toggleStatusContentProperty(status.id, 'showingTall')" + :controlled-toggle-expanding-subject="() => toggleStatusContentProperty(status.id, 'expandingSubject')" + :controlled-toggle-showing-long-subject="() => toggleStatusContentProperty(status.id, 'showingLongSubject')" + :controlled-toggle-replying="() => toggleStatusContentProperty(status.id, 'replying')" + :controlled-set-media-playing="(newVal) => toggleStatusContentProperty(status.id, 'mediaPlaying', newVal)" + + @goto="setHighlight" + @toggleExpanded="toggleExpanded" + /> + <div + v-if="showOtherRepliesButtonBelowStatus && getReplies(status.id).length > 1" + class="thread-ancestor-dive-box" + > + <div + class="thread-ancestor-dive-box-inner" + > + <i18n-t + tag="button" + scope="global" + keypath="status.ancestor_follow_with_icon" + class="button-unstyled -link thread-tree-show-replies-button" + @click.prevent="diveIntoStatus(status.id)" + > + <template #icon> + <FAIcon + icon="angle-double-right" + /> + </template> + <template #text> + <span> + {{ $tc('status.ancestor_follow', getReplies(status.id).length - 1, { numReplies: getReplies(status.id).length - 1 }) }} + </span> + </template> + </i18n-t> + </div> + </div> + </article> + </div> + <thread-tree + v-for="status in showingTopLevel" + :key="status.id" + ref="statusComponent" + :depth="0" + + :status="status" + :in-profile="inProfile" + :conversation="conversation" + :collapsable="collapsable" + :is-expanded="isExpanded" + :pinned-status-ids-object="pinnedStatusIdsObject" + :profile-user-id="profileUserId" + + :focused="focused" + :get-replies="getReplies" + :highlight="maybeHighlight" + :set-highlight="setHighlight" + :toggle-expanded="toggleExpanded" + + :simple="treeViewIsSimple" + :toggle-thread-display="toggleThreadDisplay" + :thread-display-status="threadDisplayStatus" + :show-thread-recursively="showThreadRecursively" + :total-reply-count="totalReplyCount" + :total-reply-depth="totalReplyDepth" + :status-content-properties="statusContentProperties" + :set-status-content-property="setStatusContentProperty" + :toggle-status-content-property="toggleStatusContentProperty" + :dive="canDive ? diveIntoStatus : undefined" + /> + </div> + <div + v-if="isLinearView" + class="thread-body" + > + <article> + <status + v-for="status in conversation" + :key="status.id" + ref="statusComponent" + :inline-expanded="collapsable && isExpanded" + :statusoid="status" + :expandable="!isExpanded" + :show-pinned="pinnedStatusIdsObject && pinnedStatusIdsObject[status.id]" + :focused="focused(status.id)" + :in-conversation="isExpanded" + :highlight="getHighlight()" + :replies="getReplies(status.id)" + :in-profile="inProfile" + :profile-user-id="profileUserId" + class="conversation-status status-fadein panel-body" + + :toggle-thread-display="toggleThreadDisplay" + :thread-display-status="threadDisplayStatus" + :show-thread-recursively="showThreadRecursively" + :total-reply-count="totalReplyCount" + :total-reply-depth="totalReplyDepth" + :status-content-properties="statusContentProperties" + :set-status-content-property="setStatusContentProperty" + :toggle-status-content-property="toggleStatusContentProperty" + + @goto="setHighlight" + @toggleExpanded="toggleExpanded" + /> + </article> + </div> </div> - <status - v-for="status in conversation" - :key="status.id" - ref="statusComponent" - :inline-expanded="collapsable && isExpanded" - :statusoid="status" - :expandable="!isExpanded" - :show-pinned="pinnedStatusIdsObject && pinnedStatusIdsObject[status.id]" - :focused="focused(status.id)" - :in-conversation="isExpanded" - :highlight="getHighlight()" - :replies="getReplies(status.id)" - :in-profile="inProfile" - :profile-user-id="profileUserId" - class="conversation-status status-fadein panel-body" - @goto="setHighlight" - @toggleExpanded="toggleExpanded" - /> </div> <div v-else @@ -49,19 +213,82 @@ @import '../../_variables.scss'; .Conversation { - .conversation-status { + z-index: 1; + + .conversation-dive-to-top-level-box { + padding: var(--status-margin, $status-margin); border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: var(--border, $fallback--border); border-radius: 0; + /* Make the button stretch along the whole row */ + display: flex; + align-items: stretch; + flex-direction: column; + } + + .thread-ancestors { + margin-left: var(--status-margin, $status-margin); + border-left: 2px solid var(--border, $fallback--border); } - &.-expanded { - .conversation-status:last-child { - border-bottom: none; - border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius; - border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius); + .thread-ancestor.-faded .StatusContent { + --link: var(--faintLink); + --text: var(--faint); + color: var(--text); + } + + .thread-ancestor-dive-box { + padding-left: var(--status-margin, $status-margin); + border-bottom-width: 1px; + border-bottom-style: solid; + border-bottom-color: var(--border, $fallback--border); + border-radius: 0; + /* Make the button stretch along the whole row */ + &, &-inner { + display: flex; + align-items: stretch; + flex-direction: column; } } + .thread-ancestor-dive-box-inner { + padding: var(--status-margin, $status-margin); + } + + .conversation-status { + border-bottom-width: 1px; + border-bottom-style: solid; + border-bottom-color: var(--border, $fallback--border); + border-radius: 0; + } + + .thread-ancestor-has-other-replies .conversation-status, + .thread-ancestor:last-child .conversation-status, + .thread-ancestor:last-child .thread-ancestor-dive-box, + &:last-child .conversation-status, + &.-expanded .thread-tree .conversation-status { + border-bottom: none; + } + + .thread-ancestors + .thread-tree > .conversation-status { + border-top-width: 1px; + border-top-style: solid; + border-top-color: var(--border, $fallback--border); + } + + /* expanded conversation in timeline */ + &.status-fadein.-expanded .thread-body { + border-left-width: 4px; + border-left-style: solid; + border-left-color: $fallback--cRed; + border-left-color: var(--cRed, $fallback--cRed); + border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius; + border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius); + border-bottom: 1px solid var(--border, $fallback--border); + } + + &.-expanded.status-fadein { + margin: calc(var(--status-margin, $status-margin) / 2); + } } </style> diff --git a/src/components/desktop_nav/desktop_nav.js b/src/components/desktop_nav/desktop_nav.js @@ -46,23 +46,27 @@ export default { enableMask () { return this.supportsMask && this.$store.state.instance.logoMask }, logoStyle () { return { - 'visibility': this.enableMask ? 'hidden' : 'visible' + visibility: this.enableMask ? 'hidden' : 'visible' } }, logoMaskStyle () { - return this.enableMask ? { - 'mask-image': `url(${this.$store.state.instance.logo})` - } : { - 'background-color': this.enableMask ? '' : 'transparent' - } + return this.enableMask + ? { + 'mask-image': `url(${this.$store.state.instance.logo})` + } + : { + 'background-color': this.enableMask ? '' : 'transparent' + } }, logoBgStyle () { return Object.assign({ - 'margin': `${this.$store.state.instance.logoMargin} 0`, + margin: `${this.$store.state.instance.logoMargin} 0`, opacity: this.searchBarHidden ? 1 : 0 - }, this.enableMask ? {} : { - 'background-color': this.enableMask ? '' : 'transparent' - }) + }, this.enableMask + ? {} + : { + 'background-color': this.enableMask ? '' : 'transparent' + }) }, logo () { return this.$store.state.instance.logo }, sitename () { return this.$store.state.instance.name }, diff --git a/src/components/desktop_nav/desktop_nav.scss b/src/components/desktop_nav/desktop_nav.scss @@ -1,9 +1,12 @@ @import '../../_variables.scss'; .DesktopNav { - height: 50px; width: 100%; - position: fixed; + z-index: var(--ZI_navbar); + + input { + color: var(--inputTopbarText, var(--inputText)); + } a { color: var(--topBarLink, $fallback--link); @@ -11,7 +14,7 @@ .inner-nav { display: grid; - grid-template-rows: 50px; + grid-template-rows: var(--navbar-height); grid-template-columns: 2fr auto 2fr; grid-template-areas: "sitename logo actions"; box-sizing: border-box; @@ -20,7 +23,27 @@ max-width: 980px; } - &.-logoLeft { + &.-column-stretch .inner-nav { + --miniColumn: 25rem; + --maxiColumn: 45rem; + --columnGap: 1em; + max-width: calc( + var(--sidebarColumnWidth, var(--miniColumn)) + + var(--contentColumnWidth, var(--maxiColumn)) + + var(--columnGap) + ); + } + + &.-column-stretch.-wide .inner-nav { + max-width: calc( + var(--sidebarColumnWidth, var(--miniColumn)) + + var(--contentColumnWidth, var(--maxiColumn)) + + var(--notifsColumnWidth, var(--miniColumn)) + + var(--columnGap) + ); + } + + &.-logoLeft .inner-nav { grid-template-columns: auto 2fr 2fr; grid-template-areas: "logo sitename actions"; } @@ -77,7 +100,7 @@ img { display: inline-block; - height: 50px; + height: var(--navbar-height); } } @@ -103,8 +126,8 @@ .item { flex: 1; - line-height: 50px; - height: 50px; + line-height: var(--navbar-height); + height: var(--navbar-height); overflow: hidden; display: flex; flex-wrap: wrap; @@ -114,4 +137,8 @@ text-align: right; } } + + .spacer { + width: 1em; + } } diff --git a/src/components/desktop_nav/desktop_nav.vue b/src/components/desktop_nav/desktop_nav.vue @@ -34,11 +34,11 @@ <search-bar v-if="currentUser || !privateMode" @toggled="onSearchBarToggled" - @click.stop.native + @click.stop /> <button class="button-unstyled nav-icon" - @click.stop="openSettingsModal" + @click="openSettingsModal" > <FAIcon fixed-width @@ -52,6 +52,7 @@ href="/pleroma/admin/#/login-pleroma" class="nav-icon" target="_blank" + @click.stop > <FAIcon fixed-width @@ -60,6 +61,7 @@ :title="$t('nav.administration')" /> </a> + <span class="spacer" /> <button v-if="currentUser" class="button-unstyled nav-icon" diff --git a/src/components/dialog_modal/dialog_modal.vue b/src/components/dialog_modal/dialog_modal.vue @@ -58,16 +58,7 @@ background-color: var(--bg, $fallback--bg); .dialog-modal-heading { - padding: .5em .5em; - margin-right: auto; - margin-bottom: 0; - white-space: nowrap; - color: var(--panelText); - background-color: $fallback--fg; - background-color: var(--panel, $fallback--fg); - .title { - margin-bottom: 0; text-align: center; } } diff --git a/src/components/domain_mute_card/domain_mute_card.vue b/src/components/domain_mute_card/domain_mute_card.vue @@ -9,7 +9,7 @@ class="btn button-default" > {{ $t('domain_mute_card.unmute') }} - <template v-slot:progress> + <template #progress> {{ $t('domain_mute_card.unmute_progress') }} </template> </ProgressButton> @@ -19,7 +19,7 @@ class="btn button-default" > {{ $t('domain_mute_card.mute') }} - <template v-slot:progress> + <template #progress> {{ $t('domain_mute_card.mute_progress') }} </template> </ProgressButton> diff --git a/src/components/edit_status_modal/edit_status_modal.js b/src/components/edit_status_modal/edit_status_modal.js @@ -0,0 +1,75 @@ +import PostStatusForm from '../post_status_form/post_status_form.vue' +import Modal from '../modal/modal.vue' +import statusPosterService from '../../services/status_poster/status_poster.service.js' +import get from 'lodash/get' + +const EditStatusModal = { + components: { + PostStatusForm, + Modal + }, + data () { + return { + resettingForm: false + } + }, + computed: { + isLoggedIn () { + return !!this.$store.state.users.currentUser + }, + modalActivated () { + return this.$store.state.editStatus.modalActivated + }, + isFormVisible () { + return this.isLoggedIn && !this.resettingForm && this.modalActivated + }, + params () { + return this.$store.state.editStatus.params || {} + } + }, + watch: { + params (newVal, oldVal) { + if (get(newVal, 'statusId') !== get(oldVal, 'statusId')) { + this.resettingForm = true + this.$nextTick(() => { + this.resettingForm = false + }) + } + }, + isFormVisible (val) { + if (val) { + this.$nextTick(() => this.$el && this.$el.querySelector('textarea').focus()) + } + } + }, + methods: { + doEditStatus ({ status, spoilerText, sensitive, media, contentType, poll }) { + const params = { + store: this.$store, + statusId: this.$store.state.editStatus.params.statusId, + status, + spoilerText, + sensitive, + poll, + media, + contentType + } + + return statusPosterService.editStatus(params) + .then((data) => { + return data + }) + .catch((err) => { + console.error('Error editing status', err) + return { + error: err.message + } + }) + }, + closeModal () { + this.$store.dispatch('closeEditStatusModal') + } + } +} + +export default EditStatusModal diff --git a/src/components/edit_status_modal/edit_status_modal.vue b/src/components/edit_status_modal/edit_status_modal.vue @@ -0,0 +1,48 @@ +<template> + <Modal + v-if="isFormVisible" + class="edit-form-modal-view" + @backdropClicked="closeModal" + > + <div class="edit-form-modal-panel panel"> + <div class="panel-heading"> + {{ $t('post_status.edit_status') }} + </div> + <PostStatusForm + class="panel-body" + v-bind="params" + :post-handler="doEditStatus" + :disable-polls="true" + :disable-visibility-selector="true" + @posted="closeModal" + /> + </div> + </Modal> +</template> + +<script src="./edit_status_modal.js"></script> + +<style lang="scss"> +.modal-view.edit-form-modal-view { + align-items: flex-start; +} +.edit-form-modal-panel { + flex-shrink: 0; + margin-top: 25%; + margin-bottom: 2em; + width: 100%; + max-width: 700px; + + @media (orientation: landscape) { + margin-top: 8%; + } + + .form-bottom-left { + max-width: 6.5em; + + .emoji-icon { + justify-content: right; + } + } +} +</style> diff --git a/src/components/emoji_input/emoji_input.js b/src/components/emoji_input/emoji_input.js @@ -1,8 +1,10 @@ import Completion from '../../services/completion/completion.js' import EmojiPicker from '../emoji_picker/emoji_picker.vue' +import Popover from 'src/components/popover/popover.vue' +import UnicodeDomainIndicator from '../unicode_domain_indicator/unicode_domain_indicator.vue' import { take } from 'lodash' import { findOffset } from '../../services/offset_finder/offset_finder.service.js' - +import { ensureFinalFallback } from '../../i18n/languages.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faSmileBeam @@ -31,6 +33,7 @@ library.add( */ const EmojiInput = { + emits: ['update:modelValue', 'shown'], props: { suggest: { /** @@ -57,8 +60,7 @@ const EmojiInput = { required: true, type: Function }, - // TODO VUE3: change to modelValue, change 'input' event to 'input' - value: { + modelValue: { /** * Used for v-model */ @@ -108,46 +110,122 @@ const EmojiInput = { data () { return { input: undefined, + caretEl: undefined, highlighted: 0, caret: 0, focused: false, blurTimeout: null, - showPicker: false, temporarilyHideSuggestions: false, - keepOpen: false, disableClickOutside: false, - suggestions: [] + suggestions: [], + overlayStyle: {}, + pickerShown: false } }, components: { - EmojiPicker + Popover, + EmojiPicker, + UnicodeDomainIndicator }, computed: { padEmoji () { return this.$store.getters.mergedConfig.padEmoji }, + preText () { + return this.modelValue.slice(0, this.caret) + }, + postText () { + return this.modelValue.slice(this.caret) + }, showSuggestions () { return this.focused && this.suggestions && this.suggestions.length > 0 && - !this.showPicker && + !this.pickerShown && !this.temporarilyHideSuggestions }, textAtCaret () { - return (this.wordAtCaret || {}).word || '' + return this.wordAtCaret?.word }, wordAtCaret () { - if (this.value && this.caret) { - const word = Completion.wordAtPosition(this.value, this.caret - 1) || {} + if (this.modelValue && this.caret) { + const word = Completion.wordAtPosition(this.modelValue, this.caret - 1) || {} return word } + }, + languages () { + return ensureFinalFallback(this.$store.getters.mergedConfig.interfaceLanguage) + }, + maybeLocalizedEmojiNamesAndKeywords () { + return emoji => { + const names = [emoji.displayText] + const keywords = [] + + if (emoji.displayTextI18n) { + names.push(this.$t(emoji.displayTextI18n.key, emoji.displayTextI18n.args)) + } + + if (emoji.annotations) { + this.languages.forEach(lang => { + names.push(emoji.annotations[lang]?.name) + + keywords.push(...(emoji.annotations[lang]?.keywords || [])) + }) + } + + return { + names: names.filter(k => k), + keywords: keywords.filter(k => k) + } + } + }, + maybeLocalizedEmojiName () { + return emoji => { + if (!emoji.annotations) { + return emoji.displayText + } + + if (emoji.displayTextI18n) { + return this.$t(emoji.displayTextI18n.key, emoji.displayTextI18n.args) + } + + for (const lang of this.languages) { + if (emoji.annotations[lang]?.name) { + return emoji.annotations[lang].name + } + } + + return emoji.displayText + } + }, + onInputScroll () { + this.$refs.hiddenOverlay.scrollTo({ + top: this.input.scrollTop, + left: this.input.scrollLeft + }) } }, mounted () { - const { root } = this.$refs + const { root, hiddenOverlayCaret, suggestorPopover } = this.$refs const input = root.querySelector('.emoji-input > input') || root.querySelector('.emoji-input > textarea') if (!input) return this.input = input + this.caretEl = hiddenOverlayCaret + if (suggestorPopover.setAnchorEl) { + suggestorPopover.setAnchorEl(this.caretEl) // unit test compat + this.$refs.picker.setAnchorEl(this.caretEl) + } else { + console.warn('setAnchorEl not found, are we in a unit test?') + } + const style = getComputedStyle(this.input) + this.overlayStyle.padding = style.padding + this.overlayStyle.border = style.border + this.overlayStyle.margin = style.margin + this.overlayStyle.lineHeight = style.lineHeight + this.overlayStyle.fontFamily = style.fontFamily + this.overlayStyle.fontSize = style.fontSize + this.overlayStyle.wordWrap = style.wordWrap + this.overlayStyle.whiteSpace = style.whiteSpace this.resize() input.addEventListener('blur', this.onBlur) input.addEventListener('focus', this.onFocus) @@ -157,6 +235,7 @@ const EmojiInput = { input.addEventListener('click', this.onClickInput) input.addEventListener('transitionend', this.onTransition) input.addEventListener('input', this.onInput) + input.addEventListener('scroll', this.onInputScroll) }, unmounted () { const { input } = this @@ -169,43 +248,43 @@ const EmojiInput = { input.removeEventListener('click', this.onClickInput) input.removeEventListener('transitionend', this.onTransition) input.removeEventListener('input', this.onInput) + input.removeEventListener('scroll', this.onInputScroll) } }, watch: { - showSuggestions: function (newValue) { + showSuggestions: function (newValue, oldValue) { this.$emit('shown', newValue) + if (newValue) { + this.$refs.suggestorPopover.showPopover() + } else { + this.$refs.suggestorPopover.hidePopover() + } }, textAtCaret: async function (newWord) { + if (newWord === undefined) return const firstchar = newWord.charAt(0) - this.suggestions = [] - if (newWord === firstchar) return - const matchedSuggestions = await this.suggest(newWord) + if (newWord === firstchar) { + this.suggestions = [] + return + } + const matchedSuggestions = await this.suggest(newWord, this.maybeLocalizedEmojiNamesAndKeywords) // Async: cancel if textAtCaret has changed during wait - if (this.textAtCaret !== newWord) return - if (matchedSuggestions.length <= 0) return + if (this.textAtCaret !== newWord || matchedSuggestions.length <= 0) { + this.suggestions = [] + return + } this.suggestions = take(matchedSuggestions, 5) .map(({ imageUrl, ...rest }) => ({ ...rest, img: imageUrl || '' })) - }, - suggestions (newValue) { - this.$nextTick(this.resize) } }, methods: { - focusPickerInput () { - const pickerEl = this.$refs.picker.$el - if (!pickerEl) return - const pickerInput = pickerEl.querySelector('input') - if (pickerInput) pickerInput.focus() - }, triggerShowPicker () { - this.showPicker = true - this.$refs.picker.startEmojiLoad() this.$nextTick(() => { + this.$refs.picker.showPicker() this.scrollIntoView() - this.focusPickerInput() }) // This temporarily disables "click outside" handler // since external trigger also means click originates @@ -217,21 +296,22 @@ const EmojiInput = { }, togglePicker () { this.input.focus() - this.showPicker = !this.showPicker - if (this.showPicker) { + if (!this.pickerShown) { this.scrollIntoView() + this.$refs.picker.showPicker() this.$refs.picker.startEmojiLoad() - this.$nextTick(this.focusPickerInput) + } else { + this.$refs.picker.hidePicker() } }, replace (replacement) { - const newValue = Completion.replaceWord(this.value, this.wordAtCaret, replacement) - this.$emit('input', newValue) + const newValue = Completion.replaceWord(this.modelValue, this.wordAtCaret, replacement) + this.$emit('update:modelValue', newValue) this.caret = 0 }, insert ({ insertion, keepOpen, surroundingSpace = true }) { - const before = this.value.substring(0, this.caret) || '' - const after = this.value.substring(this.caret) || '' + const before = this.modelValue.substring(0, this.caret) || '' + const after = this.modelValue.substring(this.caret) || '' /* Using a bit more smart approach to padding emojis with spaces: * - put a space before cursor if there isn't one already, unless we @@ -258,8 +338,7 @@ const EmojiInput = { spaceAfter, after ].join('') - this.keepOpen = keepOpen - this.$emit('input', newValue) + this.$emit('update:modelValue', newValue) const position = this.caret + (insertion + spaceAfter + spaceBefore).length if (!keepOpen) { this.input.focus() @@ -278,8 +357,8 @@ const EmojiInput = { if (len > 0 || suggestion) { const chosenSuggestion = suggestion || this.suggestions[this.highlighted] const replacement = chosenSuggestion.replacement - const newValue = Completion.replaceWord(this.value, this.wordAtCaret, replacement) - this.$emit('input', newValue) + const newValue = Completion.replaceWord(this.modelValue, this.wordAtCaret, replacement) + this.$emit('update:modelValue', newValue) this.highlighted = 0 const position = this.wordAtCaret.start + replacement.length @@ -318,7 +397,7 @@ const EmojiInput = { } }, scrollIntoView () { - const rootRef = this.$refs['picker'].$el + const rootRef = this.$refs.picker.$el /* Scroller is either `window` (replies in TL), sidebar (main post form, * replies in notifs) or mobile post form. Note that getting and setting * scroll is different for `Window` and `Element`s @@ -358,8 +437,11 @@ const EmojiInput = { } }) }, - onTransition (e) { - this.resize() + onPickerShown () { + this.pickerShown = true + }, + onPickerClosed () { + this.pickerShown = false }, onBlur (e) { // Clicking on any suggestion removes focus from autocomplete, @@ -367,7 +449,6 @@ const EmojiInput = { this.blurTimeout = setTimeout(() => { this.focused = false this.setCaret(e) - this.resize() }, 200) }, onClick (e, suggestion) { @@ -379,18 +460,13 @@ const EmojiInput = { this.blurTimeout = null } - if (!this.keepOpen) { - this.showPicker = false - } this.focused = true this.setCaret(e) - this.resize() this.temporarilyHideSuggestions = false }, onKeyUp (e) { const { key } = e this.setCaret(e) - this.resize() // Setting hider in keyUp to prevent suggestions from blinking // when moving away from suggested spot @@ -402,7 +478,6 @@ const EmojiInput = { }, onPaste (e) { this.setCaret(e) - this.resize() }, onKeyDown (e) { const { ctrlKey, shiftKey, key } = e @@ -447,58 +522,24 @@ const EmojiInput = { this.input.focus() } } - - this.showPicker = false - this.resize() }, onInput (e) { - this.showPicker = false this.setCaret(e) - this.resize() - this.$emit('input', e.target.value) - }, - onClickInput (e) { - this.showPicker = false - }, - onClickOutside (e) { - if (this.disableClickOutside) return - this.showPicker = false + this.$emit('update:modelValue', e.target.value) }, onStickerUploaded (e) { - this.showPicker = false this.$emit('sticker-uploaded', e) }, onStickerUploadFailed (e) { - this.showPicker = false this.$emit('sticker-upload-Failed', e) }, setCaret ({ target: { selectionStart } }) { this.caret = selectionStart + this.$nextTick(() => { + this.$refs.suggestorPopover.updateStyles() + }) }, resize () { - const panel = this.$refs.panel - if (!panel) return - const picker = this.$refs.picker.$el - const panelBody = this.$refs['panel-body'] - const { offsetHeight, offsetTop } = this.input - const offsetBottom = offsetTop + offsetHeight - - this.setPlacement(panelBody, panel, offsetBottom) - this.setPlacement(picker, picker, offsetBottom) - }, - setPlacement (container, target, offsetBottom) { - if (!container || !target) return - - target.style.top = offsetBottom + 'px' - target.style.bottom = 'auto' - - if (this.placement === 'top' || (this.placement === 'auto' && this.overflowsBottom(container))) { - target.style.top = 'auto' - target.style.bottom = this.input.offsetHeight + 'px' - } - }, - overflowsBottom (el) { - return el.getBoundingClientRect().bottom > window.innerHeight } } } diff --git a/src/components/emoji_input/emoji_input.vue b/src/components/emoji_input/emoji_input.vue @@ -1,11 +1,23 @@ <template> <div ref="root" - v-click-outside="onClickOutside" class="emoji-input" :class="{ 'with-picker': !hideEmojiButton }" > <slot /> + <!-- TODO: make the 'x' disappear if at the end maybe? --> + <div + ref="hiddenOverlay" + class="hidden-overlay" + :style="overlayStyle" + > + <span>{{ preText }}</span> + <span + ref="hiddenOverlayCaret" + class="caret" + >x</span> + <span>{{ postText }}</span> + </div> <template v-if="enableEmojiPicker"> <button v-if="!hideEmojiButton" @@ -18,44 +30,61 @@ <EmojiPicker v-if="enableEmojiPicker" ref="picker" - :class="{ hide: !showPicker }" :enable-sticker-picker="enableStickerPicker" class="emoji-picker-panel" @emoji="insert" @sticker-uploaded="onStickerUploaded" @sticker-upload-failed="onStickerUploadFailed" + @show="onPickerShown" + @close="onPickerClosed" /> </template> - <div - ref="panel" + <Popover + ref="suggestorPopover" class="autocomplete-panel" - :class="{ hide: !showSuggestions }" + placement="bottom" > - <div - ref="panel-body" - class="autocomplete-panel-body" - > + <template #content> <div - v-for="(suggestion, index) in suggestions" - :key="index" - class="autocomplete-item" - :class="{ highlighted: index === highlighted }" - @click.stop.prevent="onClick($event, suggestion)" + ref="panel-body" + class="autocomplete-panel-body" > - <span class="image"> - <img - v-if="suggestion.img" - :src="suggestion.img" - > - <span v-else>{{ suggestion.replacement }}</span> - </span> - <div class="label"> - <span class="displayText">{{ suggestion.displayText }}</span> - <span class="detailText">{{ suggestion.detailText }}</span> + <div + v-for="(suggestion, index) in suggestions" + :key="index" + class="autocomplete-item" + :class="{ highlighted: index === highlighted }" + @click.stop.prevent="onClick($event, suggestion)" + > + <span class="image"> + <img + v-if="suggestion.img" + :src="suggestion.img" + > + <span v-else>{{ suggestion.replacement }}</span> + </span> + <div class="label"> + <span + v-if="suggestion.user" + class="displayText" + > + {{ suggestion.displayText }}<UnicodeDomainIndicator + :user="suggestion.user" + :at="false" + /> + </span> + <span + v-if="!suggestion.user" + class="displayText" + > + {{ maybeLocalizedEmojiName(suggestion) }} + </span> + <span class="detailText">{{ suggestion.detailText }}</span> + </div> </div> </div> - </div> - </div> + </template> + </Popover> </div> </template> @@ -78,7 +107,7 @@ top: 0; right: 0; margin: .2em .25em; - font-size: 16px; + font-size: 1.3em; cursor: pointer; line-height: 24px; @@ -87,6 +116,7 @@ color: var(--text, $fallback--text); } } + .emoji-picker-panel { position: absolute; z-index: 20; @@ -97,89 +127,83 @@ } } - .autocomplete { - &-panel { - position: absolute; - z-index: 20; - margin-top: 2px; - - &.hide { - display: none - } + input, textarea { + flex: 1 0 auto; + } - &-body { - margin: 0 0.5em 0 0.5em; - border-radius: $fallback--tooltipRadius; - border-radius: var(--tooltipRadius, $fallback--tooltipRadius); - box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5); - box-shadow: var(--popupShadow); - min-width: 75%; - background-color: $fallback--bg; - background-color: var(--popover, $fallback--bg); - color: $fallback--link; - color: var(--popoverText, $fallback--link); - --faint: var(--popoverFaintText, $fallback--faint); - --faintLink: var(--popoverFaintLink, $fallback--faint); - --lightText: var(--popoverLightText, $fallback--lightText); - --postLink: var(--popoverPostLink, $fallback--link); - --postFaintLink: var(--popoverPostFaintLink, $fallback--link); - --icon: var(--popoverIcon, $fallback--icon); - } + .hidden-overlay { + opacity: 0; + pointer-events: none; + position: absolute; + top: 0; + bottom: 0; + right: 0; + left: 0; + overflow: hidden; + /* DEBUG STUFF */ + color: red; + /* set opacity to non-zero to see the overlay */ + + .caret { + width: 0; + margin-right: calc(-1ch - 1px); + border: 1px solid red; } + } +} +.autocomplete { + &-panel { + position: absolute; + } - &-item { - display: flex; - cursor: pointer; - padding: 0.2em 0.4em; - border-bottom: 1px solid rgba(0, 0, 0, 0.4); + &-item { + display: flex; + cursor: pointer; + padding: 0.2em 0.4em; + border-bottom: 1px solid rgba(0, 0, 0, 0.4); + height: 32px; + + .image { + width: 32px; height: 32px; + line-height: 32px; + text-align: center; + font-size: 32px; - .image { + margin-right: 4px; + + img { width: 32px; height: 32px; - line-height: 32px; - text-align: center; - font-size: 32px; - - margin-right: 4px; - - img { - width: 32px; - height: 32px; - object-fit: contain; - } + object-fit: contain; } + } - .label { - display: flex; - flex-direction: column; - justify-content: center; - margin: 0 0.1em 0 0.2em; - - .displayText { - line-height: 1.5; - } + .label { + display: flex; + flex-direction: column; + justify-content: center; + margin: 0 0.1em 0 0.2em; - .detailText { - font-size: 9px; - line-height: 9px; - } + .displayText { + line-height: 1.5; } - &.highlighted { - background-color: $fallback--fg; - background-color: var(--selectedMenuPopover, $fallback--fg); - color: var(--selectedMenuPopoverText, $fallback--text); - --faint: var(--selectedMenuPopoverFaintText, $fallback--faint); - --faintLink: var(--selectedMenuPopoverFaintLink, $fallback--faint); - --lightText: var(--selectedMenuPopoverLightText, $fallback--lightText); - --icon: var(--selectedMenuPopoverIcon, $fallback--icon); + .detailText { + font-size: 9px; + line-height: 9px; } } - } - input, textarea { - flex: 1 0 auto; + &.highlighted { + background-color: $fallback--fg; + background-color: var(--selectedMenuPopover, $fallback--fg); + color: var(--selectedMenuPopoverText, $fallback--text); + --faint: var(--selectedMenuPopoverFaintText, $fallback--faint); + --faintLink: var(--selectedMenuPopoverFaintLink, $fallback--faint); + --lightText: var(--selectedMenuPopoverLightText, $fallback--lightText); + --icon: var(--selectedMenuPopoverIcon, $fallback--icon); + } } } </style> diff --git a/src/components/emoji_input/suggestor.js b/src/components/emoji_input/suggestor.js @@ -2,7 +2,7 @@ * suggest - generates a suggestor function to be used by emoji-input * data: object providing source information for specific types of suggestions: * data.emoji - optional, an array of all emoji available i.e. - * (state.instance.emoji + state.instance.customEmoji) + * (getters.standardEmojiList + state.instance.customEmoji) * data.users - optional, an array of all known users * updateUsersList - optional, a function to search and append to users * @@ -13,10 +13,10 @@ export default data => { const emojiCurry = suggestEmoji(data.emoji) const usersCurry = data.store && suggestUsers(data.store) - return input => { + return (input, nameKeywordLocalizer) => { const firstChar = input[0] if (firstChar === ':' && data.emoji) { - return emojiCurry(input) + return emojiCurry(input, nameKeywordLocalizer) } if (firstChar === '@' && usersCurry) { return usersCurry(input) @@ -25,34 +25,34 @@ export default data => { } } -export const suggestEmoji = emojis => input => { +export const suggestEmoji = emojis => (input, nameKeywordLocalizer) => { const noPrefix = input.toLowerCase().substr(1) return emojis - .filter(({ displayText }) => displayText.toLowerCase().match(noPrefix)) - .sort((a, b) => { - let aScore = 0 - let bScore = 0 + .map(emoji => ({ ...emoji, ...nameKeywordLocalizer(emoji) })) + .filter((emoji) => (emoji.names.concat(emoji.keywords)).filter(kw => kw.toLowerCase().match(noPrefix)).length) + .map(k => { + let score = 0 // An exact match always wins - aScore += a.displayText.toLowerCase() === noPrefix ? 200 : 0 - bScore += b.displayText.toLowerCase() === noPrefix ? 200 : 0 + score += Math.max(...k.names.map(name => name.toLowerCase() === noPrefix ? 200 : 0), 0) // Prioritize custom emoji a lot - aScore += a.imageUrl ? 100 : 0 - bScore += b.imageUrl ? 100 : 0 + score += k.imageUrl ? 100 : 0 // Prioritize prefix matches somewhat - aScore += a.displayText.toLowerCase().startsWith(noPrefix) ? 10 : 0 - bScore += b.displayText.toLowerCase().startsWith(noPrefix) ? 10 : 0 + score += Math.max(...k.names.map(kw => kw.toLowerCase().startsWith(noPrefix) ? 10 : 0), 0) // Sort by length - aScore -= a.displayText.length - bScore -= b.displayText.length + score -= k.displayText.length + k.score = score + return k + }) + .sort((a, b) => { // Break ties alphabetically const alphabetically = a.displayText > b.displayText ? 0.5 : -0.5 - return bScore - aScore + alphabetically + return b.score - a.score + alphabetically }) } @@ -116,11 +116,12 @@ export const suggestUsers = ({ dispatch, state }) => { return diff + nameAlphabetically + screenNameAlphabetically /* eslint-disable camelcase */ - }).map(({ screen_name, screen_name_ui, name, profile_image_url_original }) => ({ - displayText: screen_name_ui, - detailText: name, - imageUrl: profile_image_url_original, - replacement: '@' + screen_name + ' ' + }).map((user) => ({ + user, + displayText: user.screen_name_ui, + detailText: user.name, + imageUrl: user.profile_image_url_original, + replacement: '@' + user.screen_name + ' ' })) /* eslint-enable camelcase */ diff --git a/src/components/emoji_picker/emoji_picker.js b/src/components/emoji_picker/emoji_picker.js @@ -1,31 +1,77 @@ +import { defineAsyncComponent } from 'vue' import Checkbox from '../checkbox/checkbox.vue' +import Popover from 'src/components/popover/popover.vue' +import StillImage from '../still-image/still-image.vue' +import { ensureFinalFallback } from '../../i18n/languages.js' +import lozad from 'lozad' import { library } from '@fortawesome/fontawesome-svg-core' import { faBoxOpen, faStickyNote, - faSmileBeam + faSmileBeam, + faSmile, + faUser, + faPaw, + faIceCream, + faBus, + faBasketballBall, + faLightbulb, + faCode, + faFlag } from '@fortawesome/free-solid-svg-icons' +import { debounce, trim } from 'lodash' library.add( faBoxOpen, faStickyNote, - faSmileBeam + faSmileBeam, + faSmile, + faUser, + faPaw, + faIceCream, + faBus, + faBasketballBall, + faLightbulb, + faCode, + faFlag ) -// At widest, approximately 20 emoji are visible in a row, -// loading 3 rows, could be overkill for narrow picker -const LOAD_EMOJI_BY = 60 +const UNICODE_EMOJI_GROUP_ICON = { + 'smileys-and-emotion': 'smile', + 'people-and-body': 'user', + 'animals-and-nature': 'paw', + 'food-and-drink': 'ice-cream', + 'travel-and-places': 'bus', + activities: 'basketball-ball', + objects: 'lightbulb', + symbols: 'code', + flags: 'flag' +} -// When to start loading new batch emoji, in pixels -const LOAD_EMOJI_MARGIN = 64 +const maybeLocalizedKeywords = (emoji, languages, nameLocalizer) => { + const res = [emoji.displayText, nameLocalizer(emoji)] + if (emoji.annotations) { + languages.forEach(lang => { + const keywords = emoji.annotations[lang]?.keywords || [] + const name = emoji.annotations[lang]?.name + res.push(...(keywords.concat([name]).filter(k => k))) + }) + } + return res +} -const filterByKeyword = (list, keyword = '') => { +const filterByKeyword = (list, keyword = '', languages, nameLocalizer) => { if (keyword === '') return list const keywordLowercase = keyword.toLowerCase() - let orderedEmojiList = [] + const orderedEmojiList = [] for (const emoji of list) { - const indexOfKeyword = emoji.displayText.toLowerCase().indexOf(keywordLowercase) + const indices = maybeLocalizedKeywords(emoji, languages, nameLocalizer) + .map(k => k.toLowerCase().indexOf(keywordLowercase)) + .filter(k => k > -1) + + const indexOfKeyword = indices.length ? Math.min(...indices) : -1 + if (indexOfKeyword > -1) { if (!Array.isArray(orderedEmojiList[indexOfKeyword])) { orderedEmojiList[indexOfKeyword] = [] @@ -51,16 +97,43 @@ const EmojiPicker = { showingStickers: false, groupsScrolledClass: 'scrolled-top', keepOpen: false, - customEmojiBufferSlice: LOAD_EMOJI_BY, customEmojiTimeout: null, - customEmojiLoadAllConfirmed: false + // Lazy-load only after the first time `showing` becomes true. + contentLoaded: false, + groupRefs: {}, + emojiRefs: {}, + filteredEmojiGroups: [] } }, components: { - StickerPicker: () => import('../sticker_picker/sticker_picker.vue'), - Checkbox + StickerPicker: defineAsyncComponent(() => import('../sticker_picker/sticker_picker.vue')), + Checkbox, + StillImage, + Popover }, methods: { + showPicker () { + this.$refs.popover.showPopover() + this.onShowing() + }, + hidePicker () { + this.$refs.popover.hidePopover() + }, + setAnchorEl (el) { + this.$refs.popover.setAnchorEl(el) + }, + setGroupRef (name) { + return el => { this.groupRefs[name] = el } + }, + setEmojiRef (name) { + return el => { this.emojiRefs[name] = el } + }, + onPopoverShown () { + this.$emit('show') + }, + onPopoverClosed () { + this.$emit('close') + }, onStickerUploaded (e) { this.$emit('sticker-uploaded', e) }, @@ -69,17 +142,48 @@ const EmojiPicker = { }, onEmoji (emoji) { const value = emoji.imageUrl ? `:${emoji.displayText}:` : emoji.replacement + if (!this.keepOpen) { + this.$refs.popover.hidePopover() + } this.$emit('emoji', { insertion: value, keepOpen: this.keepOpen }) }, onScroll (e) { const target = (e && e.target) || this.$refs['emoji-groups'] this.updateScrolledClass(target) this.scrolledGroup(target) - this.triggerLoadMore(target) + }, + scrolledGroup (target) { + const top = target.scrollTop + 5 + this.$nextTick(() => { + this.allEmojiGroups.forEach(group => { + const ref = this.groupRefs['group-' + group.id] + if (ref && ref.offsetTop <= top) { + this.activeGroup = group.id + } + }) + this.scrollHeader() + }) + }, + scrollHeader () { + // Scroll the active tab's header into view + const headerRef = this.groupRefs['group-header-' + this.activeGroup] + const left = headerRef.offsetLeft + const right = left + headerRef.offsetWidth + const headerCont = this.$refs.header + const currentScroll = headerCont.scrollLeft + const currentScrollRight = currentScroll + headerCont.clientWidth + const setScroll = s => { headerCont.scrollLeft = s } + + const margin = 7 // .emoji-tabs-item: padding + if (left - margin < currentScroll) { + setScroll(left - margin) + } else if (right + margin > currentScrollRight) { + setScroll(right + margin - headerCont.clientWidth) + } }, highlight (key) { - const ref = this.$refs['group-' + key] - const top = ref[0].offsetTop + const ref = this.groupRefs['group-' + key] + const top = ref.offsetTop this.setShowStickers(false) this.activeGroup = key this.$nextTick(() => { @@ -95,73 +199,83 @@ const EmojiPicker = { this.groupsScrolledClass = 'scrolled-middle' } }, - triggerLoadMore (target) { - const ref = this.$refs['group-end-custom'][0] - if (!ref) return - const bottom = ref.offsetTop + ref.offsetHeight - - const scrollerBottom = target.scrollTop + target.clientHeight - const scrollerTop = target.scrollTop - const scrollerMax = target.scrollHeight - - // Loads more emoji when they come into view - const approachingBottom = bottom - scrollerBottom < LOAD_EMOJI_MARGIN - // Always load when at the very top in case there's no scroll space yet - const atTop = scrollerTop < 5 - // Don't load when looking at unicode category or at the very bottom - const bottomAboveViewport = bottom < scrollerTop || scrollerBottom === scrollerMax - if (!bottomAboveViewport && (approachingBottom || atTop)) { - this.loadEmoji() - } + toggleStickers () { + this.showingStickers = !this.showingStickers }, - scrolledGroup (target) { - const top = target.scrollTop + 5 + setShowStickers (value) { + this.showingStickers = value + }, + filterByKeyword (list, keyword) { + return filterByKeyword(list, keyword, this.languages, this.maybeLocalizedEmojiName) + }, + initializeLazyLoad () { + this.destroyLazyLoad() this.$nextTick(() => { - this.emojisView.forEach(group => { - const ref = this.$refs['group-' + group.id] - if (ref[0].offsetTop <= top) { - this.activeGroup = group.id + this.$lozad = lozad('.still-image.emoji-picker-emoji', { + load: el => { + const name = el.getAttribute('data-emoji-name') + const vn = this.emojiRefs[name] + if (!vn) { + return + } + + vn.loadLazy() } }) + this.$lozad.observe() }) }, - loadEmoji () { - const allLoaded = this.customEmojiBuffer.length === this.filteredEmoji.length - - if (allLoaded) { - return - } - - this.customEmojiBufferSlice += LOAD_EMOJI_BY + waitForDomAndInitializeLazyLoad () { + this.$nextTick(() => this.initializeLazyLoad()) }, - startEmojiLoad (forceUpdate = false) { - if (!forceUpdate) { - this.keyword = '' + destroyLazyLoad () { + if (this.$lozad) { + if (this.$lozad.observer) { + this.$lozad.observer.disconnect() + } + if (this.$lozad.mutationObserver) { + this.$lozad.mutationObserver.disconnect() + } } + }, + onShowing () { + const oldContentLoaded = this.contentLoaded this.$nextTick(() => { - this.$refs['emoji-groups'].scrollTop = 0 + this.$refs.search.focus() }) - const bufferSize = this.customEmojiBuffer.length - const bufferPrefilledAll = bufferSize === this.filteredEmoji.length - if (bufferPrefilledAll && !forceUpdate) { - return + this.contentLoaded = true + this.waitForDomAndInitializeLazyLoad() + this.filteredEmojiGroups = this.getFilteredEmojiGroups() + if (!oldContentLoaded) { + this.$nextTick(() => { + if (this.defaultGroup) { + this.highlight(this.defaultGroup) + } + }) } - this.customEmojiBufferSlice = LOAD_EMOJI_BY }, - toggleStickers () { - this.showingStickers = !this.showingStickers - }, - setShowStickers (value) { - this.showingStickers = value + getFilteredEmojiGroups () { + return this.allEmojiGroups + .map(group => ({ + ...group, + emojis: this.filterByKeyword(group.emojis, trim(this.keyword)) + })) + .filter(group => group.emojis.length > 0) } }, watch: { keyword () { - this.customEmojiLoadAllConfirmed = false this.onScroll() - this.startEmojiLoad(true) + this.debouncedHandleKeywordChange() + }, + allCustomGroups () { + this.waitForDomAndInitializeLazyLoad() + this.filteredEmojiGroups = this.getFilteredEmojiGroups() } }, + destroyed () { + this.destroyLazyLoad() + }, computed: { activeGroupView () { return this.showingStickers ? '' : this.activeGroup @@ -172,39 +286,55 @@ const EmojiPicker = { } return 0 }, - filteredEmoji () { - return filterByKeyword( - this.$store.state.instance.customEmoji || [], - this.keyword - ) + allCustomGroups () { + return this.$store.getters.groupedCustomEmojis }, - customEmojiBuffer () { - return this.filteredEmoji.slice(0, this.customEmojiBufferSlice) + defaultGroup () { + return Object.keys(this.allCustomGroups)[0] }, - emojis () { - const standardEmojis = this.$store.state.instance.emoji || [] - const customEmojis = this.customEmojiBuffer - - return [ - { - id: 'custom', - text: this.$t('emoji.custom'), - icon: 'smile-beam', - emojis: customEmojis - }, - { - id: 'standard', - text: this.$t('emoji.unicode'), - icon: 'box-open', - emojis: filterByKeyword(standardEmojis, this.keyword) - } - ] + unicodeEmojiGroups () { + return this.$store.getters.standardEmojiGroupList.map(group => ({ + id: `standard-${group.id}`, + text: this.$t(`emoji.unicode_groups.${group.id}`), + icon: UNICODE_EMOJI_GROUP_ICON[group.id], + emojis: group.emojis + })) }, - emojisView () { - return this.emojis.filter(value => value.emojis.length > 0) + allEmojiGroups () { + return Object.entries(this.allCustomGroups) + .map(([_, v]) => v) + .concat(this.unicodeEmojiGroups) }, stickerPickerEnabled () { return (this.$store.state.instance.stickers || []).length !== 0 + }, + debouncedHandleKeywordChange () { + return debounce(() => { + this.waitForDomAndInitializeLazyLoad() + this.filteredEmojiGroups = this.getFilteredEmojiGroups() + }, 500) + }, + languages () { + return ensureFinalFallback(this.$store.getters.mergedConfig.interfaceLanguage) + }, + maybeLocalizedEmojiName () { + return emoji => { + if (!emoji.annotations) { + return emoji.displayText + } + + if (emoji.displayTextI18n) { + return this.$t(emoji.displayTextI18n.key, emoji.displayTextI18n.args) + } + + for (const lang of this.languages) { + if (emoji.annotations[lang]?.name) { + return emoji.annotations[lang].name + } + } + + return emoji.displayText + } } } } diff --git a/src/components/emoji_picker/emoji_picker.scss b/src/components/emoji_picker/emoji_picker.scss @@ -1,13 +1,15 @@ @import '../../_variables.scss'; +$emoji-picker-header-height: 36px; +$emoji-picker-header-picture-width: 32px; +$emoji-picker-header-picture-height: 32px; +$emoji-picker-emoji-size: 32px; + .emoji-picker { + width: 25em; + max-width: 100vw; display: flex; flex-direction: column; - position: absolute; - right: 0; - left: 0; - margin: 0 !important; - z-index: 1; background-color: $fallback--bg; background-color: var(--popover, $fallback--bg); color: $fallback--link; @@ -18,6 +20,23 @@ --lightText: var(--popoverLightText, $fallback--lightText); --icon: var(--popoverIcon, $fallback--icon); + &-header-image { + display: inline-flex; + justify-content: center; + align-items: center; + width: $emoji-picker-header-picture-width; + max-width: $emoji-picker-header-picture-width; + height: $emoji-picker-header-picture-height; + max-height: $emoji-picker-header-picture-height; + .still-image { + max-width: 100%; + max-height: 100%; + height: 100%; + width: 100%; + object-fit: contain; + } + } + .keep-open, .too-many-emoji { padding: 7px; @@ -36,7 +55,6 @@ .heading { display: flex; - height: 32px; padding: 10px 7px 5px; } @@ -49,6 +67,10 @@ .emoji-tabs { flex-grow: 1; + display: flex; + flex-direction: row; + flex-wrap: nowrap; + overflow-x: auto; } .emoji-groups { @@ -56,6 +78,8 @@ } .additional-tabs { + display: flex; + flex: 1; border-left: 1px solid; border-left-color: $fallback--icon; border-left-color: var(--icon, $fallback--icon); @@ -65,20 +89,26 @@ .additional-tabs, .emoji-tabs { - display: block; - min-width: 0; flex-basis: auto; - flex-shrink: 1; + display: flex; + align-content: center; &-item { padding: 0 7px; cursor: pointer; - font-size: 24px; + font-size: 1.85em; + width: $emoji-picker-header-picture-width; + max-width: $emoji-picker-header-picture-width; + height: $emoji-picker-header-picture-height; + max-height: $emoji-picker-header-picture-height; + display: flex; + align-items: center; &.disabled { opacity: 0.5; pointer-events: none; } + &.active { border-bottom: 4px solid; @@ -151,9 +181,10 @@ justify-content: left; &-title { - font-size: 12px; + font-size: 0.85em; width: 100%; margin: 0; + &.disabled { display: none; } @@ -161,22 +192,26 @@ } &-item { - width: 32px; - height: 32px; + width: $emoji-picker-emoji-size; + height: $emoji-picker-emoji-size; box-sizing: border-box; display: flex; - font-size: 32px; + line-height: $emoji-picker-emoji-size; align-items: center; justify-content: center; margin: 4px; cursor: pointer; - img { + .emoji-picker-emoji.-custom { object-fit: contain; max-width: 100%; max-height: 100%; } + .emoji-picker-emoji.-unicode { + font-size: 24px; + overflow: hidden; + } } } diff --git a/src/components/emoji_picker/emoji_picker.vue b/src/components/emoji_picker/emoji_picker.vue @@ -1,104 +1,136 @@ <template> - <div class="emoji-picker panel panel-default panel-body"> - <div class="heading"> - <span class="emoji-tabs"> + <Popover + ref="popover" + trigger="click" + popover-class="emoji-picker popover-default" + @show="onPopoverShown" + @close="onPopoverClosed" + > + <template #content> + <div class="heading"> <span - v-for="group in emojis" - :key="group.id" - class="emoji-tabs-item" - :class="{ - active: activeGroupView === group.id, - disabled: group.emojis.length === 0 - }" - :title="group.text" - @click.prevent="highlight(group.id)" + ref="header" + class="emoji-tabs" > - <FAIcon - :icon="group.icon" - fixed-width - /> + <span + v-for="group in filteredEmojiGroups" + :ref="setGroupRef('group-header-' + group.id)" + :key="group.id" + class="emoji-tabs-item" + :class="{ + active: activeGroupView === group.id + }" + :title="group.text" + @click.prevent="highlight(group.id)" + > + <span + v-if="group.image" + class="emoji-picker-header-image" + > + <still-image + :alt="group.text" + :src="group.image" + /> + </span> + <FAIcon + v-else + :icon="group.icon" + fixed-width + /> + </span> </span> - </span> - <span - v-if="stickerPickerEnabled" - class="additional-tabs" - > <span - class="stickers-tab-icon additional-tabs-item" - :class="{active: showingStickers}" - :title="$t('emoji.stickers')" - @click.prevent="toggleStickers" + v-if="stickerPickerEnabled" + class="additional-tabs" > - <FAIcon - icon="sticky-note" - fixed-width - /> + <span + class="stickers-tab-icon additional-tabs-item" + :class="{active: showingStickers}" + :title="$t('emoji.stickers')" + @click.prevent="toggleStickers" + > + <FAIcon + icon="sticky-note" + fixed-width + /> + </span> </span> - </span> - </div> - <div class="content"> + </div> <div - class="emoji-content" - :class="{hidden: showingStickers}" + v-if="contentLoaded" + class="content" > - <div class="emoji-search"> - <input - v-model="keyword" - type="text" - class="form-control" - :placeholder="$t('emoji.search_emoji')" - > - </div> <div - ref="emoji-groups" - class="emoji-groups" - :class="groupsScrolledClass" - @scroll="onScroll" + class="emoji-content" + :class="{hidden: showingStickers}" > + <div class="emoji-search"> + <input + ref="search" + v-model="keyword" + type="text" + class="form-control" + :placeholder="$t('emoji.search_emoji')" + @input="$event.target.composing = false" + > + </div> <div - v-for="group in emojisView" - :key="group.id" - class="emoji-group" + ref="emoji-groups" + class="emoji-groups" + :class="groupsScrolledClass" + @scroll="onScroll" > - <h6 - :ref="'group-' + group.id" - class="emoji-group-title" - > - {{ group.text }} - </h6> - <span - v-for="emoji in group.emojis" - :key="group.id + emoji.displayText" - :title="emoji.displayText" - class="emoji-item" - @click.stop.prevent="onEmoji(emoji)" + <div + v-for="group in filteredEmojiGroups" + :key="group.id" + class="emoji-group" > - <span v-if="!emoji.imageUrl">{{ emoji.replacement }}</span> - <img - v-else - :src="emoji.imageUrl" + <h6 + :ref="setGroupRef('group-' + group.id)" + class="emoji-group-title" > - </span> - <span :ref="'group-end-' + group.id" /> + {{ group.text }} + </h6> + <span + v-for="emoji in group.emojis" + :key="group.id + emoji.displayText" + :title="maybeLocalizedEmojiName(emoji)" + class="emoji-item" + @click.stop.prevent="onEmoji(emoji)" + > + <span + v-if="!emoji.imageUrl" + class="emoji-picker-emoji -unicode" + >{{ emoji.replacement }}</span> + <still-image + v-else + :ref="setEmojiRef(group.id + emoji.displayText)" + class="emoji-picker-emoji -custom" + :data-src="emoji.imageUrl" + :data-emoji-name="group.id + emoji.displayText" + /> + </span> + <span :ref="setGroupRef('group-end-' + group.id)" /> + </div> + </div> + <div class="keep-open"> + <Checkbox v-model="keepOpen"> + {{ $t('emoji.keep_open') }} + </Checkbox> </div> </div> - <div class="keep-open"> - <Checkbox v-model="keepOpen"> - {{ $t('emoji.keep_open') }} - </Checkbox> + <div + v-if="showingStickers" + class="stickers-content" + > + <sticker-picker + @uploaded="onStickerUploaded" + @upload-failed="onStickerUploadFailed" + /> </div> </div> - <div - v-if="showingStickers" - class="stickers-content" - > - <sticker-picker - @uploaded="onStickerUploaded" - @upload-failed="onStickerUploadFailed" - /> - </div> - </div> - </div> + </template> + </Popover> </template> <script src="./emoji_picker.js"></script> diff --git a/src/components/emoji_reactions/emoji_reactions.vue b/src/components/emoji_reactions/emoji_reactions.vue @@ -1,5 +1,5 @@ <template> - <div class="emoji-reactions"> + <div class="EmojiReactions"> <UserListPopover v-for="(reaction) in emojiReactions" :key="reaction.name" @@ -7,7 +7,7 @@ > <button class="emoji-reaction btn button-default" - :class="{ 'picked-reaction': reactedWith(reaction.name), 'not-clickable': !loggedIn }" + :class="{ '-picked-reaction': reactedWith(reaction.name), 'not-clickable': !loggedIn }" @click="emojiOnClick(reaction.name, $event)" @mouseenter="fetchEmojiReactionsByIfMissing()" > @@ -26,57 +26,59 @@ </div> </template> -<script src="./emoji_reactions.js" ></script> +<script src="./emoji_reactions.js"></script> <style lang="scss"> @import '../../_variables.scss'; -.emoji-reactions { +.EmojiReactions { display: flex; margin-top: 0.25em; flex-wrap: wrap; -} -.emoji-reaction { - padding: 0 0.5em; - margin-right: 0.5em; - margin-top: 0.5em; - display: flex; - align-items: center; - justify-content: center; - box-sizing: border-box; - .reaction-emoji { - width: 1.25em; - margin-right: 0.25em; - } - &:focus { - outline: none; - } + .emoji-reaction { + padding: 0 0.5em; + margin-right: 0.5em; + margin-top: 0.5em; + display: flex; + align-items: center; + justify-content: center; + box-sizing: border-box; - &.not-clickable { - cursor: default; - &:hover { - box-shadow: $fallback--buttonShadow; - box-shadow: var(--buttonShadow); + .reaction-emoji { + width: 1.25em; + margin-right: 0.25em; + } + + &:focus { + outline: none; + } + + &.not-clickable { + cursor: default; + &:hover { + box-shadow: $fallback--buttonShadow; + box-shadow: var(--buttonShadow); + } + } + + &.-picked-reaction { + border: 1px solid var(--accent, $fallback--link); + margin-left: -1px; // offset the border, can't use inset shadows either + margin-right: calc(0.5em - 1px); } } -} -.emoji-reaction-expand { - padding: 0 0.5em; - margin-right: 0.5em; - margin-top: 0.5em; - display: flex; - align-items: center; - justify-content: center; - &:hover { - text-decoration: underline; + .emoji-reaction-expand { + padding: 0 0.5em; + margin-right: 0.5em; + margin-top: 0.5em; + display: flex; + align-items: center; + justify-content: center; + &:hover { + text-decoration: underline; + } } -} -.picked-reaction { - border: 1px solid var(--accent, $fallback--link); - margin-left: -1px; // offset the border, can't use inset shadows either - margin-right: calc(0.5em - 1px); } - </style> diff --git a/src/components/exporter/exporter.js b/src/components/exporter/exporter.js @@ -15,18 +15,8 @@ const Exporter = { type: String, default: 'export.csv' }, - exportButtonLabel: { - type: String, - default () { - return this.$t('exporter.export') - } - }, - processingMessage: { - type: String, - default () { - return this.$t('exporter.processing') - } - } + exportButtonLabel: { type: String }, + processingMessage: { type: String } }, data () { return { diff --git a/src/components/exporter/exporter.vue b/src/components/exporter/exporter.vue @@ -7,14 +7,14 @@ spin /> - <span>{{ processingMessage }}</span> + <span>{{ processingMessage || $t('exporter.processing') }}</span> </div> <button v-else class="btn button-default" @click="process" > - {{ exportButtonLabel }} + {{ exportButtonLabel || $t('exporter.export') }} </button> </div> </template> diff --git a/src/components/extra_buttons/extra_buttons.js b/src/components/extra_buttons/extra_buttons.js @@ -6,7 +6,10 @@ import { faEyeSlash, faThumbtack, faShareAlt, - faExternalLinkAlt + faExternalLinkAlt, + faHistory, + faPlus, + faTimes } from '@fortawesome/free-solid-svg-icons' import { faBookmark as faBookmarkReg, @@ -21,13 +24,27 @@ library.add( faThumbtack, faShareAlt, faExternalLinkAlt, - faFlag + faFlag, + faHistory, + faPlus, + faTimes ) const ExtraButtons = { - props: [ 'status' ], + props: ['status'], components: { Popover }, + data () { + return { + expanded: false + } + }, methods: { + onShow () { + this.expanded = true + }, + onClose () { + this.expanded = false + }, deleteStatus () { const confirmed = window.confirm(this.$t('status.delete_confirm')) if (confirmed) { @@ -71,14 +88,32 @@ const ExtraButtons = { }, reportStatus () { this.$store.dispatch('openUserReportingModal', { userId: this.status.user.id, statusIds: [this.status.id] }) + }, + editStatus () { + this.$store.dispatch('fetchStatusSource', { id: this.status.id }) + .then(data => this.$store.dispatch('openEditStatusModal', { + statusId: this.status.id, + subject: data.spoiler_text, + statusText: data.text, + statusIsSensitive: this.status.nsfw, + statusPoll: this.status.poll, + statusFiles: [...this.status.attachments], + visibility: this.status.visibility, + statusContentType: data.content_type + })) + }, + showStatusHistory () { + const originalStatus = { ...this.status } + const stripFieldsList = ['attachments', 'created_at', 'emojis', 'text', 'raw_html', 'nsfw', 'poll', 'summary', 'summary_raw_html'] + stripFieldsList.forEach(p => delete originalStatus[p]) + this.$store.dispatch('openStatusHistoryModal', originalStatus) } }, computed: { currentUser () { return this.$store.state.users.currentUser }, canDelete () { if (!this.currentUser) { return } - const superuser = this.currentUser.rights.moderator || this.currentUser.rights.admin - return superuser || this.status.user.id === this.currentUser.id + return this.currentUser.privileges.includes('messages_delete') || this.status.user.id === this.currentUser.id }, ownStatus () { return this.status.user.id === this.currentUser.id @@ -89,9 +124,16 @@ const ExtraButtons = { canMute () { return !!this.currentUser }, + canBookmark () { + return !!this.currentUser + }, statusLink () { return `${this.$store.state.instance.server}${this.$router.resolve({ name: 'conversation', params: { id: this.status.id } }).href}` - } + }, + isEdited () { + return this.status.edited_at !== null + }, + editingAvailable () { return this.$store.state.instance.editingAvailable } } } diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue @@ -6,8 +6,10 @@ :offset="{ y: 5 }" :bound-to="{ x: 'container' }" remove-padding + @show="onShow" + @close="onClose" > - <template v-slot:content="{close}"> + <template #content="{close}"> <div class="dropdown-menu"> <button v-if="canMute && !status.thread_muted" @@ -51,27 +53,51 @@ icon="thumbtack" /><span>{{ $t("status.unpin") }}</span> </button> + <template v-if="canBookmark"> + <button + v-if="!status.bookmarked" + class="button-default dropdown-item dropdown-item-icon" + @click.prevent="bookmarkStatus" + @click="close" + > + <FAIcon + fixed-width + :icon="['far', 'bookmark']" + /><span>{{ $t("status.bookmark") }}</span> + </button> + <button + v-if="status.bookmarked" + class="button-default dropdown-item dropdown-item-icon" + @click.prevent="unbookmarkStatus" + @click="close" + > + <FAIcon + fixed-width + icon="bookmark" + /><span>{{ $t("status.unbookmark") }}</span> + </button> + </template> <button - v-if="!status.bookmarked" + v-if="ownStatus && editingAvailable" class="button-default dropdown-item dropdown-item-icon" - @click.prevent="bookmarkStatus" + @click.prevent="editStatus" @click="close" > <FAIcon fixed-width - :icon="['far', 'bookmark']" - /><span>{{ $t("status.bookmark") }}</span> + icon="pen" + /><span>{{ $t("status.edit") }}</span> </button> <button - v-if="status.bookmarked" + v-if="isEdited && editingAvailable" class="button-default dropdown-item dropdown-item-icon" - @click.prevent="unbookmarkStatus" + @click.prevent="showStatusHistory" @click="close" > <FAIcon fixed-width - icon="bookmark" - /><span>{{ $t("status.unbookmark") }}</span> + icon="history" + /><span>{{ $t("status.status_history") }}</span> </button> <button v-if="canDelete" @@ -118,21 +144,36 @@ </button> </div> </template> - <template v-slot:trigger> - <button class="button-unstyled popover-trigger"> - <FAIcon - class="fa-scale-110 fa-old-padding" - icon="ellipsis-h" - /> - </button> + <template #trigger> + <span class="button-unstyled popover-trigger"> + <FALayers class="fa-old-padding-layer"> + <FAIcon + class="fa-scale-110 " + icon="ellipsis-h" + /> + <FAIcon + v-show="!expanded" + class="focus-marker" + transform="shrink-6 up-8 right-16" + icon="plus" + /> + <FAIcon + v-show="expanded" + class="focus-marker" + transform="shrink-6 up-8 right-16" + icon="times" + /> + </FALayers> + </span> </template> </Popover> </template> -<script src="./extra_buttons.js" ></script> +<script src="./extra_buttons.js"></script> <style lang="scss"> @import '../../_variables.scss'; +@import '../../_mixins.scss'; .ExtraButtons { /* override of popover internal stuff */ @@ -149,6 +190,21 @@ color: $fallback--text; color: var(--text, $fallback--text); } + + } + + .popover-trigger-button { + @include unfocused-style { + .focus-marker { + visibility: hidden; + } + } + + @include focused-style { + .focus-marker { + visibility: visible; + } + } } } </style> diff --git a/src/components/favorite_button/favorite_button.js b/src/components/favorite_button/favorite_button.js @@ -1,13 +1,21 @@ import { mapGetters } from 'vuex' import { library } from '@fortawesome/fontawesome-svg-core' -import { faStar } from '@fortawesome/free-solid-svg-icons' +import { + faStar, + faPlus, + faMinus, + faCheck +} from '@fortawesome/free-solid-svg-icons' import { faStar as faStarRegular } from '@fortawesome/free-regular-svg-icons' library.add( faStar, - faStarRegular + faStarRegular, + faPlus, + faMinus, + faCheck ) const FavoriteButton = { @@ -31,7 +39,10 @@ const FavoriteButton = { } }, computed: { - ...mapGetters(['mergedConfig']) + ...mapGetters(['mergedConfig']), + remoteInteractionLink () { + return this.$store.getters.remoteInteractionLink({ statusId: this.status.id }) + } } } diff --git a/src/components/favorite_button/favorite_button.vue b/src/components/favorite_button/favorite_button.vue @@ -7,19 +7,45 @@ :title="$t('tool_tip.favorite')" @click.prevent="favorite()" > - <FAIcon - class="fa-scale-110 fa-old-padding" - :icon="[status.favorited ? 'fas' : 'far', 'star']" - :spin="animated" - /> + <FALayers class="fa-scale-110 fa-old-padding-layer"> + <FAIcon + class="fa-scale-110" + :icon="[status.favorited ? 'fas' : 'far', 'star']" + :spin="animated" + /> + <FAIcon + v-if="status.favorited" + class="active-marker" + transform="shrink-6 up-9 right-12" + icon="check" + /> + <FAIcon + v-if="!status.favorited" + class="focus-marker" + transform="shrink-6 up-9 right-12" + icon="plus" + /> + <FAIcon + v-else + class="focus-marker" + transform="shrink-6 up-9 right-12" + icon="minus" + /> + </FALayers> </button> - <span v-else> + <a + v-else + class="button-unstyled interactive" + target="_blank" + role="button" + :href="remoteInteractionLink" + > <FAIcon class="fa-scale-110 fa-old-padding" :title="$t('tool_tip.favorite')" :icon="['far', 'star']" /> - </span> + </a> <span v-if="!mergedConfig.hidePostStats && status.fave_num > 0" class="action-counter" @@ -29,10 +55,11 @@ </div> </template> -<script src="./favorite_button.js" ></script> +<script src="./favorite_button.js"></script> <style lang="scss"> @import '../../_variables.scss'; +@import '../../_mixins.scss'; .FavoriteButton { display: flex; @@ -57,6 +84,26 @@ color: $fallback--cOrange; color: var(--cOrange, $fallback--cOrange); } + + @include unfocused-style { + .focus-marker { + visibility: hidden; + } + + .active-marker { + visibility: visible; + } + } + + @include focused-style { + .focus-marker { + visibility: visible; + } + + .active-marker { + visibility: hidden; + } + } } } </style> diff --git a/src/components/features_panel/features_panel.vue b/src/components/features_panel/features_panel.vue @@ -32,7 +32,7 @@ </div> </template> -<script src="./features_panel.js" ></script> +<script src="./features_panel.js"></script> <style lang="scss"> .features-panel li { diff --git a/src/components/flash/flash.js b/src/components/flash/flash.js @@ -11,7 +11,7 @@ library.add( ) const Flash = { - props: [ 'src' ], + props: ['src'], data () { return { player: false, // can be true, "hidden", false. hidden = element exists @@ -39,12 +39,13 @@ const Flash = { this.player = 'error' }) this.ruffleInstance = player + this.$emit('playerOpened') }) }, closePlayer () { - console.log(this.ruffleInstance) - this.ruffleInstance.remove() + this.ruffleInstance && this.ruffleInstance.remove() this.player = false + this.$emit('playerClosed') } } } diff --git a/src/components/flash/flash.vue b/src/components/flash/flash.vue @@ -36,13 +36,6 @@ </p> </span> </button> - <button - v-if="player" - class="button-unstyled hider" - @click="closePlayer" - > - <FAIcon icon="stop" /> - </button> </div> </template> @@ -51,8 +44,9 @@ <style lang="scss"> @import '../../_variables.scss'; .Flash { + display: inline-block; width: 100%; - height: 260px; + height: 100%; position: relative; .player { @@ -60,6 +54,16 @@ width: 100%; } + .placeholder { + height: 100%; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + background: var(--bg); + color: var(--link); + } + .hider { top: 0; } @@ -76,13 +80,5 @@ display: none; visibility: 'hidden'; } - - .placeholder { - height: 100%; - flex: 1; - display: flex; - align-items: center; - justify-content: center; - } } </style> diff --git a/src/components/follow_button/follow_button.js b/src/components/follow_button/follow_button.js @@ -1,6 +1,6 @@ import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate' export default { - props: ['relationship', 'labelFollowing', 'buttonClass'], + props: ['relationship', 'user', 'labelFollowing', 'buttonClass'], data () { return { inProgress: false @@ -29,6 +29,9 @@ export default { } else { return this.$t('user_card.follow') } + }, + disabled () { + return this.inProgress || this.user.deactivated } }, methods: { diff --git a/src/components/follow_button/follow_button.vue b/src/components/follow_button/follow_button.vue @@ -2,7 +2,7 @@ <button class="btn button-default follow-button" :class="{ toggled: isPressed }" - :disabled="inProgress" + :disabled="disabled" :title="title" @click="onClick" > diff --git a/src/components/follow_card/follow_card.js b/src/components/follow_card/follow_card.js @@ -1,6 +1,7 @@ import BasicUserCard from '../basic_user_card/basic_user_card.vue' import RemoteFollow from '../remote_follow/remote_follow.vue' import FollowButton from '../follow_button/follow_button.vue' +import RemoveFollowerButton from '../remove_follower_button/remove_follower_button.vue' const FollowCard = { props: [ @@ -10,7 +11,8 @@ const FollowCard = { components: { BasicUserCard, RemoteFollow, - FollowButton + FollowButton, + RemoveFollowerButton }, computed: { isMe () { diff --git a/src/components/follow_card/follow_card.vue b/src/components/follow_card/follow_card.vue @@ -20,6 +20,12 @@ :relationship="relationship" :label-following="$t('user_card.follow_unfollow')" class="follow-card-follow-button" + :user="user" + /> + <RemoveFollowerButton + v-if="noFollowsYou && relationship.followed_by" + :relationship="relationship" + class="follow-card-button" /> </template> </div> @@ -39,6 +45,12 @@ line-height: 1.5em; } + &-button { + margin-top: 0.5em; + padding: 0 1.5em; + margin-left: 1em; + } + &-follow-button { margin-top: 0.5em; margin-left: auto; diff --git a/src/components/font_control/font_control.js b/src/components/font_control/font_control.js @@ -1,4 +1,4 @@ -import { set } from 'vue' +import { set } from 'lodash' import Select from '../select/select.vue' export default { @@ -6,11 +6,12 @@ export default { Select }, props: [ - 'name', 'label', 'value', 'fallback', 'options', 'no-inherit' + 'name', 'label', 'modelValue', 'fallback', 'options', 'no-inherit' ], + emits: ['update:modelValue'], data () { return { - lValue: this.value, + lValue: this.modelValue, availableOptions: [ this.noInherit ? '' : 'inherit', 'custom', @@ -22,7 +23,7 @@ export default { } }, beforeUpdate () { - this.lValue = this.value + this.lValue = this.modelValue }, computed: { present () { @@ -37,7 +38,7 @@ export default { }, set (v) { set(this.lValue, 'family', v) - this.$emit('input', this.lValue) + this.$emit('update:modelValue', this.lValue) } }, isCustom () { diff --git a/src/components/font_control/font_control.vue b/src/components/font_control/font_control.vue @@ -15,13 +15,14 @@ class="opt exlcude-disabled" type="checkbox" :checked="present" - @input="$emit('input', typeof value === 'undefined' ? fallback : undefined)" + @change="$emit('update:modelValue', typeof modelValue === 'undefined' ? fallback : undefined)" > <label v-if="typeof fallback !== 'undefined'" class="opt-l" :for="name + '-o'" /> + {{ ' ' }} <Select :id="name + '-font-switcher'" v-model="preset" @@ -46,7 +47,7 @@ </div> </template> -<script src="./font_control.js" ></script> +<script src="./font_control.js"></script> <style lang="scss"> @import '../../_variables.scss'; diff --git a/src/components/gallery/gallery.js b/src/components/gallery/gallery.js @@ -1,15 +1,26 @@ import Attachment from '../attachment/attachment.vue' -import { chunk, last, dropRight, sumBy } from 'lodash' +import { sumBy, set } from 'lodash' const Gallery = { props: [ 'attachments', + 'limitRows', + 'descriptions', + 'limit', 'nsfw', - 'setMedia' + 'setMedia', + 'size', + 'editable', + 'removeAttachment', + 'shiftUpAttachment', + 'shiftDnAttachment', + 'editAttachment', + 'grid' ], data () { return { - sizes: {} + sizes: {}, + hidingLong: true } }, components: { Attachment }, @@ -18,26 +29,70 @@ const Gallery = { if (!this.attachments) { return [] } - const rows = chunk(this.attachments, 3) - if (last(rows).length === 1 && rows.length > 1) { - // if 1 attachment on last row -> add it to the previous row instead - const lastAttachment = last(rows)[0] - const allButLastRow = dropRight(rows) - last(allButLastRow).push(lastAttachment) - return allButLastRow + const attachments = this.limit > 0 + ? this.attachments.slice(0, this.limit) + : this.attachments + if (this.size === 'hide') { + return attachments.map(item => ({ minimal: true, items: [item] })) } + const rows = this.grid + ? [{ grid: true, items: attachments }] + : attachments.reduce((acc, attachment, i) => { + if (attachment.mimetype.includes('audio')) { + return [...acc, { audio: true, items: [attachment] }, { items: [] }] + } + if (!( + attachment.mimetype.includes('image') || + attachment.mimetype.includes('video') || + attachment.mimetype.includes('flash') + )) { + return [...acc, { minimal: true, items: [attachment] }, { items: [] }] + } + const maxPerRow = 3 + const attachmentsRemaining = this.attachments.length - i + 1 + const currentRow = acc[acc.length - 1].items + currentRow.push(attachment) + if (currentRow.length >= maxPerRow && attachmentsRemaining > maxPerRow) { + return [...acc, { items: [] }] + } else { + return acc + } + }, [{ items: [] }]).filter(_ => _.items.length > 0) return rows }, - useContainFit () { - return this.$store.getters.mergedConfig.useContainFit + attachmentsDimensionalScore () { + return this.rows.reduce((acc, row) => { + let size = 0 + if (row.minimal) { + size += 1 / 8 + } else if (row.audio) { + size += 1 / 4 + } else { + size += 1 / (row.items.length + 0.6) + } + return acc + size + }, 0) + }, + tooManyAttachments () { + if (this.editable || this.size === 'small') { + return false + } else if (this.size === 'hide') { + return this.attachments.length > 8 + } else { + return this.attachmentsDimensionalScore > 1 + } } }, methods: { - onNaturalSizeLoad (id, size) { - this.$set(this.sizes, id, size) + onNaturalSizeLoad ({ id, width, height }) { + set(this.sizes, id, { width, height }) }, - rowStyle (itemsPerRow) { - return { 'padding-bottom': `${(100 / (itemsPerRow + 0.6))}%` } + rowStyle (row) { + if (row.audio) { + return { 'padding-bottom': '25%' } // fixed reduced height for audio + } else if (!row.minimal && !row.grid) { + return { 'padding-bottom': `${(100 / (row.items.length + 0.6))}%` } + } }, itemStyle (id, row) { const total = sumBy(row, item => this.getAspectRatio(item.id)) @@ -46,6 +101,16 @@ const Gallery = { getAspectRatio (id) { const size = this.sizes[id] return size ? size.width / size.height : 1 + }, + toggleHidingLong (event) { + this.hidingLong = event + }, + openGallery () { + this.$store.dispatch('setMedia', this.attachments) + this.$store.dispatch('setCurrentMedia', this.attachments[0]) + }, + onMedia () { + this.$store.dispatch('setMedia', this.attachments) } } } diff --git a/src/components/gallery/gallery.vue b/src/components/gallery/gallery.vue @@ -1,26 +1,83 @@ <template> <div ref="galleryContainer" - style="width: 100%;" + class="Gallery" + :class="{ '-long': tooManyAttachments && hidingLong }" > + <div class="gallery-rows"> + <div + v-for="(row, rowIndex) in rows" + :key="rowIndex" + class="gallery-row" + :style="rowStyle(row)" + :class="{ '-audio': row.audio, '-minimal': row.minimal, '-grid': grid }" + > + <div + class="gallery-row-inner" + :class="{ '-grid': grid }" + > + <Attachment + v-for="(attachment, attachmentIndex) in row.items" + :key="attachment.id" + class="gallery-item" + :nsfw="nsfw" + :attachment="attachment" + :size="size" + :editable="editable" + :remove="removeAttachment" + :shift-up="!(attachmentIndex === 0 && rowIndex === 0) && shiftUpAttachment" + :shift-dn="!(attachmentIndex === row.items.length - 1 && rowIndex === rows.length - 1) && shiftDnAttachment" + :edit="editAttachment" + :description="descriptions && descriptions[attachment.id]" + :hide-description="size === 'small' || tooManyAttachments && hidingLong" + :style="itemStyle(attachment.id, row.items)" + @setMedia="onMedia" + @naturalSizeLoad="onNaturalSizeLoad" + /> + </div> + </div> + </div> <div - v-for="(row, index) in rows" - :key="index" - class="gallery-row" - :style="rowStyle(row.length)" - :class="{ 'contain-fit': useContainFit, 'cover-fit': !useContainFit }" + v-if="tooManyAttachments" + class="many-attachments" > - <div class="gallery-row-inner"> - <attachment - v-for="attachment in row" - :key="attachment.id" - :set-media="setMedia" - :nsfw="nsfw" - :attachment="attachment" - :allow-play="false" - :natural-size-load="onNaturalSizeLoad.bind(null, attachment.id)" - :style="itemStyle(attachment.id, row)" - /> + <div class="many-attachments-text"> + {{ $t("status.many_attachments", { number: attachments.length }) }} + </div> + <div class="many-attachments-buttons"> + <span + v-if="!hidingLong" + class="many-attachments-button" + > + <button + class="button-unstyled -link" + @click="toggleHidingLong(true)" + > + {{ $t("status.collapse_attachments") }} + </button> + </span> + <span + v-if="hidingLong" + class="many-attachments-button" + > + <button + class="button-unstyled -link" + @click="toggleHidingLong(false)" + > + {{ $t("status.show_all_attachments") }} + </button> + </span> + <span + v-if="hidingLong" + class="many-attachments-button" + > + <button + class="button-unstyled -link" + @click="openGallery" + > + {{ $t("status.open_gallery") }} + </button> + </span> </div> </div> </div> @@ -31,12 +88,66 @@ <style lang="scss"> @import '../../_variables.scss'; -.gallery-row { - position: relative; - height: 0; - width: 100%; - flex-grow: 1; - margin-top: 0.5em; +.Gallery { + .gallery-rows { + display: flex; + flex-direction: column; + } + + .gallery-row { + position: relative; + height: 0; + width: 100%; + flex-grow: 1; + + &:not(:first-child) { + margin-top: 0.5em; + } + } + + &.-long { + .gallery-rows { + max-height: 25em; + overflow: hidden; + mask: + linear-gradient(to top, white, transparent) bottom/100% 70px no-repeat, + linear-gradient(to top, white, white); + + /* Autoprefixed seem to ignore this one, and also syntax is different */ + -webkit-mask-composite: xor; + mask-composite: exclude; + } + } + + .many-attachments-text { + text-align: center; + line-height: 2; + } + + .many-attachments-buttons { + display: flex; + } + + .many-attachments-button { + display: flex; + flex: 1; + justify-content: center; + line-height: 2; + + button { + padding: 0 2em; + } + } + + .gallery-row { + &.-grid, + &.-minimal { + height: auto; + .gallery-row-inner { + position: relative; + } + } + } .gallery-row-inner { position: absolute; @@ -48,9 +159,24 @@ flex-direction: row; flex-wrap: nowrap; align-content: stretch; + + &.-grid { + width: 100%; + height: auto; + position: relative; + display: grid; + grid-column-gap: 0.5em; + grid-row-gap: 0.5em; + grid-template-columns: repeat(auto-fill, minmax(15em, 1fr)); + + .gallery-item { + margin: 0; + height: 200px; + } + } } - .gallery-row-inner .attachment { + .gallery-item { margin: 0 0.5em 0 0; flex-grow: 1; height: 100%; @@ -61,32 +187,5 @@ margin: 0; } } - - .image-attachment { - width: 100%; - height: 100%; - } - - .video-container { - height: 100%; - } - - &.contain-fit { - img, - video, - canvas { - object-fit: contain; - height: 100%; - } - } - - &.cover-fit { - img, - video, - canvas { - object-fit: cover; - } - } } - </style> diff --git a/src/components/global_notice_list/global_notice_list.vue b/src/components/global_notice_list/global_notice_list.vue @@ -29,10 +29,10 @@ .global-notice-list { position: fixed; - top: 50px; + top: calc(var(--navbar-height) + 0.5em); width: 100%; pointer-events: none; - z-index: 1001; + z-index: var(--ZI_navbar_popovers); display: flex; flex-direction: column; align-items: center; @@ -44,20 +44,18 @@ max-width: calc(100% - 3em); display: flex; padding-left: 1.5em; - line-height: 2em; + line-height: 2; + margin-bottom: 0.5em; + .notice-message { flex: 1 1 100%; } - i { - flex: 0 0; - width: 1.5em; - cursor: pointer; - } } .global-error { background-color: var(--alertPopupError, $fallback--cRed); color: var(--alertPopupErrorText, $fallback--text); + .svg-inline--fa { color: var(--alertPopupErrorText, $fallback--text); } @@ -66,6 +64,7 @@ .global-warning { background-color: var(--alertPopupWarning, $fallback--cOrange); color: var(--alertPopupWarningText, $fallback--text); + .svg-inline--fa { color: var(--alertPopupWarningText, $fallback--text); } diff --git a/src/components/hashtag_link/hashtag_link.vue b/src/components/hashtag_link/hashtag_link.vue @@ -14,6 +14,6 @@ </span> </template> -<script src="./hashtag_link.js"/> +<script src="./hashtag_link.js" /> -<style lang="scss" src="./hashtag_link.scss"/> +<style lang="scss" src="./hashtag_link.scss" /> diff --git a/src/components/image_cropper/image_cropper.js b/src/components/image_cropper/image_cropper.js @@ -95,7 +95,7 @@ const ImageCropper = { const fileInput = this.$refs.input if (fileInput.files != null && fileInput.files[0] != null) { this.file = fileInput.files[0] - let reader = new window.FileReader() + const reader = new window.FileReader() reader.onload = (e) => { this.dataUrl = e.target.result this.$emit('open') @@ -117,7 +117,7 @@ const ImageCropper = { const fileInput = this.$refs.input fileInput.addEventListener('change', this.readFile) }, - beforeDestroy: function () { + beforeUnmount: function () { // remove the event listeners const trigger = this.getTriggerDOM() if (trigger) { diff --git a/src/components/importer/importer.js b/src/components/importer/importer.js @@ -15,24 +15,9 @@ const Importer = { type: Function, required: true }, - submitButtonLabel: { - type: String, - default () { - return this.$t('importer.submit') - } - }, - successMessage: { - type: String, - default () { - return this.$t('importer.success') - } - }, - errorMessage: { - type: String, - default () { - return this.$t('importer.error') - } - } + submitButtonLabel: { type: String }, + successMessage: { type: String }, + errorMessage: { type: String } }, data () { return { diff --git a/src/components/importer/importer.vue b/src/components/importer/importer.vue @@ -18,21 +18,31 @@ class="btn button-default" @click="submit" > - {{ submitButtonLabel }} + {{ submitButtonLabel || $t('importer.submit') }} </button> <div v-if="success"> - <FAIcon - icon="times" + <button + class="button-unstyled" @click="dismiss" - /> - <p>{{ successMessage }}</p> + > + <FAIcon + icon="times" + /> + </button> + {{ ' ' }} + <span>{{ successMessage || $t('importer.success') }}</span> </div> <div v-else-if="error"> - <FAIcon - icon="times" + <button + class="button-unstyled" @click="dismiss" - /> - <p>{{ errorMessage }}</p> + > + <FAIcon + icon="times" + /> + </button> + {{ ' ' }} + <span>{{ errorMessage || $t('importer.error') }}</span> </div> </div> </template> diff --git a/src/components/instance_specific_panel/instance_specific_panel.vue b/src/components/instance_specific_panel/instance_specific_panel.vue @@ -10,4 +10,4 @@ </div> </template> -<script src="./instance_specific_panel.js" ></script> +<script src="./instance_specific_panel.js"></script> diff --git a/src/components/interactions/interactions.js b/src/components/interactions/interactions.js @@ -1,9 +1,12 @@ import Notifications from '../notifications/notifications.vue' +import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' const tabModeDict = { mentions: ['mention'], 'likes+repeats': ['repeat', 'like'], follows: ['follow'], + reactions: ['pleroma:emoji_reaction'], + reports: ['pleroma:report'], moves: ['move'] } @@ -11,7 +14,8 @@ const Interactions = { data () { return { allowFollowingMove: this.$store.state.users.currentUser.allow_following_move, - filterMode: tabModeDict['mentions'] + filterMode: tabModeDict.mentions, + canSeeReports: this.$store.state.users.currentUser.privileges.includes('reports_manage_reports') } }, methods: { @@ -20,7 +24,8 @@ const Interactions = { } }, components: { - Notifications + Notifications, + TabSwitcher } } diff --git a/src/components/interactions/interactions.vue b/src/components/interactions/interactions.vue @@ -22,6 +22,15 @@ :label="$t('interactions.follows')" /> <span + key="reactions" + :label="$t('interactions.emoji_reactions')" + /> + <span + v-if="canSeeReports" + key="reports" + :label="$t('interactions.reports')" + /> + <span v-if="!allowFollowingMove" key="moves" :label="$t('interactions.moves')" diff --git a/src/components/interface_language_switcher/interface_language_switcher.vue b/src/components/interface_language_switcher/interface_language_switcher.vue @@ -1,11 +1,12 @@ <template> <div> <label for="interface-language-switcher"> - {{ $t('settings.interfaceLanguage') }} + {{ promptText }} </label> + {{ ' ' }} <Select id="interface-language-switcher" - v-model="language" + v-model="controlledLanguage" > <option v-for="lang in languages" @@ -19,39 +20,44 @@ </template> <script> -import languagesObject from '../../i18n/messages' import localeService from '../../services/locale/locale.service.js' -import ISO6391 from 'iso-639-1' -import _ from 'lodash' import Select from '../select/select.vue' export default { components: { + // eslint-disable-next-line vue/no-reserved-component-names Select }, + props: { + promptText: { + type: String, + required: true + }, + language: { + type: String, + required: true + }, + setLanguage: { + type: Function, + required: true + } + }, computed: { languages () { - return _.map(languagesObject.languages, (code) => ({ code: code, name: this.getLanguageName(code) })).sort((a, b) => a.name.localeCompare(b.name)) + return localeService.languages }, - language: { - get: function () { return this.$store.getters.mergedConfig.interfaceLanguage }, + controlledLanguage: { + get: function () { return this.language }, set: function (val) { - this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val }) + this.setLanguage(val) } } }, methods: { getLanguageName (code) { - const specialLanguageNames = { - 'ja_easy': 'やさしいにほんご', - 'zh': '简体中文', - 'zh_Hant': '繁體中文' - } - const languageName = specialLanguageNames[code] || ISO6391.getNativeName(code) - const browserLocale = localeService.internalToBrowserLocale(code) - return languageName.charAt(0).toLocaleUpperCase(browserLocale) + languageName.slice(1) + return localeService.getLanguageName(code) } } } diff --git a/src/components/link-preview/link-preview.vue b/src/components/link-preview/link-preview.vue @@ -63,7 +63,7 @@ } .card-host { - font-size: 12px; + font-size: 0.85em; } .card-description { diff --git a/src/components/lists/lists.js b/src/components/lists/lists.js @@ -0,0 +1,27 @@ +import ListsCard from '../lists_card/lists_card.vue' + +const Lists = { + data () { + return { + isNew: false + } + }, + components: { + ListsCard + }, + computed: { + lists () { + return this.$store.state.lists.allLists + } + }, + methods: { + cancelNewList () { + this.isNew = false + }, + newList () { + this.isNew = true + } + } +} + +export default Lists diff --git a/src/components/lists/lists.vue b/src/components/lists/lists.vue @@ -0,0 +1,33 @@ +<template> + <div class="Lists panel panel-default"> + <div class="panel-heading"> + <div class="title"> + {{ $t('lists.lists') }} + </div> + <router-link + :to="{ name: 'lists-new' }" + class="button-default btn new-list-button" + > + {{ $t("lists.new") }} + </router-link> + </div> + <div class="panel-body"> + <ListsCard + v-for="list in lists.slice().reverse()" + :key="list" + :list="list" + class="list-item" + /> + </div> + </div> +</template> + +<script src="./lists.js"></script> + +<style lang="scss"> +.Lists { + .new-list-button { + padding: 0 0.5em; + } +} +</style> diff --git a/src/components/lists_card/lists_card.js b/src/components/lists_card/lists_card.js @@ -0,0 +1,16 @@ +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faEllipsisH +} from '@fortawesome/free-solid-svg-icons' + +library.add( + faEllipsisH +) + +const ListsCard = { + props: [ + 'list' + ] +} + +export default ListsCard diff --git a/src/components/lists_card/lists_card.vue b/src/components/lists_card/lists_card.vue @@ -0,0 +1,51 @@ +<template> + <div class="list-card"> + <router-link + :to="{ name: 'lists-timeline', params: { id: list.id } }" + class="list-name" + > + {{ list.title }} + </router-link> + <router-link + :to="{ name: 'lists-edit', params: { id: list.id } }" + class="button-list-edit" + > + <FAIcon + class="fa-scale-110 fa-old-padding" + icon="ellipsis-h" + /> + </router-link> + </div> +</template> + +<script src="./lists_card.js"></script> + +<style lang="scss"> +@import '../../_variables.scss'; + +.list-card { + display: flex; +} + +.list-name, +.button-list-edit { + margin: 0; + padding: 1em; + color: $fallback--link; + color: var(--link, $fallback--link); + + &:hover { + background-color: $fallback--lightBg; + background-color: var(--selectedMenu, $fallback--lightBg); + color: $fallback--link; + color: var(--selectedMenuText, $fallback--link); + --faint: var(--selectedMenuFaintText, $fallback--faint); + --faintLink: var(--selectedMenuFaintLink, $fallback--faint); + --lightText: var(--selectedMenuLightText, $fallback--lightText); + } +} + +.list-name { + flex-grow: 1; +} +</style> diff --git a/src/components/lists_edit/lists_edit.js b/src/components/lists_edit/lists_edit.js @@ -0,0 +1,145 @@ +import { mapState, mapGetters } from 'vuex' +import BasicUserCard from '../basic_user_card/basic_user_card.vue' +import ListsUserSearch from '../lists_user_search/lists_user_search.vue' +import PanelLoading from 'src/components/panel_loading/panel_loading.vue' +import UserAvatar from '../user_avatar/user_avatar.vue' +import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faSearch, + faChevronLeft +} from '@fortawesome/free-solid-svg-icons' + +library.add( + faSearch, + faChevronLeft +) + +const ListsNew = { + components: { + BasicUserCard, + UserAvatar, + ListsUserSearch, + TabSwitcher, + PanelLoading + }, + data () { + return { + title: '', + titleDraft: '', + membersUserIds: [], + removedUserIds: new Set([]), // users we added for members, to undo + searchUserIds: [], + addedUserIds: new Set([]), // users we added from search, to undo + searchLoading: false, + reallyDelete: false + } + }, + created () { + if (!this.id) return + this.$store.dispatch('fetchList', { listId: this.id }) + .then(() => { + this.title = this.findListTitle(this.id) + this.titleDraft = this.title + }) + this.$store.dispatch('fetchListAccounts', { listId: this.id }) + .then(() => { + this.membersUserIds = this.findListAccounts(this.id) + this.membersUserIds.forEach(userId => { + this.$store.dispatch('fetchUserIfMissing', userId) + }) + }) + }, + computed: { + id () { + return this.$route.params.id + }, + membersUsers () { + return [...this.membersUserIds, ...this.addedUserIds] + .map(userId => this.findUser(userId)).filter(user => user) + }, + searchUsers () { + return this.searchUserIds.map(userId => this.findUser(userId)).filter(user => user) + }, + ...mapState({ + currentUser: state => state.users.currentUser + }), + ...mapGetters(['findUser', 'findListTitle', 'findListAccounts']) + }, + methods: { + onInput () { + this.search(this.query) + }, + toggleRemoveMember (user) { + if (this.removedUserIds.has(user.id)) { + this.id && this.addUser(user) + this.removedUserIds.delete(user.id) + } else { + this.id && this.removeUser(user.id) + this.removedUserIds.add(user.id) + } + }, + toggleAddFromSearch (user) { + if (this.addedUserIds.has(user.id)) { + this.id && this.removeUser(user.id) + this.addedUserIds.delete(user.id) + } else { + this.id && this.addUser(user) + this.addedUserIds.add(user.id) + } + }, + isRemoved (user) { + return this.removedUserIds.has(user.id) + }, + isAdded (user) { + return this.addedUserIds.has(user.id) + }, + addUser (user) { + this.$store.dispatch('addListAccount', { accountId: this.user.id, listId: this.id }) + }, + removeUser (userId) { + this.$store.dispatch('removeListAccount', { accountId: this.user.id, listId: this.id }) + }, + onSearchLoading (results) { + this.searchLoading = true + }, + onSearchLoadingDone (results) { + this.searchLoading = false + }, + onSearchResults (results) { + this.searchLoading = false + this.searchUserIds = results + }, + updateListTitle () { + this.$store.dispatch('setList', { listId: this.id, title: this.titleDraft }) + .then(() => { + this.title = this.findListTitle(this.id) + }) + }, + createList () { + this.$store.dispatch('createList', { title: this.titleDraft }) + .then((list) => { + return this + .$store + .dispatch('setListAccounts', { listId: list.id, accountIds: [...this.addedUserIds] }) + .then(() => list.id) + }) + .then((listId) => { + this.$router.push({ name: 'lists-timeline', params: { id: listId } }) + }) + .catch((e) => { + this.$store.dispatch('pushGlobalNotice', { + messageKey: 'lists.error', + messageArgs: [e.message], + level: 'error' + }) + }) + }, + deleteList () { + this.$store.dispatch('deleteList', { listId: this.id }) + this.$router.push({ name: 'lists' }) + } + } +} + +export default ListsNew diff --git a/src/components/lists_edit/lists_edit.vue b/src/components/lists_edit/lists_edit.vue @@ -0,0 +1,228 @@ +<template> + <div class="panel-default panel ListEdit"> + <div + ref="header" + class="panel-heading list-edit-heading" + > + <button + class="button-unstyled go-back-button" + @click="$router.back" + > + <FAIcon + size="lg" + icon="chevron-left" + /> + </button> + <div class="title"> + <i18n-t + v-if="id" + keypath="lists.editing_list" + > + <template #listTitle> + {{ title }} + </template> + </i18n-t> + <i18n-t + v-else + keypath="lists.creating_list" + /> + </div> + </div> + <div class="panel-body"> + <div class="input-wrap"> + <label for="list-edit-title">{{ $t('lists.title') }}</label> + {{ ' ' }} + <input + id="list-edit-title" + ref="title" + v-model="titleDraft" + > + <button + v-if="id" + class="btn button-default follow-button" + @click="updateListTitle" + > + {{ $t('lists.update_title') }} + </button> + </div> + <tab-switcher + class="list-member-management" + :scrollable-tabs="true" + > + <div + v-if="id || addedUserIds.size > 0" + :label="$t('lists.manage_members')" + class="members-list" + > + <div class="users-list"> + <div + v-for="user in membersUsers" + :key="user.id" + class="member" + > + <BasicUserCard + :user="user" + > + <button + class="btn button-default follow-button" + @click="toggleRemoveMember(user)" + > + {{ isRemoved(user) ? $t('general.undo') : $t('lists.remove_from_list') }} + </button> + </BasicUserCard> + </div> + </div> + </div> + + <div + class="search-list" + :label="$t('lists.add_members')" + > + <ListsUserSearch + @results="onSearchResults" + @loading="onSearchLoading" + @loadingDone="onSearchLoadingDone" + /> + <div + v-if="searchLoading" + class="loading" + > + <PanelLoading /> + </div> + <div + v-else + class="users-list" + > + <div + v-for="user in searchUsers" + :key="user.id" + class="member" + > + <BasicUserCard + :user="user" + > + <span + v-if="membersUserIds.includes(user.id)" + > + {{ $t('lists.is_in_list') }} + </span> + <button + v-if="!membersUserIds.includes(user.id)" + class="btn button-default follow-button" + @click="toggleAddFromSearch(user)" + > + {{ isAdded(user) ? $t('general.undo') : $t('lists.add_to_list') }} + </button> + <button + v-else + class="btn button-default follow-button" + @click="toggleRemoveMember(user)" + > + {{ isRemoved(user) ? $t('general.undo') : $t('lists.remove_from_list') }} + </button> + </BasicUserCard> + </div> + </div> + </div> + </tab-switcher> + </div> + <div class="panel-footer"> + <span class="spacer" /> + <button + v-if="!id" + class="btn button-default footer-button" + @click="createList" + > + {{ $t('lists.create') }} + </button> + <button + v-else-if="!reallyDelete" + class="btn button-default footer-button" + @click="reallyDelete = true" + > + {{ $t('lists.delete') }} + </button> + <template v-else> + {{ $t('lists.really_delete') }} + <button + class="btn button-default footer-button" + @click="deleteList" + > + {{ $t('general.yes') }} + </button> + <button + class="btn button-default footer-button" + @click="reallyDelete = false" + > + {{ $t('general.no') }} + </button> + </template> + </div> + </div> +</template> + +<script src="./lists_edit.js"></script> + +<style lang="scss"> +@import '../../_variables.scss'; + +.ListEdit { + --panel-body-padding: 0.5em; + + height: calc(100vh - var(--navbar-height)); + overflow: hidden; + display: flex; + flex-direction: column; + + .list-edit-heading { + grid-template-columns: auto minmax(50%, 1fr); + } + + .panel-body { + display: flex; + flex: 1; + flex-direction: column; + overflow: hidden; + } + + .list-member-management { + flex: 1 0 auto; + } + + .search-icon { + margin-right: 0.3em; + } + + .users-list { + padding-bottom: 0.7rem; + overflow-y: auto; + } + + & .search-list, + & .members-list { + overflow: hidden; + flex-direction: column; + min-height: 0; + } + + .go-back-button { + text-align: center; + line-height: 1; + height: 100%; + align-self: start; + width: var(--__panel-heading-height-inner); + } + + .btn { + margin: 0 0.5em; + } + + .panel-footer { + grid-template-columns: minmax(10%, 1fr); + + .footer-button { + min-width: 9em; + } + } +} +</style> diff --git a/src/components/lists_menu/lists_menu_content.js b/src/components/lists_menu/lists_menu_content.js @@ -0,0 +1,22 @@ +import { mapState } from 'vuex' +import NavigationEntry from 'src/components/navigation/navigation_entry.vue' +import { getListEntries } from 'src/components/navigation/filter.js' + +export const ListsMenuContent = { + props: [ + 'showPin' + ], + components: { + NavigationEntry + }, + computed: { + ...mapState({ + lists: getListEntries, + currentUser: state => state.users.currentUser, + privateMode: state => state.instance.private, + federating: state => state.instance.federating + }) + } +} + +export default ListsMenuContent diff --git a/src/components/lists_menu/lists_menu_content.vue b/src/components/lists_menu/lists_menu_content.vue @@ -0,0 +1,12 @@ +<template> + <ul> + <NavigationEntry + v-for="item in lists" + :key="item.name" + :show-pin="showPin" + :item="item" + /> + </ul> +</template> + +<script src="./lists_menu_content.js"></script> diff --git a/src/components/lists_timeline/lists_timeline.js b/src/components/lists_timeline/lists_timeline.js @@ -0,0 +1,36 @@ +import Timeline from '../timeline/timeline.vue' +const ListsTimeline = { + data () { + return { + listId: null + } + }, + components: { + Timeline + }, + computed: { + timeline () { return this.$store.state.statuses.timelines.list } + }, + watch: { + $route: function (route) { + if (route.name === 'lists-timeline' && route.params.id !== this.listId) { + this.listId = route.params.id + this.$store.dispatch('stopFetchingTimeline', 'list') + this.$store.commit('clearTimeline', { timeline: 'list' }) + this.$store.dispatch('fetchList', { listId: this.listId }) + this.$store.dispatch('startFetchingTimeline', { timeline: 'list', listId: this.listId }) + } + } + }, + created () { + this.listId = this.$route.params.id + this.$store.dispatch('fetchList', { listId: this.listId }) + this.$store.dispatch('startFetchingTimeline', { timeline: 'list', listId: this.listId }) + }, + unmounted () { + this.$store.dispatch('stopFetchingTimeline', 'list') + this.$store.commit('clearTimeline', { timeline: 'list' }) + } +} + +export default ListsTimeline diff --git a/src/components/lists_timeline/lists_timeline.vue b/src/components/lists_timeline/lists_timeline.vue @@ -0,0 +1,10 @@ +<template> + <Timeline + title="list.name" + :timeline="timeline" + :list-id="listId" + timeline-name="list" + /> +</template> + +<script src="./lists_timeline.js"></script> diff --git a/src/components/lists_user_search/lists_user_search.js b/src/components/lists_user_search/lists_user_search.js @@ -0,0 +1,51 @@ +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faSearch, + faChevronLeft +} from '@fortawesome/free-solid-svg-icons' +import { debounce } from 'lodash' +import Checkbox from '../checkbox/checkbox.vue' + +library.add( + faSearch, + faChevronLeft +) + +const ListsUserSearch = { + components: { + Checkbox + }, + emits: ['loading', 'loadingDone', 'results'], + data () { + return { + loading: false, + query: '', + followingOnly: true + } + }, + methods: { + onInput: debounce(function () { + this.search(this.query) + }, 2000), + search (query) { + if (!query) { + this.loading = false + return + } + + this.loading = true + this.$emit('loading') + this.userIds = [] + this.$store.dispatch('search', { q: query, resolve: true, type: 'accounts', following: this.followingOnly }) + .then(data => { + this.$emit('results', data.accounts.map(a => a.id)) + }) + .finally(() => { + this.loading = false + this.$emit('loadingDone') + }) + } + } +} + +export default ListsUserSearch diff --git a/src/components/lists_user_search/lists_user_search.vue b/src/components/lists_user_search/lists_user_search.vue @@ -0,0 +1,47 @@ +<template> + <div class="ListsUserSearch"> + <div class="input-wrap"> + <div class="input-search"> + <FAIcon + class="search-icon fa-scale-110 fa-old-padding" + icon="search" + /> + </div> + <input + ref="search" + v-model="query" + :placeholder="$t('lists.search')" + @input="onInput" + > + </div> + <div class="input-wrap"> + <Checkbox + v-model="followingOnly" + @change="onInput" + > + {{ $t('lists.following_only') }} + </Checkbox> + </div> + </div> +</template> + +<script src="./lists_user_search.js"></script> +<style lang="scss"> +@import '../../_variables.scss'; + +.ListsUserSearch { + .input-wrap { + display: flex; + margin: 0.7em 0.5em 0.7em 0.5em; + + input { + width: 100%; + } + } + + .search-icon { + margin-right: 0.3em; + } +} + +</style> diff --git a/src/components/login_form/login_form.js b/src/components/login_form/login_form.js @@ -83,7 +83,7 @@ const LoginForm = { }, clearError () { this.error = false }, focusOnPasswordInput () { - let passwordInput = this.$refs.passwordInput + const passwordInput = this.$refs.passwordInput passwordInput.focus() passwordInput.setSelectionRange(0, passwordInput.value.length) } diff --git a/src/components/login_form/login_form.vue b/src/components/login_form/login_form.vue @@ -76,17 +76,21 @@ > <div class="alert error"> {{ error }} - <FAIcon - class="fa-scale-110 fa-old-padding" - icon="times" + <button + class="button-unstyled" @click="clearError" - /> + > + <FAIcon + class="fa-scale-110 fa-old-padding" + icon="times" + /> + </button> </div> </div> </div> </template> -<script src="./login_form.js" ></script> +<script src="./login_form.js"></script> <style lang="scss"> @import '../../_variables.scss'; @@ -97,7 +101,7 @@ padding: 0.6em; .btn { - min-height: 28px; + min-height: 2em; width: 10em; } diff --git a/src/components/media_modal/media_modal.js b/src/components/media_modal/media_modal.js @@ -1,24 +1,46 @@ import StillImage from '../still-image/still-image.vue' import VideoAttachment from '../video_attachment/video_attachment.vue' import Modal from '../modal/modal.vue' -import fileTypeService from '../../services/file_type/file_type.service.js' +import PinchZoom from '../pinch_zoom/pinch_zoom.vue' +import SwipeClick from '../swipe_click/swipe_click.vue' import GestureService from '../../services/gesture_service/gesture_service' +import Flash from 'src/components/flash/flash.vue' +import fileTypeService from '../../services/file_type/file_type.service.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faChevronLeft, - faChevronRight + faChevronRight, + faCircleNotch, + faTimes } from '@fortawesome/free-solid-svg-icons' library.add( faChevronLeft, - faChevronRight + faChevronRight, + faCircleNotch, + faTimes ) const MediaModal = { components: { StillImage, VideoAttachment, - Modal + PinchZoom, + SwipeClick, + Modal, + Flash + }, + data () { + return { + loading: false, + swipeDirection: GestureService.DIRECTION_LEFT, + swipeThreshold: () => { + const considerableMoveRatio = 1 / 4 + return window.innerWidth * considerableMoveRatio + }, + pinchZoomMinScale: 1, + pinchZoomScaleResetLimit: 1.2 + } }, computed: { showing () { @@ -27,6 +49,9 @@ const MediaModal = { media () { return this.$store.state.mediaViewer.media }, + description () { + return this.currentMedia.description + }, currentIndex () { return this.$store.state.mediaViewer.currentIndex }, @@ -37,43 +62,62 @@ const MediaModal = { return this.media.length > 1 }, type () { - return this.currentMedia ? fileTypeService.fileType(this.currentMedia.mimetype) : null + return this.currentMedia ? this.getType(this.currentMedia) : null } }, - created () { - this.mediaSwipeGestureRight = GestureService.swipeGesture( - GestureService.DIRECTION_RIGHT, - this.goPrev, - 50 - ) - this.mediaSwipeGestureLeft = GestureService.swipeGesture( - GestureService.DIRECTION_LEFT, - this.goNext, - 50 - ) - }, methods: { - mediaTouchStart (e) { - GestureService.beginSwipe(e, this.mediaSwipeGestureRight) - GestureService.beginSwipe(e, this.mediaSwipeGestureLeft) - }, - mediaTouchMove (e) { - GestureService.updateSwipe(e, this.mediaSwipeGestureRight) - GestureService.updateSwipe(e, this.mediaSwipeGestureLeft) + getType (media) { + return fileTypeService.fileType(media.mimetype) }, hide () { - this.$store.dispatch('closeMediaViewer') + // HACK: Closing immediately via a touch will cause the click + // to be processed on the content below the overlay + const transitionTime = 100 // ms + setTimeout(() => { + this.$store.dispatch('closeMediaViewer') + }, transitionTime) + }, + hideIfNotSwiped (event) { + // If we have swiped over SwipeClick, do not trigger hide + const comp = this.$refs.swipeClick + if (!comp) { + this.hide() + } else { + comp.$gesture.click(event) + } }, goPrev () { if (this.canNavigate) { const prevIndex = this.currentIndex === 0 ? this.media.length - 1 : (this.currentIndex - 1) - this.$store.dispatch('setCurrent', this.media[prevIndex]) + const newMedia = this.media[prevIndex] + if (this.getType(newMedia) === 'image') { + this.loading = true + } + this.$store.dispatch('setCurrentMedia', newMedia) } }, goNext () { if (this.canNavigate) { const nextIndex = this.currentIndex === this.media.length - 1 ? 0 : (this.currentIndex + 1) - this.$store.dispatch('setCurrent', this.media[nextIndex]) + const newMedia = this.media[nextIndex] + if (this.getType(newMedia) === 'image') { + this.loading = true + } + this.$store.dispatch('setCurrentMedia', newMedia) + } + }, + onImageLoaded () { + this.loading = false + }, + handleSwipePreview (offsets) { + this.$refs.pinchZoom.setTransform({ scale: 1, x: offsets[0], y: 0 }) + }, + handleSwipeEnd (sign) { + this.$refs.pinchZoom.setTransform({ scale: 1, x: 0, y: 0 }) + if (sign > 0) { + this.goNext() + } else if (sign < 0) { + this.goPrev() } }, handleKeyupEvent (e) { @@ -98,7 +142,7 @@ const MediaModal = { document.addEventListener('keyup', this.handleKeyupEvent) document.addEventListener('keydown', this.handleKeydownEvent) }, - destroyed () { + unmounted () { window.removeEventListener('popstate', this.hide) document.removeEventListener('keyup', this.handleKeyupEvent) document.removeEventListener('keydown', this.handleKeydownEvent) diff --git a/src/components/media_modal/media_modal.vue b/src/components/media_modal/media_modal.vue @@ -2,18 +2,38 @@ <Modal v-if="showing" class="media-modal-view" - @backdropClicked="hide" + @backdropClicked="hideIfNotSwiped" > - <img + <SwipeClick v-if="type === 'image'" - class="modal-image" - :src="currentMedia.url" - :alt="currentMedia.description" - :title="currentMedia.description" - @touchstart.stop="mediaTouchStart" - @touchmove.stop="mediaTouchMove" - @click="hide" + ref="swipeClick" + class="modal-image-container" + :direction="swipeDirection" + :threshold="swipeThreshold" + @preview-requested="handleSwipePreview" + @swipe-finished="handleSwipeEnd" + @swipeless-clicked="hide" > + <PinchZoom + ref="pinchZoom" + class="modal-image-container-inner" + selector=".modal-image" + reach-min-scale-strategy="reset" + stop-propagate-handled="stop-propgate-handled" + :allow-pan-min-scale="pinchZoomMinScale" + :min-scale="pinchZoomMinScale" + :reset-to-min-scale-limit="pinchZoomScaleResetLimit" + > + <img + :class="{ loading }" + class="modal-image" + :src="currentMedia.url" + :alt="currentMedia.description" + :title="currentMedia.description" + @load="onImageLoaded" + > + </PinchZoom> + </SwipeClick> <VideoAttachment v-if="type === 'video'" class="modal-image" @@ -28,38 +48,84 @@ :title="currentMedia.description" controls /> + <Flash + v-if="type === 'flash'" + class="modal-image" + :src="currentMedia.url" + :alt="currentMedia.description" + :title="currentMedia.description" + /> <button v-if="canNavigate" :title="$t('media_modal.previous')" - class="modal-view-button-arrow modal-view-button-arrow--prev" + class="modal-view-button modal-view-button-arrow modal-view-button-arrow--prev" @click.stop.prevent="goPrev" > <FAIcon - class="arrow-icon" + class="button-icon arrow-icon" icon="chevron-left" /> </button> <button v-if="canNavigate" :title="$t('media_modal.next')" - class="modal-view-button-arrow modal-view-button-arrow--next" + class="modal-view-button modal-view-button-arrow modal-view-button-arrow--next" @click.stop.prevent="goNext" > <FAIcon - class="arrow-icon" + class="button-icon arrow-icon" icon="chevron-right" /> </button> + <button + class="modal-view-button modal-view-button-hide" + :title="$t('media_modal.hide')" + @click.stop.prevent="hide" + > + <FAIcon + class="button-icon" + icon="times" + /> + </button> + + <span + v-if="description" + class="description" + > + {{ description }} + </span> + <span + class="counter" + > + {{ $tc('media_modal.counter', currentIndex + 1, { current: currentIndex + 1, total: media.length }) }} + </span> + <span + v-if="loading" + class="loading-spinner" + > + <FAIcon + spin + icon="circle-notch" + size="5x" + /> + </span> </Modal> </template> <script src="./media_modal.js"></script> <style lang="scss"> +$modal-view-button-icon-height: 3em; +$modal-view-button-icon-half-height: calc(#{$modal-view-button-icon-height} / 2); +$modal-view-button-icon-width: 3em; +$modal-view-button-icon-margin: 0.5em; + .modal-view.media-modal-view { - z-index: 1001; + z-index: var(--ZI_media_modal); + flex-direction: column; - .modal-view-button-arrow { + .modal-view-button-arrow, + .modal-view-button-hide { opacity: 0.75; &:focus, @@ -67,69 +133,154 @@ outline: none; box-shadow: none; } + &:hover { opacity: 1; } } + overflow: hidden; } -@keyframes media-fadein { - from { - opacity: 0; +.media-modal-view { + @keyframes media-fadein { + from { + opacity: 0; + } + to { + opacity: 1; + } } - to { - opacity: 1; + + .modal-image-container { + display: flex; + overflow: hidden; + align-items: center; + flex-direction: column; + max-width: 100%; + max-height: 100%; + width: 100%; + height: 100%; + flex-grow: 1; + justify-content: center; + + &-inner { + width: 100%; + height: 100%; + flex-grow: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } } -} -.modal-image { - max-width: 90%; - max-height: 90%; - box-shadow: 0px 5px 15px 0 rgba(0, 0, 0, 0.5); - image-orientation: from-image; // NOTE: only FF supports this - animation: 0.1s cubic-bezier(0.7, 0, 1, 0.6) media-fadein; -} + .description, + .counter { + /* Hardcoded since background is also hardcoded */ + color: white; + margin-top: 1em; + text-shadow: 0 0 10px black, 0 0 10px black; + padding: 0.2em 2em; + } + + .description { + flex: 0 0 auto; + overflow-y: auto; + min-height: 1em; + max-width: 500px; + max-height: 9.5em; + word-break: break-all; + } -.modal-view-button-arrow { - position: absolute; - display: block; - top: 50%; - margin-top: -50px; - width: 70px; - height: 100px; - border: 0; - padding: 0; - opacity: 0; - box-shadow: none; - background: none; - appearance: none; - overflow: visible; - cursor: pointer; - transition: opacity 333ms cubic-bezier(.4,0,.22,1); - - .arrow-icon { + .modal-image { + max-width: 100%; + max-height: 100%; + image-orientation: from-image; // NOTE: only FF supports this + animation: 0.1s cubic-bezier(0.7, 0, 1, 0.6) media-fadein; + + &.loading { + opacity: 0.5; + } + } + + .loading-spinner { + width: 100%; + height: 100%; position: absolute; - top: 35px; - height: 30px; - width: 32px; - font-size: 14px; - line-height: 30px; - color: #FFF; - text-align: center; - background-color: rgba(0,0,0,.3); + pointer-events: none; + display: flex; + justify-content: center; + align-items: center; + + svg { + color: white; + } } - &--prev { - left: 0; + .modal-view-button { + border: 0; + padding: 0; + opacity: 0; + box-shadow: none; + background: none; + appearance: none; + overflow: visible; + cursor: pointer; + transition: opacity 333ms cubic-bezier(.4,0,.22,1); + height: $modal-view-button-icon-height; + width: $modal-view-button-icon-width; + + .button-icon { + position: absolute; + height: $modal-view-button-icon-height; + width: $modal-view-button-icon-width; + font-size: 1rem; + line-height: $modal-view-button-icon-height; + color: #FFF; + text-align: center; + background-color: rgba(0,0,0,.3); + } + } + + .modal-view-button-arrow { + position: absolute; + display: block; + top: 50%; + margin-top: $modal-view-button-icon-half-height; + width: $modal-view-button-icon-width; + height: $modal-view-button-icon-height; + .arrow-icon { - left: 6px; + position: absolute; + top: 0; + line-height: $modal-view-button-icon-height; + color: #FFF; + text-align: center; + background-color: rgba(0,0,0,.3); + } + + &--prev { + left: 0; + .arrow-icon { + left: $modal-view-button-icon-margin; + } + } + + &--next { + right: 0; + .arrow-icon { + right: $modal-view-button-icon-margin; + } } } - &--next { + .modal-view-button-hide { + position: absolute; + top: 0; right: 0; - .arrow-icon { - right: 6px; + .button-icon { + top: $modal-view-button-icon-margin; + right: $modal-view-button-icon-margin; } } } diff --git a/src/components/media_upload/media_upload.js b/src/components/media_upload/media_upload.js @@ -42,7 +42,8 @@ const mediaUpload = { .then((fileData) => { self.$emit('uploaded', fileData) self.decreaseUploadCount() - }, (error) => { // eslint-disable-line handle-callback-err + }, (error) => { + console.error('Error uploading file', error) self.$emit('upload-failed', 'default') self.decreaseUploadCount() }) @@ -73,7 +74,7 @@ const mediaUpload = { 'disabled' ], watch: { - 'dropFiles': function (fileInfos) { + dropFiles: function (fileInfos) { if (!this.uploading) { this.multiUpload(fileInfos) } diff --git a/src/components/media_upload/media_upload.vue b/src/components/media_upload/media_upload.vue @@ -17,21 +17,25 @@ /> <input v-if="uploadReady" + class="hidden-input-file" :disabled="disabled" type="file" - style="position: fixed; top: -100em" multiple="true" @change="change" > </label> </template> -<script src="./media_upload.js" ></script> +<script src="./media_upload.js"></script> <style lang="scss"> @import '../../_variables.scss'; .media-upload { - cursor: pointer; + cursor: pointer; // We use <label> for interactivity... i wonder if it's fine + + .hidden-input-file { + display: none; + } } </style> diff --git a/src/components/mention_link/mention_link.js b/src/components/mention_link/mention_link.js @@ -1,6 +1,9 @@ import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import { mapGetters, mapState } from 'vuex' import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' +import UserAvatar from '../user_avatar/user_avatar.vue' +import UnicodeDomainIndicator from '../unicode_domain_indicator/unicode_domain_indicator.vue' +import { defineAsyncComponent } from 'vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faAt @@ -12,6 +15,11 @@ library.add( const MentionLink = { name: 'MentionLink', + components: { + UserAvatar, + UnicodeDomainIndicator, + UserPopover: defineAsyncComponent(() => import('../user_popover/user_popover.vue')) + }, props: { url: { required: true, @@ -30,15 +38,30 @@ const MentionLink = { type: String } }, + data () { + return { + hasSelection: false + } + }, methods: { onClick () { + if (this.shouldShowTooltip) return const link = generateProfileLink( this.userId || this.user.id, this.userScreenName || this.user.screen_name ) this.$router.push(link) + }, + handleSelection () { + this.hasSelection = document.getSelection().containsNode(this.$refs.full, true) } }, + mounted () { + document.addEventListener('selectionchange', this.handleSelection) + }, + unmounted () { + document.removeEventListener('selectionchange', this.handleSelection) + }, computed: { user () { return this.url && this.$store && this.$store.getters.findUserByUrl(this.url) @@ -50,6 +73,10 @@ const MentionLink = { userName () { return this.user && this.userNameFullUi.split('@')[0] }, + serverName () { + // XXX assumed that domain does not contain @ + return this.user && (this.userNameFullUi.split('@')[1] || this.$store.getters.instanceDomain) + }, userNameFull () { return this.user && this.user.screen_name }, @@ -79,12 +106,44 @@ const MentionLink = { classnames () { return [ { - '-you': this.isYou, - '-highlighted': this.highlight + '-you': this.isYou && this.shouldBoldenYou, + '-highlighted': this.highlight, + '-has-selection': this.hasSelection }, this.highlightType ] }, + useAtIcon () { + return this.mergedConfig.useAtIcon + }, + isRemote () { + return this.userName !== this.userNameFull + }, + shouldShowFullUserName () { + const conf = this.mergedConfig.mentionLinkDisplay + if (conf === 'short') { + return false + } else if (conf === 'full') { + return true + } else { // full_for_remote + return this.isRemote + } + }, + shouldShowTooltip () { + return this.mergedConfig.mentionLinkShowTooltip + }, + shouldShowAvatar () { + return this.mergedConfig.mentionLinkShowAvatar + }, + shouldShowYous () { + return this.mergedConfig.mentionLinkShowYous + }, + shouldBoldenYou () { + return this.mergedConfig.mentionLinkBoldenYou + }, + shouldFadeDomain () { + return this.mergedConfig.mentionLinkFadeDomain + }, ...mapGetters(['mergedConfig']), ...mapState({ currentUser: state => state.users.currentUser diff --git a/src/components/mention_link/mention_link.scss b/src/components/mention_link/mention_link.scss @@ -1,15 +1,27 @@ +@import '../../_variables.scss'; + .MentionLink { position: relative; white-space: normal; - display: inline-block; + display: inline; color: var(--link); + word-break: normal; & .new, & .original { - display: inline-block; + display: inline; border-radius: 2px; } + .mention-avatar { + border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); + width: 1.5em; + height: 1.5em; + vertical-align: middle; + user-select: none; + margin-right: 0.2em; + } + .full { position: absolute; display: inline-block; @@ -27,7 +39,8 @@ user-select: all; } - .short { + & .short.-with-tooltip, + & .you { user-select: none; } @@ -36,19 +49,25 @@ white-space: nowrap; } + .shortName { + white-space: normal; + } + .new { &.-you { - & .shortName, - & .full { + .shortName { font-weight: 600; } } + &.-has-selection { + color: var(--alertNeutralText, $fallback--text); + background-color: var(--alertNeutral, $fallback--fg); + } .at { color: var(--link); opacity: 0.8; display: inline-block; - height: 50%; line-height: 1; padding: 0 0.1em; vertical-align: -25%; @@ -56,8 +75,7 @@ } &.-striped { - & .userName, - & .full { + & .shortName { background-image: repeating-linear-gradient( 135deg, @@ -70,22 +88,29 @@ } &.-solid { - & .userName, - & .full { + .shortName { background-image: linear-gradient(var(--____highlight-tintColor2), var(--____highlight-tintColor2)); } } &.-side { - & .userName, - & .userNameFull { + .shortName { box-shadow: 0 -5px 3px -4px inset var(--____highlight-solidColor); } } } - &:hover .new .full { - opacity: 1; - pointer-events: initial; + .full { + pointer-events: none; } + + .serverName.-faded { + color: var(--faintLink, $fallback--link); + } +} + +.mention-link-popover { + max-width: 70ch; + max-height: 20rem; + overflow: hidden; } diff --git a/src/components/mention_link/mention_link.vue b/src/components/mention_link/mention_link.vue @@ -9,48 +9,67 @@ class="original" target="_blank" v-html="content" - /> - <!-- eslint-enable vue/no-v-html --> - <span - v-if="user" - class="new" - :style="style" - :class="classnames" + /><!-- eslint-enable vue/no-v-html --> + <UserPopover + v-else + :user-id="user.id" + :disabled="!shouldShowTooltip" > - <a - class="short button-unstyled" - :href="url" - @click.prevent="onClick" - > - <!-- eslint-disable vue/no-v-html --> - <FAIcon - size="sm" - icon="at" - class="at" - /><span class="shortName"><span - class="userName" - v-html="userName" - /></span> - <span - v-if="isYou" - class="you" - >{{ $t('status.you') }}</span> - <!-- eslint-enable vue/no-v-html --> - </a> <span - v-if="userName !== userNameFull" - class="full popover-default" - :class="[highlightType]" + v-if="user" + class="new" + :style="style" + :class="classnames" > - <span - class="userNameFull" - v-text="'@' + userNameFull" - /> + <a + class="short button-unstyled" + :class="{ '-with-tooltip': shouldShowTooltip }" + :href="url" + @click.prevent="onClick" + > + <!-- eslint-disable vue/no-v-html --> + <UserAvatar + v-if="shouldShowAvatar" + class="mention-avatar" + :user="user" + /><span + class="shortName" + ><FAIcon + v-if="useAtIcon" + size="sm" + icon="at" + class="at" + />{{ !useAtIcon ? '@' : '' }}<span + class="userName" + v-html="userName" + /><span + v-if="shouldShowFullUserName" + class="serverName" + :class="{ '-faded': shouldFadeDomain }" + v-html="'@' + serverName" + /><UnicodeDomainIndicator + v-if="shouldShowFullUserName" + :user="user" + /> + </span> + <span + v-if="isYou && shouldShowYous" + :class="{ '-you': shouldBoldenYou }" + > {{ ' ' + $t('status.you') }}</span> + <!-- eslint-enable vue/no-v-html --> + </a><span + ref="full" + class="full" + > + <!-- eslint-disable vue/no-v-html --> + @<span v-html="userName" /><span v-html="'@' + serverName" /> + <!-- eslint-enable vue/no-v-html --> + </span> </span> - </span> + </UserPopover> </span> </template> -<script src="./mention_link.js"/> +<script src="./mention_link.js" /> -<style lang="scss" src="./mention_link.scss"/> +<style lang="scss" src="./mention_link.scss" /> diff --git a/src/components/mentions_line/mentions_line.scss b/src/components/mentions_line/mentions_line.scss @@ -1,11 +1,13 @@ .MentionsLine { + word-break: break-all; + + .mention-link:not(:first-child)::before { + content: ' '; + } + .showMoreLess { + margin-left: 0.5em; white-space: normal; color: var(--link); } - - .fullExtraMentions, - .mention-link:not(:last-child) { - margin-right: 0.25em; - } } diff --git a/src/components/mentions_line/mentions_line.vue b/src/components/mentions_line/mentions_line.vue @@ -6,7 +6,6 @@ class="mention-link" :content="mention.content" :url="mention.url" - :first-mention="false" /><span v-if="manyMentions" class="extraMentions" @@ -14,15 +13,13 @@ <span v-if="expanded" class="fullExtraMentions" - > - <MentionLink - v-for="mention in extraMentions" - :key="mention.index" - class="mention-link" - :content="mention.content" - :url="mention.url" - :first-mention="false" - /> + >{{ ' ' }}<MentionLink + v-for="mention in extraMentions" + :key="mention.index" + class="mention-link" + :content="mention.content" + :url="mention.url" + /> </span><button v-if="!expanded" class="button-unstyled showMoreLess" @@ -39,5 +36,5 @@ </span> </span> </template> -<script src="./mentions_line.js" ></script> +<script src="./mentions_line.js"></script> <style lang="scss" src="./mentions_line.scss" /> diff --git a/src/components/mfa_form/recovery_form.vue b/src/components/mfa_form/recovery_form.vue @@ -56,13 +56,17 @@ > <div class="alert error"> {{ error }} - <FAIcon - class="fa-scale-110 fa-old-padding" - icon="times" + <button + class="button-unstyled" @click="clearError" - /> + > + <FAIcon + class="fa-scale-110 fa-old-padding" + icon="times" + /> + </button> </div> </div> </div> </template> -<script src="./recovery_form.js" ></script> +<script src="./recovery_form.js"></script> diff --git a/src/components/mfa_form/totp_form.vue b/src/components/mfa_form/totp_form.vue @@ -58,12 +58,16 @@ > <div class="alert error"> {{ error }} - <FAIcon - size="lg" - class="fa-scale-110 fa-old-padding" - icon="times" + <button + class="button-unstyled" @click="clearError" - /> + > + <FAIcon + size="lg" + class="fa-scale-110 fa-old-padding" + icon="times" + /> + </button> </div> </div> </div> diff --git a/src/components/mobile_nav/mobile_nav.js b/src/components/mobile_nav/mobile_nav.js @@ -2,33 +2,40 @@ import SideDrawer from '../side_drawer/side_drawer.vue' import Notifications from '../notifications/notifications.vue' import { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils' import GestureService from '../../services/gesture_service/gesture_service' +import NavigationPins from 'src/components/navigation/navigation_pins.vue' import { mapGetters } from 'vuex' import { library } from '@fortawesome/fontawesome-svg-core' import { faTimes, faBell, - faBars + faBars, + faArrowUp, + faMinus } from '@fortawesome/free-solid-svg-icons' library.add( faTimes, faBell, - faBars + faBars, + faArrowUp, + faMinus ) const MobileNav = { components: { SideDrawer, - Notifications + Notifications, + NavigationPins }, data: () => ({ notificationsCloseGesture: undefined, - notificationsOpen: false + notificationsOpen: false, + notificationsAtTop: true }), created () { this.notificationsCloseGesture = GestureService.swipeGesture( GestureService.DIRECTION_RIGHT, - this.closeMobileNotifications, + () => this.closeMobileNotifications(true), 50 ) }, @@ -47,7 +54,10 @@ const MobileNav = { isChat () { return this.$route.name === 'chat' }, - ...mapGetters(['unreadChatCount']) + ...mapGetters(['unreadChatCount', 'unreadAnnouncementCount']), + chatsPinned () { + return new Set(this.$store.state.serverSideStorage.prefsStorage.collections.pinnedNavItems).has('chats') + } }, methods: { toggleMobileSidebar () { @@ -56,12 +66,14 @@ const MobileNav = { openMobileNotifications () { this.notificationsOpen = true }, - closeMobileNotifications () { + closeMobileNotifications (markRead) { if (this.notificationsOpen) { // make sure to mark notifs seen only when the notifs were open and not // from close-calls. this.notificationsOpen = false - this.markNotificationsAsSeen() + if (markRead) { + this.markNotificationsAsSeen() + } } }, notificationsTouchStart (e) { @@ -73,14 +85,19 @@ const MobileNav = { scrollToTop () { window.scrollTo(0, 0) }, + scrollMobileNotificationsToTop () { + this.$refs.mobileNotifications.scrollTo(0, 0) + }, logout () { this.$router.replace('/main/public') this.$store.dispatch('logout') }, markNotificationsAsSeen () { - this.$refs.notifications.markAsSeen() + // this.$refs.notifications.markAsSeen() + this.$store.dispatch('markNotificationsAsSeen') }, onScroll ({ target: { scrollTop, clientHeight, scrollHeight } }) { + this.notificationsAtTop = scrollTop > 0 if (scrollTop + clientHeight >= scrollHeight) { this.$refs.notifications.fetchOlderNotifications() } diff --git a/src/components/mobile_nav/mobile_nav.vue b/src/components/mobile_nav/mobile_nav.vue @@ -5,12 +5,13 @@ <nav id="nav" class="mobile-nav" - :class="{ 'mobile-hidden': isChat }" @click="scrollToTop()" > <div class="item"> <button class="button-unstyled mobile-nav-button" + :title="$t('nav.mobile_sidebar')" + :aria-expanaded="$refs.sideDrawer && !$refs.sideDrawer.closed" @click.stop.prevent="toggleMobileSidebar()" > <FAIcon @@ -18,23 +19,16 @@ icon="bars" /> <div - v-if="unreadChatCount" + v-if="(unreadChatCount && !chatsPinned) || unreadAnnouncementCount" class="alert-dot" /> </button> - <router-link - v-if="!hideSitename" - class="site-name" - :to="{ name: 'root' }" - active-class="home" - > - {{ sitename }} - </router-link> - </div> - <div class="item right"> + <NavigationPins class="pins" /> + </div> <div class="item right"> <button v-if="currentUser" class="button-unstyled mobile-nav-button" + :title="unseenNotificationsCount ? $t('nav.mobile_notifications_unread_active') : $t('nav.mobile_notifications')" @click.stop.prevent="openMobileNotifications()" > <FAIcon @@ -48,35 +42,48 @@ </button> </div> </nav> - <div + <aside v-if="currentUser" class="mobile-notifications-drawer" - :class="{ 'closed': !notificationsOpen }" + :class="{ '-closed': !notificationsOpen }" @touchstart.stop="notificationsTouchStart" @touchmove.stop="notificationsTouchMove" > <div class="mobile-notifications-header"> <span class="title">{{ $t('notifications.notifications') }}</span> - <a - class="mobile-nav-button" - @click.stop.prevent="closeMobileNotifications()" + <span class="spacer" /> + <button + v-if="notificationsAtTop" + class="button-unstyled mobile-nav-button" + :title="$t('general.scroll_to_top')" + @click.stop.prevent="scrollMobileNotificationsToTop" + > + <FALayers class="fa-scale-110 fa-old-padding-layer"> + <FAIcon icon="arrow-up" /> + <FAIcon + icon="minus" + transform="up-7" + /> + </FALayers> + </button> + <button + class="button-unstyled mobile-nav-button" + :title="$t('nav.mobile_notifications_close')" + @click.stop.prevent="closeMobileNotifications(true)" > <FAIcon class="fa-scale-110 fa-old-padding" icon="times" /> - </a> + </button> </div> <div + id="mobile-notifications" + ref="mobileNotifications" class="mobile-notifications" @scroll="onScroll" - > - <Notifications - ref="notifications" - :no-heading="true" - /> - </div> - </div> + /> + </aside> <SideDrawer ref="sideDrawer" :logout="logout" @@ -90,15 +97,19 @@ @import '../../_variables.scss'; .MobileNav { + z-index: var(--ZI_navbar); + .mobile-nav { display: grid; - line-height: 50px; - height: 50px; + line-height: var(--navbar-height); grid-template-rows: 50px; grid-template-columns: 2fr auto; width: 100%; - position: fixed; box-sizing: border-box; + + a { + color: var(--topBarLink, $fallback--link); + } } .mobile-inner-nav { @@ -150,11 +161,12 @@ transition-property: transform; transition-duration: 0.25s; transform: translateX(0); - z-index: 1001; + z-index: var(--ZI_navbar); -webkit-overflow-scrolling: touch; - &.closed { + &.-closed { transform: translateX(100%); + box-shadow: none; } } @@ -162,7 +174,7 @@ display: flex; align-items: center; justify-content: space-between; - z-index: 1; + z-index: calc(var(--ZI_navbar) + 100); width: 100%; height: 50px; line-height: 50px; @@ -173,19 +185,30 @@ box-shadow: 0px 0px 4px rgba(0,0,0,.6); box-shadow: var(--topBarShadow); + .spacer { + flex: 1; + } + .title { font-size: 1.3em; margin-left: 0.6em; } } + .pins { + flex: 1; + + .pinned-item { + flex-grow: 1; + } + } + .mobile-notifications { margin-top: 50px; width: 100vw; - height: calc(100vh - 50px); + height: calc(100vh - var(--navbar-height)); overflow-x: hidden; overflow-y: scroll; - color: $fallback--text; color: var(--text, $fallback--text); background-color: $fallback--bg; @@ -195,14 +218,17 @@ padding: 0; border-radius: 0; box-shadow: none; + .panel { border-radius: 0; margin: 0; box-shadow: none; } - .panel:after { + + .panel::after { border-radius: 0; } + .panel .panel-heading { border-radius: 0; box-shadow: none; diff --git a/src/components/mobile_post_status_button/mobile_post_status_button.js b/src/components/mobile_post_status_button/mobile_post_status_button.js @@ -10,7 +10,8 @@ library.add( const HIDDEN_FOR_PAGES = new Set([ 'chats', - 'chat' + 'chat', + 'lists-edit' ]) const MobilePostStatusButton = { @@ -29,7 +30,7 @@ const MobilePostStatusButton = { } window.addEventListener('resize', this.handleOSK) }, - destroyed () { + unmounted () { if (this.autohideFloatingPostButton) { this.deactivateFloatingPostButtonAutohide() } @@ -45,7 +46,7 @@ const MobilePostStatusButton = { return this.autohideFloatingPostButton && (this.hidden || this.inputActive) }, isPersistent () { - return !!this.$store.getters.mergedConfig.showNewPostButton + return !!this.$store.getters.mergedConfig.alwaysShowNewPostButton }, autohideFloatingPostButton () { return !!this.$store.getters.mergedConfig.autohideFloatingPostButton diff --git a/src/components/mobile_post_status_button/mobile_post_status_button.vue b/src/components/mobile_post_status_button/mobile_post_status_button.vue @@ -1,13 +1,13 @@ <template> - <div v-if="isLoggedIn"> - <button - class="button-default new-status-button" - :class="{ 'hidden': isHidden, 'always-show': isPersistent }" - @click="openPostForm" - > - <FAIcon icon="pen" /> - </button> - </div> + <button + v-if="isLoggedIn" + class="MobilePostButton button-default new-status-button" + :class="{ 'hidden': isHidden, 'always-show': isPersistent }" + :title="$t('post_status.new_status')" + @click="openPostForm" + > + <FAIcon icon="pen" /> + </button> </template> <script src="./mobile_post_status_button.js"></script> @@ -15,25 +15,27 @@ <style lang="scss"> @import '../../_variables.scss'; -.new-status-button { - width: 5em; - height: 5em; - border-radius: 100%; - position: fixed; - bottom: 1.5em; - right: 1.5em; - // TODO: this needs its own color, it has to stand out enough and link color - // is not very optimal for this particular use. - background-color: $fallback--fg; - background-color: var(--btn, $fallback--fg); - display: flex; - justify-content: center; - align-items: center; - box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3), 0px 4px 6px rgba(0, 0, 0, 0.3); - z-index: 10; - - transition: 0.35s transform; - transition-timing-function: cubic-bezier(0, 1, 0.5, 1); +.MobilePostButton { + &.button-default { + width: 5em; + height: 5em; + border-radius: 100%; + position: fixed; + bottom: 1.5em; + right: 1.5em; + // TODO: this needs its own color, it has to stand out enough and link color + // is not very optimal for this particular use. + background-color: $fallback--fg; + background-color: var(--btn, $fallback--fg); + display: flex; + justify-content: center; + align-items: center; + box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3), 0px 4px 6px rgba(0, 0, 0, 0.3); + z-index: 10; + + transition: 0.35s transform; + transition-timing-function: cubic-bezier(0, 1, 0.5, 1); + } &.hidden { transform: translateY(150%); diff --git a/src/components/modal/modal.vue b/src/components/modal/modal.vue @@ -12,6 +12,9 @@ <script> export default { + provide: { + popoversZLayer: 'modals' + }, props: { isOpen: { type: Boolean, @@ -26,7 +29,7 @@ export default { classes () { return { 'modal-background': !this.noBackground, - 'open': this.isOpen + open: this.isOpen } } } @@ -35,7 +38,7 @@ export default { <style lang="scss"> .modal-view { - z-index: 1000; + z-index: var(--ZI_modals); position: fixed; top: 0; left: 0; diff --git a/src/components/moderation_tools/moderation_tools.js b/src/components/moderation_tools/moderation_tools.js @@ -41,14 +41,26 @@ const ModerationTools = { tagsSet () { return new Set(this.user.tags) }, - hasTagPolicy () { - return this.$store.state.instance.tagPolicyAvailable + canGrantRole () { + return this.user.is_local && !this.user.deactivated && this.$store.state.users.currentUser.role === 'admin' + }, + canChangeActivationState () { + return this.privileged('users_manage_activation_state') + }, + canDeleteAccount () { + return this.privileged('users_delete') + }, + canUseTagPolicy () { + return this.$store.state.instance.tagPolicyAvailable && this.privileged('users_manage_tags') } }, methods: { hasTag (tagName) { return this.tagsSet.has(tagName) }, + privileged (privilege) { + return this.$store.state.users.currentUser.privileges.includes(privilege) + }, toggleTag (tag) { const store = this.$store if (this.tagsSet.has(tag)) { diff --git a/src/components/moderation_tools/moderation_tools.vue b/src/components/moderation_tools/moderation_tools.vue @@ -8,9 +8,9 @@ @show="setToggled(true)" @close="setToggled(false)" > - <template v-slot:content> + <template #content> <div class="dropdown-menu"> - <span v-if="user.is_local"> + <span v-if="canGrantRole"> <button class="button-default dropdown-item" @click="toggleRight(&quot;admin&quot;)" @@ -24,28 +24,31 @@ {{ $t(!!user.rights.moderator ? 'user_card.admin_menu.revoke_moderator' : 'user_card.admin_menu.grant_moderator') }} </button> <div + v-if="canChangeActivationState || canDeleteAccount" role="separator" class="dropdown-divider" /> </span> <button + v-if="canChangeActivationState" class="button-default dropdown-item" @click="toggleActivationStatus()" > {{ $t(!!user.deactivated ? 'user_card.admin_menu.activate_account' : 'user_card.admin_menu.deactivate_account') }} </button> <button + v-if="canDeleteAccount" class="button-default dropdown-item" @click="deleteUserDialog(true)" > {{ $t('user_card.admin_menu.delete_account') }} </button> <div - v-if="hasTagPolicy" + v-if="canUseTagPolicy" role="separator" class="dropdown-divider" /> - <span v-if="hasTagPolicy"> + <span v-if="canUseTagPolicy"> <button class="button-default dropdown-item" @click="toggleTag(tags.FORCE_NSFW)" @@ -122,7 +125,7 @@ </span> </div> </template> - <template v-slot:trigger> + <template #trigger> <button class="btn button-default btn-block moderation-tools-button" :class="{ toggled }" @@ -132,16 +135,16 @@ </button> </template> </Popover> - <portal to="modal"> + <teleport to="#modal"> <DialogModal v-if="showDeleteUserDialog" :on-cancel="deleteUserDialog.bind(this, false)" > - <template v-slot:header> + <template #header> {{ $t('user_card.admin_menu.delete_user') }} </template> <p>{{ $t('user_card.admin_menu.delete_user_confirmation') }}</p> - <template v-slot:footer> + <template #footer> <button class="btn button-default" @click="deleteUserDialog(false)" @@ -156,7 +159,7 @@ </button> </template> </DialogModal> - </portal> + </teleport> </div> </template> diff --git a/src/components/mrf_transparency_panel/mrf_transparency_panel.js b/src/components/mrf_transparency_panel/mrf_transparency_panel.js @@ -9,10 +9,10 @@ import { get } from 'lodash' */ const toInstanceReasonObject = (instances, info, key) => { return instances.map(instance => { - if (info[key] && info[key][instance] && info[key][instance]['reason']) { - return { instance: instance, reason: info[key][instance]['reason'] } + if (info[key] && info[key][instance] && info[key][instance].reason) { + return { instance, reason: info[key][instance].reason } } - return { instance: instance, reason: '' } + return { instance, reason: '' } }) } diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js @@ -1,5 +1,10 @@ -import TimelineMenuContent from '../timeline_menu/timeline_menu_content.vue' +import ListsMenuContent from 'src/components/lists_menu/lists_menu_content.vue' import { mapState, mapGetters } from 'vuex' +import { TIMELINES, ROOT_ITEMS } from 'src/components/navigation/navigation.js' +import { filterNavigation } from 'src/components/navigation/filter.js' +import NavigationEntry from 'src/components/navigation/navigation_entry.vue' +import NavigationPins from 'src/components/navigation/navigation_pins.vue' +import Checkbox from 'src/components/checkbox/checkbox.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -12,7 +17,9 @@ import { faComments, faBell, faInfoCircle, - faStream + faStream, + faList, + faBullhorn } from '@fortawesome/free-solid-svg-icons' library.add( @@ -25,26 +32,53 @@ library.add( faComments, faBell, faInfoCircle, - faStream + faStream, + faList, + faBullhorn ) - const NavPanel = { + props: ['forceExpand', 'forceEditMode'], created () { - if (this.currentUser && this.currentUser.locked) { - this.$store.dispatch('startFetchingFollowRequests') - } }, components: { - TimelineMenuContent + ListsMenuContent, + NavigationEntry, + NavigationPins, + Checkbox }, data () { return { - showTimelines: false + editMode: false, + showTimelines: false, + showLists: false, + timelinesList: Object.entries(TIMELINES).map(([k, v]) => ({ ...v, name: k })), + rootList: Object.entries(ROOT_ITEMS).map(([k, v]) => ({ ...v, name: k })) } }, methods: { toggleTimelines () { this.showTimelines = !this.showTimelines + }, + toggleLists () { + this.showLists = !this.showLists + }, + toggleEditMode () { + this.editMode = !this.editMode + }, + toggleCollapse () { + this.$store.commit('setPreference', { path: 'simple.collapseNav', value: !this.collapsed }) + this.$store.dispatch('pushServerSideStorage') + }, + isPinned (item) { + return this.pinnedItems.has(item) + }, + togglePin (item) { + if (this.isPinned(item)) { + this.$store.commit('removeCollectionPreference', { path: 'collections.pinnedNavItems', value: item }) + } else { + this.$store.commit('addCollectionPreference', { path: 'collections.pinnedNavItems', value: item }) + } + this.$store.dispatch('pushServerSideStorage') } }, computed: { @@ -53,9 +87,40 @@ const NavPanel = { followRequestCount: state => state.api.followRequests.length, privateMode: state => state.instance.private, federating: state => state.instance.federating, - pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable + pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable, + supportsAnnouncements: state => state.announcements.supportsAnnouncements, + pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems), + collapsed: state => state.serverSideStorage.prefsStorage.simple.collapseNav }), - ...mapGetters(['unreadChatCount']) + timelinesItems () { + return filterNavigation( + Object + .entries({ ...TIMELINES }) + .map(([k, v]) => ({ ...v, name: k })), + { + hasChats: this.pleromaChatMessagesAvailable, + hasAnnouncements: this.supportsAnnouncements, + isFederating: this.federating, + isPrivate: this.privateMode, + currentUser: this.currentUser + } + ) + }, + rootItems () { + return filterNavigation( + Object + .entries({ ...ROOT_ITEMS }) + .map(([k, v]) => ({ ...v, name: k })), + { + hasChats: this.pleromaChatMessagesAvailable, + hasAnnouncements: this.supportsAnnouncements, + isFederating: this.federating, + isPrivate: this.privateMode, + currentUser: this.currentUser + } + ) + }, + ...mapGetters(['unreadChatCount', 'unreadAnnouncementCount']) } } diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue @@ -1,96 +1,105 @@ <template> <div class="NavPanel"> <div class="panel panel-default"> - <ul> - <li v-if="currentUser || !privateMode"> - <button - class="button-unstyled menu-item" - @click="toggleTimelines" - > - <FAIcon - fixed-width - class="fa-scale-110" - icon="stream" - />{{ $t("nav.timelines") }} - <FAIcon - class="timelines-chevron" - fixed-width - :icon="showTimelines ? 'chevron-up' : 'chevron-down'" + <div + v-if="!forceExpand" + class="panel-heading nav-panel-heading" + > + <NavigationPins :limit="6" /> + <div class="spacer" /> + <button + class="button-unstyled" + @click="toggleCollapse" + > + <FAIcon + class="navigation-chevron" + fixed-width + :icon="collapsed ? 'chevron-down' : 'chevron-up'" + /> + </button> + </div> + <ul + v-if="!collapsed || forceExpand" + class="panel-body" + > + <NavigationEntry + v-if="currentUser || !privateMode" + :show-pin="false" + :item="{ icon: 'stream', label: 'nav.timelines' }" + :aria-expanded="showTimelines ? 'true' : 'false'" + @click="toggleTimelines" + > + <FAIcon + class="timelines-chevron" + fixed-width + :icon="showTimelines ? 'chevron-up' : 'chevron-down'" + /> + </NavigationEntry> + <div + v-show="showTimelines" + class="timelines-background" + > + <div class="timelines"> + <NavigationEntry + v-for="item in timelinesItems" + :key="item.name" + :show-pin="editMode || forceEditMode" + :item="item" /> - </button> - <div - v-show="showTimelines" - class="timelines-background" - > - <TimelineMenuContent class="timelines" /> </div> - </li> - <li v-if="currentUser"> - <router-link - class="menu-item" - :to="{ name: 'interactions', params: { username: currentUser.screen_name } }" - > - <FAIcon - fixed-width - class="fa-scale-110" - icon="bell" - />{{ $t("nav.interactions") }} - </router-link> - </li> - <li v-if="currentUser && pleromaChatMessagesAvailable"> - <router-link - class="menu-item" - :to="{ name: 'chats', params: { username: currentUser.screen_name } }" - > - <div - v-if="unreadChatCount" - class="badge badge-notification" - > - {{ unreadChatCount }} - </div> - <FAIcon - fixed-width - class="fa-scale-110" - icon="comments" - />{{ $t("nav.chats") }} - </router-link> - </li> - <li v-if="currentUser && currentUser.locked"> - <router-link - class="menu-item" - :to="{ name: 'friend-requests' }" - > - <FAIcon - fixed-width - class="fa-scale-110" - icon="user-plus" - />{{ $t("nav.friend_requests") }} - <span - v-if="followRequestCount > 0" - class="badge badge-notification" - > - {{ followRequestCount }} - </span> - </router-link> - </li> - <li> + </div> + <NavigationEntry + v-if="currentUser" + :show-pin="false" + :item="{ icon: 'list', label: 'nav.lists' }" + :aria-expanded="showLists ? 'true' : 'false'" + @click="toggleLists" + > <router-link - class="menu-item" - :to="{ name: 'about' }" + :title="$t('lists.manage_lists')" + class="extra-button" + :to="{ name: 'lists' }" + @click.stop > <FAIcon + class="extra-button" fixed-width - class="fa-scale-110" - icon="info-circle" - />{{ $t("nav.about") }} + icon="wrench" + /> </router-link> - </li> + <FAIcon + class="timelines-chevron" + fixed-width + :icon="showLists ? 'chevron-up' : 'chevron-down'" + /> + </NavigationEntry> + <div + v-show="showLists" + class="timelines-background" + > + <ListsMenuContent + :show-pin="editMode || forceEditMode" + class="timelines" + /> + </div> + <NavigationEntry + v-for="item in rootItems" + :key="item.name" + :show-pin="editMode || forceEditMode" + :item="item" + /> + <NavigationEntry + v-if="!forceEditMode && currentUser" + :show-pin="false" + :item="{ label: editMode ? $t('nav.edit_finish') : $t('nav.edit_pinned'), icon: editMode ? 'check' : 'wrench' }" + @click="toggleEditMode" + /> </ul> </div> </div> </template> -<script src="./nav_panel.js" ></script> +<script src="./nav_panel.js"></script> <style lang="scss"> @import '../../_variables.scss'; @@ -112,8 +121,9 @@ border-bottom: 1px solid; border-color: $fallback--border; border-color: var(--border, $fallback--border); - padding: 0; + } + > li { &:first-child .menu-item { border-top-right-radius: $fallback--panelRadius; border-top-right-radius: var(--panelRadius, $fallback--panelRadius); @@ -133,42 +143,10 @@ border: none; } - .menu-item { - display: block; - box-sizing: border-box; - height: 3.5em; - line-height: 3.5em; - padding: 0 1em; - width: 100%; - color: $fallback--link; - color: var(--link, $fallback--link); - - &:hover { - background-color: $fallback--lightBg; - background-color: var(--selectedMenu, $fallback--lightBg); - color: $fallback--link; - color: var(--selectedMenuText, $fallback--link); - --faint: var(--selectedMenuFaintText, $fallback--faint); - --faintLink: var(--selectedMenuFaintLink, $fallback--faint); - --lightText: var(--selectedMenuLightText, $fallback--lightText); - --icon: var(--selectedMenuIcon, $fallback--icon); - } - - &.router-link-active { - font-weight: bolder; - background-color: $fallback--lightBg; - background-color: var(--selectedMenu, $fallback--lightBg); - color: $fallback--text; - color: var(--selectedMenuText, $fallback--text); - --faint: var(--selectedMenuFaintText, $fallback--faint); - --faintLink: var(--selectedMenuFaintLink, $fallback--faint); - --lightText: var(--selectedMenuLightText, $fallback--lightText); - --icon: var(--selectedMenuIcon, $fallback--icon); - - &:hover { - text-decoration: underline; - } - } + .navigation-chevron { + margin-left: 0.8em; + margin-right: 0.8em; + font-size: 1.1em; } .timelines-chevron { @@ -180,7 +158,7 @@ padding: 0 0 0 0.6em; background-color: $fallback--lightBg; background-color: var(--selectedMenu, $fallback--lightBg); - border-top: 1px solid; + border-bottom: 1px solid; border-color: $fallback--border; border-color: var(--border, $fallback--border); } @@ -190,14 +168,9 @@ background-color: var(--bg, $fallback--bg); } - .fa-scale-110 { - margin-right: 0.8em; - } - - .badge { - position: absolute; - right: 0.6rem; - top: 1.25em; + .nav-panel-heading { + // breaks without a unit + --panel-heading-height-padding: 0em; } } </style> diff --git a/src/components/navigation/filter.js b/src/components/navigation/filter.js @@ -0,0 +1,19 @@ +export const filterNavigation = (list = [], { hasChats, hasAnnouncements, isFederating, isPrivate, currentUser }) => { + return list.filter(({ criteria, anon, anonRoute }) => { + const set = new Set(criteria || []) + if (!isFederating && set.has('federating')) return false + if (!currentUser && isPrivate && set.has('!private')) return false + if (!currentUser && !(anon || anonRoute)) return false + if ((!currentUser || !currentUser.locked) && set.has('lockedUser')) return false + if (!hasChats && set.has('chats')) return false + if (!hasAnnouncements && set.has('announcements')) return false + return true + }) +} + +export const getListEntries = state => state.lists.allLists.map(list => ({ + name: 'list-' + list.id, + routeObject: { name: 'lists-timeline', params: { id: list.id } }, + labelRaw: list.title, + iconLetter: list.title[0] +})) diff --git a/src/components/navigation/navigation.js b/src/components/navigation/navigation.js @@ -0,0 +1,82 @@ +export const USERNAME_ROUTES = new Set([ + 'bookmarks', + 'dms', + 'interactions', + 'notifications', + 'chat', + 'chats', + 'user-profile' +]) + +export const TIMELINES = { + home: { + route: 'friends', + icon: 'home', + label: 'nav.home_timeline', + criteria: ['!private'] + }, + public: { + route: 'public-timeline', + anon: true, + icon: 'users', + label: 'nav.public_tl', + criteria: ['!private'] + }, + twkn: { + route: 'public-external-timeline', + anon: true, + icon: 'globe', + label: 'nav.twkn', + criteria: ['!private', 'federating'] + }, + bookmarks: { + route: 'bookmarks', + icon: 'bookmark', + label: 'nav.bookmarks' + }, + favorites: { + routeObject: { name: 'user-profile', query: { tab: 'favorites' } }, + icon: 'star', + label: 'user_card.favorites' + }, + dms: { + route: 'dms', + icon: 'envelope', + label: 'nav.dms' + } +} + +export const ROOT_ITEMS = { + interactions: { + route: 'interactions', + icon: 'bell', + label: 'nav.interactions' + }, + chats: { + route: 'chats', + icon: 'comments', + label: 'nav.chats', + badgeGetter: 'unreadChatCount', + criteria: ['chats'] + }, + friendRequests: { + route: 'friend-requests', + icon: 'user-plus', + label: 'nav.friend_requests', + criteria: ['lockedUser'], + badgeGetter: 'followRequestCount' + }, + about: { + route: 'about', + anon: true, + icon: 'info-circle', + label: 'nav.about' + }, + announcements: { + route: 'announcements', + icon: 'bullhorn', + label: 'nav.announcements', + badgeGetter: 'unreadAnnouncementCount', + criteria: ['announcements'] + } +} diff --git a/src/components/navigation/navigation_entry.js b/src/components/navigation/navigation_entry.js @@ -0,0 +1,51 @@ +import { mapState } from 'vuex' +import { USERNAME_ROUTES } from 'src/components/navigation/navigation.js' +import OptionalRouterLink from 'src/components/optional_router_link/optional_router_link.vue' +import { library } from '@fortawesome/fontawesome-svg-core' +import { faThumbtack } from '@fortawesome/free-solid-svg-icons' + +library.add(faThumbtack) + +const NavigationEntry = { + props: ['item', 'showPin'], + components: { + OptionalRouterLink + }, + methods: { + isPinned (value) { + return this.pinnedItems.has(value) + }, + togglePin (value) { + if (this.isPinned(value)) { + this.$store.commit('removeCollectionPreference', { path: 'collections.pinnedNavItems', value }) + } else { + this.$store.commit('addCollectionPreference', { path: 'collections.pinnedNavItems', value }) + } + this.$store.dispatch('pushServerSideStorage') + } + }, + computed: { + routeTo () { + if (!this.item.route && !this.item.routeObject) return null + let route + if (this.item.routeObject) { + route = this.item.routeObject + } else { + route = { name: (this.item.anon || this.currentUser) ? this.item.route : this.item.anonRoute } + } + if (USERNAME_ROUTES.has(route.name)) { + route.params = { username: this.currentUser.screen_name, name: this.currentUser.screen_name } + } + return route + }, + getters () { + return this.$store.getters + }, + ...mapState({ + currentUser: state => state.users.currentUser, + pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems) + }) + } +} + +export default NavigationEntry diff --git a/src/components/navigation/navigation_entry.vue b/src/components/navigation/navigation_entry.vue @@ -0,0 +1,133 @@ +<template> + <OptionalRouterLink + v-slot="{ isActive, href, navigate } = {}" + ass="ass" + :to="routeTo" + > + <li + class="NavigationEntry menu-item" + :class="{ '-active': isActive }" + v-bind="$attrs" + > + <component + :is="routeTo ? 'a' : 'button'" + class="main-link button-unstyled" + :href="href" + @click="navigate" + > + <span> + <FAIcon + v-if="item.icon" + fixed-width + class="fa-scale-110 menu-icon" + :icon="item.icon" + /> + </span> + <span + v-if="item.iconLetter" + class="icon iconLetter fa-scale-110 menu-icon" + >{{ item.iconLetter }} + </span> + <span class="label"> + {{ item.labelRaw || $t(item.label) }} + </span> + </component> + <slot /> + <div + v-if="item.badgeGetter && getters[item.badgeGetter]" + class="badge badge-notification" + > + {{ getters[item.badgeGetter] }} + </div> + <button + v-if="showPin && currentUser" + type="button" + class="button-unstyled extra-button" + :title="$t(isPinned ? 'general.unpin' : 'general.pin' )" + :aria-pressed="!!isPinned" + @click.stop.prevent="togglePin(item.name)" + > + <FAIcon + v-if="showPin && currentUser" + fixed-width + class="fa-scale-110" + :class="{ 'veryfaint': !isPinned(item.name) }" + :transform="!isPinned(item.name) ? 'rotate-45' : ''" + icon="thumbtack" + /> + </button> + </li> + </OptionalRouterLink> +</template> + +<script src="./navigation_entry.js"></script> + +<style lang="scss"> +@import '../../_variables.scss'; + +.NavigationEntry { + display: flex; + box-sizing: border-box; + align-items: baseline; + height: 3.5em; + line-height: 3.5em; + padding: 0 1em; + width: 100%; + color: $fallback--link; + color: var(--link, $fallback--link); + + .timelines-chevron { + margin-right: 0; + } + + .main-link { + flex: 1; + } + + .menu-icon { + margin-right: 0.8em; + } + + .extra-button { + width: 3em; + text-align: center; + + &:last-child { + margin-right: -0.8em; + } + } + + &:hover { + background-color: $fallback--lightBg; + background-color: var(--selectedMenu, $fallback--lightBg); + color: $fallback--link; + color: var(--selectedMenuText, $fallback--link); + --faint: var(--selectedMenuFaintText, $fallback--faint); + --faintLink: var(--selectedMenuFaintLink, $fallback--faint); + --lightText: var(--selectedMenuLightText, $fallback--lightText); + + .menu-icon { + --icon: var(--text, $fallback--icon); + } + } + + &.-active { + font-weight: bolder; + background-color: $fallback--lightBg; + background-color: var(--selectedMenu, $fallback--lightBg); + color: $fallback--text; + color: var(--selectedMenuText, $fallback--text); + --faint: var(--selectedMenuFaintText, $fallback--faint); + --faintLink: var(--selectedMenuFaintLink, $fallback--faint); + --lightText: var(--selectedMenuLightText, $fallback--lightText); + + .menu-icon { + --icon: var(--text, $fallback--icon); + } + + &:hover { + text-decoration: underline; + } + } +} +</style> diff --git a/src/components/navigation/navigation_pins.js b/src/components/navigation/navigation_pins.js @@ -0,0 +1,94 @@ +import { mapState } from 'vuex' +import { TIMELINES, ROOT_ITEMS, USERNAME_ROUTES } from 'src/components/navigation/navigation.js' +import { getListEntries, filterNavigation } from 'src/components/navigation/filter.js' + +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faUsers, + faGlobe, + faBookmark, + faEnvelope, + faComments, + faBell, + faInfoCircle, + faStream, + faList +} from '@fortawesome/free-solid-svg-icons' + +library.add( + faUsers, + faGlobe, + faBookmark, + faEnvelope, + faComments, + faBell, + faInfoCircle, + faStream, + faList +) + +const NavPanel = { + props: ['limit'], + methods: { + getRouteTo (item) { + if (item.routeObject) { + return item.routeObject + } + const route = { name: (item.anon || this.currentUser) ? item.route : item.anonRoute } + if (USERNAME_ROUTES.has(route.name)) { + route.params = { username: this.currentUser.screen_name } + } + return route + } + }, + computed: { + getters () { + return this.$store.getters + }, + ...mapState({ + lists: getListEntries, + currentUser: state => state.users.currentUser, + followRequestCount: state => state.api.followRequests.length, + privateMode: state => state.instance.private, + federating: state => state.instance.federating, + pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable, + pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems) + }), + pinnedList () { + if (!this.currentUser) { + return filterNavigation([ + { ...TIMELINES.public, name: 'public' }, + { ...TIMELINES.twkn, name: 'twkn' }, + { ...ROOT_ITEMS.about, name: 'about' } + ], + { + hasChats: this.pleromaChatMessagesAvailable, + isFederating: this.federating, + isPrivate: this.privateMode, + currentUser: this.currentUser + }) + } + return filterNavigation( + [ + ...Object + .entries({ ...TIMELINES }) + .filter(([k]) => this.pinnedItems.has(k)) + .map(([k, v]) => ({ ...v, name: k })), + ...this.lists.filter((k) => this.pinnedItems.has(k.name)), + ...Object + .entries({ ...ROOT_ITEMS }) + .filter(([k]) => this.pinnedItems.has(k)) + .map(([k, v]) => ({ ...v, name: k })) + ], + { + hasChats: this.pleromaChatMessagesAvailable, + isFederating: this.federating, + isPrivate: this.privateMode, + currentUser: this.currentUser + } + ).slice(0, this.limit) + } + } +} + +export default NavPanel diff --git a/src/components/navigation/navigation_pins.vue b/src/components/navigation/navigation_pins.vue @@ -0,0 +1,74 @@ +<template> + <span class="NavigationPins"> + <router-link + v-for="item in pinnedList" + :key="item.name" + class="pinned-item" + :to="getRouteTo(item)" + :title="item.labelRaw || $t(item.label)" + > + <FAIcon + v-if="item.icon" + fixed-width + :icon="item.icon" + /> + <span + v-if="item.iconLetter" + class="iconLetter fa-scale-110 fa-old-padding" + >{{ item.iconLetter }}</span> + <div + v-if="item.badgeGetter && getters[item.badgeGetter]" + class="alert-dot" + /> + </router-link> + </span> +</template> + +<script src="./navigation_pins.js"></script> + +<style lang="scss"> +@import '../../_variables.scss'; +.NavigationPins { + display: flex; + flex-wrap: wrap; + overflow: hidden; + height: 100%; + + .alert-dot { + border-radius: 100%; + height: 0.5em; + width: 0.5em; + position: absolute; + right: calc(50% - 0.75em); + top: calc(50% - 0.5em); + background-color: $fallback--cRed; + background-color: var(--badgeNotification, $fallback--cRed); + } + + .pinned-item { + position: relative; + flex: 1 0 3em; + min-width: 2em; + text-align: center; + overflow: visible; + box-sizing: border-box; + height: 100%; + + & .svg-inline--fa, + & .iconLetter { + margin: 0; + } + + &.router-link-active { + color: $fallback--text; + color: var(--panelText, $fallback--text); + border-bottom: 4px solid; + + & .svg-inline--fa, + & .iconLetter { + color: inherit; + } + } + } +} +</style> diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js @@ -4,7 +4,10 @@ import Status from '../status/status.vue' import UserAvatar from '../user_avatar/user_avatar.vue' import UserCard from '../user_card/user_card.vue' import Timeago from '../timeago/timeago.vue' +import Report from '../report/report.vue' +import UserLink from '../user_link/user_link.vue' import RichContent from 'src/components/rich_content/rich_content.jsx' +import UserPopover from '../user_popover/user_popover.vue' import { isStatusNotification } from '../../services/notification_utils/notification_utils.js' import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' @@ -17,7 +20,9 @@ import { faUserPlus, faEyeSlash, faUser, - faSuitcaseRolling + faSuitcaseRolling, + faExpandAlt, + faCompressAlt } from '@fortawesome/free-solid-svg-icons' library.add( @@ -28,29 +33,34 @@ library.add( faUserPlus, faUser, faEyeSlash, - faSuitcaseRolling + faSuitcaseRolling, + faExpandAlt, + faCompressAlt ) const Notification = { data () { return { - userExpanded: false, + statusExpanded: false, betterShadow: this.$store.state.interface.browserSupport.cssFilter, unmuted: false } }, - props: [ 'notification' ], + props: ['notification'], components: { StatusContent, UserAvatar, UserCard, Timeago, Status, - RichContent + Report, + RichContent, + UserPopover, + UserLink }, methods: { - toggleUserExpanded () { - this.userExpanded = !this.userExpanded + toggleStatusExpanded () { + this.statusExpanded = !this.statusExpanded }, generateUserProfileLink (user) { return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames) diff --git a/src/components/notification/notification.scss b/src/components/notification/notification.scss @@ -2,7 +2,18 @@ // TODO Copypaste from Status, should unify it somehow .Notification { - --emoji-size: 14px; + border-bottom: 1px solid; + border-color: $fallback--border; + border-color: var(--border, $fallback--border); + word-wrap: break-word; + word-break: break-word; + --emoji-size: 14px; + + &:hover { + --_still-image-img-visibility: visible; + --_still-image-canvas-visibility: hidden; + --_still-image-label-visibility: hidden; + } &.-muted { padding: 0.25em 0.6em; diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue @@ -1,18 +1,23 @@ <template> - <Status + <article v-if="notification.type === 'mention'" - :compact="true" - :statusoid="notification.status" - /> - <div v-else> + > + <Status + class="Notification" + :compact="true" + :statusoid="notification.status" + /> + </article> + <article v-else> <div v-if="needMute && !unmuted" class="Notification container -muted" > <small> - <router-link :to="userProfileLink"> - {{ notification.from_profile.screen_name_ui }} - </router-link> + <user-link + :user="notification.from_profile" + :at="false" + /> </small> <button class="button-unstyled unmute" @@ -32,22 +37,23 @@ > <a class="avatar-container" - :href="notification.from_profile.statusnet_profile_url" - @click.stop.prevent.capture="toggleUserExpanded" + :href="$router.resolve(userProfileLink).href" + @click.prevent > - <UserAvatar - :compact="true" - :better-shadow="betterShadow" - :user="notification.from_profile" - /> + <UserPopover + :user-id="notification.from_profile.id" + :overlay-centers="true" + > + <UserAvatar + class="post-avatar" + :bot="botIndicator" + :compact="true" + :better-shadow="betterShadow" + :user="notification.from_profile" + /> + </UserPopover> </a> <div class="notification-right"> - <UserCard - v-if="userExpanded" - :user-id="getUser(notification).id" - :rounded="true" - :bordered="true" - /> <span class="notification-details"> <div class="name-and-action"> <!-- eslint-disable vue/no-v-html --> @@ -64,12 +70,16 @@ v-else class="username" :title="'@'+notification.from_profile.screen_name_ui" - >{{ notification.from_profile.name }}</span> + > + {{ notification.from_profile.name }} + </span> + {{ ' ' }} <span v-if="notification.type === 'like'"> <FAIcon class="type-icon" icon="star" /> + {{ ' ' }} <small>{{ $t('notifications.favorited_you') }}</small> </span> <span v-if="notification.type === 'repeat'"> @@ -78,6 +88,7 @@ icon="retweet" :title="$t('tool_tip.repeat')" /> + {{ ' ' }} <small>{{ $t('notifications.repeated_you') }}</small> </span> <span v-if="notification.type === 'follow'"> @@ -85,6 +96,7 @@ class="type-icon" icon="user-plus" /> + {{ ' ' }} <small>{{ $t('notifications.followed_you') }}</small> </span> <span v-if="notification.type === 'follow_request'"> @@ -92,6 +104,7 @@ class="type-icon" icon="user" /> + {{ ' ' }} <small>{{ $t('notifications.follow_request') }}</small> </span> <span v-if="notification.type === 'move'"> @@ -99,15 +112,30 @@ class="type-icon" icon="suitcase-rolling" /> + {{ ' ' }} <small>{{ $t('notifications.migrated_to') }}</small> </span> <span v-if="notification.type === 'pleroma:emoji_reaction'"> <small> - <i18n path="notifications.reacted_with"> + <i18n-t + scope="global" + keypath="notifications.reacted_with" + > <span class="emoji-reaction-emoji">{{ notification.emoji }}</span> - </i18n> + </i18n-t> </small> </span> + <span v-if="notification.type === 'pleroma:report'"> + <small>{{ $t('notifications.submitted_report') }}</small> + </span> + <span v-if="notification.type === 'poll'"> + <FAIcon + class="type-icon" + icon="poll-h" + /> + {{ ' ' }} + <small>{{ $t('notifications.poll_ended') }}</small> + </span> </div> <div v-if="isStatusNotification" @@ -116,13 +144,25 @@ <router-link v-if="notification.status" :to="{ name: 'conversation', params: { id: notification.status.id } }" - class="faint-link" + class="timeago-link faint-link" > <Timeago :time="notification.created_at" :auto-update="240" /> </router-link> + <button + class="button-unstyled expand-icon" + @click.prevent="toggleStatusExpanded" + :title="$t('tool_tip.toggle_expand')" + :aria-expanded="statusExpanded" + > + <FAIcon + class="fa-scale-110" + fixed-width + :icon="statusExpanded ? 'compress-alt' : 'expand-alt'" + /> + </button> </div> <div v-else @@ -138,6 +178,8 @@ <button v-if="needMute" class="button-unstyled" + :title="$t('tool_tip.toggle_mute')" + :aria-expanded="!unmuted" @click.prevent="toggleMute" > <FAIcon @@ -150,47 +192,58 @@ v-if="notification.type === 'follow' || notification.type === 'follow_request'" class="follow-text" > - <router-link - :to="userProfileLink" + <user-link class="follow-name" - > - @{{ notification.from_profile.screen_name_ui }} - </router-link> + :user="notification.from_profile" + /> <div v-if="notification.type === 'follow_request'" style="white-space: nowrap;" > - <FAIcon - icon="check" - class="fa-scale-110 fa-old-padding follow-request-accept" + <button + class="button-unstyled" :title="$t('tool_tip.accept_follow_request')" @click="approveUser()" - /> - <FAIcon - icon="times" - class="fa-scale-110 fa-old-padding follow-request-reject" + > + <FAIcon + icon="check" + class="fa-scale-110 fa-old-padding follow-request-accept" + /> + </button> + <button + class="button-unstyled" :title="$t('tool_tip.reject_follow_request')" @click="denyUser()" - /> + > + <FAIcon + icon="times" + class="fa-scale-110 fa-old-padding follow-request-reject" + /> + </button> </div> </div> <div v-else-if="notification.type === 'move'" class="move-text" > - <router-link :to="targetUserProfileLink"> - @{{ notification.target.screen_name_ui }} - </router-link> + <user-link + :user="notification.target" + /> </div> + <Report + v-else-if="notification.type === 'pleroma:report'" + :report-id="notification.report.id" + /> <template v-else> - <status-content - class="faint" + <StatusContent + :class="{ faint: !statusExpanded }" + :compact="!statusExpanded" :status="notification.action" /> </template> </div> </div> - </div> + </article> </template> <script src="./notification.js"></script> diff --git a/src/components/notifications/notification_filters.vue b/src/components/notifications/notification_filters.vue @@ -5,7 +5,7 @@ placement="bottom" :bound-to="{ x: 'container' }" > - <template v-slot:content> + <template #content> <div class="dropdown-menu"> <button class="button-default dropdown-item" @@ -61,10 +61,19 @@ :class="{ 'menu-checkbox-checked': filters.moves }" />{{ $t('settings.notification_visibility_moves') }} </button> + <button + class="button-default dropdown-item" + @click="toggleNotificationFilter('polls')" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-checked': filters.polls }" + />{{ $t('settings.notification_visibility_polls') }} + </button> </div> </template> - <template v-slot:trigger> - <button class="button-unstyled"> + <template #trigger> + <button class="filter-trigger-button button-unstyled"> <FAIcon icon="filter" /> </button> </template> @@ -100,23 +109,3 @@ export default { } } </script> - -<style lang="scss"> - -.NotificationFilters { - align-self: stretch; - - > button { - font-size: 1.2em; - padding-left: 0.7em; - padding-right: 0.2em; - line-height: 100%; - height: 100%; - } - - .dropdown-item { - margin: 0; - } -} - -</style> diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js @@ -1,3 +1,4 @@ +import { computed } from 'vue' import { mapGetters } from 'vuex' import Notification from '../notification/notification.vue' import NotificationFilters from './notification_filters.vue' @@ -9,10 +10,12 @@ import { } from '../../services/notification_utils/notification_utils.js' import FaviconService from '../../services/favicon_service/favicon_service.js' import { library } from '@fortawesome/fontawesome-svg-core' -import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' +import { faCircleNotch, faArrowUp, faMinus } from '@fortawesome/free-solid-svg-icons' library.add( - faCircleNotch + faCircleNotch, + faArrowUp, + faMinus ) const DEFAULT_SEEN_TO_DISPLAY_COUNT = 30 @@ -23,16 +26,17 @@ const Notifications = { NotificationFilters }, props: { - // Disables display of panel header - noHeading: Boolean, // Disables panel styles, unread mark, potentially other notification-related actions // meant for "Interactions" timeline minimalMode: Boolean, // Custom filter mode, an array of strings, possible values 'mention', 'repeat', 'like', 'follow', used to override global filter for use in "Interactions" timeline - filterMode: Array + filterMode: Array, + // Disable teleporting (i.e. for /users/user/notifications) + disableTeleport: Boolean }, data () { return { + showScrollTop: false, bottomedOut: false, // How many seen notifications to display in the list. The more there are, // the heavier the page becomes. This count is increased when loading @@ -40,6 +44,11 @@ const Notifications = { seenToDisplayCount: DEFAULT_SEEN_TO_DISPLAY_COUNT } }, + provide () { + return { + popoversZLayer: computed(() => this.popoversZLayer) + } + }, computed: { mainClass () { return this.minimalMode ? '' : 'panel panel-default' @@ -60,15 +69,46 @@ const Notifications = { return this.unseenNotifications.length }, unseenCountTitle () { - return this.unseenCount + (this.unreadChatCount) + return this.unseenCount + (this.unreadChatCount) + this.unreadAnnouncementCount }, loading () { return this.$store.state.statuses.notifications.loading }, + noHeading () { + const { layoutType } = this.$store.state.interface + return this.minimalMode || layoutType === 'mobile' + }, + teleportTarget () { + const { layoutType } = this.$store.state.interface + const map = { + wide: '#notifs-column', + mobile: '#mobile-notifications' + } + return map[layoutType] || '#notifs-sidebar' + }, + popoversZLayer () { + const { layoutType } = this.$store.state.interface + return layoutType === 'mobile' ? 'navbar' : null + }, notificationsToDisplay () { return this.filteredNotifications.slice(0, this.unseenCount + this.seenToDisplayCount) }, - ...mapGetters(['unreadChatCount']) + noSticky () { return this.$store.getters.mergedConfig.disableStickyHeaders }, + ...mapGetters(['unreadChatCount', 'unreadAnnouncementCount']) + }, + mounted () { + this.scrollerRef = this.$refs.root.closest('.column.-scrollable') + if (!this.scrollerRef) { + this.scrollerRef = this.$refs.root.closest('.mobile-notifications') + } + if (!this.scrollerRef) { + this.scrollerRef = this.$refs.root.closest('.column.main') + } + this.scrollerRef.addEventListener('scroll', this.updateScrollPosition) + }, + unmounted () { + if (!this.scrollerRef) return + this.scrollerRef.removeEventListener('scroll', this.updateScrollPosition) }, watch: { unseenCountTitle (count) { @@ -79,9 +119,29 @@ const Notifications = { FaviconService.clearFaviconBadge() this.$store.dispatch('setPageTitle', '') } + }, + teleportTarget () { + // handle scroller change + this.$nextTick(() => { + this.scrollerRef.removeEventListener('scroll', this.updateScrollPosition) + this.scrollerRef = this.$refs.root.closest('.column.-scrollable') + if (!this.scrollerRef) { + this.scrollerRef = this.$refs.root.closest('.mobile-notifications') + } + this.scrollerRef.addEventListener('scroll', this.updateScrollPosition) + this.updateScrollPosition() + }) } }, methods: { + scrollToTop () { + const scrollable = this.scrollerRef + scrollable.scrollTo({ top: this.$refs.root.offsetTop }) + // this.$refs.root.scrollIntoView({ behavior: 'smooth', block: 'start' }) + }, + updateScrollPosition () { + this.showScrollTop = this.$refs.root.offsetTop < this.scrollerRef.scrollTop + }, markAsSeen () { this.$store.dispatch('markNotificationsAsSeen') this.seenToDisplayCount = DEFAULT_SEEN_TO_DISPLAY_COUNT diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss @@ -11,10 +11,6 @@ color: var(--text, $fallback--text); } - .notifications-footer { - border: none; - } - .notification { position: relative; @@ -37,11 +33,6 @@ .notification { box-sizing: border-box; - border-bottom: 1px solid; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); - word-wrap: break-word; - word-break: break-word; &:hover .animated.Avatar { canvas { @@ -52,6 +43,10 @@ } } + &:last-child .Notification { + border-bottom: none; + } + .non-mention { display: flex; flex: 1; @@ -64,13 +59,13 @@ height: 32px; } - --link: var(--faintLink); - --text: var(--faint); + .faint { + --link: var(--faintLink); + --text: var(--faint); + } } .follow-request-accept { - cursor: pointer; - &:hover { color: $fallback--text; color: var(--text, $fallback--text); @@ -78,8 +73,6 @@ } .follow-request-reject { - cursor: pointer; - &:hover { color: $fallback--cRed; color: var(--cRed, $fallback--cRed); @@ -119,16 +112,26 @@ min-width: 3em; text-align: right; } + + .timeago-link { + margin-right: 0.2em; + } + + .expand-icon { + .svg-inline--fa { + margin-left: 0.25em; + } + } } .emoji-reaction-emoji { - font-size: 16px; + font-size: 1.3em; } .notification-details { - min-width: 0px; + min-width: 0; word-wrap: break-word; - line-height:18px; + line-height: var(--post-line-height); position: relative; overflow: hidden; width: 100%; @@ -151,7 +154,7 @@ } .timeago { - margin-right: .2em; + margin-right: 0.2em; } .status-content { @@ -164,7 +167,8 @@ margin: 0 0 0.3em; padding: 0; font-size: 1em; - line-height:20px; + line-height: 1.5; + small { font-weight: lighter; } diff --git a/src/components/notifications/notifications.vue b/src/components/notifications/notifications.vue @@ -1,69 +1,100 @@ <template> - <div - :class="{ minimal: minimalMode }" - class="Notifications" + <teleport + :disabled="minimalMode || disableTeleport" + :to="teleportTarget" > - <div :class="mainClass"> - <div - v-if="!noHeading" - class="panel-heading" - > - <div class="title"> - {{ $t('notifications.notifications') }} - <span - v-if="unseenCount" - class="badge badge-notification unseen-count" - >{{ unseenCount }}</span> - </div> - <button - v-if="unseenCount" - class="button-default read-button" - @click.prevent="markAsSeen" - > - {{ $t('notifications.read') }} - </button> - <NotificationFilters /> - </div> - <div class="panel-body"> + <component + :is="noHeading ? 'div' : 'aside'" + ref="root" + :class="{ minimal: minimalMode }" + class="Notifications" + > + <div :class="mainClass"> <div - v-for="notification in notificationsToDisplay" - :key="notification.id" - class="notification" - :class="{&quot;unseen&quot;: !minimalMode && !notification.seen}" + v-if="!noHeading" + class="notifications-heading panel-heading -sticky" > - <div class="notification-overlay" /> - <notification :notification="notification" /> + <div class="title"> + {{ $t('notifications.notifications') }} + <span + v-if="unseenCount" + class="badge badge-notification unseen-count" + >{{ unseenCount }}</span> + </div> + <div + v-if="showScrollTop" + class="rightside-button" + > + <button + class="button-unstyled scroll-to-top-button" + type="button" + :title="$t('general.scroll_to_top')" + @click="scrollToTop" + > + <FALayers class="fa-scale-110 fa-old-padding-layer"> + <FAIcon icon="arrow-up" /> + <FAIcon + icon="minus" + transform="up-7" + /> + </FALayers> + </button> + </div> + <button + v-if="unseenCount" + class="button-default read-button" + type="button" + @click.prevent="markAsSeen" + > + {{ $t('notifications.read') }} + </button> + <NotificationFilters class="rightside-button" /> </div> - </div> - <div class="panel-footer notifications-footer"> <div - v-if="bottomedOut" - class="new-status-notification text-center faint" + class="panel-body" + role="feed" > - {{ $t('notifications.no_more_notifications') }} + <div + v-for="notification in notificationsToDisplay" + :key="notification.id" + role="listitem" + class="notification" + :class="{unseen: !minimalMode && !notification.seen}" + > + <div class="notification-overlay" /> + <notification :notification="notification" /> + </div> </div> - <button - v-else-if="!loading" - class="button-unstyled -link -fullwidth" - @click.prevent="fetchOlderNotifications()" - > - <div class="new-status-notification text-center"> - {{ minimalMode ? $t('interactions.load_older') : $t('notifications.load_older') }} + <div class="panel-footer"> + <div + v-if="bottomedOut" + class="new-status-notification text-center faint" + > + {{ $t('notifications.no_more_notifications') }} + </div> + <button + v-else-if="!loading" + class="button-unstyled -link -fullwidth" + @click.prevent="fetchOlderNotifications()" + > + <div class="new-status-notification text-center"> + {{ minimalMode ? $t('interactions.load_older') : $t('notifications.load_older') }} + </div> + </button> + <div + v-else + class="new-status-notification text-center" + > + <FAIcon + icon="circle-notch" + spin + size="lg" + /> </div> - </button> - <div - v-else - class="new-status-notification text-center" - > - <FAIcon - icon="circle-notch" - spin - size="lg" - /> </div> </div> - </div> - </div> + </component> + </teleport> </template> <script src="./notifications.js"></script> diff --git a/src/components/opacity_input/opacity_input.vue b/src/components/opacity_input/opacity_input.vue @@ -11,21 +11,21 @@ </label> <Checkbox v-if="typeof fallback !== 'undefined'" - :checked="present" + :model-value="present" :disabled="disabled" class="opt" - @change="$emit('input', !present ? fallback : undefined)" + @update:modelValue="$emit('update:modelValue', !present ? fallback : undefined)" /> <input :id="name" class="input-number" type="number" - :value="value || fallback" + :value="modelValue || fallback" :disabled="!present || disabled" max="1" min="0" step=".05" - @input="$emit('input', $event.target.value)" + @input="$emit('update:modelValue', $event.target.value)" > </div> </template> @@ -37,11 +37,12 @@ export default { Checkbox }, props: [ - 'name', 'value', 'fallback', 'disabled' + 'name', 'modelValue', 'fallback', 'disabled' ], + emits: ['update:modelValue'], computed: { present () { - return typeof this.value !== 'undefined' + return typeof this.modelValue !== 'undefined' } } } diff --git a/src/components/optional_router_link/optional_router_link.vue b/src/components/optional_router_link/optional_router_link.vue @@ -0,0 +1,23 @@ +<template> + <!-- eslint-disable vue/no-multiple-template-root --> + <router-link + v-if="to" + v-slot="props" + :to="to" + custom + > + <slot + v-bind="props" + /> + </router-link> + <slot + v-else + v-bind="{}" + /> +</template> + +<script> +export default { + props: ['to'] +} +</script> diff --git a/src/components/password_reset/password_reset.vue b/src/components/password_reset/password_reset.vue @@ -91,14 +91,18 @@ flex-direction: column; margin-top: 0.6em; max-width: 18rem; + + > * { + min-width: 0; + } } .form-group { display: flex; flex-direction: column; margin-bottom: 1em; - padding: 0.3em 0.0em 0.3em; - line-height: 24px; + padding: 0.3em 0; + line-height: 1.85em; } .error { @@ -110,7 +114,7 @@ .alert { padding: 0.5em; - margin: 0.3em 0.0em 1em; + margin: 0.3em 0 1em; } .password-reset-required { diff --git a/src/components/pinch_zoom/pinch_zoom.js b/src/components/pinch_zoom/pinch_zoom.js @@ -0,0 +1,13 @@ +import PinchZoom from '@kazvmoe-infra/pinch-zoom-element' + +export default { + methods: { + setTransform ({ scale, x, y }) { + this.$el.setTransform({ scale, x, y }) + } + }, + created () { + // Make lint happy + (() => PinchZoom)() + } +} diff --git a/src/components/pinch_zoom/pinch_zoom.vue b/src/components/pinch_zoom/pinch_zoom.vue @@ -0,0 +1,11 @@ +<template> + <pinch-zoom + class="pinch-zoom-parent" + v-bind="$attrs" + v-on="$listeners" + > + <slot /> + </pinch-zoom> +</template> + +<script src="./pinch_zoom.js"></script> diff --git a/src/components/poll/poll.js b/src/components/poll/poll.js @@ -21,7 +21,7 @@ export default { } this.$store.dispatch('trackPoll', this.pollId) }, - destroyed () { + unmounted () { this.$store.dispatch('untrackPoll', this.pollId) }, computed: { diff --git a/src/components/poll/poll.vue b/src/components/poll/poll.vue @@ -71,13 +71,18 @@ {{ $tc("polls.votes_count", poll.votes_count, { count: poll.votes_count }) }}&nbsp;·&nbsp; </template> </div> - <i18n :path="expired ? 'polls.expired' : 'polls.expires_in'"> - <Timeago - :time="expiresAt" - :auto-update="60" - :now-threshold="0" - /> - </i18n> + <span> + <i18n-t + scope="global" + :keypath="expired ? 'polls.expired' : 'polls.expires_in'" + > + <Timeago + :time="expiresAt" + :auto-update="60" + :now-threshold="0" + /> + </i18n-t> + </span> </div> </div> </template> diff --git a/src/components/poll/poll_form.vue b/src/components/poll/poll_form.vue @@ -72,6 +72,7 @@ :max="maxExpirationInCurrentUnit" @change="expiryAmountChange" > + {{ ' ' }} <Select v-model="expiryUnit" unstyled="true" @@ -83,7 +84,7 @@ :key="unit" :value="unit" > - {{ $t(`time.${unit}_short`, ['']) }} + {{ $tc(`time.unit.${unit}_short`, expiryAmount, ['']) }} </option> </Select> </div> diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js @@ -4,7 +4,7 @@ const Popover = { // Action to trigger popover: either 'hover' or 'click' trigger: String, - // Either 'top' or 'bottom' + // 'top', 'bottom', 'left', 'right' placement: String, // Takes object with properties 'x' and 'y', values of these can be @@ -31,40 +31,88 @@ const Popover = { // If true, subtract padding when calculating position for the popover, // use it when popover offset looks to be different on top vs bottom. - removePadding: Boolean + removePadding: Boolean, + + // self-explanatory (i hope) + disabled: Boolean, + + // Instead of putting popover next to anchor, overlay popover's center on top of anchor's center + overlayCenters: Boolean, + + // What selector (witin popover!) to use for determining center of popover + overlayCentersSelector: String, + + // Lets hover popover stay when clicking inside of it + stayOnClick: Boolean, + + triggerAttrs: { + type: Object, + default: {} + } }, + inject: ['popoversZLayer'], // override popover z layer data () { return { + // lockReEntry is a flag that is set when mouse cursor is leaving the popover's content + // so that if mouse goes back into popover it won't be re-shown again to prevent annoyance + // with popovers refusing to be hidden when user wants to interact with something in below popover + anchorEl: null, + // There's an issue where having teleport enabled by default causes things just... + // not render at all, i.e. main post status form and its emoji inputs + teleport: false, + lockReEntry: false, hidden: true, - styles: { opacity: 0 }, - oldSize: { width: 0, height: 0 } + styles: {}, + oldSize: { width: 0, height: 0 }, + scrollable: null, + // used to avoid blinking if hovered onto popover + graceTimeout: null, + parentPopover: null, + disableClickOutside: false, + childrenShown: new Set() } }, methods: { + setAnchorEl (el) { + this.anchorEl = el + this.updateStyles() + }, containerBoundingClientRect () { const container = this.boundToSelector ? this.$el.closest(this.boundToSelector) : this.$el.offsetParent return container.getBoundingClientRect() }, updateStyles () { if (this.hidden) { - this.styles = { - opacity: 0 - } + this.styles = {} return } // Popover will be anchored around this element, trigger ref is the container, so // its children are what are inside the slot. Expect only one v-slot:trigger. - const anchorEl = (this.$refs.trigger && this.$refs.trigger.children[0]) || this.$el + const anchorEl = this.anchorEl || (this.$refs.trigger && this.$refs.trigger.children[0]) || this.$el // SVGs don't have offsetWidth/Height, use fallback - const anchorWidth = anchorEl.offsetWidth || anchorEl.clientWidth const anchorHeight = anchorEl.offsetHeight || anchorEl.clientHeight - const screenBox = anchorEl.getBoundingClientRect() - // Screen position of the origin point for popover - const origin = { x: screenBox.left + screenBox.width * 0.5, y: screenBox.top } + const anchorWidth = anchorEl.offsetWidth || anchorEl.clientWidth + const anchorScreenBox = anchorEl.getBoundingClientRect() + + const anchorStyle = getComputedStyle(anchorEl) + const topPadding = parseFloat(anchorStyle.paddingTop) + const bottomPadding = parseFloat(anchorStyle.paddingBottom) + const rightPadding = parseFloat(anchorStyle.paddingRight) + const leftPadding = parseFloat(anchorStyle.paddingLeft) + + // Screen position of the origin point for popover = center of the anchor + const origin = { + x: anchorScreenBox.left + anchorWidth * 0.5, + y: anchorScreenBox.top + anchorHeight * 0.5 + } const content = this.$refs.content + const overlayCenter = this.overlayCenters + ? this.$refs.content.querySelector(this.overlayCentersSelector) + : null + // Minor optimization, don't call a slow reflow call if we don't have to - const parentBounds = this.boundTo && + const parentScreenBox = this.boundTo && (this.boundTo.x === 'container' || this.boundTo.y === 'container') && this.containerBoundingClientRect() @@ -72,82 +120,179 @@ const Popover = { // What are the screen bounds for the popover? Viewport vs container // when using viewport, using default margin values to dodge the navbar - const xBounds = this.boundTo && this.boundTo.x === 'container' ? { - min: parentBounds.left + (margin.left || 0), - max: parentBounds.right - (margin.right || 0) - } : { - min: 0 + (margin.left || 10), - max: window.innerWidth - (margin.right || 10) - } + const xBounds = this.boundTo && this.boundTo.x === 'container' + ? { + min: parentScreenBox.left + (margin.left || 0), + max: parentScreenBox.right - (margin.right || 0) + } + : { + min: 0 + (margin.left || 10), + max: window.innerWidth - (margin.right || 10) + } - const yBounds = this.boundTo && this.boundTo.y === 'container' ? { - min: parentBounds.top + (margin.top || 0), - max: parentBounds.bottom - (margin.bottom || 0) - } : { - min: 0 + (margin.top || 50), - max: window.innerHeight - (margin.bottom || 5) - } + const yBounds = this.boundTo && this.boundTo.y === 'container' + ? { + min: parentScreenBox.top + (margin.top || 0), + max: parentScreenBox.bottom - (margin.bottom || 0) + } + : { + min: 0 + (margin.top || 50), + max: window.innerHeight - (margin.bottom || 5) + } let horizOffset = 0 + let vertOffset = 0 + + if (overlayCenter) { + const box = content.getBoundingClientRect() + const overlayCenterScreenBox = overlayCenter.getBoundingClientRect() + const leftInnerOffset = overlayCenterScreenBox.left - box.left + const topInnerOffset = overlayCenterScreenBox.top - box.top + horizOffset = -leftInnerOffset - overlayCenter.offsetWidth * 0.5 + vertOffset = -topInnerOffset - overlayCenter.offsetHeight * 0.5 + } else { + horizOffset = content.offsetWidth * -0.5 + vertOffset = content.offsetHeight * -0.5 + } + + const leftBorder = origin.x + horizOffset + const rightBorder = leftBorder + content.offsetWidth + const topBorder = origin.y + vertOffset + const bottomBorder = topBorder + content.offsetHeight // If overflowing from left, move it so that it doesn't - if ((origin.x - content.offsetWidth * 0.5) < xBounds.min) { - horizOffset += -(origin.x - content.offsetWidth * 0.5) + xBounds.min + if (leftBorder < xBounds.min) { + horizOffset += xBounds.min - leftBorder } // If overflowing from right, move it so that it doesn't - if ((origin.x + horizOffset + content.offsetWidth * 0.5) > xBounds.max) { - horizOffset -= (origin.x + horizOffset + content.offsetWidth * 0.5) - xBounds.max + if (rightBorder > xBounds.max) { + horizOffset -= rightBorder - xBounds.max } - // Default to whatever user wished with placement prop - let usingTop = this.placement !== 'bottom' - - // Handle special cases, first force to displaying on top if there's not space on bottom, - // regardless of what placement value was. Then check if there's not space on top, and - // force to bottom, again regardless of what placement value was. - if (origin.y + content.offsetHeight > yBounds.max) usingTop = true - if (origin.y - content.offsetHeight < yBounds.min) usingTop = false + // If overflowing from top, move it so that it doesn't + if (topBorder < yBounds.min) { + vertOffset += yBounds.min - topBorder + } - let vPadding = 0 - if (this.removePadding && usingTop) { - const anchorStyle = getComputedStyle(anchorEl) - vPadding = parseFloat(anchorStyle.paddingTop) + parseFloat(anchorStyle.paddingBottom) + // If overflowing from bottom, move it so that it doesn't + if (bottomBorder > yBounds.max) { + vertOffset -= bottomBorder - yBounds.max } - const yOffset = (this.offset && this.offset.y) || 0 - const translateY = usingTop - ? -anchorHeight + vPadding - yOffset - content.offsetHeight - : yOffset + let translateX = 0 + let translateY = 0 + + if (overlayCenter) { + translateX = origin.x + horizOffset + translateY = origin.y + vertOffset + } else if (this.placement !== 'right' && this.placement !== 'left') { + // Default to whatever user wished with placement prop + let usingTop = this.placement !== 'bottom' + + // Handle special cases, first force to displaying on top if there's not space on bottom, + // regardless of what placement value was. Then check if there's not space on top, and + // force to bottom, again regardless of what placement value was. + const topBoundary = origin.y - anchorHeight * 0.5 + (this.removePadding ? topPadding : 0) + const bottomBoundary = origin.y + anchorHeight * 0.5 - (this.removePadding ? bottomPadding : 0) + if (bottomBoundary + content.offsetHeight > yBounds.max) usingTop = true + if (topBoundary - content.offsetHeight < yBounds.min) usingTop = false - const xOffset = (this.offset && this.offset.x) || 0 - const translateX = anchorWidth * 0.5 - content.offsetWidth * 0.5 + horizOffset + xOffset + const yOffset = (this.offset && this.offset.y) || 0 + translateY = usingTop + ? topBoundary - yOffset - content.offsetHeight + : bottomBoundary + yOffset + + const xOffset = (this.offset && this.offset.x) || 0 + translateX = origin.x + horizOffset + xOffset + } else { + // Default to whatever user wished with placement prop + let usingRight = this.placement !== 'left' + + // Handle special cases, first force to displaying on top if there's not space on bottom, + // regardless of what placement value was. Then check if there's not space on top, and + // force to bottom, again regardless of what placement value was. + const rightBoundary = origin.x - anchorWidth * 0.5 + (this.removePadding ? rightPadding : 0) + const leftBoundary = origin.x + anchorWidth * 0.5 - (this.removePadding ? leftPadding : 0) + if (leftBoundary + content.offsetWidth > xBounds.max) usingRight = true + if (rightBoundary - content.offsetWidth < xBounds.min) usingRight = false + + const xOffset = (this.offset && this.offset.x) || 0 + translateX = usingRight + ? rightBoundary - xOffset - content.offsetWidth + : leftBoundary + xOffset + + const yOffset = (this.offset && this.offset.y) || 0 + translateY = origin.y + vertOffset + yOffset + } - // Note, separate translateX and translateY avoids blurry text on chromium, - // single translate or translate3d resulted in blurry text. this.styles = { - opacity: 1, - transform: `translateX(${Math.round(translateX)}px) translateY(${Math.round(translateY)}px)` + left: `${Math.round(translateX)}px`, + top: `${Math.round(translateY)}px` + } + + if (this.popoversZLayer) { + this.styles['--ZI_popover_override'] = `var(--ZI_${this.popoversZLayer}_popovers)` + } + if (parentScreenBox) { + this.styles.maxWidth = `${Math.round(parentScreenBox.width)}px` } }, showPopover () { + if (this.disabled) return + this.disableClickOutside = true + setTimeout(() => { + this.disableClickOutside = false + }, 0) const wasHidden = this.hidden this.hidden = false + this.parentPopover && this.parentPopover.onChildPopoverState(this, true) + if (this.trigger === 'click' || this.stayOnClick) { + document.addEventListener('click', this.onClickOutside) + } + this.scrollable.addEventListener('scroll', this.onScroll) + this.scrollable.addEventListener('resize', this.onResize) this.$nextTick(() => { if (wasHidden) this.$emit('show') this.updateStyles() }) }, hidePopover () { + if (this.disabled) return if (!this.hidden) this.$emit('close') this.hidden = true - this.styles = { opacity: 0 } + this.parentPopover && this.parentPopover.onChildPopoverState(this, false) + if (this.trigger === 'click') { + document.removeEventListener('click', this.onClickOutside) + } + this.scrollable.removeEventListener('scroll', this.onScroll) + this.scrollable.removeEventListener('resize', this.onResize) }, onMouseenter (e) { - if (this.trigger === 'hover') this.showPopover() + if (this.trigger === 'hover') { + this.lockReEntry = false + clearTimeout(this.graceTimeout) + this.graceTimeout = null + this.showPopover() + } }, onMouseleave (e) { - if (this.trigger === 'hover') this.hidePopover() + if (this.trigger === 'hover' && this.childrenShown.size === 0) { + this.graceTimeout = setTimeout(() => this.hidePopover(), 1) + } + }, + onMouseenterContent (e) { + if (this.trigger === 'hover' && !this.lockReEntry) { + this.lockReEntry = true + clearTimeout(this.graceTimeout) + this.graceTimeout = null + this.showPopover() + } + }, + onMouseleaveContent (e) { + if (this.trigger === 'hover' && this.childrenShown.size === 0) { + this.graceTimeout = setTimeout(() => this.hidePopover(), 1) + } }, onClick (e) { if (this.trigger === 'click') { @@ -159,9 +304,26 @@ const Popover = { } }, onClickOutside (e) { + if (this.disableClickOutside) return if (this.hidden) return + if (this.$refs.content && this.$refs.content.contains(e.target)) return if (this.$el.contains(e.target)) return + if (this.childrenShown.size > 0) return this.hidePopover() + if (this.parentPopover) this.parentPopover.onClickOutside(e) + }, + onScroll (e) { + this.updateStyles() + }, + onResize (e) { + this.updateStyles() + }, + onChildPopoverState (childRef, state) { + if (state) { + this.childrenShown.add(childRef) + } else { + this.childrenShown.delete(childRef) + } } }, updated () { @@ -175,11 +337,19 @@ const Popover = { this.oldSize = { width: content.offsetWidth, height: content.offsetHeight } } }, - created () { - document.addEventListener('click', this.onClickOutside) + mounted () { + this.teleport = true + let scrollable = this.$refs.trigger.closest('.column.-scrollable') || + this.$refs.trigger.closest('.mobile-notifications') + if (!scrollable) scrollable = window + this.scrollable = scrollable + let parent = this.$parent + while (parent && parent.$.type.name !== 'Popover') { + parent = parent.$parent + } + this.parentPopover = parent }, - destroyed () { - document.removeEventListener('click', this.onClickOutside) + beforeUnmount () { this.hidePopover() } } diff --git a/src/components/popover/popover.vue b/src/components/popover/popover.vue @@ -1,30 +1,41 @@ <template> - <div + <span @mouseenter="onMouseenter" @mouseleave="onMouseleave" > <button ref="trigger" - class="button-unstyled -fullwidth popover-trigger-button" + class="button-unstyled popover-trigger-button" type="button" + v-bind="triggerAttrs" @click="onClick" > <slot name="trigger" /> </button> - <div - v-if="!hidden" - ref="content" - :style="styles" - class="popover" - :class="popoverClass || 'popover-default'" + <teleport + :disabled="!teleport" + to="#popovers" > - <slot - name="content" - class="popover-inner" - :close="hidePopover" - /> - </div> - </div> + <transition name="fade"> + <div + v-if="!hidden" + ref="content" + :style="styles" + class="popover" + :class="popoverClass || 'popover-default'" + @mouseenter="onMouseenterContent" + @mouseleave="onMouseleaveContent" + @click="onClickContent" + > + <slot + name="content" + class="popover-inner" + :close="hidePopover" + /> + </div> + </transition> + </teleport> + </span> </template> <script src="./popover.js" /> @@ -33,20 +44,32 @@ @import '../../_variables.scss'; .popover-trigger-button { - display: block; + display: inline-block; } .popover { - z-index: 8; - position: absolute; + z-index: var(--ZI_popover_override, var(--ZI_popovers)); + position: fixed; min-width: 0; + max-width: calc(100vw - 20px); + box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5); + box-shadow: var(--popupShadow); } .popover-default { - transition: opacity 0.3s; + &:after { + content: ''; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + z-index: 3; + box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.6); + box-shadow: var(--panelShadow); + pointer-events: none; + } - box-shadow: 1px 1px 4px rgba(0,0,0,.6); - box-shadow: var(--panelShadow); border-radius: $fallback--btnRadius; border-radius: var(--btnRadius, $fallback--btnRadius); @@ -65,11 +88,11 @@ .dropdown-menu { display: block; padding: .5rem 0; - font-size: 1rem; + font-size: 1em; text-align: left; list-style: none; max-width: 100vw; - z-index: 10; + z-index: var(--ZI_popover_override, var(--ZI_popovers)); white-space: nowrap; .dropdown-divider { @@ -82,9 +105,9 @@ .dropdown-item { line-height: 21px; - overflow: auto; + overflow: hidden; display: block; - padding: .5em 0.75em; + padding: 0.5em 0.75em; clear: both; font-weight: 400; text-align: inherit; @@ -107,17 +130,25 @@ } } + &.-has-submenu { + .chevron-icon { + margin-right: 0.25rem; + margin-left: 2rem; + } + } + &:active, &:hover { background-color: $fallback--lightBg; background-color: var(--selectedMenuPopover, $fallback--lightBg); - color: $fallback--link; - color: var(--selectedMenuPopoverText, $fallback--link); + box-shadow: none; + --btnText: var(--selectedMenuPopoverText, $fallback--link); --faint: var(--selectedMenuPopoverFaintText, $fallback--faint); --faintLink: var(--selectedMenuPopoverFaintLink, $fallback--faint); --lightText: var(--selectedMenuPopoverLightText, $fallback--lightText); --icon: var(--selectedMenuPopoverIcon, $fallback--icon); svg { color: var(--selectedMenuPopoverIcon, $fallback--icon); + --icon: var(--selectedMenuPopoverIcon, $fallback--icon); } } @@ -142,12 +173,41 @@ content: '✓'; } - &.menu-checkbox-radio::after { - font-size: 2em; - content: '•'; + &.-radio { + border-radius: 9999px; + + &.menu-checkbox-checked::after { + font-size: 2em; + content: '•'; + } } } } + + .button-default.dropdown-item { + &, + i[class*=icon-] { + color: $fallback--text; + color: var(--btnText, $fallback--text); + } + + &:active { + background-color: $fallback--lightBg; + background-color: var(--selectedMenuPopover, $fallback--lightBg); + color: $fallback--link; + color: var(--selectedMenuPopoverText, $fallback--link); + } + + &:disabled { + color: $fallback--text; + color: var(--btnDisabledText, $fallback--text); + } + + &.toggled { + color: $fallback--text; + color: var(--btnToggledText, $fallback--text); + } + } } </style> diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js @@ -4,6 +4,7 @@ import ScopeSelector from '../scope_selector/scope_selector.vue' import EmojiInput from '../emoji_input/emoji_input.vue' import PollForm from '../poll/poll_form.vue' import Attachment from '../attachment/attachment.vue' +import Gallery from 'src/components/gallery/gallery.vue' import StatusContent from '../status_content/status_content.vue' import fileTypeService from '../../services/file_type/file_type.service.js' import { findOffset } from '../../services/offset_finder/offset_finder.service.js' @@ -40,7 +41,7 @@ const buildMentionsString = ({ user, attentions = [] }, currentUser) => { allAttentions = uniqBy(allAttentions, 'id') allAttentions = reject(allAttentions, { id: currentUser.id }) - let mentions = map(allAttentions, (attention) => { + const mentions = map(allAttentions, (attention) => { return `@${attention.screen_name}` }) @@ -54,6 +55,14 @@ const pxStringToNumber = (str) => { const PostStatusForm = { props: [ + 'statusId', + 'statusText', + 'statusIsSensitive', + 'statusPoll', + 'statusFiles', + 'statusMediaDescriptions', + 'statusScope', + 'statusContentType', 'replyTo', 'repliedUser', 'attentions', @@ -61,6 +70,7 @@ const PostStatusForm = { 'subject', 'disableSubject', 'disableScopeSelector', + 'disableVisibilitySelector', 'disableNotice', 'disableLockWarning', 'disablePolls', @@ -77,6 +87,12 @@ const PostStatusForm = { 'emojiPickerPlacement', 'optimisticPosting' ], + emits: [ + 'posted', + 'resize', + 'mediaplay', + 'mediapause' + ], components: { MediaUpload, EmojiInput, @@ -85,7 +101,8 @@ const PostStatusForm = { Checkbox, Select, Attachment, - StatusContent + StatusContent, + Gallery }, mounted () { this.updateIdempotencyKey() @@ -117,22 +134,38 @@ const PostStatusForm = { const { postContentType: contentType, sensitiveByDefault } = this.$store.getters.mergedConfig + let statusParams = { + spoilerText: this.subject || '', + status: statusText, + nsfw: !!sensitiveByDefault, + files: [], + poll: {}, + mediaDescriptions: {}, + visibility: scope, + contentType + } + + if (this.statusId) { + const statusContentType = this.statusContentType || contentType + statusParams = { + spoilerText: this.subject || '', + status: this.statusText || '', + nsfw: this.statusIsSensitive || !!sensitiveByDefault, + files: this.statusFiles || [], + poll: this.statusPoll || {}, + mediaDescriptions: this.statusMediaDescriptions || {}, + visibility: this.statusScope || scope, + contentType: statusContentType + } + } + return { dropFiles: [], uploadingFiles: false, error: null, posting: false, highlighted: 0, - newStatus: { - spoilerText: this.subject || '', - status: statusText, - nsfw: !!sensitiveByDefault, - files: [], - poll: {}, - mediaDescriptions: {}, - visibility: scope, - contentType - }, + newStatus: statusParams, caret: 0, pollFormVisible: false, showDropIcon: 'hide', @@ -156,7 +189,7 @@ const PostStatusForm = { emojiUserSuggestor () { return suggestor({ emoji: [ - ...this.$store.state.instance.emoji, + ...this.$store.getters.standardEmojiList, ...this.$store.state.instance.customEmoji ], store: this.$store @@ -165,13 +198,13 @@ const PostStatusForm = { emojiSuggestor () { return suggestor({ emoji: [ - ...this.$store.state.instance.emoji, + ...this.$store.getters.standardEmojiList, ...this.$store.state.instance.customEmoji ] }) }, emoji () { - return this.$store.state.instance.emoji || [] + return this.$store.getters.standardEmojiList || [] }, customEmoji () { return this.$store.state.instance.customEmoji || [] @@ -228,13 +261,16 @@ const PostStatusForm = { uploadFileLimitReached () { return this.newStatus.files.length >= this.fileLimit }, + isEdit () { + return typeof this.statusId !== 'undefined' && this.statusId.trim() !== '' + }, ...mapGetters(['mergedConfig']), ...mapState({ mobileLayout: state => state.interface.mobileLayout }) }, watch: { - 'newStatus': { + newStatus: { deep: true, handler () { this.statusChanged() @@ -265,7 +301,7 @@ const PostStatusForm = { this.$refs.textarea.focus() }) } - let el = this.$el.querySelector('textarea') + const el = this.$el.querySelector('textarea') el.style.height = 'auto' el.style.height = undefined this.error = null @@ -384,10 +420,25 @@ const PostStatusForm = { this.$emit('resize', { delayed: true }) }, removeMediaFile (fileInfo) { - let index = this.newStatus.files.indexOf(fileInfo) + const index = this.newStatus.files.indexOf(fileInfo) this.newStatus.files.splice(index, 1) this.$emit('resize') }, + editAttachment (fileInfo, newText) { + this.newStatus.mediaDescriptions[fileInfo.id] = newText + }, + shiftUpMediaFile (fileInfo) { + const { files } = this.newStatus + const index = this.newStatus.files.indexOf(fileInfo) + files.splice(index, 1) + files.splice(index - 1, 0, fileInfo) + }, + shiftDnMediaFile (fileInfo) { + const { files } = this.newStatus + const index = this.newStatus.files.indexOf(fileInfo) + files.splice(index, 1) + files.splice(index + 1, 0, fileInfo) + }, uploadFailed (errString, templateArgs) { templateArgs = templateArgs || {} this.error = this.$t('upload.error.base') + ' ' + this.$t('upload.error.' + errString, templateArgs) @@ -439,7 +490,7 @@ const PostStatusForm = { }, onEmojiInputInput (e) { this.$nextTick(() => { - this.resize(this.$refs['textarea']) + this.resize(this.$refs.textarea) }) }, resize (e) { @@ -450,12 +501,11 @@ const PostStatusForm = { if (target.value === '') { target.style.height = null this.$emit('resize') - this.$refs['emoji-input'].resize() return } - const formRef = this.$refs['form'] - const bottomRef = this.$refs['bottom'] + const formRef = this.$refs.form + const bottomRef = this.$refs.bottom /* Scroller is either `window` (replies in TL), sidebar (main post form, * replies in notifs) or mobile post form. Note that getting and setting * scroll is different for `Window` and `Element`s @@ -463,7 +513,7 @@ const PostStatusForm = { const bottomBottomPaddingStr = window.getComputedStyle(bottomRef)['padding-bottom'] const bottomBottomPadding = pxStringToNumber(bottomBottomPaddingStr) - const scrollerRef = this.$el.closest('.sidebar-scroller') || + const scrollerRef = this.$el.closest('.column.-scrollable') || this.$el.closest('.post-form-modal-view') || window @@ -537,11 +587,9 @@ const PostStatusForm = { } else { scrollerRef.scrollTop = targetScroll } - - this.$refs['emoji-input'].resize() }, showEmojiPicker () { - this.$refs['textarea'].focus() + this.$refs.textarea.focus() this.$refs['emoji-input'].triggerShowPicker() }, clearError () { diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue @@ -8,21 +8,13 @@ @submit.prevent @dragover.prevent="fileDrag" > - <div - v-show="showDropIcon !== 'hide'" - :style="{ animation: showDropIcon === 'show' ? 'fade-in 0.25s' : 'fade-out 0.5s' }" - class="drop-indicator" - @dragleave="fileDragStop" - @drop.stop="fileDrop" - > - <FAIcon :icon="uploadFileLimitReached ? 'ban' : 'upload'" /> - </div> <div class="form-group"> - <i18n + <i18n-t v-if="!$store.state.users.currentUser.locked && newStatus.visibility == 'private' && !disableLockWarning" - path="post_status.account_not_locked_warning" + keypath="post_status.account_not_locked_warning" tag="p" class="visibility-notice" + scope="global" > <button class="button-unstyled -link" @@ -30,7 +22,7 @@ > {{ $t('post_status.account_not_locked_warning_link') }} </button> - </i18n> + </i18n-t> <p v-if="!hideScopeNotice && newStatus.visibility === 'public'" class="visibility-notice notice-dismissible" @@ -75,6 +67,13 @@ <span v-else>{{ $t('post_status.direct_warning_to_all') }}</span> </p> <div + v-if="isEdit" + class="visibility-notice edit-warning" + > + <p>{{ $t('post_status.edit_remote_warning') }}</p> + <p>{{ $t('post_status.edit_unsupported_warning') }}</p> + </div> + <div v-if="!disablePreview" class="preview-heading faint" > @@ -178,6 +177,7 @@ class="visibility-tray" > <scope-selector + v-if="!disableVisibilitySelector" :show-all="showAllScopes" :user-default="userDefaultScope" :original-scope="copyMessageScope" @@ -277,42 +277,45 @@ </button> </div> <div + v-show="showDropIcon !== 'hide'" + :style="{ animation: showDropIcon === 'show' ? 'fade-in 0.25s' : 'fade-out 0.5s' }" + class="drop-indicator" + @dragleave="fileDragStop" + @drop.stop="fileDrop" + > + <FAIcon :icon="uploadFileLimitReached ? 'ban' : 'upload'" /> + </div> + <div v-if="error" class="alert error" > Error: {{ error }} - <FAIcon - class="fa-scale-110 fa-old-padding" - icon="times" + <button + class="button-unstyled" @click="clearError" - /> - </div> - <div class="attachments"> - <div - v-for="file in newStatus.files" - :key="file.url" - class="media-upload-wrapper" > - <button - class="button-unstyled hider" - @click="removeMediaFile(file)" - > - <FAIcon icon="times" /> - </button> - <attachment - :attachment="file" - :set-media="() => $store.dispatch('setMedia', newStatus.files)" - size="small" - allow-play="false" + <FAIcon + class="fa-scale-110 fa-old-padding" + icon="times" /> - <input - v-model="newStatus.mediaDescriptions[file.id]" - type="text" - :placeholder="$t('post_status.media_description')" - @keydown.enter.prevent="" - > - </div> + </button> </div> + <gallery + v-if="newStatus.files && newStatus.files.length > 0" + class="attachments" + :grid="true" + :nsfw="false" + :attachments="newStatus.files" + :descriptions="newStatus.mediaDescriptions" + :set-media="() => $store.dispatch('setMedia', newStatus.files)" + :editable="true" + :edit-attachment="editAttachment" + :remove-attachment="removeMediaFile" + :shift-up-attachment="newStatus.files.length > 1 && shiftUpMediaFile" + :shift-dn-attachment="newStatus.files.length > 1 && shiftDnMediaFile" + @play="$emit('mediaplay', attachment.id)" + @pause="$emit('mediapause', attachment.id)" + /> <div v-if="newStatus.files.length > 0 && !disableSensitivityCheckbox" class="upload_settings" @@ -330,31 +333,18 @@ <style lang="scss"> @import '../../_variables.scss'; -.tribute-container { - ul { - padding: 0px; - li { - display: flex; - align-items: center; - } - } - img { - padding: 3px; - width: 16px; - height: 16px; - border-radius: $fallback--avatarAltRadius; - border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); - } -} - .post-status-form { position: relative; + .attachments { + margin-bottom: 0.5em; + } + .form-bottom { display: flex; justify-content: space-between; padding: 0.5em; - height: 32px; + height: 2.5em; button { width: 10em; @@ -412,7 +402,6 @@ border-radius: var(--tooltipRadius, $fallback--tooltipRadius); padding: 0.5em; margin: 0; - line-height: 1.4em; } .text-format { @@ -426,13 +415,26 @@ display: flex; justify-content: space-between; padding-top: 5px; + align-items: baseline; + } + + .visibility-notice.edit-warning { + > :first-child { + margin-top: 0; + } + + > :last-child { + margin-bottom: 0; + } } .media-upload-icon, .poll-icon, .emoji-icon { - font-size: 26px; + font-size: 1.85em; line-height: 1.1; flex: 1; padding: 0 0.1em; + display: flex; + align-items: center; &.selected, &:hover { // needs to be specific to override icon default color @@ -459,21 +461,17 @@ // Order is not necessary but a good indicator .media-upload-icon { order: 1; - text-align: left; + justify-content: left; } .emoji-icon { order: 2; - text-align: center; + justify-content: center; } .poll-icon { order: 3; - text-align: right; - } - - .poll-icon { - cursor: pointer; + justify-content: right; } .error { @@ -507,19 +505,6 @@ flex-direction: column; } - .attachments .media-upload-wrapper { - position: relative; - - .attachment { - margin: 0; - padding: 0; - } - } - - .btn { - cursor: pointer; - } - .btn[disabled] { cursor: not-allowed; } @@ -535,26 +520,20 @@ display: flex; flex-direction: column; padding: 0.25em 0.5em 0.5em; - line-height:24px; - } - - form textarea.form-cw { - line-height:16px; - resize: none; - overflow: hidden; - transition: min-height 200ms 100ms; - min-height: 1px; + line-height: 1.85; } .form-post-body { - height: 16px; // Only affects the empty-height - line-height: 16px; - resize: none; + // TODO: make a resizable textarea component? + box-sizing: content-box; // needed for easier computation of dynamic size overflow: hidden; transition: min-height 200ms 100ms; - padding-bottom: 1.75em; - min-height: 1px; - box-sizing: content-box; + // stock padding + 1 line of text (for counter) + padding-bottom: calc(var(--_padding) + var(--post-line-height) * 1em); + // two lines of text + height: calc(var(--post-line-height) * 1em); + min-height: calc(var(--post-line-height) * 1em); + resize: none; &.scrollable-form { overflow-y: auto; @@ -578,10 +557,6 @@ } } - .btn { - cursor: pointer; - } - .btn[disabled] { cursor: not-allowed; } @@ -598,7 +573,6 @@ .drop-indicator { position: absolute; - z-index: 1; width: 100%; height: 100%; font-size: 5em; @@ -616,11 +590,4 @@ border: 2px dashed var(--text, $fallback--text); } } - -// todo: unify with attachment.vue (otherwise the uploaded images are not minified unless a status with an attachment was displayed before) -img.media-upload, .media-upload-container > video { - line-height: 0; - max-height: 200px; - max-width: 100%; -} </style> diff --git a/src/components/public_and_external_timeline/public_and_external_timeline.js b/src/components/public_and_external_timeline/public_and_external_timeline.js @@ -9,7 +9,7 @@ const PublicAndExternalTimeline = { created () { this.$store.dispatch('startFetchingTimeline', { timeline: 'publicAndExternal' }) }, - destroyed () { + unmounted () { this.$store.dispatch('stopFetchingTimeline', 'publicAndExternal') } } diff --git a/src/components/public_timeline/public_timeline.js b/src/components/public_timeline/public_timeline.js @@ -9,7 +9,7 @@ const PublicTimeline = { created () { this.$store.dispatch('startFetchingTimeline', { timeline: 'public' }) }, - destroyed () { + unmounted () { this.$store.dispatch('stopFetchingTimeline', 'public') } diff --git a/src/components/quick_filter_settings/quick_filter_settings.js b/src/components/quick_filter_settings/quick_filter_settings.js @@ -0,0 +1,70 @@ +import Popover from '../popover/popover.vue' +import { mapGetters } from 'vuex' +import { library } from '@fortawesome/fontawesome-svg-core' +import { faFilter, faFont, faWrench } from '@fortawesome/free-solid-svg-icons' + +library.add( + faFilter, + faFont, + faWrench +) + +const QuickFilterSettings = { + props: { + conversation: Boolean + }, + components: { + Popover + }, + methods: { + setReplyVisibility (visibility) { + this.$store.dispatch('setOption', { name: 'replyVisibility', value: visibility }) + this.$store.dispatch('queueFlushAll') + }, + openTab (tab) { + this.$store.dispatch('openSettingsModalTab', tab) + } + }, + computed: { + ...mapGetters(['mergedConfig']), + loggedIn () { + return !!this.$store.state.users.currentUser + }, + replyVisibilitySelf: { + get () { return this.mergedConfig.replyVisibility === 'self' }, + set () { this.setReplyVisibility('self') } + }, + replyVisibilityFollowing: { + get () { return this.mergedConfig.replyVisibility === 'following' }, + set () { this.setReplyVisibility('following') } + }, + replyVisibilityAll: { + get () { return this.mergedConfig.replyVisibility === 'all' }, + set () { this.setReplyVisibility('all') } + }, + hideMedia: { + get () { return this.mergedConfig.hideAttachments || this.mergedConfig.hideAttachmentsInConv }, + set () { + const value = !this.hideMedia + this.$store.dispatch('setOption', { name: 'hideAttachments', value }) + this.$store.dispatch('setOption', { name: 'hideAttachmentsInConv', value }) + } + }, + hideMutedPosts: { + get () { return this.mergedConfig.hideFilteredStatuses }, + set () { + const value = !this.hideMutedPosts + this.$store.dispatch('setOption', { name: 'hideFilteredStatuses', value }) + } + }, + muteBotStatuses: { + get () { return this.mergedConfig.muteBotStatuses }, + set () { + const value = !this.muteBotStatuses + this.$store.dispatch('setOption', { name: 'muteBotStatuses', value }) + } + } + } +} + +export default QuickFilterSettings diff --git a/src/components/quick_filter_settings/quick_filter_settings.vue b/src/components/quick_filter_settings/quick_filter_settings.vue @@ -0,0 +1,88 @@ +<template> + <Popover + trigger="click" + class="QuickFilterSettings" + :bound-to="{ x: 'container' }" + :trigger-attrs="{ title: $t('timeline.quick_filter_settings') }" + > + <template #content> + <div class="dropdown-menu"> + <div v-if="loggedIn"> + <button + v-if="!conversation" + class="button-default dropdown-item" + @click="replyVisibilityAll = true" + > + <span + class="menu-checkbox -radio" + :class="{ 'menu-checkbox-checked': replyVisibilityAll }" + />{{ $t('settings.reply_visibility_all') }} + </button> + <button + v-if="!conversation" + class="button-default dropdown-item" + @click="replyVisibilityFollowing = true" + > + <span + class="menu-checkbox -radio" + :class="{ 'menu-checkbox-checked': replyVisibilityFollowing }" + />{{ $t('settings.reply_visibility_following_short') }} + </button> + <button + v-if="!conversation" + class="button-default dropdown-item" + @click="replyVisibilitySelf = true" + > + <span + class="menu-checkbox -radio" + :class="{ 'menu-checkbox-checked': replyVisibilitySelf }" + />{{ $t('settings.reply_visibility_self_short') }} + </button> + <div + v-if="!conversation" + role="separator" + class="dropdown-divider" + /> + </div> + <button + class="button-default dropdown-item" + @click="muteBotStatuses = !muteBotStatuses" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-checked': muteBotStatuses }" + />{{ $t('settings.mute_bot_posts') }} + </button> + <button + class="button-default dropdown-item" + @click="hideMedia = !hideMedia" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-checked': hideMedia }" + />{{ $t('settings.hide_media_previews') }} + </button> + <button + class="button-default dropdown-item" + @click="hideMutedPosts = !hideMutedPosts" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-checked': hideMutedPosts }" + />{{ $t('settings.hide_all_muted_posts') }} + </button> + <button + class="button-default dropdown-item dropdown-item-icon" + @click="openTab('filtering')" + > + <FAIcon icon="font" />{{ $t('settings.word_filter_and_more') }} + </button> + </div> + </template> + <template #trigger> + <FAIcon icon="filter" /> + </template> + </Popover> +</template> + +<script src="./quick_filter_settings.js"></script> diff --git a/src/components/quick_view_settings/quick_view_settings.js b/src/components/quick_view_settings/quick_view_settings.js @@ -0,0 +1,69 @@ +import Popover from '../popover/popover.vue' +import { mapGetters } from 'vuex' +import { library } from '@fortawesome/fontawesome-svg-core' +import { faList, faFolderTree, faBars, faWrench } from '@fortawesome/free-solid-svg-icons' + +library.add( + faList, + faFolderTree, + faBars, + faWrench +) + +const QuickViewSettings = { + props: { + conversation: Boolean + }, + components: { + Popover + }, + methods: { + setConversationDisplay (visibility) { + this.$store.dispatch('setOption', { name: 'conversationDisplay', value: visibility }) + }, + openTab (tab) { + this.$store.dispatch('openSettingsModalTab', tab) + } + }, + computed: { + ...mapGetters(['mergedConfig']), + loggedIn () { + return !!this.$store.state.users.currentUser + }, + conversationDisplay: { + get () { return this.mergedConfig.conversationDisplay }, + set (newVal) { this.setConversationDisplay(newVal) } + }, + autoUpdate: { + get () { return this.mergedConfig.streaming }, + set () { + const value = !this.autoUpdate + this.$store.dispatch('setOption', { name: 'streaming', value }) + } + }, + collapseWithSubjects: { + get () { return this.mergedConfig.collapseMessageWithSubject }, + set () { + const value = !this.collapseWithSubjects + this.$store.dispatch('setOption', { name: 'collapseMessageWithSubject', value }) + } + }, + showUserAvatars: { + get () { return this.mergedConfig.mentionLinkShowAvatar }, + set () { + const value = !this.showUserAvatars + console.log(value) + this.$store.dispatch('setOption', { name: 'mentionLinkShowAvatar', value }) + } + }, + muteBotStatuses: { + get () { return this.mergedConfig.muteBotStatuses }, + set () { + const value = !this.muteBotStatuses + this.$store.dispatch('setOption', { name: 'muteBotStatuses', value }) + } + } + } +} + +export default QuickViewSettings diff --git a/src/components/quick_view_settings/quick_view_settings.vue b/src/components/quick_view_settings/quick_view_settings.vue @@ -0,0 +1,75 @@ +<template> + <Popover + trigger="click" + class="QuickViewSettings" + :bound-to="{ x: 'container' }" + :trigger-attrs="{ title: $t('timeline.quick_view_settings') }" + > + <template #content> + <div class="dropdown-menu"> + <button + class="button-default dropdown-item" + @click="conversationDisplay = 'tree'" + > + <span + class="menu-checkbox -radio" + :class="{ 'menu-checkbox-checked': conversationDisplay === 'tree' }" + /><FAIcon icon="folder-tree" /> {{ $t('settings.conversation_display_tree_quick') }} + </button> + <button + class="button-default dropdown-item" + @click="conversationDisplay = 'linear'" + > + <span + class="menu-checkbox -radio" + :class="{ 'menu-checkbox-checked': conversationDisplay === 'linear' }" + /><FAIcon icon="list" /> {{ $t('settings.conversation_display_linear_quick') }} + </button> + <div + role="separator" + class="dropdown-divider" + /> + <button + class="button-default dropdown-item" + @click="showUserAvatars = !showUserAvatars" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-checked': showUserAvatars }" + />{{ $t('settings.mention_link_show_avatar_quick') }} + </button> + <button + v-if="!conversation" + class="button-default dropdown-item" + @click="autoUpdate = !autoUpdate" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-checked': autoUpdate }" + />{{ $t('settings.auto_update') }} + </button> + <button + v-if="!conversation" + class="button-default dropdown-item" + @click="collapseWithSubjects = !collapseWithSubjects" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-checked': collapseWithSubjects }" + />{{ $t('settings.collapse_subject') }} + </button> + <button + class="button-default dropdown-item dropdown-item-icon" + @click="openTab('general')" + > + <FAIcon icon="wrench" />{{ $t('settings.more_settings') }} + </button> + </div> + </template> + <template #trigger> + <FAIcon icon="bars" /> + </template> + </Popover> +</template> + +<script src="./quick_view_settings.js"></script> diff --git a/src/components/range_input/range_input.vue b/src/components/range_input/range_input.vue @@ -15,7 +15,7 @@ class="opt" type="checkbox" :checked="present" - @input="$emit('input', !present ? fallback : undefined)" + @change="$emit('update:modelValue', !present ? fallback : undefined)" > <label v-if="typeof fallback !== 'undefined'" @@ -26,23 +26,23 @@ :id="name" class="input-number" type="range" - :value="value || fallback" + :value="modelValue || fallback" :disabled="!present || disabled" :max="max || hardMax || 100" :min="min || hardMin || 0" :step="step || 1" - @input="$emit('input', $event.target.value)" + @input="$emit('update:modelValue', $event.target.value)" > <input :id="name" class="input-number" type="number" - :value="value || fallback" + :value="modelValue || fallback" :disabled="!present || disabled" :max="hardMax" :min="hardMin" :step="step || 1" - @input="$emit('input', $event.target.value)" + @input="$emit('update:modelValue', $event.target.value)" > </div> </template> @@ -50,11 +50,12 @@ <script> export default { props: [ - 'name', 'value', 'fallback', 'disabled', 'label', 'max', 'min', 'step', 'hardMin', 'hardMax' + 'name', 'modelValue', 'fallback', 'disabled', 'label', 'max', 'min', 'step', 'hardMin', 'hardMax' ], + emits: ['update:modelValue'], computed: { present () { - return typeof this.value !== 'undefined' + return typeof this.modelValue !== 'undefined' } } } diff --git a/src/components/react_button/react_button.js b/src/components/react_button/react_button.js @@ -1,14 +1,22 @@ import Popover from '../popover/popover.vue' +import { ensureFinalFallback } from '../../i18n/languages.js' import { library } from '@fortawesome/fontawesome-svg-core' +import { faPlus, faTimes } from '@fortawesome/free-solid-svg-icons' import { faSmileBeam } from '@fortawesome/free-regular-svg-icons' +import { trim } from 'lodash' -library.add(faSmileBeam) +library.add( + faPlus, + faTimes, + faSmileBeam +) const ReactButton = { props: ['status'], data () { return { - filterWord: '' + filterWord: '', + expanded: false } }, components: { @@ -24,41 +32,90 @@ const ReactButton = { } close() }, + onShow () { + this.expanded = true + this.focusInput() + }, + onClose () { + this.expanded = false + }, focusInput () { this.$nextTick(() => { const input = this.$el.querySelector('input') if (input) input.focus() }) + }, + // Vaguely adjusted copypaste from emoji_input and emoji_picker! + maybeLocalizedEmojiNamesAndKeywords (emoji) { + const names = [emoji.displayText] + const keywords = [] + + if (emoji.displayTextI18n) { + names.push(this.$t(emoji.displayTextI18n.key, emoji.displayTextI18n.args)) + } + + if (emoji.annotations) { + this.languages.forEach(lang => { + names.push(emoji.annotations[lang]?.name) + + keywords.push(...(emoji.annotations[lang]?.keywords || [])) + }) + } + + return { + names: names.filter(k => k), + keywords: keywords.filter(k => k) + } + }, + maybeLocalizedEmojiName (emoji) { + if (!emoji.annotations) { + return emoji.displayText + } + + if (emoji.displayTextI18n) { + return this.$t(emoji.displayTextI18n.key, emoji.displayTextI18n.args) + } + + for (const lang of this.languages) { + if (emoji.annotations[lang]?.name) { + return emoji.annotations[lang].name + } + } + + return emoji.displayText } }, computed: { commonEmojis () { - return [ - { displayText: 'thumbsup', replacement: '👍' }, - { displayText: 'angry', replacement: '😠' }, - { displayText: 'eyes', replacement: '👀' }, - { displayText: 'joy', replacement: '😂' }, - { displayText: 'fire', replacement: '🔥' } - ] + const hardcodedSet = new Set(['👍', '😠', '👀', '😂', '🔥']) + return this.$store.getters.standardEmojiList.filter(emoji => hardcodedSet.has(emoji.replacement)) + }, + languages () { + return ensureFinalFallback(this.$store.getters.mergedConfig.interfaceLanguage) }, emojis () { if (this.filterWord !== '') { - const filterWordLowercase = this.filterWord.toLowerCase() - let orderedEmojiList = [] - for (const emoji of this.$store.state.instance.emoji) { - if (emoji.replacement === this.filterWord) return [emoji] + const keywordLowercase = trim(this.filterWord.toLowerCase()) + + const orderedEmojiList = [] + for (const emoji of this.$store.getters.standardEmojiList) { + const indices = this.maybeLocalizedEmojiNamesAndKeywords(emoji) + .keywords + .map(k => k.toLowerCase().indexOf(keywordLowercase)) + .filter(k => k > -1) + + const indexOfKeyword = indices.length ? Math.min(...indices) : -1 - const indexOfFilterWord = emoji.displayText.toLowerCase().indexOf(filterWordLowercase) - if (indexOfFilterWord > -1) { - if (!Array.isArray(orderedEmojiList[indexOfFilterWord])) { - orderedEmojiList[indexOfFilterWord] = [] + if (indexOfKeyword > -1) { + if (!Array.isArray(orderedEmojiList[indexOfKeyword])) { + orderedEmojiList[indexOfKeyword] = [] } - orderedEmojiList[indexOfFilterWord].push(emoji) + orderedEmojiList[indexOfKeyword].push(emoji) } } return orderedEmojiList.flat() } - return this.$store.state.instance.emoji || [] + return this.$store.getters.standardEmojiList || [] }, mergedConfig () { return this.$store.getters.mergedConfig diff --git a/src/components/react_button/react_button.vue b/src/components/react_button/react_button.vue @@ -6,14 +6,17 @@ :offset="{ y: 5 }" :bound-to="{ x: 'container' }" remove-padding - @show="focusInput" + popover-class="ReactButton popover-default" + @show="onShow" + @close="onClose" > - <template v-slot:content="{close}"> + <template #content="{close}"> <div class="reaction-picker-filter"> <input v-model="filterWord" size="1" :placeholder="$t('emoji.search_emoji')" + @input="$event.target.composing = false" > </div> <div class="reaction-picker"> @@ -21,7 +24,7 @@ v-for="emoji in commonEmojis" :key="emoji.replacement" class="emoji-button" - :title="emoji.displayText" + :title="maybeLocalizedEmojiName(emoji)" @click="addReaction($event, emoji.replacement, close)" > {{ emoji.replacement }} @@ -31,7 +34,7 @@ v-for="(emoji, key) in emojis" :key="key" class="emoji-button" - :title="emoji.displayText" + :title="maybeLocalizedEmojiName(emoji)" @click="addReaction($event, emoji.replacement, close)" > {{ emoji.replacement }} @@ -39,24 +42,39 @@ <div class="reaction-bottom-fader" /> </div> </template> - <template v-slot:trigger> - <button + <template #trigger> + <span class="button-unstyled popover-trigger" :title="$t('tool_tip.add_reaction')" > - <FAIcon - class="fa-scale-110 fa-old-padding" - :icon="['far', 'smile-beam']" - /> - </button> + <FALayers> + <FAIcon + class="fa-scale-110 fa-old-padding" + :icon="['far', 'smile-beam']" + /> + <FAIcon + v-show="!expanded" + class="focus-marker" + transform="shrink-6 up-9 right-17" + icon="plus" + /> + <FAIcon + v-show="expanded" + class="focus-marker" + transform="shrink-6 up-9 right-17" + icon="times" + /> + </FALayers> + </span> </template> </Popover> </template> -<script src="./react_button.js" ></script> +<script src="./react_button.js"></script> <style lang="scss"> @import '../../_variables.scss'; +@import '../../_mixins.scss'; .ReactButton { .reaction-picker-filter { @@ -101,7 +119,7 @@ cursor: pointer; flex-basis: 20%; - line-height: 1.5em; + line-height: 1.5; align-content: center; &:hover { @@ -123,6 +141,21 @@ color: $fallback--text; color: var(--text, $fallback--text); } + + } + + .popover-trigger-button { + @include unfocused-style { + .focus-marker { + visibility: hidden; + } + } + + @include focused-style { + .focus-marker { + visibility: visible; + } + } } } diff --git a/src/components/registration/registration.js b/src/components/registration/registration.js @@ -1,9 +1,11 @@ -import { validationMixin } from 'vuelidate' -import { required, requiredIf, sameAs } from 'vuelidate/lib/validators' +import useVuelidate from '@vuelidate/core' +import { required, requiredIf, sameAs } from '@vuelidate/validators' import { mapActions, mapState } from 'vuex' +import InterfaceLanguageSwitcher from '../interface_language_switcher/interface_language_switcher.vue' +import localeService from '../../services/locale/locale.service.js' const registration = { - mixins: [validationMixin], + setup () { return { v$: useVuelidate() } }, data: () => ({ user: { email: '', @@ -11,10 +13,14 @@ const registration = { username: '', password: '', confirm: '', - reason: '' + reason: '', + language: '' }, captcha: {} }), + components: { + InterfaceLanguageSwitcher + }, validations () { return { user: { @@ -24,9 +30,10 @@ const registration = { password: { required }, confirm: { required, - sameAsPassword: sameAs('password') + sameAs: sameAs(this.user.password) }, - reason: { required: requiredIf(() => this.accountApprovalRequired) } + reason: { required: requiredIf(() => this.accountApprovalRequired) }, + language: {} } } }, @@ -64,10 +71,13 @@ const registration = { this.user.captcha_solution = this.captcha.solution this.user.captcha_token = this.captcha.token this.user.captcha_answer_data = this.captcha.answer_data + if (this.user.language) { + this.user.language = localeService.internalToBackendLocale(this.user.language) + } - this.$v.$touch() + this.v$.$touch() - if (!this.$v.$invalid) { + if (!this.v$.$invalid) { try { await this.signUp(this.user) this.$router.push({ name: 'friends' }) diff --git a/src/components/registration/registration.vue b/src/components/registration/registration.vue @@ -12,7 +12,7 @@ <div class="text-fields"> <div class="form-group" - :class="{ 'form-group--error': $v.user.username.$error }" + :class="{ 'form-group--error': v$.user.username.$error }" > <label class="form--label" @@ -20,18 +20,19 @@ >{{ $t('login.username') }}</label> <input id="sign-up-username" - v-model.trim="$v.user.username.$model" + v-model.trim="v$.user.username.$model" :disabled="isPending" class="form-control" + :aria-required="true" :placeholder="$t('registration.username_placeholder')" > </div> <div - v-if="$v.user.username.$dirty" + v-if="v$.user.username.$dirty" class="form-error" > <ul> - <li v-if="!$v.user.username.required"> + <li v-if="!v$.user.username.required"> <span>{{ $t('registration.validations.username_required') }}</span> </li> </ul> @@ -39,7 +40,7 @@ <div class="form-group" - :class="{ 'form-group--error': $v.user.fullname.$error }" + :class="{ 'form-group--error': v$.user.fullname.$error }" > <label class="form--label" @@ -47,18 +48,19 @@ >{{ $t('registration.fullname') }}</label> <input id="sign-up-fullname" - v-model.trim="$v.user.fullname.$model" + v-model.trim="v$.user.fullname.$model" :disabled="isPending" class="form-control" + :aria-required="true" :placeholder="$t('registration.fullname_placeholder')" > </div> <div - v-if="$v.user.fullname.$dirty" + v-if="v$.user.fullname.$dirty" class="form-error" > <ul> - <li v-if="!$v.user.fullname.required"> + <li v-if="!v$.user.fullname.required"> <span>{{ $t('registration.validations.fullname_required') }}</span> </li> </ul> @@ -66,26 +68,27 @@ <div class="form-group" - :class="{ 'form-group--error': $v.user.email.$error }" + :class="{ 'form-group--error': v$.user.email.$error }" > <label class="form--label" for="email" - >{{ $t('registration.email') }}</label> + >{{ accountActivationRequired ? $t('registration.email') : $t('registration.email_optional') }}</label> <input id="email" - v-model="$v.user.email.$model" + v-model="v$.user.email.$model" :disabled="isPending" class="form-control" type="email" + :aria-required="accountActivationRequired" > </div> <div - v-if="$v.user.email.$dirty" + v-if="v$.user.email.$dirty" class="form-error" > <ul> - <li v-if="!$v.user.email.required"> + <li v-if="!v$.user.email.required"> <span>{{ $t('registration.validations.email_required') }}</span> </li> </ul> @@ -95,7 +98,7 @@ <label class="form--label" for="bio" - >{{ $t('registration.bio') }} ({{ $t('general.optional') }})</label> + >{{ $t('registration.bio_optional') }}</label> <textarea id="bio" v-model="user.bio" @@ -107,7 +110,7 @@ <div class="form-group" - :class="{ 'form-group--error': $v.user.password.$error }" + :class="{ 'form-group--error': v$.user.password.$error }" > <label class="form--label" @@ -119,14 +122,15 @@ :disabled="isPending" class="form-control" type="password" + :aria-required="true" > </div> <div - v-if="$v.user.password.$dirty" + v-if="v$.user.password.$dirty" class="form-error" > <ul> - <li v-if="!$v.user.password.required"> + <li v-if="!v$.user.password.required"> <span>{{ $t('registration.validations.password_required') }}</span> </li> </ul> @@ -134,7 +138,7 @@ <div class="form-group" - :class="{ 'form-group--error': $v.user.confirm.$error }" + :class="{ 'form-group--error': v$.user.confirm.$error }" > <label class="form--label" @@ -146,23 +150,36 @@ :disabled="isPending" class="form-control" type="password" + :aria-required="true" > </div> <div - v-if="$v.user.confirm.$dirty" + v-if="v$.user.confirm.$dirty" class="form-error" > <ul> - <li v-if="!$v.user.confirm.required"> + <li v-if="v$.user.confirm.required.$invalid"> <span>{{ $t('registration.validations.password_confirmation_required') }}</span> </li> - <li v-if="!$v.user.confirm.sameAsPassword"> + <li v-if="v$.user.confirm.sameAs.$invalid"> <span>{{ $t('registration.validations.password_confirmation_match') }}</span> </li> </ul> </div> <div + class="form-group" + :class="{ 'form-group--error': v$.user.language.$error }" + > + <interface-language-switcher + for="email-language" + :prompt-text="$t('registration.email_language')" + :language="v$.user.language.$model" + :set-language="val => v$.user.language.$model = val" + /> + </div> + + <div v-if="accountApprovalRequired" class="form-group" > @@ -271,7 +288,10 @@ $validations-cRed: #f04124; .container { display: flex; flex-direction: row; - //margin-bottom: 1em; + + > * { + min-width: 0; + } } .terms-of-service { @@ -294,8 +314,8 @@ $validations-cRed: #f04124; .form-group { display: flex; flex-direction: column; - padding: 0.3em 0.0em 0.3em; - line-height:24px; + padding: 0.3em 0; + line-height: 2; margin-bottom: 1em; } @@ -315,7 +335,7 @@ $validations-cRed: #f04124; text-align: left; span { - font-size: 12px; + font-size: 0.85em; } } @@ -341,7 +361,7 @@ $validations-cRed: #f04124; .btn { margin-top: 0.6em; - height: 28px; + height: 2em; } .error { diff --git a/src/components/remote_follow/remote_follow.js b/src/components/remote_follow/remote_follow.js @@ -1,5 +1,5 @@ export default { - props: [ 'user' ], + props: ['user'], computed: { subscribeUrl () { // eslint-disable-next-line no-undef diff --git a/src/components/remote_follow/remote_follow.vue b/src/components/remote_follow/remote_follow.vue @@ -32,7 +32,7 @@ .remote-button { width: 100%; - min-height: 28px; + min-height: 2em; } } </style> diff --git a/src/components/remove_follower_button/remove_follower_button.js b/src/components/remove_follower_button/remove_follower_button.js @@ -0,0 +1,25 @@ +export default { + props: ['relationship'], + data () { + return { + inProgress: false + } + }, + computed: { + label () { + if (this.inProgress) { + return this.$t('user_card.follow_progress') + } else { + return this.$t('user_card.remove_follower') + } + } + }, + methods: { + onClick () { + this.inProgress = true + this.$store.dispatch('removeUserFromFollowers', this.relationship.id).then(() => { + this.inProgress = false + }) + } + } +} diff --git a/src/components/remove_follower_button/remove_follower_button.vue b/src/components/remove_follower_button/remove_follower_button.vue @@ -0,0 +1,13 @@ +<template> + <button + class="btn button-default follow-button" + :class="{ toggled: inProgress }" + :disabled="inProgress" + :title="$t('user_card.remove_follower')" + @click="onClick" + > + {{ label }} + </button> +</template> + +<script src="./remove_follower_button.js"></script> diff --git a/src/components/reply_button/reply_button.js b/src/components/reply_button/reply_button.js @@ -1,7 +1,15 @@ import { library } from '@fortawesome/fontawesome-svg-core' -import { faReply } from '@fortawesome/free-solid-svg-icons' +import { + faReply, + faPlus, + faTimes +} from '@fortawesome/free-solid-svg-icons' -library.add(faReply) +library.add( + faReply, + faPlus, + faTimes +) const ReplyButton = { name: 'ReplyButton', @@ -9,6 +17,9 @@ const ReplyButton = { computed: { loggedIn () { return !!this.$store.state.users.currentUser + }, + remoteInteractionLink () { + return this.$store.getters.remoteInteractionLink({ statusId: this.status.id }) } } } diff --git a/src/components/reply_button/reply_button.vue b/src/components/reply_button/reply_button.vue @@ -7,18 +7,38 @@ :title="$t('tool_tip.reply')" @click.prevent="$emit('toggle')" > - <FAIcon - class="fa-scale-110 fa-old-padding" - icon="reply" - /> + <FALayers class="fa-old-padding-layer"> + <FAIcon + class="fa-scale-110" + icon="reply" + /> + <FAIcon + v-if="!replying" + class="focus-marker" + transform="shrink-6 up-8 right-11" + icon="plus" + /> + <FAIcon + v-else + class="focus-marker" + transform="shrink-6 up-8 right-11" + icon="times" + /> + </FALayers> </button> - <span v-else> + <a + v-else + class="button-unstyled interactive" + target="_blank" + role="button" + :href="remoteInteractionLink" + > <FAIcon icon="reply" class="fa-scale-110 fa-old-padding" :title="$t('tool_tip.reply')" /> - </span> + </a> <span v-if="status.replies_count > 0" class="action-counter" @@ -32,6 +52,7 @@ <style lang="scss"> @import '../../_variables.scss'; +@import '../../_mixins.scss'; .ReplyButton { display: flex; @@ -52,6 +73,18 @@ color: $fallback--cBlue; color: var(--cBlue, $fallback--cBlue); } + + @include unfocused-style { + .focus-marker { + visibility: hidden; + } + } + + @include focused-style { + .focus-marker { + visibility: visible; + } + } } } diff --git a/src/components/report/report.js b/src/components/report/report.js @@ -0,0 +1,34 @@ +import Select from '../select/select.vue' +import StatusContent from '../status_content/status_content.vue' +import Timeago from '../timeago/timeago.vue' +import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' + +const Report = { + props: [ + 'reportId' + ], + components: { + Select, + StatusContent, + Timeago + }, + computed: { + report () { + return this.$store.state.reports.reports[this.reportId] || {} + }, + state: { + get: function () { return this.report.state }, + set: function (val) { this.setReportState(val) } + } + }, + methods: { + generateUserProfileLink (user) { + return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames) + }, + setReportState (state) { + return this.$store.dispatch('setReportState', { id: this.report.id, state }) + } + } +} + +export default Report diff --git a/src/components/report/report.scss b/src/components/report/report.scss @@ -0,0 +1,43 @@ +@import '../../_variables.scss'; + +.Report { + .report-content { + margin: 0.5em 0 1em; + } + + .report-state { + margin: 0.5em 0 1em; + } + + .reported-status { + border: 1px solid $fallback--faint; + border-color: var(--faint, $fallback--faint); + border-radius: $fallback--inputRadius; + border-radius: var(--inputRadius, $fallback--inputRadius); + color: $fallback--text; + color: var(--text, $fallback--text); + display: block; + padding: 0.5em; + margin: 0.5em 0; + + .status-content { + pointer-events: none; + } + + .reported-status-heading { + display: flex; + width: 100%; + justify-content: space-between; + margin-bottom: 0.2em; + } + + .reported-status-name { + font-weight: bold; + } + } + + .note { + width: 100%; + margin-bottom: 0.5em; + } +} diff --git a/src/components/report/report.vue b/src/components/report/report.vue @@ -0,0 +1,74 @@ +<template> + <div class="Report"> + <div class="reported-user"> + <span>{{ $t('report.reported_user') }}</span> + <router-link :to="generateUserProfileLink(report.acct)"> + @{{ report.acct.screen_name }} + </router-link> + </div> + <div class="reporter"> + <span>{{ $t('report.reporter') }}</span> + <router-link :to="generateUserProfileLink(report.actor)"> + @{{ report.actor.screen_name }} + </router-link> + </div> + <div class="report-state"> + <span>{{ $t('report.state') }}</span> + <Select + :id="report-state" + v-model="state" + class="form-control" + > + <option + v-for="state in ['open', 'closed', 'resolved']" + :key="state" + :value="state" + > + {{ $t('report.state_' + state) }} + </option> + </Select> + </div> + <RichContent + class="report-content" + :html="report.content" + :emoji="[]" + /> + <div v-if="report.statuses.length"> + <small>{{ $t('report.reported_statuses') }}</small> + <router-link + v-for="status in report.statuses" + :key="status.id" + :to="{ name: 'conversation', params: { id: status.id } }" + class="reported-status" + > + <div class="reported-status-heading"> + <span class="reported-status-name">{{ status.user.name }}</span> + <Timeago + :time="status.created_at" + :auto-update="240" + class="faint" + /> + </div> + <status-content :status="status" /> + </router-link> + </div> + <div v-if="report.notes.length"> + <small>{{ $t('report.notes') }}</small> + <div + v-for="note in report.notes" + :key="note.id" + class="note" + > + <span>{{ note.content }}</span> + <Timeago + :time="note.created_at" + :auto-update="240" + class="faint" + /> + </div> + </div> + </div> +</template> + +<script src="./report.js"></script> +<style src="./report.scss" lang="scss"></style> diff --git a/src/components/retweet_button/retweet_button.js b/src/components/retweet_button/retweet_button.js @@ -1,7 +1,17 @@ import { library } from '@fortawesome/fontawesome-svg-core' -import { faRetweet } from '@fortawesome/free-solid-svg-icons' +import { + faRetweet, + faPlus, + faMinus, + faCheck +} from '@fortawesome/free-solid-svg-icons' -library.add(faRetweet) +library.add( + faRetweet, + faPlus, + faMinus, + faCheck +) const RetweetButton = { props: ['status', 'loggedIn', 'visibility'], @@ -26,6 +36,9 @@ const RetweetButton = { computed: { mergedConfig () { return this.$store.getters.mergedConfig + }, + remoteInteractionLink () { + return this.$store.getters.remoteInteractionLink({ statusId: this.status.id }) } } } diff --git a/src/components/retweet_button/retweet_button.vue b/src/components/retweet_button/retweet_button.vue @@ -7,11 +7,31 @@ :title="$t('tool_tip.repeat')" @click.prevent="retweet()" > - <FAIcon - class="fa-scale-110 fa-old-padding" - icon="retweet" - :spin="animated" - /> + <FALayers class="fa-old-padding-layer"> + <FAIcon + class="fa-scale-110" + icon="retweet" + :spin="animated" + /> + <FAIcon + v-if="status.repeated" + class="active-marker" + transform="shrink-6 up-9 right-12" + icon="check" + /> + <FAIcon + v-if="!status.repeated" + class="focus-marker" + transform="shrink-6 up-9 right-12" + icon="plus" + /> + <FAIcon + v-else + class="focus-marker" + transform="shrink-6 up-9 right-12" + icon="minus" + /> + </FALayers> </button> <span v-else-if="loggedIn"> <FAIcon @@ -20,13 +40,19 @@ :title="$t('timeline.no_retweet_hint')" /> </span> - <span v-else> + <a + v-else + class="button-unstyled interactive" + target="_blank" + role="button" + :href="remoteInteractionLink" + > <FAIcon class="fa-scale-110 fa-old-padding" icon="retweet" :title="$t('tool_tip.repeat')" /> - </span> + </a> <span v-if="!mergedConfig.hidePostStats && status.repeat_num > 0" class="no-event" @@ -36,10 +62,11 @@ </div> </template> -<script src="./retweet_button.js" ></script> +<script src="./retweet_button.js"></script> <style lang="scss"> @import '../../_variables.scss'; +@import '../../_mixins.scss'; .RetweetButton { display: flex; @@ -64,6 +91,26 @@ color: $fallback--cGreen; color: var(--cGreen, $fallback--cGreen); } + + @include unfocused-style { + .focus-marker { + visibility: hidden; + } + + .active-marker { + visibility: visible; + } + } + + @include focused-style { + .focus-marker { + visibility: visible; + } + + .active-marker { + visibility: hidden; + } + } } } </style> diff --git a/src/components/rich_content/rich_content.jsx b/src/components/rich_content/rich_content.jsx @@ -1,4 +1,3 @@ -import Vue from 'vue' import { unescape, flattenDeep } from 'lodash' import { getTagName, processTextForEmoji, getAttrs } from 'src/services/html_converter/utility.service.js' import { convertHtmlToTree } from 'src/services/html_converter/html_tree_converter.service.js' @@ -27,8 +26,12 @@ import './rich_content.scss' * * Apart from that one small hiccup with emit in render this _should_ be vue3-ready */ -export default Vue.component('RichContent', { +export default { name: 'RichContent', + components: { + MentionsLine, + HashtagLink + }, props: { // Original html content html: { @@ -58,7 +61,7 @@ export default Vue.component('RichContent', { } }, // NEVER EVER TOUCH DATA INSIDE RENDER - render (h) { + render () { // Pre-process HTML const { newHtml: html } = preProcessPerLine(this.html, this.greentext) let currentMentions = null // Current chain of mentions, we group all mentions together @@ -76,18 +79,19 @@ export default Vue.component('RichContent', { const renderImage = (tag) => { return <StillImage - {...{ attrs: getAttrs(tag) }} + {...getAttrs(tag)} class="img" /> } const renderHashtag = (attrs, children, encounteredTextReverse) => { - const linkData = getLinkData(attrs, children, tagsIndex++) + const { index, ...linkData } = getLinkData(attrs, children, tagsIndex++) writtenTags.push(linkData) if (!encounteredTextReverse) { lastTags.push(linkData) } - return <HashtagLink {...{ props: linkData }}/> + const { url, tag, content } = linkData + return <HashtagLink url={url} tag={tag} content={content}/> } const renderMention = (attrs, children) => { @@ -120,7 +124,8 @@ export default Vue.component('RichContent', { // don't include spaces when processing mentions - we'll include them // in MentionsLine lastSpacing = item - return currentMentions !== null ? item.trim() : item + // Don't remove last space in a container (fixes poast mentions) + return (index !== array.length - 1) && (currentMentions !== null) ? item.trim() : item } currentMentions = null @@ -145,6 +150,7 @@ export default Vue.component('RichContent', { if (Array.isArray(item)) { const [opener, children, closer] = item const Tag = getTagName(opener) + const fullAttrs = getAttrs(opener, () => true) const attrs = getAttrs(opener) const previouslyMentions = currentMentions !== null /* During grouping of mentions we trim all the empty text elements @@ -166,7 +172,7 @@ export default Vue.component('RichContent', { return ['', [mentionsLinePadding, renderImage(opener)], ''] case 'a': // replace mentions with MentionLink if (!this.handleLinks) break - if (attrs['class'] && attrs['class'].includes('mention')) { + if (fullAttrs.class && fullAttrs.class.includes('mention')) { // Handling mentions here return renderMention(attrs, children) } else { @@ -174,7 +180,7 @@ export default Vue.component('RichContent', { break } case 'span': - if (this.handleLinks && attrs['class'] && attrs['class'].includes('h-card')) { + if (this.handleLinks && fullAttrs.class && fullAttrs.class.includes('h-card')) { return ['', children.map(processItem), ''] } } @@ -208,23 +214,25 @@ export default Vue.component('RichContent', { const [opener, children] = item const Tag = opener === '' ? '' : getTagName(opener) switch (Tag) { - case 'a': // replace mentions with MentionLink + case 'a': { // replace mentions with MentionLink if (!this.handleLinks) break - const attrs = getAttrs(opener) + const fullAttrs = getAttrs(opener, () => true) + const attrs = getAttrs(opener, () => true) // should only be this if ( - (attrs['class'] && attrs['class'].includes('hashtag')) || // Pleroma style - (attrs['rel'] === 'tag') // Mastodon style + (fullAttrs.class && fullAttrs.class.includes('hashtag')) || // Pleroma style + (fullAttrs.rel === 'tag') // Mastodon style ) { return renderHashtag(attrs, children, encounteredTextReverse) } else { attrs.target = '_blank' const newChildren = [...children].reverse().map(processItemReverse).reverse() - return <a {...{ attrs }}> + return <a {...attrs}> { newChildren } </a> } + } case '': return [...children].reverse().map(processItemReverse).reverse() } @@ -234,7 +242,7 @@ export default Vue.component('RichContent', { const newChildren = Array.isArray(children) ? [...children].reverse().map(processItemReverse).reverse() : children - return <Tag {...{ attrs: getAttrs(opener) }}> + return <Tag {...getAttrs(opener)}> { newChildren } </Tag> } else { @@ -265,7 +273,7 @@ export default Vue.component('RichContent', { return result } -}) +} const getLinkData = (attrs, children, index) => { const stripTags = (item) => { diff --git a/src/components/scope_selector/scope_selector.vue b/src/components/scope_selector/scope_selector.vue @@ -16,6 +16,7 @@ class="fa-scale-110 fa-old-padding" /> </button> + {{ ' ' }} <button v-if="showPrivate" class="button-unstyled scope" @@ -29,6 +30,7 @@ class="fa-scale-110 fa-old-padding" /> </button> + {{ ' ' }} <button v-if="showUnlisted" class="button-unstyled scope" @@ -42,6 +44,7 @@ class="fa-scale-110 fa-old-padding" /> </button> + {{ ' ' }} <button v-if="showPublic" class="button-unstyled scope" diff --git a/src/components/search/search.js b/src/components/search/search.js @@ -1,12 +1,14 @@ import FollowCard from '../follow_card/follow_card.vue' import Conversation from '../conversation/conversation.vue' import Status from '../status/status.vue' +import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' import map from 'lodash/map' import { library } from '@fortawesome/fontawesome-svg-core' import { faCircleNotch, faSearch } from '@fortawesome/free-solid-svg-icons' +import { uniqBy } from 'lodash' library.add( faCircleNotch, @@ -17,7 +19,8 @@ const Search = { components: { FollowCard, Conversation, - Status + Status, + TabSwitcher }, props: [ 'query' @@ -30,7 +33,11 @@ const Search = { userIds: [], statuses: [], hashtags: [], - currenResultTab: 'statuses' + currenResultTab: 'statuses', + + statusesOffset: 0, + lastStatusFetchCount: 0, + lastQuery: '' } }, computed: { @@ -59,26 +66,42 @@ const Search = { this.$router.push({ name: 'search', query: { query } }) this.$refs.searchInput.focus() }, - search (query) { + search (query, searchType = null) { if (!query) { this.loading = false return } this.loading = true - this.userIds = [] - this.statuses = [] - this.hashtags = [] this.$refs.searchInput.blur() + if (this.lastQuery !== query) { + this.userIds = [] + this.hashtags = [] + this.statuses = [] + + this.statusesOffset = 0 + this.lastStatusFetchCount = 0 + } - this.$store.dispatch('search', { q: query, resolve: true }) + this.$store.dispatch('search', { q: query, resolve: true, offset: this.statusesOffset, type: searchType }) .then(data => { this.loading = false - this.userIds = map(data.accounts, 'id') - this.statuses = data.statuses - this.hashtags = data.hashtags + + const oldLength = this.statuses.length + + // Always append to old results. If new results are empty, this doesn't change anything + this.userIds = this.userIds.concat(map(data.accounts, 'id')) + this.statuses = uniqBy(this.statuses.concat(data.statuses), 'id') + this.hashtags = this.hashtags.concat(data.hashtags) + this.currenResultTab = this.getActiveTab() this.loaded = true + + // Offset from whatever we already have + this.statusesOffset = this.statuses.length + // Because the amount of new statuses can actually be zero, compare to old lenght instead + this.lastStatusFetchCount = this.statuses.length - oldLength + this.lastQuery = query }) }, resultCount (tabName) { diff --git a/src/components/search/search.vue b/src/components/search/search.vue @@ -22,7 +22,7 @@ </button> </div> <div - v-if="loading" + v-if="loading && statusesOffset == 0" class="text-center loading-icon" > <FAIcon @@ -55,12 +55,6 @@ </div> <div class="panel-body"> <div v-if="currenResultTab === 'statuses'"> - <div - v-if="visibleStatuses.length === 0 && !loading && loaded" - class="search-result-heading" - > - <h4>{{ $t('search.no_results') }}</h4> - </div> <Status v-for="status in visibleStatuses" :key="status.id" @@ -71,6 +65,33 @@ :statusoid="status" :no-heading="false" /> + <button + v-if="!loading && loaded && lastStatusFetchCount > 0" + class="more-statuses-button button-unstyled -link -fullwidth" + @click.prevent="search(searchTerm, 'statuses')" + > + <div class="new-status-notification text-center"> + {{ $t('search.load_more') }} + </div> + </button> + <div + v-else-if="loading && statusesOffset > 0" + class="text-center loading-icon" + > + <FAIcon + icon="circle-notch" + spin + size="lg" + /> + </div> + <div + v-if="(visibleStatuses.length === 0 || lastStatusFetchCount === 0) && !loading && loaded" + class="search-result-heading" + > + <h4> + {{ visibleStatuses.length === 0 ? $t('search.no_results') : $t('search.no_more_results') }} + </h4> + </div> </div> <div v-else-if="currenResultTab === 'people'"> <div @@ -208,6 +229,11 @@ color: $fallback--text; color: var(--text, $fallback--text); } -} + } + + .more-statuses-button { + height: 3.5em; + line-height: 3.5em; + } </style> diff --git a/src/components/search_bar/search_bar.js b/src/components/search_bar/search_bar.js @@ -16,7 +16,7 @@ const SearchBar = { error: false }), watch: { - '$route': function (route) { + $route: function (route) { if (route.name === 'search') { this.searchTerm = route.query.query } diff --git a/src/components/search_bar/search_bar.vue b/src/components/search_bar/search_bar.vue @@ -47,6 +47,8 @@ class="cancel-icon fa-scale-110 fa-old-padding" /> </button> + <span class="spacer" /> + <span class="spacer" /> </template> </div> </template> diff --git a/src/components/select/select.js b/src/components/select/select.js @@ -8,12 +8,9 @@ library.add( ) export default { - model: { - prop: 'value', - event: 'change' - }, + emits: ['update:modelValue'], props: [ - 'value', + 'modelValue', 'disabled', 'unstyled', 'kind' diff --git a/src/components/select/select.vue b/src/components/select/select.vue @@ -1,4 +1,3 @@ - <template> <label class="Select input" @@ -6,11 +5,12 @@ > <select :disabled="disabled" - :value="value" - @change="$emit('change', $event.target.value)" + :value="modelValue" + @change="$emit('update:modelValue', $event.target.value)" > <slot /> </select> + {{ ' ' }} <FAIcon class="select-down-icon" icon="chevron-down" @@ -23,7 +23,8 @@ <style lang="scss"> @import '../../_variables.scss'; -.Select { +/* TODO fix order of styles */ +label.Select { padding: 0; select { @@ -38,10 +39,10 @@ padding: 0 2em 0 .2em; font-family: sans-serif; font-family: var(--inputFont, sans-serif); - font-size: 14px; + font-size: 1em; width: 100%; z-index: 1; - height: 28px; + height: 2em; line-height: 16px; } @@ -51,9 +52,10 @@ bottom: 0; right: 5px; height: 100%; + width: 0.875em; color: $fallback--text; color: var(--inputText, $fallback--text); - line-height: 28px; + line-height: 2; z-index: 0; pointer-events: none; } diff --git a/src/components/selectable_list/selectable_list.vue b/src/components/selectable_list/selectable_list.vue @@ -6,9 +6,9 @@ > <div class="selectable-list-checkbox-wrapper"> <Checkbox - :checked="allSelected" + :model-value="allSelected" :indeterminate="someSelected" - @change="toggleAll" + @update:model-value="toggleAll" > {{ $t('selectable_list.select_all') }} </Checkbox> @@ -24,15 +24,15 @@ :items="items" :get-key="getKey" > - <template v-slot:item="{item}"> + <template #item="{item}"> <div class="selectable-list-item-inner" :class="{ 'selectable-list-item-selected-inner': isSelected(item) }" > <div class="selectable-list-checkbox-wrapper"> <Checkbox - :checked="isSelected(item)" - @change="checked => toggle(checked, item)" + :model-value="isSelected(item)" + @update:model-value="checked => toggle(checked, item)" /> </div> <slot @@ -41,7 +41,7 @@ /> </div> </template> - <template v-slot:empty> + <template #empty> <slot name="empty" /> </template> </List> diff --git a/src/components/settings_modal/helpers/boolean_setting.js b/src/components/settings_modal/helpers/boolean_setting.js @@ -1,14 +1,17 @@ import { get, set } from 'lodash' import Checkbox from 'src/components/checkbox/checkbox.vue' import ModifiedIndicator from './modified_indicator.vue' +import ServerSideIndicator from './server_side_indicator.vue' export default { components: { Checkbox, - ModifiedIndicator + ModifiedIndicator, + ServerSideIndicator }, props: [ 'path', - 'disabled' + 'disabled', + 'expert' ], computed: { pathDefault () { @@ -26,13 +29,28 @@ export default { defaultState () { return get(this.$parent, this.pathDefault) }, + isServerSide () { + return this.path.startsWith('serverSide_') + }, isChanged () { - return this.state !== this.defaultState + return !this.path.startsWith('serverSide_') && this.state !== this.defaultState + }, + matchesExpertLevel () { + return (this.expert || 0) <= this.$parent.expertLevel } }, methods: { update (e) { + const [firstSegment, ...rest] = this.path.split('.') set(this.$parent, this.path, e) + // Updating nested properties does not trigger update on its parent. + // probably still not as reliable, but works for depth=1 at least + if (rest.length > 0) { + set(this.$parent, firstSegment, { ...get(this.$parent, firstSegment) }) + } + }, + reset () { + set(this.$parent, this.path, this.defaultState) } } } diff --git a/src/components/settings_modal/helpers/boolean_setting.vue b/src/components/settings_modal/helpers/boolean_setting.vue @@ -1,11 +1,12 @@ <template> <label + v-if="matchesExpertLevel" class="BooleanSetting" > <Checkbox - :checked="state" + :model-value="state" :disabled="disabled" - @change="update" + @update:modelValue="update" > <span v-if="!!$slots.default" @@ -13,7 +14,12 @@ > <slot /> </span> - <ModifiedIndicator :changed="isChanged" /> + {{ ' ' }} + <ModifiedIndicator + :changed="isChanged" + :onclick="reset" + /> + <ServerSideIndicator :server-side="isServerSide" /> </Checkbox> </label> </template> diff --git a/src/components/settings_modal/helpers/choice_setting.js b/src/components/settings_modal/helpers/choice_setting.js @@ -1,15 +1,18 @@ import { get, set } from 'lodash' import Select from 'src/components/select/select.vue' import ModifiedIndicator from './modified_indicator.vue' +import ServerSideIndicator from './server_side_indicator.vue' export default { components: { Select, - ModifiedIndicator + ModifiedIndicator, + ServerSideIndicator }, props: [ 'path', 'disabled', - 'options' + 'options', + 'expert' ], computed: { pathDefault () { @@ -27,13 +30,22 @@ export default { defaultState () { return get(this.$parent, this.pathDefault) }, + isServerSide () { + return this.path.startsWith('serverSide_') + }, isChanged () { - return this.state !== this.defaultState + return !this.path.startsWith('serverSide_') && this.state !== this.defaultState + }, + matchesExpertLevel () { + return (this.expert || 0) <= this.$parent.expertLevel } }, methods: { update (e) { set(this.$parent, this.path, e) + }, + reset () { + set(this.$parent, this.path, this.defaultState) } } } diff --git a/src/components/settings_modal/helpers/choice_setting.vue b/src/components/settings_modal/helpers/choice_setting.vue @@ -1,12 +1,14 @@ <template> <label + v-if="matchesExpertLevel" class="ChoiceSetting" > <slot /> + {{ ' ' }} <Select - :value="state" + :model-value="state" :disabled="disabled" - @change="update" + @update:modelValue="update" > <option v-for="option in options" @@ -17,7 +19,11 @@ {{ option.value === defaultState ? $t('settings.instance_default_simple') : '' }} </option> </Select> - <ModifiedIndicator :changed="isChanged" /> + <ModifiedIndicator + :changed="isChanged" + :onclick="reset" + /> + <ServerSideIndicator :server-side="isServerSide" /> </label> </template> diff --git a/src/components/settings_modal/helpers/integer_setting.js b/src/components/settings_modal/helpers/integer_setting.js @@ -0,0 +1,44 @@ +import { get, set } from 'lodash' +import ModifiedIndicator from './modified_indicator.vue' +export default { + components: { + ModifiedIndicator + }, + props: { + path: String, + disabled: Boolean, + min: Number, + expert: [Number, String] + }, + computed: { + pathDefault () { + const [firstSegment, ...rest] = this.path.split('.') + return [firstSegment + 'DefaultValue', ...rest].join('.') + }, + state () { + const value = get(this.$parent, this.path) + if (value === undefined) { + return this.defaultState + } else { + return value + } + }, + defaultState () { + return get(this.$parent, this.pathDefault) + }, + isChanged () { + return this.state !== this.defaultState + }, + matchesExpertLevel () { + return (this.expert || 0) <= this.$parent.expertLevel + } + }, + methods: { + update (e) { + set(this.$parent, this.path, parseInt(e.target.value)) + }, + reset () { + set(this.$parent, this.path, this.defaultState) + } + } +} diff --git a/src/components/settings_modal/helpers/integer_setting.vue b/src/components/settings_modal/helpers/integer_setting.vue @@ -0,0 +1,27 @@ +<template> + <span + v-if="matchesExpertLevel" + class="IntegerSetting" + > + <label :for="path"> + <slot /> + </label> + <input + :id="path" + class="number-input" + type="number" + step="1" + :disabled="disabled" + :min="min || 0" + :value="state" + @change="update" + > + {{ ' ' }} + <ModifiedIndicator + :changed="isChanged" + :onclick="reset" + /> + </span> +</template> + +<script src="./integer_setting.js"></script> diff --git a/src/components/settings_modal/helpers/modified_indicator.vue b/src/components/settings_modal/helpers/modified_indicator.vue @@ -6,14 +6,14 @@ <Popover trigger="hover" > - <template v-slot:trigger> + <template #trigger> &nbsp; <FAIcon icon="wrench" :aria-label="$t('settings.setting_changed')" /> </template> - <template v-slot:content> + <template #content> <div class="modified-tooltip"> {{ $t('settings.setting_changed') }} </div> @@ -41,11 +41,11 @@ export default { .ModifiedIndicator { display: inline-block; position: relative; +} - .modified-tooltip { - margin: 0.5em 1em; - min-width: 10em; - text-align: center; - } +.modified-tooltip { + margin: 0.5em 1em; + min-width: 10em; + text-align: center; } </style> diff --git a/src/components/settings_modal/helpers/server_side_indicator.vue b/src/components/settings_modal/helpers/server_side_indicator.vue @@ -0,0 +1,51 @@ +<template> + <span + v-if="serverSide" + class="ServerSideIndicator" + > + <Popover + trigger="hover" + > + <template #trigger> + &nbsp; + <FAIcon + icon="server" + :aria-label="$t('settings.setting_server_side')" + /> + </template> + <template #content> + <div class="serverside-tooltip"> + {{ $t('settings.setting_server_side') }} + </div> + </template> + </Popover> + </span> +</template> + +<script> +import Popover from 'src/components/popover/popover.vue' +import { library } from '@fortawesome/fontawesome-svg-core' +import { faServer } from '@fortawesome/free-solid-svg-icons' + +library.add( + faServer +) + +export default { + components: { Popover }, + props: ['serverSide'] +} +</script> + +<style lang="scss"> +.ServerSideIndicator { + display: inline-block; + position: relative; +} + +.serverside-tooltip { + margin: 0.5em 1em; + min-width: 10em; + text-align: center; +} +</style> diff --git a/src/components/settings_modal/helpers/shared_computed_object.js b/src/components/settings_modal/helpers/shared_computed_object.js @@ -1,4 +1,5 @@ import { defaultState as configDefaultState } from 'src/modules/config.js' +import { defaultState as serverSideConfigDefaultState } from 'src/modules/serverSideConfig.js' const SharedComputedObject = () => ({ user () { @@ -22,6 +23,14 @@ const SharedComputedObject = () => ({ } }]) .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}), + ...Object.keys(serverSideConfigDefaultState) + .map(key => ['serverSide_' + key, { + get () { return this.$store.state.serverSideConfig[key] }, + set (value) { + this.$store.dispatch('setServerSideOption', { name: key, value }) + } + }]) + .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}), // Special cases (need to transform values or perform actions first) useStreamingApi: { get () { return this.$store.getters.mergedConfig.useStreamingApi }, diff --git a/src/components/settings_modal/helpers/size_setting.js b/src/components/settings_modal/helpers/size_setting.js @@ -0,0 +1,67 @@ +import { get, set } from 'lodash' +import ModifiedIndicator from './modified_indicator.vue' +import Select from 'src/components/select/select.vue' + +export const allCssUnits = ['cm', 'mm', 'in', 'px', 'pt', 'pc', 'em', 'ex', 'ch', 'rem', 'vw', 'vh', 'vmin', 'vmax', '%'] +export const defaultHorizontalUnits = ['px', 'rem', 'vw'] +export const defaultVerticalUnits = ['px', 'rem', 'vh'] + +export default { + components: { + ModifiedIndicator, + Select + }, + props: { + path: String, + disabled: Boolean, + min: Number, + units: { + type: [String], + default: () => allCssUnits + }, + expert: [Number, String] + }, + computed: { + pathDefault () { + const [firstSegment, ...rest] = this.path.split('.') + return [firstSegment + 'DefaultValue', ...rest].join('.') + }, + stateUnit () { + return (this.state || '').replace(/\d+/, '') + }, + stateValue () { + return (this.state || '').replace(/\D+/, '') + }, + state () { + const value = get(this.$parent, this.path) + if (value === undefined) { + return this.defaultState + } else { + return value + } + }, + defaultState () { + return get(this.$parent, this.pathDefault) + }, + isChanged () { + return this.state !== this.defaultState + }, + matchesExpertLevel () { + return (this.expert || 0) <= this.$parent.expertLevel + } + }, + methods: { + update (e) { + set(this.$parent, this.path, e) + }, + reset () { + set(this.$parent, this.path, this.defaultState) + }, + updateValue (e) { + set(this.$parent, this.path, parseInt(e.target.value) + this.stateUnit) + }, + updateUnit (e) { + set(this.$parent, this.path, this.stateValue + e.target.value) + } + } +} diff --git a/src/components/settings_modal/helpers/size_setting.vue b/src/components/settings_modal/helpers/size_setting.vue @@ -0,0 +1,54 @@ +<template> + <span + v-if="matchesExpertLevel" + class="SizeSetting" + > + <label + :for="path" + class="size-label" + > + <slot /> + </label> + <input + :id="path" + class="number-input" + type="number" + step="1" + :disabled="disabled" + :min="min || 0" + :value="stateValue" + @change="updateValue" + > + <Select + :id="path" + :model-value="stateUnit" + :disabled="disabled" + class="css-unit-input" + @change="updateUnit" + > + <option + v-for="option in units" + :key="option" + :value="option" + > + {{ option }} + </option> + </Select> + {{ ' ' }} + <ModifiedIndicator + :changed="isChanged" + :onclick="reset" + /> + </span> +</template> + +<script src="./size_setting.js"></script> + +<style lang="scss"> +.css-unit-input, .css-unit-input select { + margin-left: 0.5em; + width: 4em !important; + max-width: 4em !important; + min-width: 4em !important; +} +</style> diff --git a/src/components/settings_modal/settings_modal.js b/src/components/settings_modal/settings_modal.js @@ -3,6 +3,7 @@ import PanelLoading from 'src/components/panel_loading/panel_loading.vue' import AsyncComponentError from 'src/components/async_component_error/async_component_error.vue' import getResettableAsyncComponent from 'src/services/resettable_async_component.js' import Popover from '../popover/popover.vue' +import Checkbox from 'src/components/checkbox/checkbox.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { cloneDeep } from 'lodash' import { @@ -51,11 +52,12 @@ const SettingsModal = { components: { Modal, Popover, + Checkbox, SettingsModalContent: getResettableAsyncComponent( () => import('./settings_modal_content.vue'), { - loading: PanelLoading, - error: AsyncComponentError, + loadingComponent: PanelLoading, + errorComponent: AsyncComponentError, delay: 0 } ) @@ -159,6 +161,15 @@ const SettingsModal = { }, modalPeeked () { return this.$store.state.interface.settingsModalState === 'minimized' + }, + expertLevel: { + get () { + return this.$store.state.config.expertLevel > 0 + }, + set (value) { + console.log(value) + this.$store.dispatch('setOption', { name: 'expertLevel', value: value ? 1 : 0 }) + } } } } diff --git a/src/components/settings_modal/settings_modal.scss b/src/components/settings_modal/settings_modal.scss @@ -2,6 +2,18 @@ .settings-modal { overflow: hidden; + .setting-list, + .option-list { + list-style-type: none; + padding-left: 2em; + li { + margin-bottom: 0.5em; + } + .suboptions { + margin-top: 0.3em + } + } + &.peek { .settings-modal-panel { /* Explanation: @@ -42,10 +54,22 @@ overflow-y: hidden; .btn { - min-height: 28px; + min-height: 2em; min-width: 10em; padding: 0 2em; } } } + + .settings-footer { + display: flex; + >* { + margin-right: 0.5em; + } + + .extra-content { + display: flex; + flex-grow: 1; + } + } } diff --git a/src/components/settings_modal/settings_modal.vue b/src/components/settings_modal/settings_modal.vue @@ -11,23 +11,14 @@ {{ $t('settings.settings') }} </span> <transition name="fade"> - <template v-if="currentSaveStateNotice"> - <div - v-if="currentSaveStateNotice.error" - class="alert error" - @click.prevent - > - {{ $t('settings.saving_err') }} - </div> - - <div - v-if="!currentSaveStateNotice.error" - class="alert transparent" - @click.prevent - > - {{ $t('settings.saving_ok') }} - </div> - </template> + <div + v-if="currentSaveStateNotice" + class="alert" + :class="{ transparent: !currentSaveStateNotice.error, error: currentSaveStateNotice.error}" + @click.prevent + > + {{ currentSaveStateNotice.error ? $t('settings.saving_err') : $t('settings.saving_ok') }} + </div> </transition> <button class="btn button-default" @@ -53,7 +44,7 @@ <div class="panel-body"> <SettingsModalContent v-if="modalOpenedOnce" /> </div> - <div class="panel-footer"> + <div class="panel-footer settings-footer"> <Popover class="export" trigger="click" @@ -62,18 +53,19 @@ :bound-to="{ x: 'container' }" remove-padding > - <template v-slot:trigger> + <template #trigger> <button class="btn button-default" :title="$t('general.close')" > <span>{{ $t("settings.file_export_import.backup_restore") }}</span> + {{ ' ' }} <FAIcon icon="chevron-down" /> </button> </template> - <template v-slot:content="{close}"> + <template #content="{close}"> <div class="dropdown-menu"> <button class="button-default dropdown-item dropdown-item-icon" @@ -108,6 +100,17 @@ </div> </template> </Popover> + + <Checkbox + :model-value="!!expertLevel" + @update:modelValue="expertLevel = Number($event)" + > + {{ $t("settings.expert_mode") }} + </Checkbox> + <span + id="unscrolled-content" + class="extra-content" + /> </div> </div> </Modal> diff --git a/src/components/settings_modal/settings_modal_content.js b/src/components/settings_modal/settings_modal_content.js @@ -1,4 +1,4 @@ -import TabSwitcher from 'src/components/tab_switcher/tab_switcher.js' +import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' import DataImportExportTab from './tabs/data_import_export_tab.vue' import MutesAndBlocksTab from './tabs/mutes_and_blocks_tab.vue' @@ -53,6 +53,9 @@ const SettingsModalContent = { }, open () { return this.$store.state.interface.settingsModalState !== 'hidden' + }, + bodyLock () { + return this.$store.state.interface.settingsModalState === 'visible' } }, methods: { @@ -60,8 +63,8 @@ const SettingsModalContent = { const targetTab = this.$store.state.interface.settingsModalTargetTab // We're being told to open in specific tab if (targetTab) { - const tabIndex = this.$refs.tabSwitcher.$slots.default.findIndex(elm => { - return elm.data && elm.data.attrs['data-tab-name'] === targetTab + const tabIndex = this.$refs.tabSwitcher.$slots.default().findIndex(elm => { + return elm.props && elm.props['data-tab-name'] === targetTab }) if (tabIndex >= 0) { this.$refs.tabSwitcher.setTab(tabIndex) diff --git a/src/components/settings_modal/settings_modal_content.vue b/src/components/settings_modal/settings_modal_content.vue @@ -4,6 +4,7 @@ class="settings_tab-switcher" :side-tab-bar="true" :scrollable-tabs="true" + :body-scroll-lock="bodyLock" > <div :label="$t('settings.general')" diff --git a/src/components/settings_modal/tabs/data_import_export_tab.js b/src/components/settings_modal/tabs/data_import_export_tab.js @@ -7,11 +7,16 @@ const DataImportExportTab = { data () { return { activeTab: 'profile', - newDomainToMute: '' + newDomainToMute: '', + listBackupsError: false, + addBackupError: false, + addedBackup: false, + backups: [] } }, created () { this.$store.dispatch('fetchTokens') + this.fetchBackups() }, components: { Importer, @@ -72,6 +77,28 @@ const DataImportExportTab = { } return user.screen_name }).join('\n') + }, + addBackup () { + this.$store.state.api.backendInteractor.addBackup() + .then((res) => { + this.addedBackup = true + this.addBackupError = false + }) + .catch((error) => { + this.addedBackup = false + this.addBackupError = error + }) + .then(() => this.fetchBackups()) + }, + fetchBackups () { + this.$store.state.api.backendInteractor.listBackups() + .then((res) => { + this.backups = res + this.listBackupsError = false + }) + .catch((error) => { + this.listBackupsError = error.error + }) } } } diff --git a/src/components/settings_modal/tabs/data_import_export_tab.vue b/src/components/settings_modal/tabs/data_import_export_tab.vue @@ -53,6 +53,67 @@ :export-button-label="$t('settings.mute_export_button')" /> </div> + <div class="setting-item"> + <h2>{{ $t('settings.account_backup') }}</h2> + <p>{{ $t('settings.account_backup_description') }}</p> + <table> + <thead> + <tr> + <th>{{ $t('settings.account_backup_table_head') }}</th> + <th /> + </tr> + </thead> + <tbody> + <tr + v-for="backup in backups" + :key="backup.id" + > + <td>{{ backup.inserted_at }}</td> + <td class="actions"> + <a + v-if="backup.processed" + target="_blank" + :href="backup.url" + > + {{ $t('settings.download_backup') }} + </a> + <span + v-else + > + {{ $t('settings.backup_not_ready') }} + </span> + </td> + </tr> + </tbody> + </table> + <div + v-if="listBackupsError" + class="alert error" + > + {{ $t('settings.list_backups_error', { error }) }} + <button + :title="$t('settings.hide_list_backups_error_action')" + @click="listBackupsError = false" + > + <FAIcon + class="fa-scale-110 fa-old-padding" + icon="times" + /> + </button> + </div> + <button + class="btn button-default" + @click="addBackup" + > + {{ $t('settings.add_backup') }} + </button> + <p v-if="addedBackup"> + {{ $t('settings.added_backup') }} + </p> + <template v-if="addBackupError !== false"> + <p>{{ $t('settings.add_backup_error', { error: addBackupError }) }}</p> + </template> + </div> </div> </template> diff --git a/src/components/settings_modal/tabs/filtering_tab.js b/src/components/settings_modal/tabs/filtering_tab.js @@ -1,6 +1,7 @@ import { filter, trim } from 'lodash' import BooleanSetting from '../helpers/boolean_setting.vue' import ChoiceSetting from '../helpers/choice_setting.vue' +import IntegerSetting from '../helpers/integer_setting.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' @@ -17,7 +18,8 @@ const FilteringTab = { }, components: { BooleanSetting, - ChoiceSetting + ChoiceSetting, + IntegerSetting }, computed: { ...SharedComputedObject(), @@ -36,15 +38,6 @@ const FilteringTab = { }, // Updating nested properties watch: { - notificationVisibility: { - handler (value) { - this.$store.dispatch('setOption', { - name: 'notificationVisibility', - value: this.$store.getters.mergedConfig.notificationVisibility - }) - }, - deep: true - }, replyVisibility () { this.$store.dispatch('queueFlushAll') } diff --git a/src/components/settings_modal/tabs/filtering_tab.vue b/src/components/settings_modal/tabs/filtering_tab.vue @@ -1,73 +1,110 @@ <template> <div :label="$t('settings.filtering')"> <div class="setting-item"> - <div class="select-multiple"> - <span class="label">{{ $t('settings.notification_visibility') }}</span> - <ul class="option-list"> - <li> - <BooleanSetting path="notificationVisibility.likes"> - {{ $t('settings.notification_visibility_likes') }} - </BooleanSetting> - </li> - <li> - <BooleanSetting path="notificationVisibility.repeats"> - {{ $t('settings.notification_visibility_repeats') }} - </BooleanSetting> - </li> - <li> - <BooleanSetting path="notificationVisibility.follows"> - {{ $t('settings.notification_visibility_follows') }} - </BooleanSetting> - </li> - <li> - <BooleanSetting path="notificationVisibility.mentions"> - {{ $t('settings.notification_visibility_mentions') }} - </BooleanSetting> - </li> - <li> - <BooleanSetting path="notificationVisibility.moves"> - {{ $t('settings.notification_visibility_moves') }} - </BooleanSetting> - </li> - <li> - <BooleanSetting path="notificationVisibility.emojiReactions"> - {{ $t('settings.notification_visibility_emoji_reactions') }} - </BooleanSetting> - </li> - </ul> - </div> - <ChoiceSetting - id="replyVisibility" - path="replyVisibility" - :options="replyVisibilityOptions" - > - {{ $t('settings.replies_in_timeline') }} - </ChoiceSetting> - <div> - <BooleanSetting path="hidePostStats"> - {{ $t('settings.hide_post_stats') }} - </BooleanSetting> - </div> - <div> - <BooleanSetting path="hideUserStats"> - {{ $t('settings.hide_user_stats') }} - </BooleanSetting> - </div> + <h2>{{ $t('settings.posts') }}</h2> + <ul class="setting-list"> + <li> + <BooleanSetting path="hideFilteredStatuses"> + {{ $t('settings.hide_filtered_statuses') }} + </BooleanSetting> + <ul + class="setting-list suboptions" + :class="[{disabled: !streaming}]" + > + <li> + <BooleanSetting + :disabled="hideFilteredStatuses" + path="hideWordFilteredPosts" + > + {{ $t('settings.hide_wordfiltered_statuses') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting + v-if="user" + :disabled="hideFilteredStatuses" + path="hideMutedThreads" + > + {{ $t('settings.hide_muted_threads') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting + v-if="user" + :disabled="hideFilteredStatuses" + path="hideMutedPosts" + > + {{ $t('settings.hide_muted_posts') }} + </BooleanSetting> + </li> + </ul> + </li> + <li> + <BooleanSetting path="muteBotStatuses"> + {{ $t('settings.mute_bot_posts') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting path="hidePostStats"> + {{ $t('settings.hide_post_stats') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting path="hideBotIndication"> + {{ $t('settings.hide_bot_indication') }} + </BooleanSetting> + </li> + <ChoiceSetting + v-if="user" + id="replyVisibility" + path="replyVisibility" + :options="replyVisibilityOptions" + > + {{ $t('settings.replies_in_timeline') }} + </ChoiceSetting> + <li> + <h3>{{ $t('settings.wordfilter') }}</h3> + <textarea + id="muteWords" + v-model="muteWordsString" + class="resize-height" + /> + <div>{{ $t('settings.filtering_explanation') }}</div> + </li> + <h3>{{ $t('settings.attachments') }}</h3> + <li> + <IntegerSetting + path="maxThumbnails" + expert="1" + :min="0" + > + {{ $t('settings.max_thumbnails') }} + </IntegerSetting> + </li> + <li> + <BooleanSetting path="hideAttachments"> + {{ $t('settings.hide_attachments_in_tl') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting path="hideAttachmentsInConv"> + {{ $t('settings.hide_attachments_in_convo') }} + </BooleanSetting> + </li> + </ul> </div> - <div class="setting-item"> - <div> - <p>{{ $t('settings.filtering_explanation') }}</p> - <textarea - id="muteWords" - v-model="muteWordsString" - class="resize-height" - /> - </div> - <div> - <BooleanSetting path="hideFilteredStatuses"> - {{ $t('settings.hide_filtered_statuses') }} - </BooleanSetting> - </div> + <div + v-if="expertLevel > 0" + class="setting-item" + > + <h2>{{ $t('settings.user_profiles') }}</h2> + <ul class="setting-list"> + <li> + <BooleanSetting path="hideUserStats"> + {{ $t('settings.hide_user_stats') }} + </BooleanSetting> + </li> + </ul> </div> </div> </template> diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js @@ -1,8 +1,12 @@ import BooleanSetting from '../helpers/boolean_setting.vue' import ChoiceSetting from '../helpers/choice_setting.vue' +import ScopeSelector from 'src/components/scope_selector/scope_selector.vue' +import IntegerSetting from '../helpers/integer_setting.vue' +import SizeSetting, { defaultHorizontalUnits } from '../helpers/size_setting.vue' import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' +import ServerSideIndicator from '../helpers/server_side_indicator.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faGlobe @@ -20,6 +24,31 @@ const GeneralTab = { value: mode, label: this.$t(`settings.subject_line_${mode === 'masto' ? 'mastodon' : mode}`) })), + conversationDisplayOptions: ['tree', 'linear'].map(mode => ({ + key: mode, + value: mode, + label: this.$t(`settings.conversation_display_${mode}`) + })), + conversationOtherRepliesButtonOptions: ['below', 'inside'].map(mode => ({ + key: mode, + value: mode, + label: this.$t(`settings.conversation_other_replies_button_${mode}`) + })), + mentionLinkDisplayOptions: ['short', 'full_for_remote', 'full'].map(mode => ({ + key: mode, + value: mode, + label: this.$t(`settings.mention_link_display_${mode}`) + })), + thirdColumnModeOptions: ['none', 'notifications', 'postform'].map(mode => ({ + key: mode, + value: mode, + label: this.$t(`settings.third_column_mode_${mode}`) + })), + userPopoverAvatarActionOptions: ['close', 'zoom', 'open'].map(mode => ({ + key: mode, + value: mode, + label: this.$t(`settings.user_popover_avatar_action_${mode}`) + })), loopSilentAvailable: // Firefox Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') || @@ -32,9 +61,16 @@ const GeneralTab = { components: { BooleanSetting, ChoiceSetting, - InterfaceLanguageSwitcher + IntegerSetting, + SizeSetting, + InterfaceLanguageSwitcher, + ScopeSelector, + ServerSideIndicator }, computed: { + horizontalUnits () { + return defaultHorizontalUnits + }, postFormats () { return this.$store.state.instance.postFormats || [] }, @@ -45,13 +81,35 @@ const GeneralTab = { label: this.$t(`post_status.content_type["${format}"]`) })) }, + columns () { + const mode = this.$store.getters.mergedConfig.thirdColumnMode + + const notif = mode === 'none' ? [] : ['notifs'] + + if (this.$store.getters.mergedConfig.sidebarRight || mode === 'postform') { + return [...notif, 'content', 'sidebar'] + } else { + return ['sidebar', 'content', ...notif] + } + }, instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel }, instanceWallpaperUsed () { return this.$store.state.instance.background && !this.$store.state.users.currentUser.background_image }, instanceShoutboxPresent () { return this.$store.state.instance.shoutAvailable }, + language: { + get: function () { return this.$store.getters.mergedConfig.interfaceLanguage }, + set: function (val) { + this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val }) + } + }, ...SharedComputedObject() + }, + methods: { + changeDefaultScope (value) { + this.$store.dispatch('setServerSideOption', { name: 'defaultScope', value }) + } } } diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue @@ -4,41 +4,25 @@ <h2>{{ $t('settings.interface') }}</h2> <ul class="setting-list"> <li> - <interface-language-switcher /> + <interface-language-switcher + :prompt-text="$t('settings.interfaceLanguage')" + :language="language" + :set-language="val => language = val" + /> </li> <li v-if="instanceSpecificPanelPresent"> <BooleanSetting path="hideISP"> {{ $t('settings.hide_isp') }} </BooleanSetting> </li> - <li> - <BooleanSetting path="sidebarRight"> - {{ $t('settings.right_sidebar') }} - </BooleanSetting> - </li> <li v-if="instanceWallpaperUsed"> <BooleanSetting path="hideInstanceWallpaper"> {{ $t('settings.hide_wallpaper') }} </BooleanSetting> </li> - <li v-if="instanceShoutboxPresent"> - <BooleanSetting path="hideShoutbox"> - {{ $t('settings.hide_shoutbox') }} - </BooleanSetting> - </li> - </ul> - </div> - <div class="setting-item"> - <h2>{{ $t('nav.timeline') }}</h2> - <ul class="setting-list"> - <li> - <BooleanSetting path="hideMutedPosts"> - {{ $t('settings.hide_muted_posts') }} - </BooleanSetting> - </li> <li> - <BooleanSetting path="collapseMessageWithSubject"> - {{ $t('settings.collapse_subject') }} + <BooleanSetting path="stopGifs"> + {{ $t('settings.stop_gifs') }} </BooleanSetting> </li> <li> @@ -60,111 +44,191 @@ </ul> </li> <li> - <BooleanSetting path="useStreamingApi"> + <BooleanSetting + path="useStreamingApi" + expert="1" + > {{ $t('settings.useStreamingApi') }} - <br> - <small> - {{ $t('settings.useStreamingApiWarning') }} - </small> </BooleanSetting> </li> <li> - <BooleanSetting path="emojiReactionsOnTimeline"> - {{ $t('settings.emoji_reactions_on_timeline') }} + <BooleanSetting + path="virtualScrolling" + expert="1" + > + {{ $t('settings.virtual_scrolling') }} </BooleanSetting> </li> <li> - <BooleanSetting path="virtualScrolling"> - {{ $t('settings.virtual_scrolling') }} - </BooleanSetting> + <ChoiceSetting + id="userPopoverAvatarAction" + path="userPopoverAvatarAction" + :options="userPopoverAvatarActionOptions" + expert="1" + > + {{ $t('settings.user_popover_avatar_action') }} + </ChoiceSetting> </li> - </ul> - </div> - - <div class="setting-item"> - <h2>{{ $t('settings.composing') }}</h2> - <ul class="setting-list"> <li> - <BooleanSetting path="scopeCopy"> - {{ $t('settings.scope_copy') }} + <BooleanSetting + path="userPopoverOverlay" + expert="1" + > + {{ $t('settings.user_popover_avatar_overlay') }} </BooleanSetting> </li> <li> - <BooleanSetting path="alwaysShowSubjectInput"> - {{ $t('settings.subject_input_always_show') }} + <BooleanSetting + path="alwaysShowNewPostButton" + expert="1" + > + {{ $t('settings.always_show_post_button') }} </BooleanSetting> </li> <li> - <ChoiceSetting - id="subjectLineBehavior" - path="subjectLineBehavior" - :options="subjectLineOptions" + <BooleanSetting + path="autohideFloatingPostButton" + expert="1" > - {{ $t('settings.subject_line_behavior') }} - </ChoiceSetting> + {{ $t('settings.autohide_floating_post_button') }} + </BooleanSetting> </li> - <li v-if="postFormats.length > 0"> - <ChoiceSetting - id="postContentType" - path="postContentType" - :options="postContentOptions" + <li v-if="instanceShoutboxPresent"> + <BooleanSetting + path="hideShoutbox" + expert="1" > - {{ $t('settings.post_status_content_type') }} - </ChoiceSetting> + {{ $t('settings.hide_shoutbox') }} + </BooleanSetting> </li> <li> - <BooleanSetting path="minimalScopesMode"> - {{ $t('settings.minimal_scopes_mode') }} - </BooleanSetting> + <h3>{{ $t('settings.columns') }}</h3> </li> <li> - <BooleanSetting path="sensitiveByDefault"> - {{ $t('settings.sensitive_by_default') }} + <BooleanSetting path="disableStickyHeaders"> + {{ $t('settings.disable_sticky_headers') }} </BooleanSetting> </li> <li> - <BooleanSetting path="alwaysShowNewPostButton"> - {{ $t('settings.always_show_post_button') }} + <BooleanSetting path="showScrollbars"> + {{ $t('settings.show_scrollbars') }} </BooleanSetting> </li> <li> - <BooleanSetting path="autohideFloatingPostButton"> - {{ $t('settings.autohide_floating_post_button') }} + <BooleanSetting path="sidebarRight"> + {{ $t('settings.right_sidebar') }} </BooleanSetting> </li> <li> - <BooleanSetting path="padEmoji"> - {{ $t('settings.pad_emoji') }} + <BooleanSetting path="navbarColumnStretch"> + {{ $t('settings.navbar_column_stretch') }} </BooleanSetting> </li> + <li> + <ChoiceSetting + v-if="user" + id="thirdColumnMode" + path="thirdColumnMode" + :options="thirdColumnModeOptions" + > + {{ $t('settings.third_column_mode') }} + </ChoiceSetting> + </li> + <li v-if="expertLevel > 0"> + {{ $t('settings.column_sizes') }} + <div class="column-settings"> + <SizeSetting + v-for="column in columns" + :key="column" + :path="column + 'ColumnWidth'" + :units="horizontalUnits" + expert="1" + > + {{ $t('settings.column_sizes_' + column) }} + </SizeSetting> + </div> + </li> </ul> </div> - <div class="setting-item"> - <h2>{{ $t('settings.attachments') }}</h2> + <h2>{{ $t('settings.post_look_feel') }}</h2> <ul class="setting-list"> <li> - <BooleanSetting path="hideAttachments"> - {{ $t('settings.hide_attachments_in_tl') }} + <ChoiceSetting + id="conversationDisplay" + path="conversationDisplay" + :options="conversationDisplayOptions" + > + {{ $t('settings.conversation_display') }} + </ChoiceSetting> + </li> + <ul + v-if="conversationDisplay !== 'linear'" + class="setting-list suboptions" + > + <li> + <BooleanSetting path="conversationTreeAdvanced"> + {{ $t('settings.tree_advanced') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting + path="conversationTreeFadeAncestors" + :expert="1" + > + {{ $t('settings.tree_fade_ancestors') }} + </BooleanSetting> + </li> + <li> + <IntegerSetting + path="maxDepthInThread" + :min="3" + :expert="1" + > + {{ $t('settings.max_depth_in_thread') }} + </IntegerSetting> + </li> + <li> + <ChoiceSetting + id="conversationOtherRepliesButton" + path="conversationOtherRepliesButton" + :options="conversationOtherRepliesButtonOptions" + :expert="1" + > + {{ $t('settings.conversation_other_replies_button') }} + </ChoiceSetting> + </li> + </ul> + <li> + <BooleanSetting path="collapseMessageWithSubject"> + {{ $t('settings.collapse_subject') }} </BooleanSetting> </li> <li> - <BooleanSetting path="hideAttachmentsInConv"> - {{ $t('settings.hide_attachments_in_convo') }} + <BooleanSetting + path="emojiReactionsOnTimeline" + expert="1" + > + {{ $t('settings.emoji_reactions_on_timeline') }} </BooleanSetting> </li> <li> - <label for="maxThumbnails"> - {{ $t('settings.max_thumbnails') }} - </label> - <input - id="maxThumbnails" - path.number="maxThumbnails" - class="number-input" - type="number" - min="0" - step="1" + <BooleanSetting + v-if="user" + path="serverSide_stripRichContent" + expert="1" > + {{ $t('settings.no_rich_text_description') }} + </BooleanSetting> + </li> + <h3>{{ $t('settings.attachments') }}</h3> + <li> + <BooleanSetting + path="useContainFit" + expert="1" + > + {{ $t('settings.use_contain_fit') }} + </BooleanSetting> </li> <li> <BooleanSetting path="hideNsfw"> @@ -175,6 +239,7 @@ <li> <BooleanSetting path="preloadImage" + expert="1" :disabled="!hideNsfw" > {{ $t('settings.preload_images') }} @@ -183,6 +248,7 @@ <li> <BooleanSetting path="useOneClickNsfw" + expert="1" :disabled="!hideNsfw" > {{ $t('settings.use_one_click_nsfw') }} @@ -190,12 +256,10 @@ </li> </ul> <li> - <BooleanSetting path="stopGifs"> - {{ $t('settings.stop_gifs') }} - </BooleanSetting> - </li> - <li> - <BooleanSetting path="loopVideo"> + <BooleanSetting + path="loopVideo" + expert="1" + > {{ $t('settings.loop_video') }} </BooleanSetting> <ul @@ -205,6 +269,7 @@ <li> <BooleanSetting path="loopVideoSilentOnly" + expert="1" :disabled="!loopVideo || !loopSilentAvailable" > {{ $t('settings.loop_video_silent_only') }} @@ -219,35 +284,171 @@ </ul> </li> <li> - <BooleanSetting path="playVideosInModal"> + <BooleanSetting + path="playVideosInModal" + expert="1" + > {{ $t('settings.play_videos_in_modal') }} </BooleanSetting> </li> + <h3>{{ $t('settings.mention_links') }}</h3> <li> - <BooleanSetting path="useContainFit"> - {{ $t('settings.use_contain_fit') }} + <ChoiceSetting + id="mentionLinkDisplay" + path="mentionLinkDisplay" + :options="mentionLinkDisplayOptions" + > + {{ $t('settings.mention_link_display') }} + </ChoiceSetting> + </li> + <li> + <BooleanSetting + path="mentionLinkShowTooltip" + expert="1" + > + {{ $t('settings.mention_link_use_tooltip') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting + path="useAtIcon" + expert="1" + > + {{ $t('settings.use_at_icon') }} </BooleanSetting> </li> - </ul> - </div> - - <div class="setting-item"> - <h2>{{ $t('settings.notifications') }}</h2> - <ul class="setting-list"> <li> - <BooleanSetting path="webPushNotifications"> - {{ $t('settings.enable_web_push_notifications') }} + <BooleanSetting path="mentionLinkShowAvatar"> + {{ $t('settings.mention_link_show_avatar') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting + path="mentionLinkFadeDomain" + expert="1" + > + {{ $t('settings.mention_link_fade_domain') }} + </BooleanSetting> + </li> + <li v-if="user"> + <BooleanSetting + path="mentionLinkBoldenYou" + expert="1" + > + {{ $t('settings.mention_link_bolden_you') }} + </BooleanSetting> + </li> + <h3 v-if="expertLevel > 0"> + {{ $t('settings.fun') }} + </h3> + <li> + <BooleanSetting + path="greentext" + expert="1" + > + {{ $t('settings.greentext') }} + </BooleanSetting> + </li> + <li v-if="user"> + <BooleanSetting + path="mentionLinkShowYous" + expert="1" + > + {{ $t('settings.show_yous') }} </BooleanSetting> </li> </ul> </div> - <div class="setting-item"> - <h2>{{ $t('settings.fun') }}</h2> + <div + v-if="user" + class="setting-item" + > + <h2>{{ $t('settings.composing') }}</h2> <ul class="setting-list"> <li> - <BooleanSetting path="greentext"> - {{ $t('settings.greentext') }} + <label for="default-vis"> + {{ $t('settings.default_vis') }} <ServerSideIndicator :server-side="true" /> + <ScopeSelector + class="scope-selector" + :show-all="true" + :user-default="serverSide_defaultScope" + :initial-scope="serverSide_defaultScope" + :on-scope-change="changeDefaultScope" + /> + </label> + </li> + <li> + <!-- <BooleanSetting path="serverSide_defaultNSFW"> --> + <BooleanSetting path="sensitiveByDefault"> + {{ $t('settings.sensitive_by_default') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting + path="scopeCopy" + expert="1" + > + {{ $t('settings.scope_copy') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting + path="alwaysShowSubjectInput" + expert="1" + > + {{ $t('settings.subject_input_always_show') }} + </BooleanSetting> + </li> + <li> + <ChoiceSetting + id="subjectLineBehavior" + path="subjectLineBehavior" + :options="subjectLineOptions" + expert="1" + > + {{ $t('settings.subject_line_behavior') }} + </ChoiceSetting> + </li> + <li v-if="postFormats.length > 0"> + <ChoiceSetting + id="postContentType" + path="postContentType" + :options="postContentOptions" + > + {{ $t('settings.post_status_content_type') }} + </ChoiceSetting> + </li> + <li> + <BooleanSetting + path="minimalScopesMode" + expert="1" + > + {{ $t('settings.minimal_scopes_mode') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting + path="alwaysShowNewPostButton" + expert="1" + > + {{ $t('settings.always_show_post_button') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting + path="autohideFloatingPostButton" + expert="1" + > + {{ $t('settings.autohide_floating_post_button') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting + path="padEmoji" + expert="1" + > + {{ $t('settings.pad_emoji') }} </BooleanSetting> </li> </ul> @@ -256,3 +457,16 @@ </template> <script src="./general_tab.js"></script> + +<style lang="scss"> +.column-settings { + display: flex; + justify-content: space-evenly; + flex-wrap: wrap; +} +.column-settings .size-label { + display: block; + margin-bottom: 0.5em; + margin-top: 0.5em; +} +</style> diff --git a/src/components/settings_modal/tabs/mutes_and_blocks_tab.js b/src/components/settings_modal/tabs/mutes_and_blocks_tab.js @@ -2,7 +2,7 @@ import get from 'lodash/get' import map from 'lodash/map' import reject from 'lodash/reject' import Autosuggest from 'src/components/autosuggest/autosuggest.vue' -import TabSwitcher from 'src/components/tab_switcher/tab_switcher.js' +import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' import BlockCard from 'src/components/block_card/block_card.vue' import MuteCard from 'src/components/mute_card/mute_card.vue' import DomainMuteCard from 'src/components/domain_mute_card/domain_mute_card.vue' diff --git a/src/components/settings_modal/tabs/mutes_and_blocks_tab.scss b/src/components/settings_modal/tabs/mutes_and_blocks_tab.scss @@ -8,7 +8,7 @@ .bulk-actions { text-align: right; padding: 0 1em; - min-height: 28px; + min-height: 2em; } .bulk-action-button { diff --git a/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue b/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue @@ -10,7 +10,7 @@ :query="queryUserIds" :placeholder="$t('settings.search_user_to_block')" > - <template v-slot="row"> + <template #default="row"> <BlockCard :user-id="row.item" /> @@ -21,7 +21,7 @@ :refresh="true" :get-key="i => i" > - <template v-slot:header="{selected}"> + <template #header="{selected}"> <div class="bulk-actions"> <ProgressButton v-if="selected.length > 0" @@ -29,7 +29,7 @@ :click="() => blockUsers(selected)" > {{ $t('user_card.block') }} - <template v-slot:progress> + <template #progress> {{ $t('user_card.block_progress') }} </template> </ProgressButton> @@ -39,16 +39,16 @@ :click="() => unblockUsers(selected)" > {{ $t('user_card.unblock') }} - <template v-slot:progress> + <template #progress> {{ $t('user_card.unblock_progress') }} </template> </ProgressButton> </div> </template> - <template v-slot:item="{item}"> + <template #item="{item}"> <BlockCard :user-id="item" /> </template> - <template v-slot:empty> + <template #empty> {{ $t('settings.no_blocks') }} </template> </BlockList> @@ -56,14 +56,14 @@ <div :label="$t('settings.mutes_tab')"> <tab-switcher> - <div label="Users"> + <div :label="$t('settings.user_mutes')"> <div class="usersearch-wrapper"> <Autosuggest :filter="filterUnMutedUsers" :query="queryUserIds" :placeholder="$t('settings.search_user_to_mute')" > - <template v-slot="row"> + <template #default="row"> <MuteCard :user-id="row.item" /> @@ -74,7 +74,7 @@ :refresh="true" :get-key="i => i" > - <template v-slot:header="{selected}"> + <template #header="{selected}"> <div class="bulk-actions"> <ProgressButton v-if="selected.length > 0" @@ -82,7 +82,7 @@ :click="() => muteUsers(selected)" > {{ $t('user_card.mute') }} - <template v-slot:progress> + <template #progress> {{ $t('user_card.mute_progress') }} </template> </ProgressButton> @@ -92,16 +92,16 @@ :click="() => unmuteUsers(selected)" > {{ $t('user_card.unmute') }} - <template v-slot:progress> + <template #progress> {{ $t('user_card.unmute_progress') }} </template> </ProgressButton> </div> </template> - <template v-slot:item="{item}"> + <template #item="{item}"> <MuteCard :user-id="item" /> </template> - <template v-slot:empty> + <template #empty> {{ $t('settings.no_mutes') }} </template> </MuteList> @@ -114,7 +114,7 @@ :query="queryKnownDomains" :placeholder="$t('settings.type_domains_to_mute')" > - <template v-slot="row"> + <template #default="row"> <DomainMuteCard :domain="row.item" /> @@ -125,7 +125,7 @@ :refresh="true" :get-key="i => i" > - <template v-slot:header="{selected}"> + <template #header="{selected}"> <div class="bulk-actions"> <ProgressButton v-if="selected.length > 0" @@ -133,16 +133,16 @@ :click="() => unmuteDomains(selected)" > {{ $t('domain_mute_card.unmute') }} - <template v-slot:progress> + <template #progress> {{ $t('domain_mute_card.unmute_progress') }} </template> </ProgressButton> </div> </template> - <template v-slot:item="{item}"> + <template #item="{item}"> <DomainMuteCard :domain="item" /> </template> - <template v-slot:empty> + <template #empty> {{ $t('settings.no_mutes') }} </template> </DomainMuteList> diff --git a/src/components/settings_modal/tabs/notifications_tab.js b/src/components/settings_modal/tabs/notifications_tab.js @@ -1,4 +1,5 @@ -import Checkbox from 'src/components/checkbox/checkbox.vue' +import BooleanSetting from '../helpers/boolean_setting.vue' +import SharedComputedObject from '../helpers/shared_computed_object.js' const NotificationsTab = { data () { @@ -9,12 +10,13 @@ const NotificationsTab = { } }, components: { - Checkbox + BooleanSetting }, computed: { user () { return this.$store.state.users.currentUser - } + }, + ...SharedComputedObject() }, methods: { updateNotificationSettings () { diff --git a/src/components/settings_modal/tabs/notifications_tab.vue b/src/components/settings_modal/tabs/notifications_tab.vue @@ -2,30 +2,82 @@ <div :label="$t('settings.notifications')"> <div class="setting-item"> <h2>{{ $t('settings.notification_setting_filters') }}</h2> - <p> - <Checkbox v-model="notificationSettings.block_from_strangers"> - {{ $t('settings.notification_setting_block_from_strangers') }} - </Checkbox> - </p> + <ul class="setting-list"> + <li> + <BooleanSetting path="serverSide_blockNotificationsFromStrangers"> + {{ $t('settings.notification_setting_block_from_strangers') }} + </BooleanSetting> + </li> + <li class="select-multiple"> + <span class="label">{{ $t('settings.notification_visibility') }}</span> + <ul class="option-list"> + <li> + <BooleanSetting path="notificationVisibility.likes"> + {{ $t('settings.notification_visibility_likes') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting path="notificationVisibility.repeats"> + {{ $t('settings.notification_visibility_repeats') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting path="notificationVisibility.follows"> + {{ $t('settings.notification_visibility_follows') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting path="notificationVisibility.mentions"> + {{ $t('settings.notification_visibility_mentions') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting path="notificationVisibility.moves"> + {{ $t('settings.notification_visibility_moves') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting path="notificationVisibility.emojiReactions"> + {{ $t('settings.notification_visibility_emoji_reactions') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting path="notificationVisibility.polls"> + {{ $t('settings.notification_visibility_polls') }} + </BooleanSetting> + </li> + </ul> + </li> + </ul> </div> - <div class="setting-item"> + <div + v-if="expertLevel > 0" + class="setting-item" + > <h2>{{ $t('settings.notification_setting_privacy') }}</h2> - <p> - <Checkbox v-model="notificationSettings.hide_notification_contents"> - {{ $t('settings.notification_setting_hide_notification_contents') }} - </Checkbox> - </p> + <ul class="setting-list"> + <li> + <BooleanSetting + path="webPushNotifications" + expert="1" + > + {{ $t('settings.enable_web_push_notifications') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting + path="serverSide_webPushHideContents" + expert="1" + > + {{ $t('settings.notification_setting_hide_notification_contents') }} + </BooleanSetting> + </li> + </ul> </div> <div class="setting-item"> <p>{{ $t('settings.notification_mutes') }}</p> <p>{{ $t('settings.notification_blocks') }}</p> - <button - class="btn button-default" - @click="updateNotificationSettings" - > - {{ $t('settings.save') }} - </button> </div> </div> </template> diff --git a/src/components/settings_modal/tabs/profile_tab.js b/src/components/settings_modal/tabs/profile_tab.js @@ -8,6 +8,11 @@ import EmojiInput from 'src/components/emoji_input/emoji_input.vue' import suggestor from 'src/components/emoji_input/suggestor.js' import Autosuggest from 'src/components/autosuggest/autosuggest.vue' import Checkbox from 'src/components/checkbox/checkbox.vue' +import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue' +import BooleanSetting from '../helpers/boolean_setting.vue' +import SharedComputedObject from '../helpers/shared_computed_object.js' +import localeService from 'src/services/locale/locale.service.js' + import { library } from '@fortawesome/fontawesome-svg-core' import { faTimes, @@ -27,25 +32,18 @@ const ProfileTab = { newName: this.$store.state.users.currentUser.name_unescaped, newBio: unescape(this.$store.state.users.currentUser.description), newLocked: this.$store.state.users.currentUser.locked, - newNoRichText: this.$store.state.users.currentUser.no_rich_text, - newDefaultScope: this.$store.state.users.currentUser.default_scope, newFields: this.$store.state.users.currentUser.fields.map(field => ({ name: field.name, value: field.value })), - hideFollows: this.$store.state.users.currentUser.hide_follows, - hideFollowers: this.$store.state.users.currentUser.hide_followers, - hideFollowsCount: this.$store.state.users.currentUser.hide_follows_count, - hideFollowersCount: this.$store.state.users.currentUser.hide_followers_count, showRole: this.$store.state.users.currentUser.show_role, role: this.$store.state.users.currentUser.role, - discoverable: this.$store.state.users.currentUser.discoverable, bot: this.$store.state.users.currentUser.bot, - allowFollowingMove: this.$store.state.users.currentUser.allow_following_move, pickAvatarBtnVisible: true, bannerUploading: false, backgroundUploading: false, banner: null, bannerPreview: null, background: null, - backgroundPreview: null + backgroundPreview: null, + emailLanguage: this.$store.state.users.currentUser.language || '' } }, components: { @@ -54,26 +52,31 @@ const ProfileTab = { EmojiInput, Autosuggest, ProgressButton, - Checkbox + Checkbox, + BooleanSetting, + InterfaceLanguageSwitcher }, computed: { user () { return this.$store.state.users.currentUser }, + ...SharedComputedObject(), emojiUserSuggestor () { return suggestor({ emoji: [ - ...this.$store.state.instance.emoji, + ...this.$store.getters.standardEmojiList, ...this.$store.state.instance.customEmoji ], store: this.$store }) }, emojiSuggestor () { - return suggestor({ emoji: [ - ...this.$store.state.instance.emoji, - ...this.$store.state.instance.customEmoji - ] }) + return suggestor({ + emoji: [ + ...this.$store.getters.standardEmojiList, + ...this.$store.state.instance.customEmoji + ] + }) }, userSuggestor () { return suggestor({ store: this.$store }) @@ -114,27 +117,25 @@ const ProfileTab = { }, methods: { updateProfile () { + const params = { + note: this.newBio, + locked: this.newLocked, + // Backend notation. + /* eslint-disable camelcase */ + display_name: this.newName, + fields_attributes: this.newFields.filter(el => el != null), + bot: this.bot, + show_role: this.showRole + /* eslint-enable camelcase */ + } + + if (this.emailLanguage) { + params.language = localeService.internalToBackendLocale(this.emailLanguage) + } + this.$store.state.api.backendInteractor - .updateProfile({ - params: { - note: this.newBio, - locked: this.newLocked, - // Backend notation. - /* eslint-disable camelcase */ - display_name: this.newName, - fields_attributes: this.newFields.filter(el => el != null), - default_scope: this.newDefaultScope, - no_rich_text: this.newNoRichText, - hide_follows: this.hideFollows, - hide_followers: this.hideFollowers, - discoverable: this.discoverable, - bot: this.bot, - allow_following_move: this.allowFollowingMove, - hide_follows_count: this.hideFollowsCount, - hide_followers_count: this.hideFollowersCount, - show_role: this.showRole - /* eslint-enable camelcase */ - } }).then((user) => { + .updateProfile({ params }) + .then((user) => { this.newFields.splice(user.fields.length) merge(this.newFields, user.fields) this.$store.commit('addNewUsers', [user]) @@ -204,8 +205,8 @@ const ProfileTab = { submitAvatar (cropper, file) { const that = this return new Promise((resolve, reject) => { - function updateAvatar (avatar) { - that.$store.state.api.backendInteractor.updateProfileImages({ avatar }) + function updateAvatar (avatar, avatarName) { + that.$store.state.api.backendInteractor.updateProfileImages({ avatar, avatarName }) .then((user) => { that.$store.commit('addNewUsers', [user]) that.$store.commit('setCurrentUser', user) @@ -218,9 +219,9 @@ const ProfileTab = { } if (cropper) { - cropper.getCroppedCanvas().toBlob(updateAvatar, file.type) + cropper.getCroppedCanvas().toBlob((data) => updateAvatar(data, file.name), file.type) } else { - updateAvatar(file) + updateAvatar(file, file.name) } }) }, diff --git a/src/components/settings_modal/tabs/profile_tab.scss b/src/components/settings_modal/tabs/profile_tab.scss @@ -54,16 +54,20 @@ border-radius: var(--tooltipRadius, $fallback--tooltipRadius); background-color: rgba(0, 0, 0, 0.6); opacity: 0.7; - color: white; width: 1.5em; height: 1.5em; text-align: center; line-height: 1.5em; font-size: 1.5em; cursor: pointer; + &:hover { opacity: 1; } + + svg { + color: white; + } } .oauth-tokens { @@ -85,7 +89,7 @@ &-bulk-actions { text-align: right; padding: 0 1em; - min-height: 28px; + min-height: 2em; button { width: 10em; diff --git a/src/components/settings_modal/tabs/profile_tab.vue b/src/components/settings_modal/tabs/profile_tab.vue @@ -25,61 +25,6 @@ class="bio resize-height" /> </EmojiInput> - <p> - <Checkbox v-model="newLocked"> - {{ $t('settings.lock_account_description') }} - </Checkbox> - </p> - <div> - <label for="default-vis">{{ $t('settings.default_vis') }}</label> - <div - id="default-vis" - class="visibility-tray" - > - <scope-selector - :show-all="true" - :user-default="newDefaultScope" - :initial-scope="newDefaultScope" - :on-scope-change="changeVis" - /> - </div> - </div> - <p> - <Checkbox v-model="newNoRichText"> - {{ $t('settings.no_rich_text_description') }} - </Checkbox> - </p> - <p> - <Checkbox v-model="hideFollows"> - {{ $t('settings.hide_follows_description') }} - </Checkbox> - </p> - <p class="setting-subitem"> - <Checkbox - v-model="hideFollowsCount" - :disabled="!hideFollows" - > - {{ $t('settings.hide_follows_count_description') }} - </Checkbox> - </p> - <p> - <Checkbox v-model="hideFollowers"> - {{ $t('settings.hide_followers_description') }} - </Checkbox> - </p> - <p class="setting-subitem"> - <Checkbox - v-model="hideFollowersCount" - :disabled="!hideFollowers" - > - {{ $t('settings.hide_followers_count_description') }} - </Checkbox> - </p> - <p> - <Checkbox v-model="allowFollowingMove"> - {{ $t('settings.allow_following_move') }} - </Checkbox> - </p> <p v-if="role === 'admin' || role === 'moderator'"> <Checkbox v-model="showRole"> <template v-if="role === 'admin'"> @@ -90,11 +35,6 @@ </template> </Checkbox> </p> - <p> - <Checkbox v-model="discoverable"> - {{ $t('settings.discoverable') }} - </Checkbox> - </p> <div v-if="maxFields > 0"> <p>{{ $t('settings.profile_fields.label') }}</p> <div @@ -128,8 +68,9 @@ class="delete-field button-unstyled -hover-highlight" @click="deleteField(i)" > + <!-- TODO something is wrong with v-show here --> <FAIcon - v-show="newFields.length > 1" + v-if="newFields.length > 1" icon="times" /> </button> @@ -148,6 +89,13 @@ {{ $t('settings.bot') }} </Checkbox> </p> + <p> + <interface-language-switcher + :prompt-text="$t('settings.email_language')" + :language="emailLanguage" + :set-language="val => emailLanguage = val" + /> + </p> <button :disabled="newName && newName.length === 0" class="btn button-default" @@ -166,14 +114,17 @@ :src="user.profile_image_url_original" class="current-avatar" > - <FAIcon + <button v-if="!isDefaultAvatar && pickAvatarBtnVisible" :title="$t('settings.reset_avatar')" - class="reset-button" - icon="times" - type="button" + class="button-unstyled reset-button" @click="resetAvatar" - /> + > + <FAIcon + icon="times" + type="button" + /> + </button> </div> <p>{{ $t('settings.set_new_avatar') }}</p> <button @@ -195,14 +146,17 @@ <h2>{{ $t('settings.profile_banner') }}</h2> <div class="banner-background-preview"> <img :src="user.cover_photo"> - <FAIcon + <button v-if="!isDefaultBanner" + class="button-unstyled reset-button" :title="$t('settings.reset_profile_banner')" - class="reset-button" - icon="times" - type="button" @click="resetBanner" - /> + > + <FAIcon + icon="times" + type="button" + /> + </button> </div> <p>{{ $t('settings.set_new_profile_banner') }}</p> <img @@ -234,14 +188,17 @@ <h2>{{ $t('settings.profile_background') }}</h2> <div class="banner-background-preview"> <img :src="user.background_image"> - <FAIcon + <button v-if="!isDefaultBackground" + class="button-unstyled reset-button" :title="$t('settings.reset_profile_background')" - class="reset-button" - icon="times" - type="button" @click="resetBackground" - /> + > + <FAIcon + icon="times" + type="button" + /> + </button> </div> <p>{{ $t('settings.set_new_profile_background') }}</p> <img @@ -269,6 +226,67 @@ {{ $t('settings.save') }} </button> </div> + <div class="setting-item"> + <h2>{{ $t('settings.account_privacy') }}</h2> + <ul class="setting-list"> + <li> + <BooleanSetting path="serverSide_locked"> + {{ $t('settings.lock_account_description') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting path="serverSide_discoverable"> + {{ $t('settings.discoverable') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting path="serverSide_allowFollowingMove"> + {{ $t('settings.allow_following_move') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting path="serverSide_hideFavorites"> + {{ $t('settings.hide_favorites_description') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting path="serverSide_hideFollowers"> + {{ $t('settings.hide_followers_description') }} + </BooleanSetting> + <ul + class="setting-list suboptions" + :class="[{disabled: !serverSide_hideFollowers}]" + > + <li> + <BooleanSetting + path="serverSide_hideFollowersCount" + :disabled="!serverSide_hideFollowers" + > + {{ $t('settings.hide_followers_count_description') }} + </BooleanSetting> + </li> + </ul> + </li> + <li> + <BooleanSetting path="serverSide_hideFollows"> + {{ $t('settings.hide_follows_description') }} + </BooleanSetting> + <ul + class="setting-list suboptions" + :class="[{disabled: !serverSide_hideFollows}]" + > + <li> + <BooleanSetting + path="serverSide_hideFollowsCount" + :disabled="!serverSide_hideFollows" + > + {{ $t('settings.hide_follows_count_description') }} + </BooleanSetting> + </li> + </ul> + </li> + </ul> + </div> </div> </template> diff --git a/src/components/settings_modal/tabs/security_tab/mfa.js b/src/components/settings_modal/tabs/security_tab/mfa.js @@ -32,8 +32,8 @@ const Mfa = { components: { 'recovery-codes': RecoveryCodes, 'totp-item': TOTP, - 'qrcode': VueQrcode, - 'confirm': Confirm + qrcode: VueQrcode, + confirm: Confirm }, computed: { canSetupOTP () { @@ -139,7 +139,7 @@ const Mfa = { // fetch settings from server async fetchSettings () { - let result = await this.backendInteractor.settingsMFA() + const result = await this.backendInteractor.settingsMFA() if (result.error) return this.settings = result.settings this.settings.available = true diff --git a/src/components/settings_modal/tabs/security_tab/mfa_totp.js b/src/components/settings_modal/tabs/security_tab/mfa_totp.js @@ -10,7 +10,7 @@ export default { inProgress: false // progress peform request to disable otp method }), components: { - 'confirm': Confirm + confirm: Confirm }, computed: { isActivated () { diff --git a/src/components/settings_modal/tabs/security_tab/security_tab.js b/src/components/settings_modal/tabs/security_tab/security_tab.js @@ -13,13 +13,23 @@ const SecurityTab = { deletingAccount: false, deleteAccountConfirmPasswordInput: '', deleteAccountError: false, - changePasswordInputs: [ '', '', '' ], + changePasswordInputs: ['', '', ''], changedPassword: false, - changePasswordError: false + changePasswordError: false, + moveAccountTarget: '', + moveAccountPassword: '', + movedAccount: false, + moveAccountError: false, + aliases: [], + listAliasesError: false, + addAliasTarget: '', + addedAlias: false, + addAliasError: false } }, created () { this.$store.dispatch('fetchTokens') + this.fetchAliases() }, components: { ProgressButton, @@ -92,6 +102,49 @@ const SecurityTab = { } }) }, + moveAccount () { + const params = { + targetAccount: this.moveAccountTarget, + password: this.moveAccountPassword + } + this.$store.state.api.backendInteractor.moveAccount(params) + .then((res) => { + if (res.status === 'success') { + this.movedAccount = true + this.moveAccountError = false + } else { + this.movedAccount = false + this.moveAccountError = res.error + } + }) + }, + removeAlias (alias) { + this.$store.state.api.backendInteractor.deleteAlias({ alias }) + .then(() => this.fetchAliases()) + }, + addAlias () { + this.$store.state.api.backendInteractor.addAlias({ alias: this.addAliasTarget }) + .then((res) => { + this.addedAlias = true + this.addAliasError = false + this.addAliasTarget = '' + }) + .catch((error) => { + this.addedAlias = false + this.addAliasError = error + }) + .then(() => this.fetchAliases()) + }, + fetchAliases () { + this.$store.state.api.backendInteractor.listAliases() + .then((res) => { + this.aliases = res.aliases + this.listAliasesError = false + }) + .catch((error) => { + this.listAliasesError = error.error + }) + }, logout () { this.$store.dispatch('logout') this.$router.replace('/') diff --git a/src/components/settings_modal/tabs/security_tab/security_tab.vue b/src/components/settings_modal/tabs/security_tab/security_tab.vue @@ -103,6 +103,114 @@ </table> </div> <mfa /> + + <div class="setting-item"> + <h2>{{ $t('settings.account_alias') }}</h2> + <table> + <thead> + <tr> + <th>{{ $t('settings.account_alias_table_head') }}</th> + <th /> + </tr> + </thead> + <tbody> + <tr + v-for="alias in aliases" + :key="alias" + > + <td>{{ alias }}</td> + <td class="actions"> + <button + class="btn button-default" + @click="removeAlias(alias)" + > + {{ $t('settings.remove_alias') }} + </button> + </td> + </tr> + </tbody> + </table> + <div + v-if="listAliasesError" + class="alert error" + > + {{ $t('settings.list_aliases_error', { error }) }} + <FAIcon + class="fa-scale-110 fa-old-padding" + icon="times" + :title="$t('settings.hide_list_aliases_error_action')" + @click="listAliasesError = false" + /> + </div> + <div> + <i18n + path="settings.new_alias_target" + tag="p" + > + <code + place="example" + > + foo@example.org + </code> + </i18n> + <input + v-model="addAliasTarget" + > + </div> + <button + class="btn button-default" + @click="addAlias" + > + {{ $t('settings.save') }} + </button> + <p v-if="addedAlias"> + {{ $t('settings.added_alias') }} + </p> + <template v-if="addAliasError !== false"> + <p>{{ $t('settings.add_alias_error', { error: addAliasError }) }}</p> + </template> + </div> + + <div class="setting-item"> + <h2>{{ $t('settings.move_account') }}</h2> + <p>{{ $t('settings.move_account_notes') }}</p> + <div> + <i18n + path="settings.move_account_target" + tag="p" + > + <code + place="example" + > + foo@example.org + </code> + </i18n> + <input + v-model="moveAccountTarget" + > + </div> + <div> + <p>{{ $t('settings.current_password') }}</p> + <input + v-model="moveAccountPassword" + type="password" + autocomplete="current-password" + > + </div> + <button + class="btn button-default" + @click="moveAccount" + > + {{ $t('settings.save') }} + </button> + <p v-if="movedAccount"> + {{ $t('settings.moved_account') }} + </p> + <template v-if="moveAccountError !== false"> + <p>{{ $t('settings.move_account_error', { error: moveAccountError }) }}</p> + </template> + </div> + <div class="setting-item"> <h2>{{ $t('settings.delete_account') }}</h2> <p v-if="!deletingAccount"> @@ -133,7 +241,7 @@ class="btn button-default" @click="confirmDelete" > - {{ $t('settings.save') }} + {{ $t('settings.delete_account') }} </button> </div> </div> diff --git a/src/components/settings_modal/tabs/theme_tab/preview.vue b/src/components/settings_modal/tabs/theme_tab/preview.vue @@ -29,14 +29,17 @@ {{ $t('settings.style.preview.content') }} </h4> - <i18n path="settings.style.preview.text"> + <i18n-t + scope="global" + keypath="settings.style.preview.text" + > <code style="font-family: var(--postCodeFont)"> {{ $t('settings.style.preview.mono') }} </code> <a style="color: var(--link)"> {{ $t('settings.style.preview.link') }} </a> - </i18n> + </i18n-t> <div class="icons"> <FAIcon @@ -72,15 +75,16 @@ :^) </div> <div class="content"> - <i18n - path="settings.style.preview.fine_print" + <i18n-t + keypath="settings.style.preview.fine_print" tag="span" class="faint" + scope="global" > <a style="color: var(--faintLink)"> {{ $t('settings.style.preview.faint_link') }} </a> - </i18n> + </i18n-t> </div> </div> <div class="separator" /> diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.js b/src/components/settings_modal/tabs/theme_tab/theme_tab.js @@ -1,4 +1,3 @@ -import { set, delete as del } from 'vue' import { rgb2hex, hex2rgb, @@ -34,7 +33,7 @@ import OpacityInput from 'src/components/opacity_input/opacity_input.vue' import ShadowControl from 'src/components/shadow_control/shadow_control.vue' import FontControl from 'src/components/font_control/font_control.vue' import ContrastRatio from 'src/components/contrast_ratio/contrast_ratio.vue' -import TabSwitcher from 'src/components/tab_switcher/tab_switcher.js' +import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' import Checkbox from 'src/components/checkbox/checkbox.vue' import Select from 'src/components/select/select.vue' @@ -96,11 +95,11 @@ export default { ...Object.keys(SLOT_INHERITANCE) .map(key => [key, '']) - .reduce((acc, [key, val]) => ({ ...acc, [ key + 'ColorLocal' ]: val }), {}), + .reduce((acc, [key, val]) => ({ ...acc, [key + 'ColorLocal']: val }), {}), ...Object.keys(OPACITIES) .map(key => [key, '']) - .reduce((acc, [key, val]) => ({ ...acc, [ key + 'OpacityLocal' ]: val }), {}), + .reduce((acc, [key, val]) => ({ ...acc, [key + 'OpacityLocal']: val }), {}), shadowSelected: undefined, shadowsLocal: {}, @@ -213,12 +212,12 @@ export default { currentColors () { return Object.keys(SLOT_INHERITANCE) .map(key => [key, this[key + 'ColorLocal']]) - .reduce((acc, [key, val]) => ({ ...acc, [ key ]: val }), {}) + .reduce((acc, [key, val]) => ({ ...acc, [key]: val }), {}) }, currentOpacity () { return Object.keys(OPACITIES) .map(key => [key, this[key + 'OpacityLocal']]) - .reduce((acc, [key, val]) => ({ ...acc, [ key ]: val }), {}) + .reduce((acc, [key, val]) => ({ ...acc, [key]: val }), {}) }, currentRadii () { return { @@ -280,6 +279,9 @@ export default { opacity ) + // Temporary patch for null-y value errors + if (layers.flat().some(v => v == null)) return acc + return { ...acc, ...textColors.reduce((acc, textColorKey) => { @@ -301,6 +303,7 @@ export default { return Object.entries(ratios).reduce((acc, [k, v]) => { acc[k] = hints(v); return acc }, {}) } catch (e) { console.warn('Failure computing contrasts', e) + return {} } }, previewRules () { @@ -320,9 +323,9 @@ export default { }, set (val) { if (val) { - set(this.shadowsLocal, this.shadowSelected, this.currentShadowFallback.map(_ => Object.assign({}, _))) + this.shadowsLocal[this.shadowSelected] = this.currentShadowFallback.map(_ => Object.assign({}, _)) } else { - del(this.shadowsLocal, this.shadowSelected) + delete this.shadowsLocal[this.shadowSelected] } } }, @@ -334,7 +337,7 @@ export default { return this.shadowsLocal[this.shadowSelected] }, set (v) { - set(this.shadowsLocal, this.shadowSelected, v) + this.shadowsLocal[this.shadowSelected] = v } }, themeValid () { @@ -378,6 +381,10 @@ export default { // To separate from other random JSON files and possible future source formats _pleroma_theme_version: 2, theme, source } + }, + isActive () { + const tabSwitcher = this.$parent + return tabSwitcher ? tabSwitcher.isActive('theme') : false } }, components: { @@ -557,7 +564,7 @@ export default { .filter(_ => _.endsWith('ColorLocal') || _.endsWith('OpacityLocal')) .filter(_ => !v1OnlyNames.includes(_)) .forEach(key => { - set(this.$data, key, undefined) + this.$data[key] = undefined }) }, @@ -565,7 +572,7 @@ export default { Object.keys(this.$data) .filter(_ => _.endsWith('RadiusLocal')) .forEach(key => { - set(this.$data, key, undefined) + this.$data[key] = undefined }) }, @@ -573,7 +580,7 @@ export default { Object.keys(this.$data) .filter(_ => _.endsWith('OpacityLocal')) .forEach(key => { - set(this.$data, key, undefined) + this.$data[key] = undefined }) }, diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.scss b/src/components/settings_modal/tabs/theme_tab/theme_tab.scss @@ -245,36 +245,12 @@ border-color: var(--border, $fallback--border); } - .panel-heading { - .badge, .alert, .btn, .faint { - margin-left: 1em; - white-space: nowrap; - } - .faint { - text-overflow: ellipsis; - min-width: 2em; - overflow-x: hidden; - } - .flex-spacer { - flex: 1; - } - } .btn { - margin-left: 0; - padding: 0 1em; min-width: 3em; - min-height: 30px; } } } - .apply-container { - justify-content: center; - position: absolute; - bottom: 8px; - right: 5px; - } - .radius-item, .color-item { min-width: 20em; @@ -334,16 +310,25 @@ padding: 20px; } - .apply-container { - .btn { - min-height: 28px; - min-width: 10em; - padding: 0 2em; - } - } - .btn { margin-left: .25em; margin-right: .25em; } } + +.extra-content { + .apply-container { + display: flex; + flex-direction: row; + justify-content: space-around; + flex-grow: 1; + + .btn { + flex-grow: 1; + min-height: 2em; + min-width: 0; + max-width: 10em; + padding: 0; + } + } +} diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.vue b/src/components/settings_modal/tabs/theme_tab/theme_tab.vue @@ -903,6 +903,7 @@ <div class="tab-header shadow-selector"> <div class="select-container"> {{ $t('settings.style.shadows.component') }} + {{ ' ' }} <Select id="shadow-switcher" v-model="shadowSelected" @@ -924,6 +925,7 @@ > {{ $t('settings.style.shadows.override') }} </label> + {{ ' ' }} <input id="override" v-model="currentShadowOverriden" @@ -949,27 +951,30 @@ :fallback="currentShadowFallback" /> <div v-if="shadowSelected === 'avatar' || shadowSelected === 'avatarStatus'"> - <i18n - path="settings.style.shadows.filter_hint.always_drop_shadow" + <i18n-t + scope="global" + keypath="settings.style.shadows.filter_hint.always_drop_shadow" tag="p" > <code>filter: drop-shadow()</code> - </i18n> + </i18n-t> <p>{{ $t('settings.style.shadows.filter_hint.avatar_inset') }}</p> - <i18n - path="settings.style.shadows.filter_hint.drop_shadow_syntax" + <i18n-t + scope="global" + keypath="settings.style.shadows.filter_hint.drop_shadow_syntax" tag="p" > <code>drop-shadow</code> <code>spread-radius</code> <code>inset</code> - </i18n> - <i18n - path="settings.style.shadows.filter_hint.inset_classic" + </i18n-t> + <i18n-t + scope="global" + keypath="settings.style.shadows.filter_hint.inset_classic" tag="p" > <code>box-shadow</code> - </i18n> + </i18n-t> <p>{{ $t('settings.style.shadows.filter_hint.spread_zero') }}</p> </div> </div> @@ -1016,21 +1021,26 @@ </tab-switcher> </keep-alive> - <div class="apply-container"> - <button - class="btn button-default submit" - :disabled="!themeValid" - @click="setCustomTheme" - > - {{ $t('general.apply') }} - </button> - <button - class="btn button-default" - @click="clearAll" - > - {{ $t('settings.style.switcher.reset') }} - </button> - </div> + <teleport + v-if="isActive" + to="#unscrolled-content" + > + <div class="apply-container"> + <button + class="btn button-default submit" + :disabled="!themeValid" + @click="setCustomTheme" + > + {{ $t('general.apply') }} + </button> + <button + class="btn button-default" + @click="clearAll" + > + {{ $t('settings.style.switcher.reset') }} + </button> + </div> + </teleport> </div> </template> diff --git a/src/components/settings_modal/tabs/version_tab.vue b/src/components/settings_modal/tabs/version_tab.vue @@ -28,4 +28,4 @@ </div> </div> </template> -<script src="./version_tab.js"> +<script src="./version_tab.js" /> diff --git a/src/components/shadow_control/shadow_control.js b/src/components/shadow_control/shadow_control.js @@ -30,18 +30,19 @@ const toModel = (object = {}) => ({ }) export default { - // 'Value' and 'Fallback' can be undefined, but if they are + // 'modelValue' and 'Fallback' can be undefined, but if they are // initially vue won't detect it when they become something else // therefore i'm using "ready" which should be passed as true when // data becomes available props: [ - 'value', 'fallback', 'ready' + 'modelValue', 'fallback', 'ready' ], + emits: ['update:modelValue'], data () { return { selectedId: 0, // TODO there are some bugs regarding display of array (it's not getting updated when deleting for some reason) - cValue: (this.value || this.fallback || []).map(toModel) + cValue: (this.modelValue || this.fallback || []).map(toModel) } }, components: { @@ -70,7 +71,7 @@ export default { } }, beforeUpdate () { - this.cValue = this.value || this.fallback + this.cValue = this.modelValue || this.fallback }, computed: { anyShadows () { @@ -105,15 +106,17 @@ export default { !this.usingFallback }, usingFallback () { - return typeof this.value === 'undefined' + return typeof this.modelValue === 'undefined' }, rgb () { return hex2rgb(this.selected.color) }, style () { - return this.ready ? { - boxShadow: getCssShadow(this.fallback) - } : {} + return this.ready + ? { + boxShadow: getCssShadow(this.fallback) + } + : {} } } } diff --git a/src/components/shadow_control/shadow_control.vue b/src/components/shadow_control/shadow_control.vue @@ -204,17 +204,18 @@ v-model="selected.alpha" :disabled="!present" /> - <i18n - path="settings.style.shadows.hintV3" + <i18n-t + scope="global" + keypath="settings.style.shadows.hintV3" tag="p" > <code>--variable,mod</code> - </i18n> + </i18n-t> </div> </div> </template> -<script src="./shadow_control.js" ></script> +<script src="./shadow_control.js"></script> <style lang="scss"> @import '../../_variables.scss'; diff --git a/src/components/shout_panel/shout_panel.js b/src/components/shout_panel/shout_panel.js @@ -11,7 +11,7 @@ library.add( ) const shoutPanel = { - props: [ 'floating' ], + props: ['floating'], data () { return { currentMessage: '', diff --git a/src/components/shout_panel/shout_panel.vue b/src/components/shout_panel/shout_panel.vue @@ -57,7 +57,7 @@ > <div class="panel panel-default"> <div - class="panel-heading stub timeline-heading shout-heading" + class="panel-heading -stub timeline-heading shout-heading" @click.stop.prevent="togglePanel" > <div class="title"> @@ -79,17 +79,17 @@ .floating-shout { position: fixed; - bottom: 0px; - z-index: 1000; + bottom: 0.5em; + z-index: var(--ZI_popovers); max-width: 25em; -} -.floating-shout.left { - left: 0px; -} + &.-left { + left: 0.5em; + } -.floating-shout:not(.left) { - right: 0px; + &:not(.-left) { + right: 0.5em; + } } .shout-panel { @@ -98,7 +98,7 @@ .icon { color: $fallback--text; - color: var(--text, $fallback--text); + color: var(--panelText, $fallback--text); margin-right: 0.5em; } @@ -121,7 +121,7 @@ .shout-message { display: flex; - padding: 0.2em 0.5em + padding: 0.2em 0.5em; } .shout-avatar { @@ -137,6 +137,7 @@ .shout-input { display: flex; + textarea { flex: 1; margin: 0.6em; diff --git a/src/components/side_drawer/side_drawer.js b/src/components/side_drawer/side_drawer.js @@ -2,6 +2,7 @@ import { mapState, mapGetters } from 'vuex' import UserCard from '../user_card/user_card.vue' import { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils' import GestureService from '../../services/gesture_service/gesture_service' +import { USERNAME_ROUTES } from 'src/components/navigation/navigation.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faSignInAlt, @@ -14,7 +15,9 @@ import { faSearch, faTachometerAlt, faCog, - faInfoCircle + faInfoCircle, + faCompass, + faList } from '@fortawesome/free-solid-svg-icons' library.add( @@ -28,11 +31,13 @@ library.add( faSearch, faTachometerAlt, faCog, - faInfoCircle + faInfoCircle, + faCompass, + faList ) const SideDrawer = { - props: [ 'logout' ], + props: ['logout'], data: () => ({ closed: true, closeGesture: undefined @@ -49,7 +54,7 @@ const SideDrawer = { currentUser () { return this.$store.state.users.currentUser }, - shout () { return this.$store.state.shout.channel.state === 'joined' }, + shout () { return this.$store.state.shout.joined }, unseenNotifications () { return unseenNotificationsFromStore(this.$store) }, @@ -78,15 +83,22 @@ const SideDrawer = { return this.$store.state.instance.federating }, timelinesRoute () { + let name if (this.$store.state.interface.lastTimeline) { - return this.$store.state.interface.lastTimeline + name = this.$store.state.interface.lastTimeline + } + name = this.currentUser ? 'friends' : 'public-timeline' + if (USERNAME_ROUTES.has(name)) { + return { name, params: { username: this.currentUser.screen_name } } + } else { + return { name } } - return this.currentUser ? 'friends' : 'public-timeline' }, ...mapState({ - pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable + pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable, + supportsAnnouncements: state => state.announcements.supportsAnnouncements }), - ...mapGetters(['unreadChatCount']) + ...mapGetters(['unreadChatCount', 'unreadAnnouncementCount']) }, methods: { toggleDrawer () { diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue @@ -47,7 +47,7 @@ v-if="currentUser || !privateMode" @click="toggleDrawer" > - <router-link :to="{ name: timelinesRoute }"> + <router-link :to="timelinesRoute"> <FAIcon fixed-width class="fa-scale-110 fa-old-padding" @@ -56,6 +56,18 @@ </router-link> </li> <li + v-if="currentUser" + @click="toggleDrawer" + > + <router-link :to="{ name: 'lists' }"> + <FAIcon + fixed-width + class="fa-scale-110 fa-old-padding" + icon="list" + /> {{ $t("nav.lists") }} + </router-link> + </li> + <li v-if="currentUser && pleromaChatMessagesAvailable" @click="toggleDrawer" > @@ -180,6 +192,38 @@ </a> </li> <li + v-if="currentUser && supportsAnnouncements" + @click="toggleDrawer" + > + <router-link + :to="{ name: 'announcements' }" + > + <FAIcon + fixed-width + class="fa-scale-110 fa-old-padding" + icon="bullhorn" + /> {{ $t("nav.announcements") }} + <span + v-if="unreadAnnouncementCount" + class="badge badge-notification" + > + {{ unreadAnnouncementCount }} + </span> + </router-link> + </li> + <li + v-if="currentUser" + @click="toggleDrawer" + > + <router-link :to="{ name: 'edit-navigation' }"> + <FAIcon + fixed-width + class="fa-scale-110 fa-old-padding" + icon="compass" + /> {{ $t("nav.edit_nav_mobile") }} + </router-link> + </li> + <li v-if="currentUser" @click="toggleDrawer" > @@ -204,14 +248,14 @@ </div> </template> -<script src="./side_drawer.js" ></script> +<script src="./side_drawer.js"></script> <style lang="scss"> @import '../../_variables.scss'; .side-drawer-container { position: fixed; - z-index: 1000; + z-index: var(--ZI_navbar); top: 0; left: 0; width: 100%; diff --git a/src/components/staff_panel/staff_panel.js b/src/components/staff_panel/staff_panel.js @@ -13,16 +13,16 @@ const StaffPanel = { }, computed: { groupedStaffAccounts () { - const staffAccounts = map(this.staffAccounts, this.findUser).filter(_ => _) + const staffAccounts = map(this.staffAccounts, this.findUserByName).filter(_ => _) const groupedStaffAccounts = groupBy(staffAccounts, 'role') return [ - { role: 'admin', users: groupedStaffAccounts['admin'] }, - { role: 'moderator', users: groupedStaffAccounts['moderator'] } + { role: 'admin', users: groupedStaffAccounts.admin }, + { role: 'moderator', users: groupedStaffAccounts.moderator } ].filter(group => group.users) }, ...mapGetters([ - 'findUser' + 'findUserByName' ]), ...mapState({ staffAccounts: state => state.instance.staffAccounts diff --git a/src/components/staff_panel/staff_panel.vue b/src/components/staff_panel/staff_panel.vue @@ -24,7 +24,7 @@ </div> </template> -<script src="./staff_panel.js" ></script> +<script src="./staff_panel.js"></script> <style lang="scss"> diff --git a/src/components/status/status.js b/src/components/status/status.js @@ -4,15 +4,16 @@ import ReactButton from '../react_button/react_button.vue' import RetweetButton from '../retweet_button/retweet_button.vue' import ExtraButtons from '../extra_buttons/extra_buttons.vue' import PostStatusForm from '../post_status_form/post_status_form.vue' -import UserCard from '../user_card/user_card.vue' import UserAvatar from '../user_avatar/user_avatar.vue' import AvatarList from '../avatar_list/avatar_list.vue' import Timeago from '../timeago/timeago.vue' import StatusContent from '../status_content/status_content.vue' import RichContent from 'src/components/rich_content/rich_content.jsx' import StatusPopover from '../status_popover/status_popover.vue' +import UserPopover from '../user_popover/user_popover.vue' import UserListPopover from '../user_list_popover/user_list_popover.vue' import EmojiReactions from '../emoji_reactions/emoji_reactions.vue' +import UserLink from '../user_link/user_link.vue' import MentionsLine from 'src/components/mentions_line/mentions_line.vue' import MentionLink from 'src/components/mention_link/mention_link.vue' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' @@ -35,7 +36,10 @@ import { faStar, faEyeSlash, faEye, - faThumbtack + faThumbtack, + faChevronUp, + faChevronDown, + faAngleDoubleRight } from '@fortawesome/free-solid-svg-icons' library.add( @@ -52,9 +56,47 @@ library.add( faEllipsisH, faEyeSlash, faEye, - faThumbtack + faThumbtack, + faChevronUp, + faChevronDown, + faAngleDoubleRight ) +const camelCase = name => name.charAt(0).toUpperCase() + name.slice(1) + +const controlledOrUncontrolledGetters = list => list.reduce((res, name) => { + const camelized = camelCase(name) + const toggle = `controlledToggle${camelized}` + const controlledName = `controlled${camelized}` + const uncontrolledName = `uncontrolled${camelized}` + res[name] = function () { + return ((this.$data[toggle] !== undefined || this.$props[toggle] !== undefined) && this[toggle]) ? this[controlledName] : this[uncontrolledName] + } + return res +}, {}) + +const controlledOrUncontrolledToggle = (obj, name) => { + const camelized = camelCase(name) + const toggle = `controlledToggle${camelized}` + const uncontrolledName = `uncontrolled${camelized}` + if (obj[toggle]) { + obj[toggle]() + } else { + obj[uncontrolledName] = !obj[uncontrolledName] + } +} + +const controlledOrUncontrolledSet = (obj, name, val) => { + const camelized = camelCase(name) + const set = `controlledSet${camelized}` + const uncontrolledName = `uncontrolled${camelized}` + if (obj[set]) { + obj[set](val) + } else { + obj[uncontrolledName] = val + } +} + const Status = { name: 'Status', components: { @@ -64,7 +106,6 @@ const Status = { RetweetButton, ExtraButtons, PostStatusForm, - UserCard, UserAvatar, AvatarList, Timeago, @@ -74,7 +115,9 @@ const Status = { StatusContent, RichContent, MentionLink, - MentionsLine + MentionsLine, + UserPopover, + UserLink }, props: [ 'statusoid', @@ -89,20 +132,38 @@ const Status = { 'inlineExpanded', 'showPinned', 'inProfile', - 'profileUserId' + 'profileUserId', + + 'simpleTree', + 'controlledThreadDisplayStatus', + 'controlledToggleThreadDisplay', + 'showOtherRepliesAsButton', + + 'controlledShowingTall', + 'controlledToggleShowingTall', + 'controlledExpandingSubject', + 'controlledToggleExpandingSubject', + 'controlledShowingLongSubject', + 'controlledToggleShowingLongSubject', + 'controlledReplying', + 'controlledToggleReplying', + 'controlledMediaPlaying', + 'controlledSetMediaPlaying', + 'dive' ], data () { return { - replying: false, + uncontrolledReplying: false, unmuted: false, userExpanded: false, - mediaPlaying: [], + uncontrolledMediaPlaying: [], suspendable: true, error: null, headTailLinks: null } }, computed: { + ...controlledOrUncontrolledGetters(['replying', 'mediaPlaying']), muteWords () { return this.mergedConfig.muteWords }, @@ -166,6 +227,18 @@ const Status = { muteWordHits () { return muteWordHits(this.status, this.muteWords) }, + rtBotStatus () { + return this.statusoid.user.bot + }, + botStatus () { + return this.status.user.bot + }, + botIndicator () { + return this.botStatus && !this.hideBotIndication + }, + rtBotIndicator () { + return this.rtBotStatus && !this.hideBotIndication + }, mentionsLine () { if (!this.headTailLinks) return [] const writtenSet = new Set(this.headTailLinks.writtenMentions.map(_ => _.url)) @@ -187,25 +260,33 @@ const Status = { }, muted () { if (this.statusoid.user.id === this.currentUser.id) return false + const reasonsToMute = this.userIsMuted || + // Thread is muted + status.thread_muted || + // Wordfiltered + this.muteWordHits.length > 0 || + // bot status + (this.muteBotStatuses && this.botStatus && !this.compact) + return !this.unmuted && !this.shouldNotMute && reasonsToMute + }, + userIsMuted () { + if (this.statusoid.user.id === this.currentUser.id) return false const { status } = this const { reblog } = status const relationship = this.$store.getters.relationship(status.user.id) const relationshipReblog = reblog && this.$store.getters.relationship(reblog.user.id) - const reasonsToMute = ( - // Post is muted according to BE - status.muted || + return status.muted || // Reprööt of a muted post according to BE (reblog && reblog.muted) || // Muted user relationship.muting || // Muted user of a reprööt - (relationshipReblog && relationshipReblog.muting) || - // Thread is muted - status.thread_muted || - // Wordfiltered - this.muteWordHits.length > 0 - ) - const excusesNotToMute = ( + (relationshipReblog && relationshipReblog.muting) + }, + shouldNotMute () { + const { status } = this + const { reblog } = status + return ( ( this.inProfile && ( // Don't mute user's posts on user timeline (except reblogs) @@ -218,14 +299,26 @@ const Status = { (this.inConversation && status.thread_muted) // No excuses if post has muted words ) && !this.muteWordHits.length > 0 - - return !this.unmuted && !excusesNotToMute && reasonsToMute + }, + hideMutedUsers () { + return this.mergedConfig.hideMutedPosts + }, + hideMutedThreads () { + return this.mergedConfig.hideMutedThreads }, hideFilteredStatuses () { return this.mergedConfig.hideFilteredStatuses }, + hideWordFilteredPosts () { + return this.mergedConfig.hideWordFilteredPosts + }, hideStatus () { - return (this.muted && this.hideFilteredStatuses) || this.virtualHidden + return (!this.shouldNotMute) && ( + (this.muted && this.hideFilteredStatuses) || + (this.userIsMuted && this.hideMutedUsers) || + (this.status.thread_muted && this.hideMutedThreads) || + (this.muteWordHits.length > 0 && this.hideWordFilteredPosts) + ) }, isFocused () { // retweet or root of an expanded conversation @@ -270,11 +363,18 @@ const Status = { return uniqBy(combinedUsers, 'id') }, tags () { + // eslint-disable-next-line no-prototype-builtins return this.status.tags.filter(tagObj => tagObj.hasOwnProperty('name')).map(tagObj => tagObj.name).join(' ') }, hidePostStats () { return this.mergedConfig.hidePostStats }, + muteBotStatuses () { + return this.mergedConfig.muteBotStatuses + }, + hideBotIndication () { + return this.mergedConfig.hideBotIndication + }, currentUser () { return this.$store.state.users.currentUser }, @@ -286,6 +386,21 @@ const Status = { }, isSuspendable () { return !this.replying && this.mediaPlaying.length === 0 + }, + inThreadForest () { + return !!this.controlledThreadDisplayStatus + }, + threadShowing () { + return this.controlledThreadDisplayStatus === 'showing' + }, + visibilityLocalized () { + return this.$i18n.t('general.scope_in_timeline.' + this.status.visibility) + }, + isEdited () { + return this.status.edited_at !== null + }, + editingAvailable () { + return this.$store.state.instance.editingAvailable } }, methods: { @@ -308,7 +423,7 @@ const Status = { this.error = undefined }, toggleReplying () { - this.replying = !this.replying + controlledOrUncontrolledToggle(this, 'replying') }, gotoOriginal (id) { if (this.inConversation) { @@ -328,19 +443,21 @@ const Status = { return generateProfileLink(id, name, this.$store.state.instance.restrictedNicknames) }, addMediaPlaying (id) { - this.mediaPlaying.push(id) + controlledOrUncontrolledSet(this, 'mediaPlaying', this.mediaPlaying.concat(id)) }, removeMediaPlaying (id) { - this.mediaPlaying = this.mediaPlaying.filter(mediaId => mediaId !== id) + controlledOrUncontrolledSet(this, 'mediaPlaying', this.mediaPlaying.filter(mediaId => mediaId !== id)) }, setHeadTailLinks (headTailLinks) { this.headTailLinks = headTailLinks - } - }, - watch: { - 'highlight': function (id) { + }, + toggleThreadDisplay () { + this.controlledToggleThreadDisplay() + }, + scrollIfHighlighted (highlightId) { + const id = highlightId if (this.status.id === id) { - let rect = this.$el.getBoundingClientRect() + const rect = this.$el.getBoundingClientRect() if (rect.top < 100) { // Post is above screen, match its top to screen top window.scrollBy(0, rect.top - 100) @@ -352,6 +469,11 @@ const Status = { window.scrollBy(0, rect.bottom - window.innerHeight + 50) } } + } + }, + watch: { + highlight: function (id) { + this.scrollIfHighlighted(id) }, 'status.repeat_num': function (num) { // refetch repeats when repeat_num is changed in any way @@ -365,14 +487,9 @@ const Status = { this.$store.dispatch('fetchFavs', this.status.id) } }, - 'isSuspendable': function (val) { + isSuspendable: function (val) { this.suspendable = val } - }, - filters: { - capitalize: function (str) { - return str.charAt(0).toUpperCase() + str.slice(1) - } } } diff --git a/src/components/status/status.scss b/src/components/status/status.scss @@ -1,10 +1,10 @@ @import '../../_variables.scss'; -$status-margin: 0.75em; - .Status { min-width: 0; white-space: normal; + word-wrap: break-word; + word-break: break-word; &:hover { --_still-image-img-visibility: visible; @@ -26,15 +26,8 @@ $status-margin: 0.75em; --icon: var(--selectedPostIcon, $fallback--icon); } - &.-conversation { - border-left-width: 4px; - border-left-style: solid; - border-left-color: $fallback--cRed; - border-left-color: var(--cRed, $fallback--cRed); - } - .gravestone { - padding: $status-margin; + padding: var(--status-margin, $status-margin); color: $fallback--faint; color: var(--faint, $fallback--faint); display: flex; @@ -47,7 +40,11 @@ $status-margin: 0.75em; .status-container { display: flex; - padding: $status-margin; + padding: var(--status-margin, $status-margin); + + > * { + min-width: 0; + } &.-repeat { padding-top: 0; @@ -55,7 +52,7 @@ $status-margin: 0.75em; } .pin { - padding: $status-margin $status-margin 0; + padding: var(--status-margin, $status-margin) var(--status-margin, $status-margin) 0; display: flex; align-items: center; justify-content: flex-end; @@ -71,7 +68,7 @@ $status-margin: 0.75em; } .left-side { - margin-right: $status-margin; + margin-right: var(--status-margin, $status-margin); } .right-side { @@ -80,12 +77,11 @@ $status-margin: 0.75em; } .usercard { - margin-bottom: $status-margin; + margin-bottom: var(--status-margin, $status-margin); } .status-username { white-space: nowrap; - font-size: 14px; overflow: hidden; max-width: 85%; font-weight: bold; @@ -110,7 +106,7 @@ $status-margin: 0.75em; .heading-name-row { display: flex; justify-content: space-between; - line-height: 18px; + line-height: 1.3; a { display: inline-block; @@ -160,22 +156,29 @@ $status-margin: 0.75em; margin-right: 0.2em; } - & .heading-reply-row { + & .heading-reply-row, + & .heading-edited-row { position: relative; align-content: baseline; - font-size: 12px; - line-height: 160%; + font-size: 0.85em; + margin-top: 0.2em; + line-height: 130%; max-width: 100%; align-items: stretch; } & .reply-to-popover, - & .reply-to-no-popover { + & .reply-to-no-popover, + & .mentions { min-width: 0; margin-right: 0.4em; flex-shrink: 0; } + .reply-glued-label { + margin-right: 0.5em; + } + .reply-to-popover { .reply-to:hover::before { content: ''; @@ -209,7 +212,6 @@ $status-margin: 0.75em; & .reply-to { white-space: nowrap; position: relative; - padding-right: 0.25em; } & .mentions-text, @@ -226,8 +228,8 @@ $status-margin: 0.75em; .replies { margin-top: 0.25em; - line-height: 18px; - font-size: 12px; + line-height: 1.3; + font-size: 0.85em; display: flex; flex-wrap: wrap; @@ -241,7 +243,7 @@ $status-margin: 0.75em; } .repeat-info { - padding: 0.4em $status-margin; + padding: 0.4em var(--status-margin, $status-margin); .repeat-icon { color: $fallback--cGreen; @@ -287,7 +289,7 @@ $status-margin: 0.75em; position: relative; width: 100%; display: flex; - margin-top: $status-margin; + margin-top: var(--status-margin, $status-margin); > * { max-width: 4em; @@ -355,7 +357,7 @@ $status-margin: 0.75em; } .favs-repeated-users { - margin-top: $status-margin; + margin-top: var(--status-margin, $status-margin); } .stats { @@ -382,19 +384,19 @@ $status-margin: 0.75em; } .stat-count { - margin-right: $status-margin; + margin-right: var(--status-margin, $status-margin); user-select: none; .stat-title { color: var(--faint, $fallback--faint); - font-size: 12px; + font-size: 0.85em; text-transform: uppercase; position: relative; } .stat-number { font-weight: bolder; - font-size: 16px; + font-size: 1.1em; line-height: 1em; } @@ -408,13 +410,13 @@ $status-margin: 0.75em; margin-left: 20px; } - .avatar:not(.repeater-avatar) { + .post-avatar { width: 40px; height: 40px; // TODO define those other way somehow? // stylelint-disable rscss/class-format - &.avatar-compact { + &.-compact { width: 32px; height: 32px; } diff --git a/src/components/status/status.vue b/src/components/status/status.vue @@ -1,6 +1,7 @@ <template> <div v-if="!hideStatus" + ref="root" class="Status" :class="[{ '-focused': isFocused }, { '-conversation': inlineExpanded }]" > @@ -24,9 +25,10 @@ class="fa-scale-110 fa-old-padding repeat-icon" icon="retweet" /> - <router-link :to="userProfileLink"> - {{ status.user.screen_name_ui }} - </router-link> + <user-link + :user="status.user" + :at="false" + /> </small> <small v-if="showReasonMutedThread" @@ -77,6 +79,7 @@ <UserAvatar v-if="retweet" class="left-side repeater-avatar" + :bot="rtBotIndicator" :better-shadow="betterShadow" :user="statusoid.user" /> @@ -99,6 +102,7 @@ :to="retweeterProfileLink" >{{ retweeter }}</router-link> </span> + {{ ' ' }} <FAIcon icon="retweet" class="repeat-icon" @@ -119,25 +123,25 @@ v-if="!noHeading" class="left-side" > - <router-link - :to="userProfileLink" - @click.stop.prevent.capture.native="toggleUserExpanded" + <a + :href="$router.resolve(userProfileLink).href" + @click.prevent > - <UserAvatar - :compact="compact" - :better-shadow="betterShadow" - :user="status.user" - /> - </router-link> + <UserPopover + :user-id="status.user.id" + :overlay-centers="true" + > + <UserAvatar + class="post-avatar" + :bot="botIndicator" + :compact="compact" + :better-shadow="betterShadow" + :user="status.user" + /> + </UserPopover> + </a> </div> <div class="right-side"> - <UserCard - v-if="userExpanded" - :user-id="status.user.id" - :rounded="true" - :bordered="true" - class="usercard" - /> <div v-if="!noHeading" class="status-heading" @@ -161,13 +165,12 @@ > {{ status.user.name }} </h4> - <router-link + <user-link class="account-name" :title="status.user.screen_name_ui" - :to="userProfileLink" - > - {{ status.user.screen_name_ui }} - </router-link> + :user="status.user" + :at="false" + /> <img v-if="!!(status.user && status.user.favicon)" class="status-favicon" @@ -188,7 +191,7 @@ <span v-if="status.visibility" class="visibility-icon" - :title="status.visibility | capitalize" + :title="visibilityLocalized" > <FAIcon fixed-width @@ -219,6 +222,31 @@ class="fa-scale-110" /> </button> + <button + v-if="inThreadForest && replies && replies.length && !simpleTree" + class="button-unstyled" + :title="threadShowing ? $t('status.thread_hide') : $t('status.thread_show')" + :aria-expanded="threadShowing ? 'true' : 'false'" + @click.prevent="toggleThreadDisplay" + > + <FAIcon + fixed-width + class="fa-scale-110" + :icon="threadShowing ? 'chevron-up' : 'chevron-down'" + /> + </button> + <button + v-if="dive && !simpleTree" + class="button-unstyled" + :title="$t('status.show_only_conversation_under_this')" + @click.prevent="dive" + > + <FAIcon + fixed-width + class="fa-scale-110" + :icon="'angle-double-right'" + /> + </button> </span> </div> <div @@ -227,7 +255,7 @@ > <span v-if="isReply" - class="glued-label" + class="glued-label reply-glued-label" > <StatusPopover v-if="!isPreview" @@ -246,6 +274,7 @@ icon="reply" flip="horizontal" /> + {{ ' ' }} <span class="reply-to-text" > @@ -265,7 +294,6 @@ :url="replyProfileLink" :user-id="status.in_reply_to_user_id" :user-screen-name="status.in_reply_to_screen_name" - :first-mention="false" /> </span> @@ -292,12 +320,31 @@ class="mentions-line-first" /> </span> + {{ ' ' }} <MentionsLine v-if="hasMentionsLine" :mentions="mentionsLine.slice(1)" class="mentions-line" /> </div> + <div + v-if="isEdited && editingAvailable && !isPreview" + class="heading-edited-row" + > + <i18n-t + keypath="status.edited_at" + tag="span" + > + <template #time> + <Timeago + template-key="time.in_past" + :time="status.edited_at" + :auto-update="60" + :long-format="true" + /> + </template> + </i18n-t> + </div> </div> <StatusContent @@ -306,6 +353,12 @@ :no-heading="noHeading" :highlight="highlight" :focused="isFocused" + :controlled-showing-tall="controlledShowingTall" + :controlled-expanding-subject="controlledExpandingSubject" + :controlled-showing-long-subject="controlledShowingLongSubject" + :controlled-toggle-showing-tall="controlledToggleShowingTall" + :controlled-toggle-expanding-subject="controlledToggleExpandingSubject" + :controlled-toggle-showing-long-subject="controlledToggleShowingLongSubject" @mediaplay="addMediaPlaying($event)" @mediapause="removeMediaPlaying($event)" @parseReady="setHeadTailLinks" @@ -315,7 +368,20 @@ v-if="inConversation && !isPreview && replies && replies.length" class="replies" > - <span class="faint">{{ $t('status.replies_list') }}</span> + <button + v-if="showOtherRepliesAsButton && replies.length > 1" + class="button-unstyled -link faint" + :title="$tc('status.ancestor_follow', replies.length - 1, { numReplies: replies.length - 1 })" + @click.prevent="dive" + > + {{ $tc('status.replies_list_with_others', replies.length - 1, { numReplies: replies.length - 1 }) }} + </button> + <span + v-else + class="faint" + > + {{ $t('status.replies_list') }} + </span> <StatusPopover v-for="reply in replies" :key="reply.id" @@ -407,7 +473,11 @@ class="gravestone" > <div class="left-side"> - <UserAvatar :compact="compact" /> + <UserAvatar + class="post-avatar" + :compact="compact" + :bot="botIndicator" + /> </div> <div class="right-side"> <div class="deleted-text"> @@ -439,6 +509,6 @@ </div> </template> -<script src="./status.js" ></script> +<script src="./status.js"></script> <style src="./status.scss" lang="scss"></style> diff --git a/src/components/status_body/status_body.js b/src/components/status_body/status_body.js @@ -21,18 +21,21 @@ library.add( const StatusContent = { name: 'StatusContent', props: [ + 'compact', 'status', 'focused', 'noHeading', 'fullContent', - 'singleLine' + 'singleLine', + 'showingTall', + 'expandingSubject', + 'showingLongSubject', + 'toggleShowingTall', + 'toggleExpandingSubject', + 'toggleShowingLongSubject' ], data () { return { - showingTall: this.fullContent || (this.inConversation && this.focused), - showingLongSubject: false, - // not as computed because it sets the initial state which will be changed later - expandingSubject: !this.$store.getters.mergedConfig.collapseMessageWithSubject, postLength: this.status.text.length, parseReadyDone: false } @@ -49,6 +52,7 @@ const StatusContent = { // Using max-height + overflow: auto for status components resulted in false positives // very often with japanese characters, and it was very annoying. tallStatus () { + if (this.singleLine || this.compact) return false const lengthScore = this.status.raw_html.split(/<p|<br/).length + this.postLength / 80 return lengthScore > 20 }, @@ -113,9 +117,9 @@ const StatusContent = { }, toggleShowMore () { if (this.mightHideBecauseTall) { - this.showingTall = !this.showingTall + this.toggleShowingTall() } else if (this.mightHideBecauseSubject) { - this.expandingSubject = !this.expandingSubject + this.toggleExpandingSubject() } }, generateTagLink (tag) { diff --git a/src/components/status_body/status_body.scss b/src/components/status_body/status_body.scss @@ -1,11 +1,17 @@ @import '../../_variables.scss'; .StatusBody { + display: flex; + flex-direction: column; .emoji { --_still_image-label-scale: 0.5; } + .attachments { + margin-top: 0.5em; + } + & .text, & .summary { font-family: var(--postFont, sans-serif); @@ -13,7 +19,7 @@ overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; - line-height: 1.4em; + line-height: var(--post-line-height); } .summary { @@ -115,4 +121,54 @@ .cyantext { color: var(--postCyantext, $fallback--cBlue); } + + &.-compact { + align-items: top; + flex-direction: row; + + --emoji-size: 16px; + + & .body, + & .attachments { + max-height: 3.25em; + } + + .body { + overflow: hidden; + white-space: normal; + min-width: 5em; + flex: 5 1 auto; + mask-size: auto 3.5em, auto auto; + mask-position: 0 0, 0 0; + mask-repeat: repeat-x, repeat; + mask-image: linear-gradient(to bottom, white 2em, transparent 3em); + + /* Autoprefixed seem to ignore this one, and also syntax is different */ + -webkit-mask-composite: xor; + mask-composite: exclude; + } + + .attachments { + margin-top: 0; + flex: 1 1 0; + min-width: 5em; + height: 100%; + margin-left: 0.5em; + } + + .summary-wrapper { + .summary::after { + content: ': '; + } + + line-height: inherit; + margin: 0; + border: none; + display: inline-block; + } + + .text-wrapper { + display: inline-block; + } + } } diff --git a/src/components/status_body/status_body.vue b/src/components/status_body/status_body.vue @@ -1,5 +1,8 @@ <template> - <div class="StatusBody"> + <div + class="StatusBody" + :class="{ '-compact': compact }" + > <div class="body"> <div v-if="status.summary_raw_html" @@ -12,16 +15,16 @@ :emoji="status.emojis" /> <button - v-if="longSubject && showingLongSubject" + v-show="longSubject && showingLongSubject" class="button-unstyled -link tall-subject-hider" - @click.prevent="showingLongSubject=false" + @click.prevent="toggleShowingLongSubject" > {{ $t("status.hide_full_subject") }} </button> <button - v-else-if="longSubject" + v-show="longSubject && !showingLongSubject" class="button-unstyled -link tall-subject-hider" - @click.prevent="showingLongSubject=true" + @click.prevent="toggleShowingLongSubject" > {{ $t("status.show_full_subject") }} </button> @@ -31,7 +34,7 @@ class="text-wrapper" > <button - v-if="hideTallStatus" + v-show="hideTallStatus" class="button-unstyled -link tall-status-hider" :class="{ '-focused': focused }" @click.prevent="toggleShowMore" @@ -51,7 +54,7 @@ /> <button - v-if="hideSubjectStatus" + v-show="hideSubjectStatus" class="button-unstyled -link cw-status-hider" @click.prevent="toggleShowMore" > @@ -82,7 +85,7 @@ /> </button> <button - v-if="showingMore && !fullContent" + v-show="showingMore && !fullContent" class="button-unstyled -link status-unhider" @click.prevent="toggleShowMore" > @@ -93,5 +96,5 @@ <slot v-if="!hideSubjectStatus" /> </div> </template> -<script src="./status_body.js" ></script> +<script src="./status_body.js"></script> <style lang="scss" src="./status_body.scss" /> diff --git a/src/components/status_content/status_content.js b/src/components/status_content/status_content.js @@ -3,7 +3,6 @@ import Poll from '../poll/poll.vue' import Gallery from '../gallery/gallery.vue' import StatusBody from 'src/components/status_body/status_body.vue' import LinkPreview from '../link-preview/link-preview.vue' -import fileType from 'src/services/file_type/file_type.service' import { mapGetters, mapState } from 'vuex' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -24,16 +23,56 @@ library.add( faPollH ) +const camelCase = name => name.charAt(0).toUpperCase() + name.slice(1) + +const controlledOrUncontrolledGetters = list => list.reduce((res, name) => { + const camelized = camelCase(name) + const toggle = `controlledToggle${camelized}` + const controlledName = `controlled${camelized}` + const uncontrolledName = `uncontrolled${camelized}` + res[name] = function () { + return ((this.$data[toggle] !== undefined || this.$props[toggle] !== undefined) && this[toggle]) ? this[controlledName] : this[uncontrolledName] + } + return res +}, {}) + +const controlledOrUncontrolledToggle = (obj, name) => { + const camelized = camelCase(name) + const toggle = `controlledToggle${camelized}` + const uncontrolledName = `uncontrolled${camelized}` + if (obj[toggle]) { + obj[toggle]() + } else { + obj[uncontrolledName] = !obj[uncontrolledName] + } +} + const StatusContent = { name: 'StatusContent', props: [ 'status', + 'compact', 'focused', 'noHeading', 'fullContent', - 'singleLine' + 'singleLine', + 'controlledShowingTall', + 'controlledExpandingSubject', + 'controlledToggleShowingTall', + 'controlledToggleExpandingSubject', + 'controlledShowingLongSubject', + 'controlledToggleShowingLongSubject' ], + data () { + return { + uncontrolledShowingTall: this.fullContent || (this.inConversation && this.focused), + uncontrolledShowingLongSubject: false, + // not as computed because it sets the initial state which will be changed later + uncontrolledExpandingSubject: !this.$store.getters.mergedConfig.collapseMessageWithSubject + } + }, computed: { + ...controlledOrUncontrolledGetters(['showingTall', 'expandingSubject', 'showingLongSubject']), hideAttachments () { return (this.mergedConfig.hideAttachments && !this.inConversation) || (this.mergedConfig.hideAttachmentsInConv && this.inConversation) @@ -48,33 +87,15 @@ const StatusContent = { return true }, attachmentSize () { - if ((this.mergedConfig.hideAttachments && !this.inConversation) || + if (this.compact) { + return 'small' + } else if ((this.mergedConfig.hideAttachments && !this.inConversation) || (this.mergedConfig.hideAttachmentsInConv && this.inConversation) || (this.status.attachments.length > this.maxThumbnails)) { return 'hide' - } else if (this.compact) { - return 'small' } return 'normal' }, - galleryTypes () { - if (this.attachmentSize === 'hide') { - return [] - } - return this.mergedConfig.playVideosInModal - ? ['image', 'video'] - : ['image'] - }, - galleryAttachments () { - return this.status.attachments.filter( - file => fileType.fileMatchesSomeType(this.galleryTypes, file) - ) - }, - nonGalleryAttachments () { - return this.status.attachments.filter( - file => !fileType.fileMatchesSomeType(this.galleryTypes, file) - ) - }, maxThumbnails () { return this.mergedConfig.maxThumbnails }, @@ -91,6 +112,15 @@ const StatusContent = { StatusBody }, methods: { + toggleShowingTall () { + controlledOrUncontrolledToggle(this, 'showingTall') + }, + toggleExpandingSubject () { + controlledOrUncontrolledToggle(this, 'expandingSubject') + }, + toggleShowingLongSubject () { + controlledOrUncontrolledToggle(this, 'showingLongSubject') + }, setMedia () { const attachments = this.attachmentSize === 'hide' ? this.status.attachments : this.galleryAttachments return () => this.$store.dispatch('setMedia', attachments) diff --git a/src/components/status_content/status_content.vue b/src/components/status_content/status_content.vue @@ -1,44 +1,48 @@ <template> - <div class="StatusContent"> + <div + class="StatusContent" + :class="{ '-compact': compact }" + > <slot name="header" /> <StatusBody :status="status" + :compact="compact" :single-line="singleLine" + :showing-tall="showingTall" + :expanding-subject="expandingSubject" + :showing-long-subject="showingLongSubject" + :toggle-showing-tall="toggleShowingTall" + :toggle-expanding-subject="toggleExpandingSubject" + :toggle-showing-long-subject="toggleShowingLongSubject" @parseReady="$emit('parseReady', $event)" > - <div v-if="status.poll && status.poll.options"> + <div v-if="status.poll && status.poll.options && !compact"> <Poll :base-poll="status.poll" :emoji="status.emojis" /> </div> - <div - v-if="status.attachments.length !== 0" - class="attachments media-body" - > - <attachment - v-for="attachment in nonGalleryAttachments" - :key="attachment.id" - class="non-gallery" - :size="attachmentSize" - :nsfw="nsfwClickthrough" - :attachment="attachment" - :allow-play="true" - :set-media="setMedia()" - @play="$emit('mediaplay', attachment.id)" - @pause="$emit('mediapause', attachment.id)" - /> - <gallery - v-if="galleryAttachments.length > 0" - :nsfw="nsfwClickthrough" - :attachments="galleryAttachments" - :set-media="setMedia()" + <div v-else-if="status.poll && status.poll.options && compact"> + <FAIcon + icon="poll-h" + size="2x" /> </div> + <gallery + v-if="status.attachments.length !== 0" + class="attachments media-body" + :nsfw="nsfwClickthrough" + :attachments="status.attachments" + :limit="compact ? 1 : 0" + :size="attachmentSize" + @play="$emit('mediaplay', attachment.id)" + @pause="$emit('mediapause', attachment.id)" + /> + <div - v-if="status.card && !noHeading" + v-if="status.card && !noHeading && !compact" class="link-preview media-body" > <link-preview @@ -52,12 +56,8 @@ </div> </template> -<script src="./status_content.js" ></script> +<script src="./status_content.js"></script> <style lang="scss"> -@import '../../_variables.scss'; - -$status-margin: 0.75em; - .StatusContent { flex: 1; min-width: 0; diff --git a/src/components/status_history_modal/status_history_modal.js b/src/components/status_history_modal/status_history_modal.js @@ -0,0 +1,60 @@ +import { get } from 'lodash' +import Modal from '../modal/modal.vue' +import Status from '../status/status.vue' + +const StatusHistoryModal = { + components: { + Modal, + Status + }, + data () { + return { + statuses: [] + } + }, + computed: { + modalActivated () { + return this.$store.state.statusHistory.modalActivated + }, + params () { + return this.$store.state.statusHistory.params + }, + statusId () { + return this.params.id + }, + historyCount () { + return this.statuses.length + }, + history () { + return this.statuses + } + }, + watch: { + params (newVal, oldVal) { + const newStatusId = get(newVal, 'id') !== get(oldVal, 'id') + if (newStatusId) { + this.resetHistory() + } + + if (newStatusId || get(newVal, 'edited_at') !== get(oldVal, 'edited_at')) { + this.fetchStatusHistory() + } + } + }, + methods: { + resetHistory () { + this.statuses = [] + }, + fetchStatusHistory () { + this.$store.dispatch('fetchStatusHistory', this.params) + .then(data => { + this.statuses = data + }) + }, + closeModal () { + this.$store.dispatch('closeStatusHistoryModal') + } + } +} + +export default StatusHistoryModal diff --git a/src/components/status_history_modal/status_history_modal.vue b/src/components/status_history_modal/status_history_modal.vue @@ -0,0 +1,46 @@ +<template> + <Modal + v-if="modalActivated" + class="status-history-modal-view" + @backdropClicked="closeModal" + > + <div class="status-history-modal-panel panel"> + <div class="panel-heading"> + {{ $t('status.status_history') }} ({{ historyCount }}) + </div> + <div class="panel-body"> + <div + v-if="historyCount > 0" + class="history-body" + > + <status + v-for="status in history" + :key="status.id" + :statusoid="status" + :is-preview="true" + class="conversation-status status-fadein panel-body" + /> + </div> + </div> + </div> + </Modal> +</template> + +<script src="./status_history_modal.js"></script> + +<style lang="scss"> +.modal-view.status-history-modal-view { + align-items: flex-start; +} +.status-history-modal-panel { + flex-shrink: 0; + margin-top: 25%; + margin-bottom: 2em; + width: 100%; + max-width: 700px; + + @media (orientation: landscape) { + margin-top: 8%; + } +} +</style> diff --git a/src/components/status_popover/status_popover.js b/src/components/status_popover/status_popover.js @@ -1,6 +1,7 @@ import { find } from 'lodash' import { library } from '@fortawesome/fontawesome-svg-core' import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' +import { defineAsyncComponent } from 'vue' library.add( faCircleNotch @@ -22,8 +23,8 @@ const StatusPopover = { } }, components: { - Status: () => import('../status/status.vue'), - Popover: () => import('../popover/popover.vue') + Status: defineAsyncComponent(() => import('../status/status.vue')), + Popover: defineAsyncComponent(() => import('../popover/popover.vue')) }, methods: { enter () { @@ -37,6 +38,13 @@ const StatusPopover = { .catch(e => (this.error = true)) } } + }, + watch: { + status (newStatus, oldStatus) { + if (newStatus !== oldStatus) { + this.$nextTick(() => this.$refs.popover.updateStyles()) + } + } } } diff --git a/src/components/status_popover/status_popover.vue b/src/components/status_popover/status_popover.vue @@ -1,14 +1,16 @@ <template> <Popover + ref="popover" trigger="hover" + :stay-on-click="true" popover-class="popover-default status-popover" :bound-to="{ x: 'container' }" @show="enter" > - <template v-slot:trigger> + <template #trigger> <slot /> </template> - <template v-slot:content> + <template #content> <Status v-if="status" :is-preview="true" @@ -35,7 +37,7 @@ </Popover> </template> -<script src="./status_popover.js" ></script> +<script src="./status_popover.js"></script> <style lang="scss"> @import '../../_variables.scss'; @@ -52,8 +54,6 @@ border-width: 1px; border-radius: $fallback--tooltipRadius; border-radius: var(--tooltipRadius, $fallback--tooltipRadius); - box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5); - box-shadow: var(--popupShadow); /* TODO cleanup this */ .Status.Status { diff --git a/src/components/sticker_picker/sticker_picker.js b/src/components/sticker_picker/sticker_picker.js @@ -1,6 +1,6 @@ /* eslint-env browser */ import statusPosterService from '../../services/status_poster/status_poster.service.js' -import TabSwitcher from '../tab_switcher/tab_switcher.js' +import TabSwitcher from '../tab_switcher/tab_switcher.jsx' const StickerPicker = { components: { @@ -31,8 +31,8 @@ const StickerPicker = { fetch(sticker) .then((res) => { res.blob().then((blob) => { - var file = new File([blob], name, { mimetype: 'image/png' }) - var formData = new FormData() + const file = new File([blob], name, { mimetype: 'image/png' }) + const formData = new FormData() formData.append('file', file) statusPosterService.uploadMedia({ store, formData }) .then((fileData) => { diff --git a/src/components/still-image/still-image.js b/src/components/still-image/still-image.js @@ -5,20 +5,44 @@ const StillImage = { 'mimetype', 'imageLoadError', 'imageLoadHandler', - 'alt' + 'alt', + 'height', + 'width', + 'dataSrc' ], data () { return { + // for lazy loading, see loadLazy() + realSrc: this.src, stopGifs: this.$store.getters.mergedConfig.stopGifs } }, computed: { animated () { - return this.stopGifs && (this.mimetype === 'image/gif' || this.src.endsWith('.gif')) + if (!this.realSrc) { + return false + } + + return this.stopGifs && (this.mimetype === 'image/gif' || this.realSrc.endsWith('.gif')) + }, + style () { + const appendPx = (str) => /\d$/.test(str) ? str + 'px' : str + return { + height: this.height ? appendPx(this.height) : null, + width: this.width ? appendPx(this.width) : null + } } }, methods: { + loadLazy () { + if (this.dataSrc) { + this.realSrc = this.dataSrc + } + }, onLoad () { + if (!this.realSrc) { + return + } const image = this.$refs.src if (!image) return this.imageLoadHandler && this.imageLoadHandler(image) @@ -33,6 +57,14 @@ const StillImage = { onError () { this.imageLoadError && this.imageLoadError() } + }, + watch: { + src () { + this.realSrc = this.src + }, + dataSrc () { + this.$el.removeAttribute('data-loaded') + } } } diff --git a/src/components/still-image/still-image.vue b/src/components/still-image/still-image.vue @@ -2,6 +2,7 @@ <div class="still-image" :class="{ animated: animated }" + :style="style" > <canvas v-if="animated" @@ -10,14 +11,16 @@ <!-- NOTE: key is required to force to re-render img tag when src is changed --> <img ref="src" - :key="src" + :key="realSrc" :alt="alt" :title="alt" - :src="src" + :data-src="dataSrc" + :src="realSrc" :referrerpolicy="referrerpolicy" @load="onLoad" @error="onError" > + <slot /> </div> </template> @@ -56,10 +59,10 @@ zoom: var(--_still_image-label-scale, 1); content: 'gif'; position: absolute; - line-height: 10px; - font-size: 10px; - top: 5px; - left: 5px; + line-height: 1; + font-size: 0.7em; + top: 0.5em; + left: 0.5em; background: rgba(127, 127, 127, 0.5); color: #fff; display: block; diff --git a/src/components/swipe_click/swipe_click.js b/src/components/swipe_click/swipe_click.js @@ -0,0 +1,84 @@ +import GestureService from '../../services/gesture_service/gesture_service' + +/** + * props: + * direction: a vector that indicates the direction of the intended swipe + * threshold: the minimum distance in pixels the swipe has moved on `direction' + * for swipe-finished() to have a non-zero sign + * perpendicularTolerance: see gesture_service + * + * Events: + * preview-requested(offsets) + * Emitted when the pointer has moved. + * offsets: the offsets from the start of the swipe to the current cursor position + * + * swipe-canceled() + * Emitted when the swipe has been canceled due to a pointercancel event. + * + * swipe-finished(sign: 0|-1|1) + * Emitted when the swipe has finished. + * sign: if the swipe does not meet the threshold, 0 + * if the swipe meets the threshold in the positive direction, 1 + * if the swipe meets the threshold in the negative direction, -1 + * + * swipeless-clicked() + * Emitted when there is a click without swipe. + * This and swipe-finished() cannot be emitted for the same pointerup event. + */ +const SwipeClick = { + props: { + direction: { + type: Array + }, + threshold: { + type: Function, + default: () => 30 + }, + perpendicularTolerance: { + type: Number, + default: 1.0 + } + }, + methods: { + handlePointerDown (event) { + this.$gesture.start(event) + }, + handlePointerMove (event) { + this.$gesture.move(event) + }, + handlePointerUp (event) { + this.$gesture.end(event) + }, + handlePointerCancel (event) { + this.$gesture.cancel(event) + }, + handleNativeClick (event) { + this.$gesture.click(event) + }, + preview (offsets) { + this.$emit('preview-requested', offsets) + }, + end (sign) { + this.$emit('swipe-finished', sign) + }, + click () { + this.$emit('swipeless-clicked') + }, + cancel () { + this.$emit('swipe-canceled') + } + }, + created () { + this.$gesture = new GestureService.SwipeAndClickGesture({ + direction: this.direction, + threshold: this.threshold, + perpendicularTolerance: this.perpendicularTolerance, + swipePreviewCallback: this.preview, + swipeEndCallback: this.end, + swipeCancelCallback: this.cancel, + swipelessClickCallback: this.click + }) + } +} + +export default SwipeClick diff --git a/src/components/swipe_click/swipe_click.vue b/src/components/swipe_click/swipe_click.vue @@ -0,0 +1,14 @@ +<template> + <div + v-bind="$attrs" + @pointerdown="handlePointerDown" + @pointermove="handlePointerMove" + @pointerup="handlePointerUp" + @pointercancel="handlePointerCancel" + @click="handleNativeClick" + > + <slot /> + </div> +</template> + +<script src="./swipe_click.js"></script> diff --git a/src/components/tab_switcher/tab_switcher.js b/src/components/tab_switcher/tab_switcher.js @@ -1,156 +0,0 @@ -import Vue from 'vue' -import { mapState } from 'vuex' -import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome' - -import './tab_switcher.scss' - -export default Vue.component('tab-switcher', { - name: 'TabSwitcher', - props: { - renderOnlyFocused: { - required: false, - type: Boolean, - default: false - }, - onSwitch: { - required: false, - type: Function, - default: undefined - }, - activeTab: { - required: false, - type: String, - default: undefined - }, - scrollableTabs: { - required: false, - type: Boolean, - default: false - }, - sideTabBar: { - required: false, - type: Boolean, - default: false - } - }, - data () { - return { - active: this.$slots.default.findIndex(_ => _.tag) - } - }, - computed: { - activeIndex () { - // In case of controlled component - if (this.activeTab) { - return this.$slots.default.findIndex(slot => this.activeTab === slot.key) - } else { - return this.active - } - }, - settingsModalVisible () { - return this.settingsModalState === 'visible' - }, - ...mapState({ - settingsModalState: state => state.interface.settingsModalState - }) - }, - beforeUpdate () { - const currentSlot = this.$slots.default[this.active] - if (!currentSlot.tag) { - this.active = this.$slots.default.findIndex(_ => _.tag) - } - }, - methods: { - clickTab (index) { - return (e) => { - e.preventDefault() - this.setTab(index) - } - }, - setTab (index) { - if (typeof this.onSwitch === 'function') { - this.onSwitch.call(null, this.$slots.default[index].key) - } - this.active = index - if (this.scrollableTabs) { - this.$refs.contents.scrollTop = 0 - } - } - }, - render (h) { - const tabs = this.$slots.default - .map((slot, index) => { - if (!slot.tag) return - const classesTab = ['tab', 'button-default'] - const classesWrapper = ['tab-wrapper'] - if (this.activeIndex === index) { - classesTab.push('active') - classesWrapper.push('active') - } - if (slot.data.attrs.image) { - return ( - <div class={classesWrapper.join(' ')}> - <button - disabled={slot.data.attrs.disabled} - onClick={this.clickTab(index)} - class={classesTab.join(' ')} - type="button" - > - <img src={slot.data.attrs.image} title={slot.data.attrs['image-tooltip']}/> - {slot.data.attrs.label ? '' : slot.data.attrs.label} - </button> - </div> - ) - } - return ( - <div class={classesWrapper.join(' ')}> - <button - disabled={slot.data.attrs.disabled} - onClick={this.clickTab(index)} - class={classesTab.join(' ')} - type="button" - > - {!slot.data.attrs.icon ? '' : (<FAIcon class="tab-icon" size="2x" fixed-width icon={slot.data.attrs.icon}/>)} - <span class="text"> - {slot.data.attrs.label} - </span> - </button> - </div> - ) - }) - - const contents = this.$slots.default.map((slot, index) => { - if (!slot.tag) return - const active = this.activeIndex === index - const classes = [ active ? 'active' : 'hidden' ] - if (slot.data.attrs.fullHeight) { - classes.push('full-height') - } - const renderSlot = (!this.renderOnlyFocused || active) - ? slot - : '' - - return ( - <div class={classes}> - { - this.sideTabBar - ? <h1 class="mobile-label">{slot.data.attrs.label}</h1> - : '' - } - {renderSlot} - </div> - ) - }) - - return ( - <div class={'tab-switcher ' + (this.sideTabBar ? 'side-tabs' : 'top-tabs')}> - <div class="tabs"> - {tabs} - </div> - <div ref="contents" class={'contents' + (this.scrollableTabs ? ' scrollable-tabs' : '')} v-body-scroll-lock={this.settingsModalVisible}> - {contents} - </div> - </div> - ) - } -}) diff --git a/src/components/tab_switcher/tab_switcher.jsx b/src/components/tab_switcher/tab_switcher.jsx @@ -0,0 +1,183 @@ +// eslint-disable-next-line no-unused +import { h, Fragment } from 'vue' +import { mapState } from 'vuex' +import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome' + +import './tab_switcher.scss' + +const findFirstUsable = (slots) => slots.findIndex(_ => _.props) + +export default { + name: 'TabSwitcher', + props: { + renderOnlyFocused: { + required: false, + type: Boolean, + default: false + }, + onSwitch: { + required: false, + type: Function, + default: undefined + }, + activeTab: { + required: false, + type: String, + default: undefined + }, + scrollableTabs: { + required: false, + type: Boolean, + default: false + }, + sideTabBar: { + required: false, + type: Boolean, + default: false + }, + bodyScrollLock: { + required: false, + type: Boolean, + default: false + } + }, + data () { + return { + active: findFirstUsable(this.slots()) + } + }, + computed: { + activeIndex () { + // In case of controlled component + if (this.activeTab) { + return this.slots().findIndex(slot => slot && slot.props && this.activeTab === slot.props.key) + } else { + return this.active + } + }, + isActive () { + return tabName => { + const isWanted = slot => slot.props && slot.props['data-tab-name'] === tabName + return this.$slots.default().findIndex(isWanted) === this.activeIndex + } + }, + settingsModalVisible () { + return this.settingsModalState === 'visible' + }, + ...mapState({ + settingsModalState: state => state.interface.settingsModalState + }) + }, + beforeUpdate () { + const currentSlot = this.slots()[this.active] + if (!currentSlot.props) { + this.active = findFirstUsable(this.slots()) + } + }, + methods: { + clickTab (index) { + return (e) => { + e.preventDefault() + this.setTab(index) + } + }, + // DO NOT put it to computed, it doesn't work (caching?) + slots () { + if (this.$slots.default()[0].type === Fragment) { + return this.$slots.default()[0].children + } + return this.$slots.default() + }, + setTab (index) { + if (typeof this.onSwitch === 'function') { + this.onSwitch.call(null, this.slots()[index].key) + } + this.active = index + if (this.scrollableTabs) { + this.$refs.contents.scrollTop = 0 + } + } + }, + render () { + const tabs = this.slots() + .map((slot, index) => { + const props = slot.props + if (!props) return + const classesTab = ['tab', 'button-default'] + const classesWrapper = ['tab-wrapper'] + if (this.activeIndex === index) { + classesTab.push('active') + classesWrapper.push('active') + } + if (props.image) { + return ( + <div class={classesWrapper.join(' ')}> + <button + disabled={props.disabled} + onClick={this.clickTab(index)} + class={classesTab.join(' ')} + type="button" + > + <img src={props.image} title={props['image-tooltip']}/> + {props.label ? '' : props.label} + </button> + </div> + ) + } + return ( + <div class={classesWrapper.join(' ')}> + <button + disabled={props.disabled} + onClick={this.clickTab(index)} + class={classesTab.join(' ')} + type="button" + > + {!props.icon ? '' : (<FAIcon class="tab-icon" size="2x" fixed-width icon={props.icon}/>)} + <span class="text"> + {props.label} + </span> + </button> + </div> + ) + }) + + const contents = this.slots().map((slot, index) => { + const props = slot.props + if (!props) return + const active = this.activeIndex === index + const classes = [ active ? 'active' : 'hidden' ] + if (props.fullHeight) { + classes.push('full-height') + } + const renderSlot = (!this.renderOnlyFocused || active) + ? slot + : '' + + return ( + <div class={classes}> + { + this.sideTabBar + ? <h1 class="mobile-label">{props.label}</h1> + : '' + } + {renderSlot} + </div> + ) + }) + + return ( + <div class={'tab-switcher ' + (this.sideTabBar ? 'side-tabs' : 'top-tabs')}> + <div class="tabs"> + {tabs} + </div> + <div + ref="contents" + class={'contents' + (this.scrollableTabs ? ' scrollable-tabs' : '')} + v-body-scroll-lock={this.bodyScrollLock} + > + {contents} + </div> + </div> + ) + } +} diff --git a/src/components/tab_switcher/tab_switcher.scss b/src/components/tab_switcher/tab_switcher.scss @@ -17,6 +17,7 @@ overflow-x: auto; padding-top: 5px; flex-direction: row; + flex: 0 0 auto; &::after, &::before { content: ''; @@ -25,8 +26,9 @@ border-bottom-color: $fallback--border; border-bottom-color: var(--border, $fallback--border); } + .tab-wrapper { - height: 28px; + height: 2em; &:not(.active)::after { left: 0; @@ -166,13 +168,6 @@ position: relative; white-space: nowrap; padding: 6px 1em; - background-color: $fallback--fg; - background-color: var(--tab, $fallback--fg); - - &, &:active .tab-icon { - color: $fallback--text; - color: var(--tabText, $fallback--text); - } &:not(.active) { z-index: 4; diff --git a/src/components/tag_timeline/tag_timeline.js b/src/components/tag_timeline/tag_timeline.js @@ -18,7 +18,7 @@ const TagTimeline = { this.$store.dispatch('startFetchingTimeline', { timeline: 'tag', tag: this.tag }) } }, - destroyed () { + unmounted () { this.$store.dispatch('stopFetchingTimeline', 'tag') } } diff --git a/src/components/terms_of_service_panel/terms_of_service_panel.vue b/src/components/terms_of_service_panel/terms_of_service_panel.vue @@ -13,7 +13,7 @@ </div> </template> -<script src="./terms_of_service_panel.js" ></script> +<script src="./terms_of_service_panel.js"></script> <style lang="scss"> .tos-content { diff --git a/src/components/thread_tree/thread_tree.js b/src/components/thread_tree/thread_tree.js @@ -0,0 +1,90 @@ +import Status from '../status/status.vue' + +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faAngleDoubleDown, + faAngleDoubleRight +} from '@fortawesome/free-solid-svg-icons' + +library.add( + faAngleDoubleDown, + faAngleDoubleRight +) + +const ThreadTree = { + components: { + Status + }, + name: 'ThreadTree', + props: { + depth: Number, + status: Object, + inProfile: Boolean, + conversation: Array, + collapsable: Boolean, + isExpanded: Boolean, + pinnedStatusIdsObject: Object, + profileUserId: String, + + focused: Function, + highlight: String, + getReplies: Function, + setHighlight: Function, + toggleExpanded: Function, + + simple: Boolean, + // to control display of the whole thread forest + toggleThreadDisplay: Function, + threadDisplayStatus: Object, + showThreadRecursively: Function, + totalReplyCount: Object, + totalReplyDepth: Object, + statusContentProperties: Object, + setStatusContentProperty: Function, + toggleStatusContentProperty: Function, + dive: Function + }, + computed: { + suspendable () { + const selfSuspendable = this.$refs.statusComponent ? this.$refs.statusComponent.suspendable : true + if (this.$refs.childComponent) { + return selfSuspendable && this.$refs.childComponent.every(s => s.suspendable) + } + return selfSuspendable + }, + reverseLookupTable () { + return this.conversation.reduce((table, status, index) => { + table[status.id] = index + return table + }, {}) + }, + currentReplies () { + return this.getReplies(this.status.id).map(({ id }) => this.statusById(id)) + }, + threadShowing () { + return this.threadDisplayStatus[this.status.id] === 'showing' + }, + currentProp () { + return this.statusContentProperties[this.status.id] + } + }, + methods: { + statusById (id) { + return this.conversation[this.reverseLookupTable[id]] + }, + collapseThread () { + }, + showThread () { + }, + showAllSubthreads () { + }, + toggleCurrentProp (name) { + this.toggleStatusContentProperty(this.status.id, name) + }, + setCurrentProp (name, newVal) { + this.setStatusContentProperty(this.status.id, name) + } + } +} + +export default ThreadTree diff --git a/src/components/thread_tree/thread_tree.vue b/src/components/thread_tree/thread_tree.vue @@ -0,0 +1,135 @@ +<template> + <article class="thread-tree"> + <status + :key="status.id" + ref="statusComponent" + :inline-expanded="collapsable && isExpanded" + :statusoid="status" + :expandable="!isExpanded" + :show-pinned="pinnedStatusIdsObject && pinnedStatusIdsObject[status.id]" + :focused="focused(status.id)" + :in-conversation="isExpanded" + :highlight="highlight" + :replies="getReplies(status.id)" + :in-profile="inProfile" + :profile-user-id="profileUserId" + class="conversation-status conversation-status-treeview status-fadein panel-body" + + :simple-tree="simple" + :controlled-thread-display-status="threadDisplayStatus[status.id]" + :controlled-toggle-thread-display="() => toggleThreadDisplay(status.id)" + + :controlled-showing-tall="currentProp.showingTall" + :controlled-expanding-subject="currentProp.expandingSubject" + :controlled-showing-long-subject="currentProp.showingLongSubject" + :controlled-replying="currentProp.replying" + :controlled-media-playing="currentProp.mediaPlaying" + :controlled-toggle-showing-tall="() => toggleCurrentProp('showingTall')" + :controlled-toggle-expanding-subject="() => toggleCurrentProp('expandingSubject')" + :controlled-toggle-showing-long-subject="() => toggleCurrentProp('showingLongSubject')" + :controlled-toggle-replying="() => toggleCurrentProp('replying')" + :controlled-set-media-playing="(newVal) => setCurrentProp('mediaPlaying', newVal)" + :dive="dive ? () => dive(status.id) : undefined" + + @goto="setHighlight" + @toggleExpanded="toggleExpanded" + /> + <div + v-if="currentReplies.length && threadShowing" + class="thread-tree-replies" + > + <thread-tree + v-for="replyStatus in currentReplies" + :key="replyStatus.id" + ref="childComponent" + :depth="depth + 1" + :status="replyStatus" + + :in-profile="inProfile" + :conversation="conversation" + :collapsable="collapsable" + :is-expanded="isExpanded" + :pinned-status-ids-object="pinnedStatusIdsObject" + :profile-user-id="profileUserId" + + :focused="focused" + :get-replies="getReplies" + :highlight="highlight" + :set-highlight="setHighlight" + :toggle-expanded="toggleExpanded" + + :simple="simple" + :toggle-thread-display="toggleThreadDisplay" + :thread-display-status="threadDisplayStatus" + :show-thread-recursively="showThreadRecursively" + :total-reply-count="totalReplyCount" + :total-reply-depth="totalReplyDepth" + :status-content-properties="statusContentProperties" + :set-status-content-property="setStatusContentProperty" + :toggle-status-content-property="toggleStatusContentProperty" + :dive="dive" + /> + </div> + <div + v-if="currentReplies.length && !threadShowing" + class="thread-tree-replies thread-tree-replies-hidden" + > + <i18n-t + v-if="simple" + scope="global" + tag="button" + keypath="status.thread_follow_with_icon" + class="button-unstyled -link thread-tree-show-replies-button" + @click.prevent="dive(status.id)" + > + <template #icon> + <FAIcon + icon="angle-double-right" + /> + </template> + <template #text> + <span> + {{ $tc('status.thread_follow', totalReplyCount[status.id], { numStatus: totalReplyCount[status.id] }) }} + </span> + </template> + </i18n-t> + <i18n-t + v-else + scope="global" + tag="button" + keypath="status.thread_show_full_with_icon" + class="button-unstyled -link thread-tree-show-replies-button" + @click.prevent="showThreadRecursively(status.id)" + > + <template #icon> + <FAIcon + icon="angle-double-down" + /> + </template> + <template #text> + <span> + {{ $tc('status.thread_show_full', totalReplyCount[status.id], { numStatus: totalReplyCount[status.id], depth: totalReplyDepth[status.id] }) }} + </span> + </template> + </i18n-t> + </div> + </article> +</template> + +<script src="./thread_tree.js"></script> + +<style lang="scss"> +@import '../../_variables.scss'; +.thread-tree-replies { + margin-left: var(--status-margin, $status-margin); + border-left: 2px solid var(--border, $fallback--border); +} + +.thread-tree-replies-hidden { + padding: var(--status-margin, $status-margin); + /* Make the button stretch along the whole row */ + display: flex; + align-items: stretch; + flex-direction: column; +} +</style> diff --git a/src/components/timeago/timeago.vue b/src/components/timeago/timeago.vue @@ -3,7 +3,7 @@ :datetime="time" :title="localeDateString" > - {{ $t(relativeTime.key, [relativeTime.num]) }} + {{ relativeTimeString }} </time> </template> @@ -13,7 +13,7 @@ import localeService from 'src/services/locale/locale.service.js' export default { name: 'Timeago', - props: ['time', 'autoUpdate', 'longFormat', 'nowThreshold'], + props: ['time', 'autoUpdate', 'longFormat', 'nowThreshold', 'templateKey'], data () { return { relativeTime: { key: 'time.now', num: 0 }, @@ -26,12 +26,29 @@ export default { return typeof this.time === 'string' ? new Date(Date.parse(this.time)).toLocaleString(browserLocale) : this.time.toLocaleString(browserLocale) + }, + relativeTimeString () { + const timeString = this.$i18n.tc(this.relativeTime.key, this.relativeTime.num, [this.relativeTime.num]) + + if (typeof this.templateKey === 'string' && this.relativeTime.key !== 'time.now') { + return this.$i18n.t(this.templateKey, [timeString]) + } + + return timeString + } + }, + watch: { + time (newVal, oldVal) { + if (oldVal !== newVal) { + clearTimeout(this.interval) + this.refreshRelativeTimeObject() + } } }, created () { this.refreshRelativeTimeObject() }, - destroyed () { + unmounted () { clearTimeout(this.interval) }, methods: { diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js @@ -1,44 +1,40 @@ import Status from '../status/status.vue' +import { mapState } from 'vuex' import timelineFetcher from '../../services/timeline_fetcher/timeline_fetcher.service.js' import Conversation from '../conversation/conversation.vue' import TimelineMenu from '../timeline_menu/timeline_menu.vue' -import TimelineQuickSettings from './timeline_quick_settings.vue' +import QuickFilterSettings from '../quick_filter_settings/quick_filter_settings.vue' +import QuickViewSettings from '../quick_view_settings/quick_view_settings.vue' import { debounce, throttle, keyBy } from 'lodash' import { library } from '@fortawesome/fontawesome-svg-core' -import { faCircleNotch, faCog } from '@fortawesome/free-solid-svg-icons' +import { faCircleNotch, faCirclePlus, faCog, faMinus, faArrowUp, faCheck } from '@fortawesome/free-solid-svg-icons' library.add( faCircleNotch, - faCog + faCog, + faMinus, + faArrowUp, + faCirclePlus, + faCheck ) -export const getExcludedStatusIdsByPinning = (statuses, pinnedStatusIds) => { - const ids = [] - if (pinnedStatusIds && pinnedStatusIds.length > 0) { - for (let status of statuses) { - if (!pinnedStatusIds.includes(status.id)) { - break - } - ids.push(status.id) - } - } - return ids -} - const Timeline = { props: [ 'timeline', 'timelineName', 'title', 'userId', + 'listId', 'tag', 'embedded', 'count', 'pinnedStatusIds', - 'inProfile' + 'inProfile', + 'footerSlipgate' // reference to an element where we should put our footer ], data () { return { + showScrollTop: false, paused: false, unfocused: false, bottomedOut: false, @@ -50,9 +46,16 @@ const Timeline = { Status, Conversation, TimelineMenu, - TimelineQuickSettings + QuickFilterSettings, + QuickViewSettings }, computed: { + filteredVisibleStatuses () { + return this.timeline.visibleStatuses.filter(status => this.timelineName !== 'user' || (status.id >= this.timeline.minId && status.id <= this.timeline.maxId)) + }, + filteredPinnedStatusIds () { + return (this.pinnedStatusIds || []).filter(statusId => this.timeline.statusesObject[statusId]) + }, newStatusCount () { return this.timeline.newStatusCount }, @@ -66,35 +69,41 @@ const Timeline = { return `${this.$t('timeline.show_new')} (${this.newStatusCount})` } }, + mobileLoadButtonString () { + if (this.timeline.flushMarker !== 0) { + return '+' + } else { + return this.newStatusCount > 99 ? '∞' : this.newStatusCount + } + }, classes () { - let rootClasses = !this.embedded ? ['panel', 'panel-default'] : [] + let rootClasses = !this.embedded ? ['panel', 'panel-default'] : ['-nonpanel'] if (this.blockingClicks) rootClasses = rootClasses.concat(['-blocked', '_misclick-prevention']) return { root: rootClasses, - header: ['timeline-heading'].concat(!this.embedded ? ['panel-heading'] : []), + header: ['timeline-heading'].concat(!this.embedded ? ['panel-heading', '-sticky'] : []), body: ['timeline-body'].concat(!this.embedded ? ['panel-body'] : []), footer: ['timeline-footer'].concat(!this.embedded ? ['panel-footer'] : []) } }, // id map of statuses which need to be hidden in the main list due to pinning logic - excludedStatusIdsObject () { - const ids = getExcludedStatusIdsByPinning(this.timeline.visibleStatuses, this.pinnedStatusIds) - // Convert id array to object - return keyBy(ids) - }, pinnedStatusIdsObject () { return keyBy(this.pinnedStatusIds) }, statusesToDisplay () { const amount = this.timeline.visibleStatuses.length const statusesPerSide = Math.ceil(Math.max(3, window.innerHeight / 80)) - const min = Math.max(0, this.virtualScrollIndex - statusesPerSide) - const max = Math.min(amount, this.virtualScrollIndex + statusesPerSide) + const nonPinnedIndex = this.virtualScrollIndex - this.filteredPinnedStatusIds.length + const min = Math.max(0, nonPinnedIndex - statusesPerSide) + const max = Math.min(amount, nonPinnedIndex + statusesPerSide) return this.timeline.visibleStatuses.slice(min, max).map(_ => _.id) }, virtualScrollingEnabled () { return this.$store.getters.mergedConfig.virtualScrolling - } + }, + ...mapState({ + mobileLayout: state => state.interface.layoutType === 'mobile' + }) }, created () { const store = this.$store @@ -111,6 +120,7 @@ const Timeline = { timeline: this.timelineName, showImmediately, userId: this.userId, + listId: this.listId, tag: this.tag }) }, @@ -122,13 +132,16 @@ const Timeline = { window.addEventListener('keydown', this.handleShortKey) setTimeout(this.determineVisibleStatuses, 250) }, - destroyed () { + unmounted () { window.removeEventListener('scroll', this.handleScroll) window.removeEventListener('keydown', this.handleShortKey) if (typeof document.hidden !== 'undefined') document.removeEventListener('visibilitychange', this.handleVisibilityChange, false) this.$store.commit('setLoading', { timeline: this.timelineName, value: false }) }, methods: { + scrollToTop () { + window.scrollTo({ top: this.$el.offsetTop }) + }, stopBlockingClicks: debounce(function () { this.blockingClicks = false }, 1000), @@ -153,6 +166,7 @@ const Timeline = { this.$store.commit('showNewStatuses', { timeline: this.timelineName }) this.paused = false } + window.scrollTo({ top: 0 }) }, fetchOlderStatuses: throttle(function () { const store = this.$store @@ -165,6 +179,7 @@ const Timeline = { older: true, showImmediately: true, userId: this.userId, + listId: this.listId, tag: this.tag }).then(({ statuses }) => { if (statuses && statuses.length === 0) { @@ -226,6 +241,7 @@ const Timeline = { } }, handleScroll: throttle(function (e) { + this.showScrollTop = this.$el.offsetTop < window.scrollY this.determineVisibleStatuses() this.scrollLoad(e) }, 200), diff --git a/src/components/timeline/timeline.scss b/src/components/timeline/timeline.scss @@ -1,31 +1,58 @@ @import '../../_variables.scss'; .Timeline { - .loadmore-text { - opacity: 1; + .alert-dot { + border-radius: 100%; + height: 8px; + width: 8px; + position: absolute; + left: calc(50% - 4px); + top: calc(50% - 4px); + margin-left: 6px; + margin-top: -6px; + background-color: var(--badgeNeutral); + } + + .alert-badge { + font-size: 0.75em; + line-height: 1; + text-align: right; + border-radius: var(--tooltipRadius); + position: absolute; + left: calc(50% - 0.5em); + top: calc(50% - 0.4em); + padding: 0.2em; + margin-left: 0.7em; + margin-top: -1em; + background-color: var(--badgeNeutral); + color: var(--badgeNeutralText); + } + + .loadmore-button { + position: relative; } &.-blocked { cursor: progress; } - .timeline-heading { - max-width: 100%; - flex-wrap: nowrap; - align-items: center; - position: relative; + .conversation-heading { + top: calc(var(--__panel-heading-height) * var(--currentPanelStack, 2)); + z-index: 2; + } - .loadmore-button { - flex-shrink: 0; + &.-nonpanel { + .timeline-heading { + text-align: center; + line-height: 2.75em; + padding: 0 0.5em; } - .loadmore-text { - flex-shrink: 0; - line-height: 1em; + .timeline-heading { + .button-default, .alert { + line-height: 2em; + width: 100%; + } } } - - .timeline-footer { - border: none; - } } diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue @@ -1,86 +1,153 @@ <template> - <div :class="[classes.root, 'Timeline']"> + <div :class="['Timeline', classes.root]"> <div :class="classes.header"> - <TimelineMenu v-if="!embedded" /> - <button - v-if="showLoadButton" - class="button-default loadmore-button" - @click.prevent="showNewStatuses" - > - {{ loadButtonString }} - </button> + <TimelineMenu + v-if="!embedded" + :timeline-name="timelineName" + /> <div - v-else - class="loadmore-text faint" - @click.prevent + v-if="showScrollTop && !embedded" + class="rightside-button" > - {{ $t('timeline.up_to_date') }} + <button + class="button-unstyled scroll-to-top-button" + type="button" + :title="$t('general.scroll_to_top')" + @click="scrollToTop" + > + <FALayers class="fa-scale-110 fa-old-padding-layer"> + <FAIcon icon="arrow-up" /> + <FAIcon + icon="minus" + transform="up-7" + /> + </FALayers> + </button> </div> - <TimelineQuickSettings v-if="!embedded" /> + <template v-if="mobileLayout && !embedded"> + <div + v-if="showLoadButton" + class="rightside-button" + > + <button + class="button-unstyled loadmore-button" + :title="loadButtonString" + @click.prevent="showNewStatuses" + > + <FAIcon + fixed-width + icon="circle-plus" + /> + <div class="alert-badge"> + {{ mobileLoadButtonString }} + </div> + </button> + </div> + <div + v-else-if="!embedded" + class="loadmore-text faint veryfaint rightside-icon" + :title="$t('timeline.up_to_date')" + :aria-disabled="true" + @click.prevent + > + <FAIcon + fixed-width + icon="check" + /> + </div> + </template> + <template v-else> + <button + v-if="showLoadButton" + class="button-default loadmore-button" + @click.prevent="showNewStatuses" + > + {{ loadButtonString }} + </button> + <div + v-else-if="!embedded" + class="loadmore-text faint" + @click.prevent + > + {{ $t('timeline.up_to_date') }} + </div> + </template> + <QuickFilterSettings + v-if="!embedded" + class="rightside-button" + /> + <QuickViewSettings + v-if="!embedded" + class="rightside-button" + /> </div> <div :class="classes.body"> <div ref="timeline" class="timeline" + role="feed" > - <template v-for="statusId in pinnedStatusIds"> - <conversation - v-if="timeline.statusesObject[statusId]" - :key="statusId + '-pinned'" - class="status-fadein" - :status-id="statusId" - :collapsable="true" - :pinned-status-ids-object="pinnedStatusIdsObject" - :in-profile="inProfile" - :profile-user-id="userId" - /> - </template> - <template v-for="status in timeline.visibleStatuses"> - <conversation - v-if="!excludedStatusIdsObject[status.id]" - :key="status.id" - class="status-fadein" - :status-id="status.id" - :collapsable="true" - :in-profile="inProfile" - :profile-user-id="userId" - :virtual-hidden="virtualScrollingEnabled && !statusesToDisplay.includes(status.id)" - /> - </template> + <conversation + v-for="statusId in filteredPinnedStatusIds" + :key="statusId + '-pinned'" + role="listitem" + class="status-fadein" + :status-id="statusId" + :collapsable="true" + :pinned-status-ids-object="pinnedStatusIdsObject" + :in-profile="inProfile" + :profile-user-id="userId" + /> + <conversation + v-for="status in filteredVisibleStatuses" + :key="status.id" + role="listitem" + class="status-fadein" + :status-id="status.id" + :collapsable="true" + :in-profile="inProfile" + :profile-user-id="userId" + :virtual-hidden="virtualScrollingEnabled && !statusesToDisplay.includes(status.id)" + /> </div> </div> <div :class="classes.footer"> - <div - v-if="count===0" - class="new-status-notification text-center faint" - > - {{ $t('timeline.no_statuses') }} - </div> - <div - v-else-if="bottomedOut" - class="new-status-notification text-center faint" - > - {{ $t('timeline.no_more_statuses') }} - </div> - <button - v-else-if="!timeline.loading" - class="button-unstyled -link -fullwidth" - @click.prevent="fetchOlderStatuses()" + <teleport + :to="footerSlipgate" + :disabled="!embedded || !footerSlipgate" > - <div class="new-status-notification text-center"> - {{ $t('timeline.load_older') }} + <div + v-if="count===0" + class="new-status-notification text-center faint" + > + {{ $t('timeline.no_statuses') }} </div> - </button> - <div - v-else - class="new-status-notification text-center" - > - <FAIcon - icon="circle-notch" - spin - size="lg" - /> - </div> + <div + v-else-if="bottomedOut" + class="new-status-notification text-center faint" + > + {{ $t('timeline.no_more_statuses') }} + </div> + <button + v-else-if="!timeline.loading" + class="button-unstyled -link" + @click.prevent="fetchOlderStatuses()" + > + <div class="new-status-notification text-center"> + {{ $t('timeline.load_older') }} + </div> + </button> + <div + v-else + class="new-status-notification text-center" + > + <FAIcon + icon="circle-notch" + spin + size="lg" + /> + </div> + </teleport> </div> </div> </template> diff --git a/src/components/timeline/timeline_quick_settings.js b/src/components/timeline/timeline_quick_settings.js @@ -1,61 +0,0 @@ -import Popover from '../popover/popover.vue' -import { mapGetters } from 'vuex' -import { library } from '@fortawesome/fontawesome-svg-core' -import { faFilter, faFont, faWrench } from '@fortawesome/free-solid-svg-icons' - -library.add( - faFilter, - faFont, - faWrench -) - -const TimelineQuickSettings = { - components: { - Popover - }, - methods: { - setReplyVisibility (visibility) { - this.$store.dispatch('setOption', { name: 'replyVisibility', value: visibility }) - this.$store.dispatch('queueFlushAll') - }, - openTab (tab) { - this.$store.dispatch('openSettingsModalTab', tab) - } - }, - computed: { - ...mapGetters(['mergedConfig']), - loggedIn () { - return !!this.$store.state.users.currentUser - }, - replyVisibilitySelf: { - get () { return this.mergedConfig.replyVisibility === 'self' }, - set () { this.setReplyVisibility('self') } - }, - replyVisibilityFollowing: { - get () { return this.mergedConfig.replyVisibility === 'following' }, - set () { this.setReplyVisibility('following') } - }, - replyVisibilityAll: { - get () { return this.mergedConfig.replyVisibility === 'all' }, - set () { this.setReplyVisibility('all') } - }, - hideMedia: { - get () { return this.mergedConfig.hideAttachments || this.mergedConfig.hideAttachmentsInConv }, - set () { - const value = !this.hideMedia - this.$store.dispatch('setOption', { name: 'hideAttachments', value }) - this.$store.dispatch('setOption', { name: 'hideAttachmentsInConv', value }) - } - }, - hideMutedPosts: { - get () { return this.mergedConfig.hideMutedPosts || this.mergedConfig.hideFilteredStatuses }, - set () { - const value = !this.hideMutedPosts - this.$store.dispatch('setOption', { name: 'hideMutedPosts', value }) - this.$store.dispatch('setOption', { name: 'hideFilteredStatuses', value }) - } - } - } -} - -export default TimelineQuickSettings diff --git a/src/components/timeline/timeline_quick_settings.vue b/src/components/timeline/timeline_quick_settings.vue @@ -1,102 +0,0 @@ -<template> - <Popover - trigger="click" - class="TimelineQuickSettings" - :bound-to="{ x: 'container' }" - > - <template v-slot:content> - <div class="dropdown-menu"> - <div v-if="loggedIn"> - <button - class="button-default dropdown-item" - @click="replyVisibilityAll = true" - > - <span - class="menu-checkbox" - :class="{ 'menu-checkbox-radio': replyVisibilityAll }" - />{{ $t('settings.reply_visibility_all') }} - </button> - <button - class="button-default dropdown-item" - @click="replyVisibilityFollowing = true" - > - <span - class="menu-checkbox" - :class="{ 'menu-checkbox-radio': replyVisibilityFollowing }" - />{{ $t('settings.reply_visibility_following_short') }} - </button> - <button - class="button-default dropdown-item" - @click="replyVisibilitySelf = true" - > - <span - class="menu-checkbox" - :class="{ 'menu-checkbox-radio': replyVisibilitySelf }" - />{{ $t('settings.reply_visibility_self_short') }} - </button> - <div - role="separator" - class="dropdown-divider" - /> - </div> - <button - class="button-default dropdown-item" - @click="hideMedia = !hideMedia" - > - <span - class="menu-checkbox" - :class="{ 'menu-checkbox-checked': hideMedia }" - />{{ $t('settings.hide_media_previews') }} - </button> - <button - class="button-default dropdown-item" - @click="hideMutedPosts = !hideMutedPosts" - > - <span - class="menu-checkbox" - :class="{ 'menu-checkbox-checked': hideMutedPosts }" - />{{ $t('settings.hide_all_muted_posts') }} - </button> - <button - class="button-default dropdown-item dropdown-item-icon" - @click="openTab('filtering')" - > - <FAIcon icon="font" />{{ $t('settings.word_filter') }} - </button> - <button - class="button-default dropdown-item dropdown-item-icon" - @click="openTab('general')" - > - <FAIcon icon="wrench" />{{ $t('settings.more_settings') }} - </button> - </div> - </template> - <template v-slot:trigger> - <button class="button-unstyled"> - <FAIcon icon="filter" /> - </button> - </template> - </Popover> -</template> - -<script src="./timeline_quick_settings.js"></script> - -<style lang="scss"> - -.TimelineQuickSettings { - align-self: stretch; - - > button { - font-size: 1.2em; - padding-left: 0.7em; - padding-right: 0.2em; - line-height: 100%; - height: 100%; - } - - .dropdown-item { - margin: 0; - } -} - -</style> diff --git a/src/components/timeline_menu/timeline_menu.js b/src/components/timeline_menu/timeline_menu.js @@ -1,6 +1,10 @@ import Popover from '../popover/popover.vue' -import TimelineMenuContent from './timeline_menu_content.vue' +import NavigationEntry from 'src/components/navigation/navigation_entry.vue' +import { mapState } from 'vuex' +import { ListsMenuContent } from '../lists_menu/lists_menu_content.vue' import { library } from '@fortawesome/fontawesome-svg-core' +import { TIMELINES } from 'src/components/navigation/navigation.js' +import { filterNavigation } from 'src/components/navigation/filter.js' import { faChevronDown } from '@fortawesome/free-solid-svg-icons' @@ -11,9 +15,9 @@ library.add(faChevronDown) // because nav panel benefits from the same information. export const timelineNames = () => { return { - 'friends': 'nav.home_timeline', - 'bookmarks': 'nav.bookmarks', - 'dms': 'nav.dms', + friends: 'nav.home_timeline', + bookmarks: 'nav.bookmarks', + dms: 'nav.dms', 'public-timeline': 'nav.public_tl', 'public-external-timeline': 'nav.twkn' } @@ -22,7 +26,8 @@ export const timelineNames = () => { const TimelineMenu = { components: { Popover, - TimelineMenuContent + NavigationEntry, + ListsMenuContent }, data () { return { @@ -34,6 +39,28 @@ const TimelineMenu = { this.$store.dispatch('setLastTimeline', this.$route.name) } }, + computed: { + useListsMenu () { + const route = this.$route.name + return route === 'lists-timeline' + }, + ...mapState({ + currentUser: state => state.users.currentUser, + privateMode: state => state.instance.private, + federating: state => state.instance.federating + }), + timelinesList () { + return filterNavigation( + Object.entries(TIMELINES).map(([k, v]) => ({ ...v, name: k })), + { + hasChats: this.pleromaChatMessagesAvailable, + isFederating: this.federating, + isPrivate: this.privateMode, + currentUser: this.currentUser + } + ) + } + }, methods: { openMenu () { // $nextTick is too fast, animation won't play back but @@ -58,6 +85,9 @@ const TimelineMenu = { if (route === 'tag-timeline') { return '#' + this.$route.params.tag } + if (route === 'lists-timeline') { + return this.$store.getters.findListTitle(this.$route.params.id) + } const i18nkey = timelineNames()[this.$route.name] return i18nkey ? this.$t(i18nkey) : route } diff --git a/src/components/timeline_menu/timeline_menu.vue b/src/components/timeline_menu/timeline_menu.vue @@ -3,19 +3,29 @@ trigger="click" class="TimelineMenu" :class="{ 'open': isOpen }" - :margin="{ left: -15, right: -200 }" :bound-to="{ x: 'container' }" - popover-class="timeline-menu-popover-wrap" + bound-to-selector=".Timeline" + popover-class="timeline-menu-popover popover-default" @show="openMenu" @close="() => isOpen = false" > - <template v-slot:content> - <div class="timeline-menu-popover popover-default"> - <TimelineMenuContent /> - </div> + <template #content> + <ListsMenuContent + v-if="useListsMenu" + :show-pin="false" + class="timelines" + /> + <ul v-else> + <NavigationEntry + v-for="item in timelinesList" + :key="item.name" + :show-pin="false" + :item="item" + /> + </ul> </template> - <template v-slot:trigger> - <button class="button-unstyled title timeline-menu-title"> + <template #trigger> + <span class="button-unstyled title timeline-menu-title"> <span class="timeline-title">{{ timelineName() }}</span> <span> <FAIcon @@ -27,38 +37,22 @@ class="click-blocker" @click="blockOpen" /> - </button> + </span> </template> </Popover> </template> -<script src="./timeline_menu.js" ></script> +<script src="./timeline_menu.js"></script> <style lang="scss"> @import '../../_variables.scss'; .TimelineMenu { - flex-shrink: 1; margin-right: auto; min-width: 0; - width: 24rem; - - .timeline-menu-popover-wrap { - overflow: hidden; - // Match panel heading padding to line up menu with bottom of heading - margin-top: 0.6rem; - padding: 0 15px 15px 15px; - } - .timeline-menu-popover { - width: 24rem; - max-width: 100vw; - margin: 0; - font-size: 1rem; - border-top-right-radius: 0; - border-top-left-radius: 0; - transform: translateY(-100%); - transition: transform 100ms; + .popover-trigger-button { + vertical-align: bottom; } .panel::after { @@ -66,10 +60,6 @@ border-top-left-radius: 0; } - &.open .timeline-menu-popover { - transform: translateY(0); - } - .timeline-menu-title { margin: 0; cursor: pointer; @@ -104,6 +94,16 @@ box-shadow: var(--popoverShadow); } +} + +.timeline-menu-popover { + min-width: 24rem; + max-width: 100vw; + margin-top: 0.6rem; + font-size: 1rem; + border-top-right-radius: 0; + border-top-left-radius: 0; + ul { list-style: none; margin: 0; @@ -130,7 +130,9 @@ a { display: block; - padding: 0.6em 0.65em; + padding: 0 0.65em; + height: 3.5em; + line-height: 3.5em; &:hover { background-color: $fallback--lightBg; @@ -148,8 +150,7 @@ background-color: $fallback--lightBg; background-color: var(--selectedMenu, $fallback--lightBg); color: $fallback--text; - color: var(--selectedMenuText, $fallback--text); - --faint: var(--selectedMenuFaintText, $fallback--faint); + color: var(--selectedMenuText, $fallback--text); --faint: var(--selectedMenuFaintText, $fallback--faint); --faintLink: var(--selectedMenuFaintLink, $fallback--faint); --lightText: var(--selectedMenuLightText, $fallback--lightText); --icon: var(--selectedMenuIcon, $fallback--icon); diff --git a/src/components/timeline_menu/timeline_menu_content.js b/src/components/timeline_menu/timeline_menu_content.js @@ -1,29 +0,0 @@ -import { mapState } from 'vuex' -import { library } from '@fortawesome/fontawesome-svg-core' -import { - faUsers, - faGlobe, - faBookmark, - faEnvelope, - faHome -} from '@fortawesome/free-solid-svg-icons' - -library.add( - faUsers, - faGlobe, - faBookmark, - faEnvelope, - faHome -) - -const TimelineMenuContent = { - computed: { - ...mapState({ - currentUser: state => state.users.currentUser, - privateMode: state => state.instance.private, - federating: state => state.instance.federating - }) - } -} - -export default TimelineMenuContent diff --git a/src/components/timeline_menu/timeline_menu_content.vue b/src/components/timeline_menu/timeline_menu_content.vue @@ -1,66 +0,0 @@ -<template> - <ul> - <li v-if="currentUser"> - <router-link - class="menu-item" - :to="{ name: 'friends' }" - > - <FAIcon - fixed-width - class="fa-scale-110 fa-old-padding " - icon="home" - />{{ $t("nav.home_timeline") }} - </router-link> - </li> - <li v-if="currentUser || !privateMode"> - <router-link - class="menu-item" - :to="{ name: 'public-timeline' }" - > - <FAIcon - fixed-width - class="fa-scale-110 fa-old-padding " - icon="users" - />{{ $t("nav.public_tl") }} - </router-link> - </li> - <li v-if="federating && (currentUser || !privateMode)"> - <router-link - class="menu-item" - :to="{ name: 'public-external-timeline' }" - > - <FAIcon - fixed-width - class="fa-scale-110 fa-old-padding " - icon="globe" - />{{ $t("nav.twkn") }} - </router-link> - </li> - <li v-if="currentUser"> - <router-link - class="menu-item" - :to="{ name: 'bookmarks'}" - > - <FAIcon - fixed-width - class="fa-scale-110 fa-old-padding " - icon="bookmark" - />{{ $t("nav.bookmarks") }} - </router-link> - </li> - <li v-if="currentUser"> - <router-link - class="menu-item" - :to="{ name: 'dms', params: { username: currentUser.screen_name } }" - > - <FAIcon - fixed-width - class="fa-scale-110 fa-old-padding " - icon="envelope" - />{{ $t("nav.dms") }} - </router-link> - </li> - </ul> -</template> - -<script src="./timeline_menu_content.js" ></script> diff --git a/src/components/unicode_domain_indicator/unicode_domain_indicator.vue b/src/components/unicode_domain_indicator/unicode_domain_indicator.vue @@ -0,0 +1,26 @@ +<template> + <FAIcon + v-if="user && user.screen_name_ui_contains_non_ascii" + icon="code" + :title="$t('unicode_domain_indicator.tooltip')" + /> +</template> + +<script> +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faCode +} from '@fortawesome/free-solid-svg-icons' + +library.add( + faCode +) + +const UnicodeDomainIndicator = { + props: { + user: Object + } +} + +export default UnicodeDomainIndicator +</script> diff --git a/src/components/update_notification/update_notification.js b/src/components/update_notification/update_notification.js @@ -0,0 +1,69 @@ +import Modal from 'src/components/modal/modal.vue' +import { library } from '@fortawesome/fontawesome-svg-core' +import pleromaTan from 'src/assets/pleromatan_apology.png' +import pleromaTanFox from 'src/assets/pleromatan_apology_fox.png' +import pleromaTanMask from 'src/assets/pleromatan_apology_mask.png' +import pleromaTanFoxMask from 'src/assets/pleromatan_apology_fox_mask.png' + +import { + faTimes +} from '@fortawesome/free-solid-svg-icons' +library.add( + faTimes +) + +export const CURRENT_UPDATE_COUNTER = 1 + +const UpdateNotification = { + data () { + return { + showingImage: false, + pleromaTanVariant: Math.random() > 0.5 ? pleromaTan : pleromaTanFox, + showingMore: false + } + }, + components: { + Modal + }, + computed: { + pleromaTanStyles () { + const mask = this.pleromaTanVariant === pleromaTan ? pleromaTanMask : pleromaTanFoxMask + return { + 'shape-outside': 'url(' + mask + ')' + } + }, + shouldShow () { + return !this.$store.state.instance.disableUpdateNotification && + this.$store.state.users.currentUser && + this.$store.state.serverSideStorage.flagStorage.updateCounter < CURRENT_UPDATE_COUNTER && + !this.$store.state.serverSideStorage.prefsStorage.simple.dontShowUpdateNotifs + } + }, + methods: { + toggleShow () { + this.showingMore = !this.showingMore + }, + neverShowAgain () { + this.toggleShow() + this.$store.commit('setFlag', { flag: 'updateCounter', value: CURRENT_UPDATE_COUNTER }) + this.$store.commit('setPreference', { path: 'simple.dontShowUpdateNotifs', value: true }) + this.$store.dispatch('pushServerSideStorage') + }, + dismiss () { + this.$store.commit('setFlag', { flag: 'updateCounter', value: CURRENT_UPDATE_COUNTER }) + this.$store.dispatch('pushServerSideStorage') + } + }, + mounted () { + this.contentHeightNoImage = this.$refs.animatedText.scrollHeight + + // Workaround to get the text height only after mask loaded. A bit hacky. + const newImg = new Image() + newImg.onload = () => { + setTimeout(() => { this.showingImage = true }, 100) + } + newImg.src = this.pleromaTanVariant === pleromaTan ? pleromaTanMask : pleromaTanFoxMask + } +} + +export default UpdateNotification diff --git a/src/components/update_notification/update_notification.scss b/src/components/update_notification/update_notification.scss @@ -0,0 +1,113 @@ +@import 'src/_variables.scss'; +.UpdateNotification { + overflow: hidden; +} + +.UpdateNotificationModal { + --__top-fringe: 15em; // how much pleroma-tan should stick her head above + --__bottom-fringe: 80em; // just reserving as much as we can, number is mostly irrelevant + --__right-fringe: 8em; + + font-size: 15px; + position: relative; + transition: transform; + transition-timing-function: ease-in-out; + transition-duration: 500ms; + + .text { + max-width: 40em; + padding-left: 1em; + } + + @media all and (max-width: 800px) { + /* For mobile, the modal takes 100% of the available screen. + This ensures the minimized modal is always 50px above the browser bottom bar regardless of whether or not it is visible. + */ + width: 100vw; + } + + @media all and (max-height: 600px) { + display: none; + } + + .content { + overflow: hidden; + margin-top: calc(-1 * var(--__top-fringe)); + margin-bottom: calc(-1 * var(--__bottom-fringe)); + margin-right: calc(-1 * var(--__right-fringe)); + + &.-noImage { + .text { + padding-right: var(--__right-fringe); + } + } + } + + .panel-body { + border-width: 0 0 1px 0; + border-style: solid; + border-color: var(--border, $fallback--border); + } + + .panel-footer { + z-index: 22; + position: relative; + border-width: 0; + grid-template-columns: auto; + } + + .pleroma-tan { + object-fit: cover; + object-position: top; + transition: position, left, right, top, bottom, max-width, max-height; + transition-timing-function: ease-in-out; + transition-duration: 500ms; + width: 25em; + float: right; + z-index: 20; + position: relative; + shape-margin: 0.5em; + filter: drop-shadow(5px 5px 10px rgba(0,0,0,0.5)); + pointer-events: none; + } + + .spacer-top { + min-height: var(--__top-fringe); + } + + .spacer-bottom { + min-height: var(--__bottom-fringe); + } + + .extra-info-group { + transition: max-height, padding, height; + transition-timing-function: ease-in; + transition-duration: 700ms; + max-height: 70vh; + mask: + linear-gradient(to top, white, transparent) bottom/100% 2px no-repeat, + linear-gradient(to top, white, white); + } + + .art-credit { + text-align: right; + } + + &.-peek { + /* Explanation: + * 100vh - 100% = Distance between modal's top+bottom boundaries and screen + * (100vh - 100%) / 2 = Distance between bottom (or top) boundary and screen + */ + transform: translateY(calc(((100vh - 100%) / 2))); + + .pleroma-tan { + float: right; + z-index: 10; + shape-image-threshold: 0.7; + } + + .extra-info-group { + max-height: 0; + } + } +} diff --git a/src/components/update_notification/update_notification.vue b/src/components/update_notification/update_notification.vue @@ -0,0 +1,103 @@ +<template> + <Modal + :is-open="!!shouldShow" + class="UpdateNotification" + :no-background="true" + > + <div + class="UpdateNotificationModal panel" + :class="{ '-peek': !showingMore }" + > + <div class="panel-heading"> + <span class="title"> + {{ $t('update.big_update_title') }} + </span> + </div> + <div class="panel-body"> + <div + class="content" + :class="{ '-noImage': !showingImage }" + > + <img + v-if="showingImage" + class="pleroma-tan" + :src="pleromaTanVariant" + :style="pleromaTanStyles" + > + <div class="spacer-top" /> + <div class="text"> + <p> + {{ $t('update.big_update_content') }} + </p> + <div + ref="animatedText" + class="extra-info-group" + > + <i18n-t + keypath="update.update_bugs" + tag="p" + > + <template #pleromaGitlab> + <a + target="_blank" + href="https://git.pleroma.social/" + >{{ $t('update.update_bugs_gitlab') }}</a> + </template> + </i18n-t> + <i18n-t + keypath="update.update_changelog" + tag="p" + > + <template #theFullChangelog> + <a + target="_blank" + href="https://pleroma.social/announcements/" + >{{ $t('update.update_changelog_here') }}</a> + </template> + </i18n-t> + <p class="art-credit"> + <i18n-t + keypath="update.art_by" + tag="small" + > + <template #linkToArtist> + <a + target="_blank" + href="https://post.ebin.club/users/pipivovott" + >pipivovott</a> + </template> + </i18n-t> + </p> + </div> + </div> + <div class="spacer-bottom" /> + </div> + </div> + <div class="panel-footer"> + <button + class="button-default" + @click.prevent="neverShowAgain" + > + {{ $t("general.never_show_again") }} + </button> + <button + v-if="!showingMore" + class="button-default" + @click.prevent="toggleShow" + > + {{ $t("general.show_more") }} + </button> + <button + class="button-default" + @click.prevent="dismiss" + > + {{ $t("general.dismiss") }} + </button> + </div> + </div> + </Modal> +</template> + +<script src="./update_notification.js"></script> + +<style src="./update_notification.scss" lang="scss"></style> diff --git a/src/components/user_avatar/user_avatar.js b/src/components/user_avatar/user_avatar.js @@ -1,10 +1,21 @@ import StillImage from '../still-image/still-image.vue' +import { library } from '@fortawesome/fontawesome-svg-core' + +import { + faRobot +} from '@fortawesome/free-solid-svg-icons' + +library.add( + faRobot +) + const UserAvatar = { props: [ 'user', 'betterShadow', - 'compact' + 'compact', + 'bot' ], data () { return { diff --git a/src/components/user_avatar/user_avatar.vue b/src/components/user_avatar/user_avatar.vue @@ -1,18 +1,28 @@ <template> - <StillImage - v-if="user" + <span class="Avatar" - :alt="user.screen_name_ui" - :title="user.screen_name_ui" - :src="imgSrc(user.profile_image_url_original)" - :class="{ 'avatar-compact': compact, 'better-shadow': betterShadow }" - :image-load-error="imageLoadError" - /> - <div - v-else - class="Avatar -placeholder" - :class="{ 'avatar-compact': compact }" - /> + :class="{ '-compact': compact }" + > + <StillImage + v-if="user" + class="avatar" + :alt="user.screen_name_ui" + :title="user.screen_name_ui" + :src="imgSrc(user.profile_image_url_original)" + :image-load-error="imageLoadError" + :class="{ '-compact': compact, '-better-shadow': betterShadow }" + /> + <div + v-else + class="avatar -placeholder" + :class="{ '-compact': compact }" + /> + <FAIcon + v-if="bot" + icon="robot" + class="bot-indicator" + /> + </span> </template> <script src="./user_avatar.js"></script> @@ -25,36 +35,60 @@ --_avatarShadowInset: var(--avatarStatusShadowInset); --_still-image-label-visibility: hidden; + display: inline-block; + position: relative; width: 48px; height: 48px; - box-shadow: var(--_avatarShadowBox); - border-radius: $fallback--avatarRadius; - border-radius: var(--avatarRadius, $fallback--avatarRadius); - img { + &.-compact { + width: 32px; + height: 32px; + border-radius: $fallback--avatarAltRadius; + border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); + } + + .avatar { width: 100%; height: 100%; - } + box-shadow: var(--_avatarShadowBox); + border-radius: $fallback--avatarRadius; + border-radius: var(--avatarRadius, $fallback--avatarRadius); - &.better-shadow { - box-shadow: var(--_avatarShadowInset); - filter: var(--_avatarShadowFilter); - } + &.-better-shadow { + box-shadow: var(--_avatarShadowInset); + filter: var(--_avatarShadowFilter); + } + + &.-animated::before { + display: none; + } - &.animated::before { - display: none; + &.-compact { + border-radius: $fallback--avatarAltRadius; + border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); + } + + &.-placeholder { + background-color: $fallback--fg; + background-color: var(--fg, $fallback--fg); + } } - &.avatar-compact { - width: 32px; - height: 32px; - border-radius: $fallback--avatarAltRadius; - border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); + img { + width: 100%; + height: 100%; } - &.-placeholder { - background-color: $fallback--fg; - background-color: var(--fg, $fallback--fg); + .bot-indicator { + position: absolute; + bottom: 0; + right: 0; + margin: -0.2em; + padding: 0.2em; + background: rgba(127, 127, 127, 0.5); + color: #fff; + border-radius: var(--tooltipRadius); } + } </style> diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js @@ -4,7 +4,9 @@ import ProgressButton from '../progress_button/progress_button.vue' import FollowButton from '../follow_button/follow_button.vue' import ModerationTools from '../moderation_tools/moderation_tools.vue' import AccountActions from '../account_actions/account_actions.vue' +import UserNote from '../user_note/user_note.vue' import Select from '../select/select.vue' +import UserLink from '../user_link/user_link.vue' import RichContent from 'src/components/rich_content/rich_content.jsx' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import { mapGetters } from 'vuex' @@ -14,7 +16,9 @@ import { faRss, faSearchPlus, faExternalLinkAlt, - faEdit + faEdit, + faTimes, + faExpandAlt } from '@fortawesome/free-solid-svg-icons' library.add( @@ -22,12 +26,22 @@ library.add( faBell, faSearchPlus, faExternalLinkAlt, - faEdit + faEdit, + faTimes, + faExpandAlt ) export default { props: [ - 'userId', 'switcher', 'selected', 'hideBio', 'rounded', 'bordered', 'allowZoomingAvatar' + 'userId', + 'switcher', + 'selected', + 'hideBio', + 'rounded', + 'bordered', + 'avatarAction', // default - open profile, 'zoom' - zoom, function - call function + 'onClose', + 'hasNoteEditor' ], data () { return { @@ -47,15 +61,16 @@ export default { }, classes () { return [{ - 'user-card-rounded-t': this.rounded === 'top', // set border-top-left-radius and border-top-right-radius - 'user-card-rounded': this.rounded === true, // set border-radius for all sides - 'user-card-bordered': this.bordered === true // set border for all sides + '-rounded-t': this.rounded === 'top', // set border-top-left-radius and border-top-right-radius + '-rounded': this.rounded === true, // set border-radius for all sides + '-bordered': this.bordered === true, // set border for all sides + '-popover': !!this.onClose // set popover rounding }] }, style () { return { backgroundImage: [ - `linear-gradient(to bottom, var(--profileTint), var(--profileTint))`, + 'linear-gradient(to bottom, var(--profileTint), var(--profileTint))', `url(${this.user.cover_photo})` ].join(', ') } @@ -112,6 +127,16 @@ export default { hideFollowersCount () { return this.isOtherUser && this.user.hide_followers_count }, + showModerationMenu () { + const privileges = this.loggedIn.privileges + return this.loggedIn.role === 'admin' || privileges.includes('users_manage_activation_state') || privileges.includes('users_delete') || privileges.includes('users_manage_tags') + }, + hasNote () { + return this.relationship.note + }, + supportsNote () { + return 'note' in this.relationship + }, ...mapGetters(['mergedConfig']) }, components: { @@ -122,7 +147,9 @@ export default { ProgressButton, FollowButton, Select, - RichContent + RichContent, + UserLink, + UserNote }, methods: { muteUser () { @@ -166,10 +193,16 @@ export default { mimetype: 'image' } this.$store.dispatch('setMedia', [attachment]) - this.$store.dispatch('setCurrent', attachment) + this.$store.dispatch('setCurrentMedia', attachment) }, mentionUser () { this.$store.dispatch('openPostStatusModal', { replyTo: true, repliedUser: this.user }) + }, + onAvatarClickHandler (e) { + if (this.onAvatarClick) { + e.preventDefault() + this.onAvatarClick() + } } } } diff --git a/src/components/user_card/user_card.scss b/src/components/user_card/user_card.scss @@ -0,0 +1,352 @@ +@import '../../_variables.scss'; + +.user-card { + position: relative; + z-index: 1; + + &:hover { + --_still-image-img-visibility: visible; + --_still-image-canvas-visibility: hidden; + --_still-image-label-visibility: hidden; + } + + .panel-heading { + padding: .5em 0; + text-align: center; + box-shadow: none; + background: transparent; + flex-direction: column; + align-items: stretch; + // create new stacking context + position: relative; + } + + .panel-body { + word-wrap: break-word; + border-bottom-right-radius: inherit; + border-bottom-left-radius: inherit; + // create new stacking context + position: relative; + } + + .background-image { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + mask: linear-gradient(to top, white, transparent) bottom no-repeat, + linear-gradient(to top, white, white); + // Autoprefixer seem to ignore this one, and also syntax is different + -webkit-mask-composite: xor; + mask-composite: exclude; + background-size: cover; + mask-size: 100% 60%; + border-top-left-radius: calc(var(--__roundnessTop, --panelRadius) - 1px); + border-top-right-radius: calc(var(--__roundnessTop, --panelRadius) - 1px); + border-bottom-left-radius: calc(var(--__roundnessBottom, --panelRadius) - 1px); + border-bottom-right-radius: calc(var(--__roundnessBottom, --panelRadius) - 1px); + background-color: var(--profileBg); + z-index: -2; + + &.hide-bio { + mask-size: 100% 40px; + } + } + + &-bio { + text-align: center; + display: block; + line-height: 1.3; + padding: 1em; + margin: 0; + + a { + color: $fallback--link; + color: var(--postLink, $fallback--link); + } + + img { + object-fit: contain; + vertical-align: middle; + max-width: 100%; + max-height: 400px; + } + } + + &.-rounded-t { + border-top-left-radius: $fallback--panelRadius; + border-top-left-radius: var(--panelRadius, $fallback--panelRadius); + border-top-right-radius: $fallback--panelRadius; + border-top-right-radius: var(--panelRadius, $fallback--panelRadius); + + --__roundnessTop: var(--panelRadius); + --__roundnessBottom: 0; + } + + &.-rounded { + border-radius: $fallback--panelRadius; + border-radius: var(--panelRadius, $fallback--panelRadius); + + --__roundnessTop: var(--panelRadius); + --__roundnessBottom: var(--panelRadius); + } + + &.-popover { + border-radius: $fallback--tooltipRadius; + border-radius: var(--tooltipRadius, $fallback--tooltipRadius); + + --__roundnessTop: var(--tooltipRadius); + --__roundnessBottom: var(--tooltipRadius); + } + + &.-bordered { + border-width: 1px; + border-style: solid; + border-color: $fallback--border; + border-color: var(--border, $fallback--border); + } +} + +.user-info { + color: $fallback--lightText; + color: var(--lightText, $fallback--lightText); + padding: 0 26px; + + a { + color: $fallback--lightText; + color: var(--lightText, $fallback--lightText); + + &:hover { + color: var(--icon); + } + } + + .container { + min-width: 0; + padding: 16px 0 6px; + display: flex; + align-items: flex-start; + max-height: 56px; + + > * { + min-width: 0; + } + + > a { + vertical-align: middle; + display: flex; + } + + .Avatar { + --_avatarShadowBox: var(--avatarShadow); + --_avatarShadowFilter: var(--avatarShadowFilter); + --_avatarShadowInset: var(--avatarShadowInset); + + width: 56px; + height: 56px; + object-fit: cover; + } + } + + &-avatar { + position: relative; + cursor: pointer; + + &.-overlay { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.3); + display: flex; + justify-content: center; + align-items: center; + border-radius: $fallback--avatarRadius; + border-radius: var(--avatarRadius, $fallback--avatarRadius); + opacity: 0; + transition: opacity .2s ease; + + svg { + color: #FFF; + } + } + + &:hover &.-overlay { + opacity: 1; + } + } + + .external-link-button, .edit-profile-button { + cursor: pointer; + width: 2.5em; + text-align: center; + margin: -0.5em 0; + padding: 0.5em 0; + + &:not(:hover) .icon { + color: $fallback--lightText; + color: var(--lightText, $fallback--lightText); + } + } + + .user-summary { + display: block; + margin-left: 0.6em; + text-align: left; + text-overflow: ellipsis; + white-space: nowrap; + flex: 1 1 0; + // This is so that text doesn't get overlapped by avatar's shadow if it has + // big one + z-index: 1; + line-height: 2em; + + --emoji-size: 1.7em; + + .top-line, + .bottom-line { + display: flex; + } + } + + .user-name { + text-overflow: ellipsis; + overflow: hidden; + flex: 1 1 auto; + margin-right: 1em; + font-size: 1.1em; + } + + .bottom-line { + font-weight: light; + font-size: 1.1em; + align-items: baseline; + + .lock-icon { + margin-left: 0.5em; + } + + .user-screen-name { + min-width: 1px; + flex: 0 1 auto; + text-overflow: ellipsis; + overflow: hidden; + } + + .dailyAvg { + min-width: 1px; + flex: 0 0 auto; + margin-left: 1em; + font-size: 0.7em; + color: $fallback--text; + color: var(--text, $fallback--text); + } + + .user-role { + flex: none; + color: $fallback--text; + color: var(--alertNeutralText, $fallback--text); + background-color: $fallback--fg; + background-color: var(--alertNeutral, $fallback--fg); + } + } + + .user-meta { + margin-bottom: .15em; + display: flex; + align-items: baseline; + line-height: 22px; + flex-wrap: wrap; + + .following { + flex: 1 0 auto; + margin: 0; + margin-bottom: .25em; + text-align: left; + } + + .highlighter { + flex: 0 1 auto; + display: flex; + flex-wrap: wrap; + margin-right: -.5em; + align-self: start; + + .userHighlightCl { + padding: 2px 10px; + flex: 1 0 auto; + } + + .userHighlightSel { + padding-top: 0; + padding-bottom: 0; + flex: 1 0 auto; + } + + .userHighlightText { + width: 70px; + flex: 1 0 auto; + } + + .userHighlightCl, + .userHighlightText, + .userHighlightSel { + vertical-align: top; + margin-right: .5em; + margin-bottom: .25em; + } + } + } + .user-interactions { + position: relative; + display: flex; + flex-flow: row wrap; + margin-right: -.75em; + + > * { + margin: 0 .75em .6em 0; + white-space: nowrap; + min-width: 95px; + } + + button { + margin: 0; + } + } + + .user-note { + margin: 0 .75em .6em 0; + } +} + +.sidebar .edit-profile-button { + display: none; +} + +.user-counts { + display: flex; + line-height:16px; + padding: .5em 1.5em 0em 1.5em; + text-align: center; + justify-content: space-between; + color: $fallback--lightText; + color: var(--lightText, $fallback--lightText); + flex-wrap: wrap; +} + +.user-count { + flex: 1 0 auto; + padding: .5em 0 .5em 0; + margin: 0 .5em; + + h5 { + font-size:1em; + font-weight: bolder; + margin: 0 0 0.25em; + } + a { + text-decoration: none; + } +} diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue @@ -8,25 +8,32 @@ :style="style" class="background-image" /> - <div class="panel-heading"> + <div :class="onClose ? '' : panel-heading -flexible-height"> <div class="user-info"> <div class="container"> <a - v-if="allowZoomingAvatar" - class="user-info-avatar-link" + v-if="avatarAction === 'zoom'" + class="user-info-avatar -link" @click="zoomAvatar" > <UserAvatar :better-shadow="betterShadow" :user="user" /> - <div class="user-info-avatar-link-overlay"> + <div class="user-info-avatar -link -overlay"> <FAIcon class="fa-scale-110 fa-old-padding" icon="search-plus" /> </div> </a> + <UserAvatar + v-else-if="typeof avatarAction === 'function'" + class="user-info-avatar" + :better-shadow="betterShadow" + :user="user" + @click="avatarAction" + /> <router-link v-else :to="userProfileLink(user)" @@ -38,12 +45,16 @@ </router-link> <div class="user-summary"> <div class="top-line"> - <RichContent - :title="user.name" + <router-link + :to="userProfileLink(user)" class="user-name" - :html="user.name" - :emoji="user.emoji" - /> + > + <RichContent + :title="user.name" + :html="user.name" + :emoji="user.emoji" + /> + </router-link> <button v-if="!isOtherUser && user.is_local" class="button-unstyled edit-profile-button" @@ -72,17 +83,41 @@ :user="user" :relationship="relationship" /> - </div> - <div class="bottom-line"> <router-link - class="user-screen-name" - :title="user.screen_name_ui" + v-if="onClose" :to="userProfileLink(user)" + class="button-unstyled external-link-button" + @click="onClose" > - @{{ user.screen_name_ui }} + <FAIcon + class="icon" + icon="expand-alt" + /> </router-link> + <button + v-if="onClose" + class="button-unstyled external-link-button" + @click="onClose" + > + <FAIcon + class="icon" + icon="times" + /> + </button> + </div> + <div class="bottom-line"> + <user-link + class="user-screen-name" + :user="user" + /> <template v-if="!hideBio"> <span + v-if="user.deactivated" + class="alert user-role" + > + {{ $t('user_card.deactivated') }} + </span> + <span v-if="!!visibleRole" class="alert user-role" > @@ -135,6 +170,7 @@ class="userHighlightCl" type="color" > + {{ ' ' }} <Select :id="'userHighlightSel'+user.id" v-model="userHighlightType" @@ -160,7 +196,10 @@ class="user-interactions" > <div class="btn-group"> - <FollowButton :relationship="relationship" /> + <FollowButton + :relationship="relationship" + :user="user" + /> <template v-if="relationship.following"> <ProgressButton v-if="!relationship.subscribing" @@ -195,6 +234,7 @@ <button v-if="relationship.muting" class="btn button-default btn-block toggled" + :disabled="user.deactivated" @click="unmuteUser" > {{ $t('user_card.muted') }} @@ -202,6 +242,7 @@ <button v-else class="btn button-default btn-block" + :disabled="user.deactivated" @click="muteUser" > {{ $t('user_card.mute') }} @@ -210,13 +251,14 @@ <div> <button class="btn button-default btn-block" + :disabled="user.deactivated" @click="mentionUser" > {{ $t('user_card.mention') }} </button> </div> <ModerationTools - v-if="loggedIn.role === &quot;admin&quot;" + v-if="showModerationMenu" :user="user" /> </div> @@ -226,6 +268,12 @@ > <RemoteFollow :user="user" /> </div> + <UserNote + v-if="loggedIn && isOtherUser && (hasNote || (hasNoteEditor && supportsNote))" + :user="user" + :relationship="relationship" + :editable="hasNoteEditor" + /> </div> </div> <div @@ -263,6 +311,7 @@ class="user-card-bio" :html="user.description_html" :emoji="user.emoji" + :handle-links="true" /> </div> </div> @@ -270,320 +319,4 @@ <script src="./user_card.js"></script> -<style lang="scss"> -@import '../../_variables.scss'; - -.user-card { - position: relative; - - &:hover { - --_still-image-img-visibility: visible; - --_still-image-canvas-visibility: hidden; - --_still-image-label-visibility: hidden; - } - - .panel-heading { - padding: .5em 0; - text-align: center; - box-shadow: none; - background: transparent; - flex-direction: column; - align-items: stretch; - // create new stacking context - position: relative; - } - - .panel-body { - word-wrap: break-word; - border-bottom-right-radius: inherit; - border-bottom-left-radius: inherit; - // create new stacking context - position: relative; - } - - .background-image { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - mask: linear-gradient(to top, white, transparent) bottom no-repeat, - linear-gradient(to top, white, white); - // Autoprefixed seem to ignore this one, and also syntax is different - -webkit-mask-composite: xor; - mask-composite: exclude; - background-size: cover; - mask-size: 100% 60%; - border-top-left-radius: calc(var(--panelRadius) - 1px); - border-top-right-radius: calc(var(--panelRadius) - 1px); - background-color: var(--profileBg); - - &.hide-bio { - mask-size: 100% 40px; - } - } - - &-bio { - text-align: center; - display: block; - line-height: 18px; - padding: 1em; - margin: 0; - - a { - color: $fallback--link; - color: var(--postLink, $fallback--link); - } - - img { - object-fit: contain; - vertical-align: middle; - max-width: 100%; - max-height: 400px; - } - } - - // Modifiers - - &-rounded-t { - border-top-left-radius: $fallback--panelRadius; - border-top-left-radius: var(--panelRadius, $fallback--panelRadius); - border-top-right-radius: $fallback--panelRadius; - border-top-right-radius: var(--panelRadius, $fallback--panelRadius); - } - - &-rounded { - border-radius: $fallback--panelRadius; - border-radius: var(--panelRadius, $fallback--panelRadius); - } - - &-bordered { - border-width: 1px; - border-style: solid; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); - } -} - -.user-info { - color: $fallback--lightText; - color: var(--lightText, $fallback--lightText); - padding: 0 26px; - - .container { - padding: 16px 0 6px; - display: flex; - align-items: flex-start; - max-height: 56px; - - .Avatar { - --_avatarShadowBox: var(--avatarShadow); - --_avatarShadowFilter: var(--avatarShadowFilter); - --_avatarShadowInset: var(--avatarShadowInset); - - flex: 1 0 100%; - width: 56px; - height: 56px; - object-fit: cover; - } - } - - &-avatar-link { - position: relative; - cursor: pointer; - - &-overlay { - position: absolute; - left: 0; - top: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.3); - display: flex; - justify-content: center; - align-items: center; - border-radius: $fallback--avatarRadius; - border-radius: var(--avatarRadius, $fallback--avatarRadius); - opacity: 0; - transition: opacity .2s ease; - - svg { - color: #FFF; - } - } - - &:hover &-overlay { - opacity: 1; - } - } - - .external-link-button, .edit-profile-button { - cursor: pointer; - width: 2.5em; - text-align: center; - margin: -0.5em 0; - padding: 0.5em 0; - - &:not(:hover) .icon { - color: $fallback--lightText; - color: var(--lightText, $fallback--lightText); - } - } - - .user-summary { - display: block; - margin-left: 0.6em; - text-align: left; - text-overflow: ellipsis; - white-space: nowrap; - flex: 1 1 0; - // This is so that text doesn't get overlapped by avatar's shadow if it has - // big one - z-index: 1; - - .top-line { - display: flex; - } - } - - .user-name { - text-overflow: ellipsis; - overflow: hidden; - flex: 1 1 auto; - margin-right: 1em; - font-size: 15px; - - --emoji-size: 14px; - } - - .bottom-line { - display: flex; - font-weight: light; - font-size: 15px; - - .lock-icon { - margin-left: 0.5em; - } - - .user-screen-name { - min-width: 1px; - flex: 0 1 auto; - text-overflow: ellipsis; - overflow: hidden; - color: $fallback--lightText; - color: var(--lightText, $fallback--lightText); - } - - .dailyAvg { - min-width: 1px; - flex: 0 0 auto; - margin-left: 1em; - font-size: 0.7em; - color: $fallback--text; - color: var(--text, $fallback--text); - } - - .user-role { - flex: none; - color: $fallback--text; - color: var(--alertNeutralText, $fallback--text); - background-color: $fallback--fg; - background-color: var(--alertNeutral, $fallback--fg); - } - } - - .user-meta { - margin-bottom: .15em; - display: flex; - align-items: baseline; - font-size: 14px; - line-height: 22px; - flex-wrap: wrap; - - .following { - flex: 1 0 auto; - margin: 0; - margin-bottom: .25em; - text-align: left; - } - - .highlighter { - flex: 0 1 auto; - display: flex; - flex-wrap: wrap; - margin-right: -.5em; - align-self: start; - - .userHighlightCl { - padding: 2px 10px; - flex: 1 0 auto; - } - - .userHighlightSel { - padding-top: 0; - padding-bottom: 0; - flex: 1 0 auto; - } - - .userHighlightText { - width: 70px; - flex: 1 0 auto; - } - - .userHighlightCl, - .userHighlightText, - .userHighlightSel { - vertical-align: top; - margin-right: .5em; - margin-bottom: .25em; - } - } - } - .user-interactions { - position: relative; - display: flex; - flex-flow: row wrap; - margin-right: -.75em; - - > * { - margin: 0 .75em .6em 0; - white-space: nowrap; - min-width: 95px; - } - - button { - margin: 0; - } - } -} - -.sidebar .edit-profile-button { - display: none; -} - -.user-counts { - display: flex; - line-height:16px; - padding: .5em 1.5em 0em 1.5em; - text-align: center; - justify-content: space-between; - color: $fallback--lightText; - color: var(--lightText, $fallback--lightText); - flex-wrap: wrap; -} - -.user-count { - flex: 1 0 auto; - padding: .5em 0 .5em 0; - margin: 0 .5em; - - h5 { - font-size:1em; - font-weight: bolder; - margin: 0 0 0.25em; - } - a { - text-decoration: none; - } -} -</style> +<style lang="scss" src="./user_card.scss" /> diff --git a/src/components/user_link/user_link.vue b/src/components/user_link/user_link.vue @@ -0,0 +1,38 @@ +<template> + <router-link + :title="user.screen_name_ui" + :to="userProfileLink(user)" + > + {{ at ? '@' : '' }}{{ user.screen_name_ui }}<UnicodeDomainIndicator + :user="user" + /> + </router-link> +</template> + +<script> +import UnicodeDomainIndicator from '../unicode_domain_indicator/unicode_domain_indicator.vue' +import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' + +const UserLink = { + props: { + user: Object, + at: { + type: Boolean, + default: true + } + }, + components: { + UnicodeDomainIndicator + }, + methods: { + userProfileLink (user) { + return generateProfileLink( + user.id, user.screen_name, + this.$store.state.instance.restrictedNicknames + ) + } + } +} + +export default UserLink +</script> diff --git a/src/components/user_list_menu/user_list_menu.js b/src/components/user_list_menu/user_list_menu.js @@ -0,0 +1,93 @@ +import { library } from '@fortawesome/fontawesome-svg-core' +import { faChevronRight } from '@fortawesome/free-solid-svg-icons' +import { mapState } from 'vuex' + +import DialogModal from '../dialog_modal/dialog_modal.vue' +import Popover from '../popover/popover.vue' + +library.add(faChevronRight) + +const UserListMenu = { + props: [ + 'user' + ], + data () { + return {} + }, + components: { + DialogModal, + Popover + }, + created () { + this.$store.dispatch('fetchUserInLists', this.user.id) + }, + computed: { + ...mapState({ + allLists: state => state.lists.allLists + }), + inListsSet () { + return new Set(this.user.inLists.map(x => x.id)) + }, + lists () { + if (!this.user.inLists) return [] + return this.allLists.map(list => ({ + ...list, + inList: this.inListsSet.has(list.id) + })) + } + }, + methods: { + toggleList (listId) { + if (this.inListsSet.has(listId)) { + this.$store.dispatch('removeListAccount', { accountId: this.user.id, listId }).then((response) => { + if (!response.ok) { return } + this.$store.dispatch('fetchUserInLists', this.user.id) + }) + } else { + this.$store.dispatch('addListAccount', { accountId: this.user.id, listId }).then((response) => { + if (!response.ok) { return } + this.$store.dispatch('fetchUserInLists', this.user.id) + }) + } + }, + toggleRight (right) { + const store = this.$store + if (this.user.rights[right]) { + store.state.api.backendInteractor.deleteRight({ user: this.user, right }).then(response => { + if (!response.ok) { return } + store.commit('updateRight', { user: this.user, right, value: false }) + }) + } else { + store.state.api.backendInteractor.addRight({ user: this.user, right }).then(response => { + if (!response.ok) { return } + store.commit('updateRight', { user: this.user, right, value: true }) + }) + } + }, + toggleActivationStatus () { + this.$store.dispatch('toggleActivationStatus', { user: this.user }) + }, + deleteUserDialog (show) { + this.showDeleteUserDialog = show + }, + deleteUser () { + const store = this.$store + const user = this.user + const { id, name } = user + store.state.api.backendInteractor.deleteUser({ user }) + .then(e => { + this.$store.dispatch('markStatusesAsDeleted', status => user.id === status.user.id) + const isProfile = this.$route.name === 'external-user-profile' || this.$route.name === 'user-profile' + const isTargetUser = this.$route.params.name === name || this.$route.params.id === id + if (isProfile && isTargetUser) { + window.history.back() + } + }) + }, + setToggled (value) { + this.toggled = value + } + } +} + +export default UserListMenu diff --git a/src/components/user_list_menu/user_list_menu.vue b/src/components/user_list_menu/user_list_menu.vue @@ -0,0 +1,38 @@ +<template> + <div class="UserListMenu"> + <Popover + trigger="hover" + placement="left" + remove-padding + > + <template #content> + <div class="dropdown-menu"> + <button + v-for="list in lists" + :key="list.id" + class="button-default dropdown-item" + @click="toggleList(list.id)" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-checked': list.inList }" + /> + {{ list.title }} + </button> + </div> + </template> + <template #trigger> + <button class="btn button-default dropdown-item -has-submenu"> + {{ $t('lists.manage_lists') }} + <FAIcon + class="chevron-icon" + size="lg" + icon="chevron-right" + /> + </button> + </template> + </Popover> + </div> +</template> + +<script src="./user_list_menu.js"></script> diff --git a/src/components/user_list_popover/user_list_popover.js b/src/components/user_list_popover/user_list_popover.js @@ -1,3 +1,7 @@ +import { defineAsyncComponent } from 'vue' +import RichContent from 'src/components/rich_content/rich_content.jsx' +import UnicodeDomainIndicator from '../unicode_domain_indicator/unicode_domain_indicator.vue' + import { library } from '@fortawesome/fontawesome-svg-core' import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' @@ -11,8 +15,10 @@ const UserListPopover = { 'users' ], components: { - Popover: () => import('../popover/popover.vue'), - UserAvatar: () => import('../user_avatar/user_avatar.vue') + RichContent, + UnicodeDomainIndicator, + Popover: defineAsyncComponent(() => import('../popover/popover.vue')), + UserAvatar: defineAsyncComponent(() => import('../user_avatar/user_avatar.vue')) }, computed: { usersCapped () { diff --git a/src/components/user_list_popover/user_list_popover.vue b/src/components/user_list_popover/user_list_popover.vue @@ -4,10 +4,10 @@ placement="top" :offset="{ y: 5 }" > - <template v-slot:trigger> + <template #trigger> <slot /> </template> - <template v-slot:content> + <template #content> <div class="user-list-popover"> <template v-if="users.length"> <div @@ -22,9 +22,14 @@ /> <div class="user-list-names"> <!-- eslint-disable vue/no-v-html --> - <span v-html="user.name_html" /> + <RichContent + class="username" + :title="'@'+user.screen_name_ui" + :html="user.name_html" + :emoji="user.emoji" + /> <!-- eslint-enable vue/no-v-html --> - <span class="user-list-screen-name">{{ user.screen_name_ui }}</span> + <span class="user-list-screen-name">{{ user.screen_name_ui }}</span><UnicodeDomainIndicator :user="user" /> </div> </div> </template> @@ -40,7 +45,7 @@ </Popover> </template> -<script src="./user_list_popover.js" ></script> +<script src="./user_list_popover.js"></script> <style lang="scss"> @import '../../_variables.scss'; @@ -48,6 +53,8 @@ .user-list-popover { padding: 0.5em; + --emoji-size: 16px; + .user-list-row { padding: 0.25em; display: flex; @@ -66,7 +73,7 @@ } .user-list-screen-name { - font-size: 9px; + font-size: 0.65em; } } } diff --git a/src/components/user_note/user_note.js b/src/components/user_note/user_note.js @@ -0,0 +1,45 @@ +const UserNote = { + props: { + user: Object, + relationship: Object, + editable: Boolean + }, + data () { + return { + localNote: '', + editing: false, + frozen: false + } + }, + computed: { + shouldShow () { + return this.relationship.note || this.editing + } + }, + methods: { + startEditing () { + this.localNote = this.relationship.note + this.editing = true + }, + cancelEditing () { + this.editing = false + }, + finalizeEditing () { + this.frozen = true + + this.$store.dispatch('editUserNote', { + id: this.user.id, + comment: this.localNote + }) + .then(() => { + this.frozen = false + this.editing = false + }) + .catch(() => { + this.frozen = false + }) + } + } +} + +export default UserNote diff --git a/src/components/user_note/user_note.vue b/src/components/user_note/user_note.vue @@ -0,0 +1,88 @@ +<template> + <div + class="user-note" + > + <div class="heading"> + <span>{{ $t('user_card.note') }}</span> + <div class="buttons"> + <button + v-show="!editing && editable" + class="button-default btn" + @click="startEditing" + > + {{ $t('user_card.edit_note') }} + </button> + <button + v-show="editing" + class="button-default btn" + :disabled="frozen" + @click="finalizeEditing" + > + {{ $t('user_card.edit_note_apply') }} + </button> + <button + v-show="editing" + class="button-default btn" + :disabled="frozen" + @click="cancelEditing" + > + {{ $t('user_card.edit_note_cancel') }} + </button> + </div> + </div> + <textarea + v-show="editing" + v-model="localNote" + class="note-text" + /> + <span + v-show="!editing" + class="note-text" + :class="{ '-blank': !relationship.note }" + > + {{ relationship.note || $t('user_card.note_blank') }} + </span> + </div> +</template> + +<script src="./user_note.js"></script> + +<style lang="scss"> +@import '../../variables'; + +.user-note { + display: flex; + flex-direction: column; + + .heading { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + margin-bottom: 0.75em; + + .btn { + min-width: 95px; + } + + .buttons { + display: flex; + flex-direction: row; + justify-content: right; + + .btn { + margin-left: 0.5em; + } + } + } + + .note-text { + align-self: stretch; + } + + .note-text.-blank { + font-style: italic; + color: var(--faint, $fallback--faint); + } +} +</style> diff --git a/src/components/user_panel/user_panel.vue b/src/components/user_panel/user_panel.vue @@ -1,8 +1,8 @@ <template> - <div class="user-panel"> + <aside class="user-panel"> <div v-if="signedIn" - key="user-panel" + key="user-panel-signed" class="panel panel-default signed-in" > <UserCard @@ -16,7 +16,7 @@ v-else key="user-panel" /> - </div> + </aside> </template> <script src="./user_panel.js"></script> @@ -24,5 +24,6 @@ <style lang="scss"> .user-panel .signed-in { overflow: visible; + z-index: 10; } </style> diff --git a/src/components/user_popover/user_popover.js b/src/components/user_popover/user_popover.js @@ -0,0 +1,23 @@ +import UserCard from '../user_card/user_card.vue' +import { defineAsyncComponent } from 'vue' + +const UserPopover = { + name: 'UserPopover', + props: [ + 'userId', 'overlayCenters', 'disabled', 'overlayCentersSelector' + ], + components: { + UserCard, + Popover: defineAsyncComponent(() => import('../popover/popover.vue')) + }, + computed: { + userPopoverAvatarAction () { + return this.$store.getters.mergedConfig.userPopoverAvatarAction + }, + userPopoverOverlay () { + return this.$store.getters.mergedConfig.userPopoverOverlay + } + } +} + +export default UserPopover diff --git a/src/components/user_popover/user_popover.vue b/src/components/user_popover/user_popover.vue @@ -0,0 +1,33 @@ +<template> + <Popover + trigger="click" + popover-class="popover-default user-popover" + :overlay-centers-selector="overlayCentersSelector || '.user-info .Avatar'" + :overlay-centers="overlayCenters && userPopoverOverlay" + :disabled="disabled" + > + <template #trigger> + <slot /> + </template> + <template #content="{close}"> + <UserCard + class="user-popover" + :user-id="userId" + :hide-bio="true" + :avatar-action="userPopoverAvatarAction == 'close' ? close : userPopoverAvatarAction" + :on-close="close" + /> + </template> + </Popover> +</template> + +<script src="./user_popover.js"></script> + +<style lang="scss"> +@import '../../_variables.scss'; + +/* popover styles load on-demand, so we need to override */ +.user-popover.popover { +} + +</style> diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js @@ -3,7 +3,7 @@ import UserCard from '../user_card/user_card.vue' import FollowCard from '../follow_card/follow_card.vue' import Timeline from '../timeline/timeline.vue' import Conversation from '../conversation/conversation.vue' -import TabSwitcher from 'src/components/tab_switcher/tab_switcher.js' +import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' import RichContent from 'src/components/rich_content/rich_content.jsx' import List from '../list/list.vue' import withLoadMore from '../../hocs/with_load_more/with_load_more' @@ -39,15 +39,16 @@ const UserProfile = { return { error: false, userId: null, - tab: defaultTabKey + tab: defaultTabKey, + footerRef: null } }, created () { const routeParams = this.$route.params - this.load(routeParams.name || routeParams.id) + this.load({ name: routeParams.name, id: routeParams.id }) this.tab = get(this.$route, 'query.tab', defaultTabKey) }, - destroyed () { + unmounted () { this.stopFetching() }, computed: { @@ -78,6 +79,9 @@ const UserProfile = { } }, methods: { + setFooterRef (el) { + this.footerRef = el + }, load (userNameOrId) { const startFetchingTimeline = (timeline, userId) => { // Clear timeline only if load another user's profile @@ -102,12 +106,17 @@ const UserProfile = { this.userId = null this.error = false + const maybeId = userNameOrId.id + const maybeName = userNameOrId.name + // Check if user data is already loaded in store - const user = this.$store.getters.findUser(userNameOrId) + const user = maybeId ? this.$store.getters.findUser(maybeId) : this.$store.getters.findUserByName(maybeName) if (user) { loadById(user.id) } else { - this.$store.dispatch('fetchUser', userNameOrId) + (maybeId + ? this.$store.dispatch('fetchUser', maybeId) + : this.$store.dispatch('fetchUserByName', maybeName)) .then(({ id }) => loadById(id)) .catch((reason) => { const errorMessage = get(reason, 'error.error') @@ -146,12 +155,12 @@ const UserProfile = { watch: { '$route.params.id': function (newVal) { if (newVal) { - this.switchUser(newVal) + this.switchUser({ id: newVal }) } }, '$route.params.name': function (newVal) { if (newVal) { - this.switchUser(newVal) + this.switchUser({ name: newVal }) } }, '$route.query': function (newVal) { diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue @@ -8,8 +8,9 @@ :user-id="userId" :switcher="true" :selected="timeline.viewing" - :allow-zooming-avatar="true" + avatar-action="zoom" rounded="top" + :has-note-editor="true" /> <div v-if="user.fields_html && user.fields_html.length > 0" @@ -56,6 +57,7 @@ :user-id="userId" :pinned-status-ids="user.pinnedStatusIds" :in-profile="true" + :footer-slipgate="footerRef" /> <div v-if="followsTabVisible" @@ -64,7 +66,7 @@ :disabled="!user.friends_count" > <FriendList :user-id="userId"> - <template v-slot:item="{item}"> + <template #item="{item}"> <FollowCard :user="item" /> </template> </FriendList> @@ -76,7 +78,7 @@ :disabled="!user.followers_count" > <FollowerList :user-id="userId"> - <template v-slot:item="{item}"> + <template #item="{item}"> <FollowCard :user="item" :no-follows-you="isUs" @@ -94,6 +96,7 @@ :timeline="media" :user-id="userId" :in-profile="true" + :footer-slipgate="footerRef" /> <Timeline v-if="isUs" @@ -105,8 +108,13 @@ timeline-name="favorites" :timeline="favorites" :in-profile="true" + :footer-slipgate="footerRef" /> </tab-switcher> + <div + :ref="setFooterRef" + class="panel-footer" + /> </div> <div v-else @@ -138,6 +146,9 @@ flex: 2; flex-basis: 500px; + // No sticky header on user profile + --currentPanelStack: 1; + .user-profile-fields { margin: 0 0.5em; @@ -176,7 +187,7 @@ } .user-profile-field-name, .user-profile-field-value { - line-height: 18px; + line-height: 1.3; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; @@ -192,24 +203,6 @@ align-items: middle; padding: 2em; } - - .timeline-heading { - display: flex; - justify-content: center; - - .loadmore-button, .alert { - flex: 1; - } - - .loadmore-button { - height: 28px; - margin: 10px .6em; - } - - .title, .loadmore-text { - display: none - } - } } .user-profile-placeholder { .panel-body { diff --git a/src/components/user_reporting_modal/user_reporting_modal.js b/src/components/user_reporting_modal/user_reporting_modal.js @@ -1,15 +1,16 @@ - import Status from '../status/status.vue' import List from '../list/list.vue' import Checkbox from '../checkbox/checkbox.vue' import Modal from '../modal/modal.vue' +import UserLink from '../user_link/user_link.vue' const UserReportingModal = { components: { Status, List, Checkbox, - Modal + Modal, + UserLink }, data () { return { @@ -21,14 +22,17 @@ const UserReportingModal = { } }, computed: { + reportModal () { + return this.$store.state.reports.reportModal + }, isLoggedIn () { return !!this.$store.state.users.currentUser }, isOpen () { - return this.isLoggedIn && this.$store.state.reports.modalActivated + return this.isLoggedIn && this.reportModal.activated }, userId () { - return this.$store.state.reports.userId + return this.reportModal.userId }, user () { return this.$store.getters.findUser(this.userId) @@ -37,10 +41,10 @@ const UserReportingModal = { return !this.user.is_local && this.user.screen_name.substr(this.user.screen_name.indexOf('@') + 1) }, statuses () { - return this.$store.state.reports.statuses + return this.reportModal.statuses }, preTickedIds () { - return this.$store.state.reports.preTickedIds + return this.reportModal.preTickedIds } }, watch: { diff --git a/src/components/user_reporting_modal/user_reporting_modal.vue b/src/components/user_reporting_modal/user_reporting_modal.vue @@ -5,9 +5,13 @@ > <div class="user-reporting-panel panel"> <div class="panel-heading"> - <div class="title"> - {{ $t('user_reporting.title', [user.screen_name_ui]) }} - </div> + <i18n-t + tag="div" + keypath="user_reporting.title" + class="title" + > + <UserLink :user="user" /> + </i18n-t> </div> <div class="panel-body"> <div class="user-reporting-panel-left"> @@ -45,7 +49,7 @@ </div> <div class="user-reporting-panel-right"> <List :items="statuses"> - <template v-slot:item="{item}"> + <template #item="{item}"> <div class="status-fadein user-reporting-panel-sitem"> <Status :in-conversation="false" @@ -53,8 +57,8 @@ :statusoid="item" /> <Checkbox - :checked="isChecked(item.id)" - @change="checked => toggleStatus(checked, item.id)" + :model-value="isChecked(item.id)" + @update:model-value="checked => toggleStatus(checked, item.id)" /> </div> </template> @@ -76,17 +80,6 @@ min-height: 20vh; max-height: 80vh; - .panel-heading { - .title { - text-align: center; - // TODO: Consider making these as default of panel - flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - } - .panel-body { display: flex; flex-direction: column-reverse; @@ -98,7 +91,7 @@ &-left { padding: 1.1em 0.7em 0.7em; - line-height: 1.4em; + line-height: var(--post-line-height); box-sizing: border-box; > div { diff --git a/src/components/who_to_follow/who_to_follow.js b/src/components/who_to_follow/who_to_follow.js @@ -28,7 +28,7 @@ const WhoToFollow = { getWhoToFollow () { const credentials = this.$store.state.users.currentUser.credentials if (credentials) { - apiService.suggestions({ credentials: credentials }) + apiService.suggestions({ credentials }) .then((reply) => { this.showWhoToFollow(reply) }) diff --git a/src/components/who_to_follow_panel/who_to_follow_panel.js b/src/components/who_to_follow_panel/who_to_follow_panel.js @@ -6,9 +6,9 @@ function showWhoToFollow (panel, reply) { const shuffled = shuffle(reply) panel.usersToFollow.forEach((toFollow, index) => { - let user = shuffled[index] - let img = user.avatar || this.$store.state.instance.defaultAvatar - let name = user.acct + const user = shuffled[index] + const img = user.avatar || this.$store.state.instance.defaultAvatar + const name = user.acct toFollow.img = img toFollow.name = name @@ -24,12 +24,12 @@ function showWhoToFollow (panel, reply) { } function getWhoToFollow (panel) { - var credentials = panel.$store.state.users.currentUser.credentials + const credentials = panel.$store.state.users.currentUser.credentials if (credentials) { panel.usersToFollow.forEach(toFollow => { toFollow.name = 'Loading...' }) - apiService.suggestions({ credentials: credentials }) + apiService.suggestions({ credentials }) .then((reply) => { showWhoToFollow(panel, reply) }) diff --git a/src/components/who_to_follow_panel/who_to_follow_panel.vue b/src/components/who_to_follow_panel/who_to_follow_panel.vue @@ -27,7 +27,7 @@ </div> </template> -<script src="./who_to_follow_panel.js" ></script> +<script src="./who_to_follow_panel.js"></script> <style lang="scss"> .who-to-follow * { diff --git a/src/directives/body_scroll_lock.js b/src/directives/body_scroll_lock.js @@ -50,12 +50,12 @@ const enableBodyScroll = (el) => { } const directive = { - inserted: (el, binding) => { + mounted: (el, binding) => { if (binding.value) { disableBodyScroll(el) } }, - componentUpdated: (el, binding) => { + updated: (el, binding) => { if (binding.oldValue === binding.value) { return } @@ -66,7 +66,7 @@ const directive = { enableBodyScroll(el) } }, - unbind: (el) => { + unmounted: (el) => { enableBodyScroll(el) } } diff --git a/src/hocs/with_load_more/with_load_more.js b/src/hocs/with_load_more/with_load_more.js @@ -1,112 +0,0 @@ -import Vue from 'vue' -import isEmpty from 'lodash/isEmpty' -import { getComponentProps } from '../../services/component_utils/component_utils' -import './with_load_more.scss' - -import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome' -import { library } from '@fortawesome/fontawesome-svg-core' -import { - faCircleNotch -} from '@fortawesome/free-solid-svg-icons' - -library.add( - faCircleNotch -) - -const withLoadMore = ({ - fetch, // function to fetch entries and return a promise - select, // function to select data from store - destroy, // function called at "destroyed" lifecycle - childPropName = 'entries', // name of the prop to be passed into the wrapped component - additionalPropNames = [] // additional prop name list of the wrapper component -}) => (WrappedComponent) => { - const originalProps = Object.keys(getComponentProps(WrappedComponent)) - const props = originalProps.filter(v => v !== childPropName).concat(additionalPropNames) - - return Vue.component('withLoadMore', { - props, - data () { - return { - loading: false, - bottomedOut: false, - error: false, - entries: [] - } - }, - created () { - window.addEventListener('scroll', this.scrollLoad) - if (this.entries.length === 0) { - this.fetchEntries() - } - }, - destroyed () { - window.removeEventListener('scroll', this.scrollLoad) - destroy && destroy(this.$props, this.$store) - }, - methods: { - // Entries is not a computed because computed can't track the dynamic - // selector for changes and won't trigger after fetch. - updateEntries () { - this.entries = select(this.$props, this.$store) || [] - }, - fetchEntries () { - if (!this.loading) { - this.loading = true - this.error = false - fetch(this.$props, this.$store) - .then((newEntries) => { - this.loading = false - this.bottomedOut = isEmpty(newEntries) - }) - .catch(() => { - this.loading = false - this.error = true - }) - .finally(() => { - this.updateEntries() - }) - } - }, - scrollLoad (e) { - const bodyBRect = document.body.getBoundingClientRect() - const height = Math.max(bodyBRect.height, -(bodyBRect.y)) - if (this.loading === false && - this.bottomedOut === false && - this.$el.offsetHeight > 0 && - (window.innerHeight + window.pageYOffset) >= (height - 750) - ) { - this.fetchEntries() - } - } - }, - render (h) { - const props = { - props: { - ...this.$props, - [childPropName]: this.entries - }, - on: this.$listeners, - scopedSlots: this.$scopedSlots - } - const children = Object.entries(this.$slots).map(([key, value]) => h('template', { slot: key }, value)) - return ( - <div class="with-load-more"> - <WrappedComponent {...props}> - {children} - </WrappedComponent> - <div class="with-load-more-footer"> - {this.error && - <button onClick={this.fetchEntries} class="button-unstyled -link -fullwidth alert error"> - {this.$t('general.generic_error')} - </button> - } - {!this.error && this.loading && <FAIcon spin icon="circle-notch"/>} - {!this.error && !this.loading && !this.bottomedOut && <a onClick={this.fetchEntries}>{this.$t('general.more')}</a>} - </div> - </div> - ) - } - }) -} - -export default withLoadMore diff --git a/src/hocs/with_load_more/with_load_more.jsx b/src/hocs/with_load_more/with_load_more.jsx @@ -0,0 +1,109 @@ +// eslint-disable-next-line no-unused +import { h } from 'vue' +import isEmpty from 'lodash/isEmpty' +import { getComponentProps } from '../../services/component_utils/component_utils' +import './with_load_more.scss' + +import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome' +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faCircleNotch +} from '@fortawesome/free-solid-svg-icons' + +library.add( + faCircleNotch +) + +const withLoadMore = ({ + fetch, // function to fetch entries and return a promise + select, // function to select data from store + unmounted, // function called at "destroyed" lifecycle + childPropName = 'entries', // name of the prop to be passed into the wrapped component + additionalPropNames = [] // additional prop name list of the wrapper component +}) => (WrappedComponent) => { + const originalProps = Object.keys(getComponentProps(WrappedComponent)) + const props = originalProps.filter(v => v !== childPropName).concat(additionalPropNames) + + return { + props, + data () { + return { + loading: false, + bottomedOut: false, + error: false, + entries: [] + } + }, + created () { + window.addEventListener('scroll', this.scrollLoad) + if (this.entries.length === 0) { + this.fetchEntries() + } + }, + unmounted () { + window.removeEventListener('scroll', this.scrollLoad) + unmounted && unmounted(this.$props, this.$store) + }, + methods: { + // Entries is not a computed because computed can't track the dynamic + // selector for changes and won't trigger after fetch. + updateEntries () { + this.entries = select(this.$props, this.$store) || [] + }, + fetchEntries () { + if (!this.loading) { + this.loading = true + this.error = false + fetch(this.$props, this.$store) + .then((newEntries) => { + this.loading = false + this.bottomedOut = isEmpty(newEntries) + }) + .catch(() => { + this.loading = false + this.error = true + }) + .finally(() => { + this.updateEntries() + }) + } + }, + scrollLoad (e) { + const bodyBRect = document.body.getBoundingClientRect() + const height = Math.max(bodyBRect.height, -(bodyBRect.y)) + if (this.loading === false && + this.bottomedOut === false && + this.$el.offsetHeight > 0 && + (window.innerHeight + window.pageYOffset) >= (height - 750) + ) { + this.fetchEntries() + } + } + }, + render () { + const props = { + ...this.$props, + [childPropName]: this.entries + } + const children = this.$slots + return ( + <div class="with-load-more"> + <WrappedComponent {...props}> + {children} + </WrappedComponent> + <div class="with-load-more-footer"> + {this.error && + <button onClick={this.fetchEntries} class="button-unstyled -link -fullwidth alert error"> + {this.$t('general.generic_error')} + </button> + } + {!this.error && this.loading && <FAIcon spin icon="circle-notch"/>} + {!this.error && !this.loading && !this.bottomedOut && <a onClick={this.fetchEntries}>{this.$t('general.more')}</a>} + </div> + </div> + ) + } + } +} + +export default withLoadMore diff --git a/src/hocs/with_load_more/with_load_more.scss b/src/hocs/with_load_more/with_load_more.scss @@ -10,7 +10,7 @@ border-top-color: var(--border, $fallback--border); .error { - font-size: 14px; + font-size: 1rem; } a { diff --git a/src/hocs/with_subscription/with_subscription.js b/src/hocs/with_subscription/with_subscription.js @@ -1,94 +0,0 @@ -import Vue from 'vue' -import isEmpty from 'lodash/isEmpty' -import { getComponentProps } from '../../services/component_utils/component_utils' -import './with_subscription.scss' - -import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome' -import { library } from '@fortawesome/fontawesome-svg-core' -import { - faCircleNotch -} from '@fortawesome/free-solid-svg-icons' - -library.add( - faCircleNotch -) - -const withSubscription = ({ - fetch, // function to fetch entries and return a promise - select, // function to select data from store - childPropName = 'content', // name of the prop to be passed into the wrapped component - additionalPropNames = [] // additional prop name list of the wrapper component -}) => (WrappedComponent) => { - const originalProps = Object.keys(getComponentProps(WrappedComponent)) - const props = originalProps.filter(v => v !== childPropName).concat(additionalPropNames) - - return Vue.component('withSubscription', { - props: [ - ...props, - 'refresh' // boolean saying to force-fetch data whenever created - ], - data () { - return { - loading: false, - error: false - } - }, - computed: { - fetchedData () { - return select(this.$props, this.$store) - } - }, - created () { - if (this.refresh || isEmpty(this.fetchedData)) { - this.fetchData() - } - }, - methods: { - fetchData () { - if (!this.loading) { - this.loading = true - this.error = false - fetch(this.$props, this.$store) - .then(() => { - this.loading = false - }) - .catch(() => { - this.error = true - this.loading = false - }) - } - } - }, - render (h) { - if (!this.error && !this.loading) { - const props = { - props: { - ...this.$props, - [childPropName]: this.fetchedData - }, - on: this.$listeners, - scopedSlots: this.$scopedSlots - } - const children = Object.entries(this.$slots).map(([key, value]) => h('template', { slot: key }, value)) - return ( - <div class="with-subscription"> - <WrappedComponent {...props}> - {children} - </WrappedComponent> - </div> - ) - } else { - return ( - <div class="with-subscription-loading"> - {this.error - ? <a onClick={this.fetchData} class="alert error">{this.$t('general.generic_error')}</a> - : <FAIcon spin icon="circle-notch"/> - } - </div> - ) - } - } - }) -} - -export default withSubscription diff --git a/src/hocs/with_subscription/with_subscription.jsx b/src/hocs/with_subscription/with_subscription.jsx @@ -0,0 +1,91 @@ +// eslint-disable-next-line no-unused +import { h } from 'vue' +import isEmpty from 'lodash/isEmpty' +import { getComponentProps } from '../../services/component_utils/component_utils' +import './with_subscription.scss' + +import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome' +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faCircleNotch +} from '@fortawesome/free-solid-svg-icons' + +library.add( + faCircleNotch +) + +const withSubscription = ({ + fetch, // function to fetch entries and return a promise + select, // function to select data from store + childPropName = 'content', // name of the prop to be passed into the wrapped component + additionalPropNames = [] // additional prop name list of the wrapper component +}) => (WrappedComponent) => { + const originalProps = Object.keys(getComponentProps(WrappedComponent)) + const props = originalProps.filter(v => v !== childPropName).concat(additionalPropNames) + + return { + props: [ + ...props, + 'refresh' // boolean saying to force-fetch data whenever created + ], + data () { + return { + loading: false, + error: false + } + }, + computed: { + fetchedData () { + return select(this.$props, this.$store) + } + }, + created () { + if (this.refresh || isEmpty(this.fetchedData)) { + this.fetchData() + } + }, + methods: { + fetchData () { + if (!this.loading) { + this.loading = true + this.error = false + fetch(this.$props, this.$store) + .then(() => { + this.loading = false + }) + .catch(() => { + this.error = true + this.loading = false + }) + } + } + }, + render () { + if (!this.error && !this.loading) { + const props = { + ...this.$props, + [childPropName]: this.fetchedData + } + const children = this.$slots + return ( + <div class="with-subscription"> + <WrappedComponent {...props}> + {children} + </WrappedComponent> + </div> + ) + } else { + return ( + <div class="with-subscription-loading"> + {this.error + ? <a onClick={this.fetchData} class="alert error">{this.$t('general.generic_error')}</a> + : <FAIcon spin icon="circle-notch"/> + } + </div> + ) + } + } + } +} + +export default withSubscription diff --git a/src/hocs/with_subscription/with_subscription.scss b/src/hocs/with_subscription/with_subscription.scss @@ -4,7 +4,7 @@ text-align: center; .error { - font-size: 14px; + font-size: 1rem; } } } \ No newline at end of file diff --git a/src/i18n/ar.json b/src/i18n/ar.json @@ -200,5 +200,22 @@ "who_to_follow": { "more": "المزيد", "who_to_follow": "للمتابعة" + }, + "about": { + "mrf": { + "keyword": { + "ftl_removal": "إزالة من الخط الزمني الخاص بجميع الشبكات المعروفة", + "reject": "رفض", + "replace": "إستبدال", + "is_replaced_by": "←", + "keyword_policies": "سياسة الكلمات الدلالية" + }, + "simple": { + "simple_policies": "سياسات الخادم" + }, + "federation": "الاتحاد", + "mrf_policies": "تفعيل سياسات إعادة كتابة المنشور", + "mrf_policies_desc": "خاصية إعادة كتابة المناشير تقوم بتعديل تفاعل الاتحاد مع هذا الخادم. السياسات التالية مفعّلة:" + } } } diff --git a/src/i18n/ca.json b/src/i18n/ca.json @@ -323,7 +323,10 @@ "play_videos_in_modal": "Reproduir vídeos en un marc emergent", "file_export_import": { "errors": { - "invalid_file": "El fitxer seleccionat no és vàlid com a còpia de seguretat de la configuració. No s'ha realitzat cap canvi." + "invalid_file": "El fitxer seleccionat no és vàlid com a còpia de seguretat de la configuració. No s'ha realitzat cap canvi.", + "file_too_new": "Versió important incompatible: {fileMajor}, aquest PleromaFE (configuració versió {feMajor}) és massa antiga per gestionar-lo", + "file_too_old": "Versió important incompatible: {fileMajor}, la versió del fitxer és massa antiga i no està implementada (s'ha establert un mínim ver. {feMajor})", + "file_slightly_new": "La versió menor del fitxer és diferent, alguns paràmetres podrien no carregar-se" }, "backup_settings": "Còpia de seguretat de la configuració a un fitxer", "backup_settings_theme": "Còpia de seguretat de la configuració i tema a un fitxer", @@ -382,7 +385,8 @@ "postCode": "Text monoespai en publicació (text enriquit)", "input": "Camps d'entrada", "interface": "Interfície" - } + }, + "weight": "Pes (negreta)" }, "preview": { "input": "Acabo d'aterrar a Los Angeles.", @@ -394,7 +398,9 @@ "error": "Exemple d'error", "faint_link": "Manual d'ajuda", "checkbox": "He llegit els termes i condicions", - "link": "un bonic enllaç" + "link": "un bonic enllaç", + "fine_print": "Llegiu el nostre {0} per no aprendre res útil!", + "text": "Un grapat més de {0} i {1}" }, "shadows": { "spread": "Difon", @@ -438,7 +444,8 @@ "snapshot_missing": "No hi havia cap instantània del tema al fitxer, per tant podria veure's diferent del previst originalment.", "upgraded_from_v2": "PleromaFE s'ha actualitzat, el tema pot veure's un poc diferent de com recordes.", "fe_downgraded": "Versió de PleromaFE revertida.", - "older_version_imported": "El fitxer que has importat va ser creat en una versió del front-end més antiga." + "older_version_imported": "El fitxer que has importat va ser creat en una versió del front-end més antiga.", + "snapshot_present": "S'ha carregat la instantània del tema, de manera que tots els valors estan sobreescrits. En canvi, podeu carregar les dades reals del tema." }, "keep_as_is": "Mantindre com està", "save_load_hint": "Les opcions \"Mantindre\" conserven les opcions configurades actualment al seleccionar o carregar temes, també emmagatzema aquestes opcions quan s'exporta un tema. Quan es desactiven totes les caselles de verificació, el tema exportat ho guardarà tot.", @@ -532,7 +539,13 @@ "notification_setting_hide_notification_contents": "Amagar el remitent i els continguts de les notificacions push", "notifications": "Notificacions", "notification_mutes": "Per a deixar de rebre notificacions d'un usuari en concret, silencia'l-ho.", - "theme_help_v2_2": "Les icones per baix d'algunes entrades són indicadors del contrast del fons/text, desplaça el ratolí per a més informació. Tingues en compte que quan s'utilitzen indicadors de contrast de transparència es mostra el pitjor cas possible." + "theme_help_v2_2": "Les icones per baix d'algunes entrades són indicadors del contrast del fons/text, desplaça el ratolí per a més informació. Tingues en compte que quan s'utilitzen indicadors de contrast de transparència es mostra el pitjor cas possible.", + "hide_shoutbox": "Oculta la casella de gàbia de grills", + "always_show_post_button": "Mostra sempre el botó flotant de publicació nova", + "pad_emoji": "Acompanya els emojis amb espais en afegir des del selector", + "mentions_new_style": "Enllaços d'esment més elegants", + "mentions_new_place": "Posa les mencions en una línia separada", + "post_status_content_type": "Format de publicació" }, "time": { "day": "{0} dia", @@ -608,7 +621,6 @@ "disable_any_subscription": "Deshabilita completament seguir algú", "quarantine": "Deshabilita la federació a les entrades de les usuàries", "moderation": "Moderació", - "delete_user_confirmation": "Estàs completament segur/a? Aquesta acció no es pot desfer.", "revoke_admin": "Revoca l'Admin", "activate_account": "Activa el compte", "deactivate_account": "Desactiva el compte", @@ -617,7 +629,9 @@ "disable_remote_subscription": "Deshabilita seguir algú des d'una instància remota", "delete_user": "Esborra la usuària", "grant_admin": "Concedir permisos d'Administració", - "grant_moderator": "Concedir permisos de Moderació" + "grant_moderator": "Concedir permisos de Moderació", + "force_unlisted": "Força que les publicacions no estiguin llistades", + "sandbox": "Força que els missatges siguin només seguidors" }, "edit_profile": "Edita el perfil", "hidden": "Amagat", @@ -642,7 +656,8 @@ "solid": "Fons sòlid", "striped": "Fons a ratlles", "side": "Ratlla lateral" - } + }, + "media": "Media" }, "user_profile": { "timeline_title": "Flux personal", @@ -658,12 +673,14 @@ }, "remote_user_resolver": { "error": "No trobat.", - "searching_for": "Cercant per" + "searching_for": "Cercant per", + "remote_user_resolver": "Resolució d'usuari remot" }, "interactions": { "load_older": "Carrega antigues interaccions", "favs_repeats": "Repeticions i favorits", - "follows": "Nous seguidors" + "follows": "Nous seguidors", + "moves": "Migració d'usuaris" }, "emoji": { "stickers": "Adhesius", @@ -775,7 +792,10 @@ "pinned": "Destacat", "reply_to": "Contesta a", "pin": "Destaca al perfil", - "unmute_conversation": "Deixa de silenciar la conversa" + "unmute_conversation": "Deixa de silenciar la conversa", + "mentions": "Mencions", + "you": "(Tu)", + "plus_more": "+{number} més" }, "user_reporting": { "additional_comments": "Comentaris addicionals", @@ -801,7 +821,8 @@ "no_results": "No hi ha resultats", "people": "Persones", "hashtags": "Etiquetes", - "people_talking": "{count} persones parlant" + "people_talking": "{count} persones parlant", + "person_talking": "{count} persones parlant" }, "upload": { "file_size_units": { diff --git a/src/i18n/de.json b/src/i18n/de.json @@ -582,7 +582,6 @@ "statuses": "Beiträge", "admin_menu": { "sandbox": "Erzwinge Beiträge nur für Follower sichtbar zu sein", - "delete_user_confirmation": "Achtung! Diese Entscheidung kann nicht rückgängig gemacht werden! Trotzdem durchführen?", "grant_admin": "Administratorprivilegien gewähren", "delete_user": "Nutzer löschen", "strip_media": "Medien von Beiträgen entfernen", diff --git a/src/i18n/en.json b/src/i18n/en.json @@ -32,6 +32,27 @@ }, "staff": "Staff" }, + "announcements": { + "page_header": "Announcements", + "title": "Announcement", + "mark_as_read_action": "Mark as read", + "post_form_header": "Post announcement", + "post_placeholder": "Type your announcement content here...", + "post_action": "Post", + "post_error": "Error: {error}", + "close_error": "Close", + "delete_action": "Delete", + "start_time_prompt": "Start time: ", + "end_time_prompt": "End time: ", + "all_day_prompt": "This is an all-day event", + "published_time_display": "Published at {time}", + "start_time_display": "Starts at {time}", + "end_time_display": "Ends at {time}", + "edit_action": "Edit", + "submit_edit_action": "Submit", + "cancel_edit_action": "Cancel", + "inactive_message": "This announcement is inactive" + }, "shoutbox": { "title": "Shoutbox" }, @@ -46,7 +67,7 @@ "processing": "Processing, you'll soon be asked to download your file" }, "features_panel": { - "chat": "Chat", + "shout": "Shoutbox", "pleroma_chat_messages": "Pleroma Chat", "gopher": "Gopher", "media_proxy": "Media proxy", @@ -66,11 +87,13 @@ "more": "More", "loading": "Loading…", "generic_error": "An error occured", + "generic_error_message": "An error occured: {0}", "error_retry": "Please try again", "retry": "Try again", "optional": "optional", "show_more": "Show more", "show_less": "Show less", + "never_show_again": "Never show again", "dismiss": "Dismiss", "cancel": "Cancel", "disable": "Disable", @@ -78,14 +101,26 @@ "confirm": "Confirm", "verify": "Verify", "close": "Close", + "undo": "Undo", + "yes": "Yes", + "no": "No", "peek": "Peek", + "scroll_to_top": "Scroll to top", "role": { "admin": "Admin", "moderator": "Moderator" }, + "unpin": "Unpin item", + "pin": "Pin item", "flash_content": "Click to show Flash content using Ruffle (Experimental, may not work).", "flash_security": "Note that this can be potentially dangerous since Flash content is still arbitrary code.", - "flash_fail": "Failed to load flash content, see console for details." + "flash_fail": "Failed to load flash content, see console for details.", + "scope_in_timeline": { + "direct": "Direct", + "private": "Followers-only", + "public": "Public", + "unlisted": "Unlisted" + } }, "image_cropper": { "crop_picture": "Crop picture", @@ -118,7 +153,9 @@ }, "media_modal": { "previous": "Previous", - "next": "Next" + "next": "Next", + "counter": "{current} / {total}", + "hide": "Close media viewer" }, "nav": { "about": "About", @@ -138,7 +175,16 @@ "who_to_follow": "Who to follow", "preferences": "Preferences", "timelines": "Timelines", - "chats": "Chats" + "chats": "Chats", + "lists": "Lists", + "edit_nav_mobile": "Customize navigation bar", + "edit_pinned": "Edit pinned items", + "edit_finish": "Done editing", + "mobile_sidebar": "Toggle mobile sidebar", + "mobile_notifications": "Open notifications", + "mobile_notifications": "Open notifications (there are unread ones)", + "mobile_notifications_close": "Close notifications", + "announcements": "Announcements" }, "notifications": { "broken_favorite": "Unknown status, searching for it…", @@ -152,7 +198,9 @@ "repeated_you": "repeated your status", "no_more_notifications": "No more notifications", "migrated_to": "migrated to", - "reacted_with": "reacted with {0}" + "reacted_with": "reacted with {0}", + "submitted_report": "submitted a report", + "poll_ended": "poll has ended" }, "polls": { "add_poll": "Add poll", @@ -178,8 +226,20 @@ "add_emoji": "Insert emoji", "custom": "Custom emoji", "unicode": "Unicode emoji", + "unicode_groups": { + "activities": "Activities", + "animals-and-nature": "Animals & Nature", + "flags": "Flags", + "food-and-drink": "Food & Drink", + "objects": "Objects", + "people-and-body": "People & Body", + "smileys-and-emotion": "Smileys & Emotion", + "symbols": "Symbols", + "travel-and-places": "Travel & Places" + }, "load_all_hint": "Loaded first {saneAmount} emoji, loading all emoji may cause performance issues.", - "load_all": "Loading all {emojiAmount} emoji" + "load_all": "Loading all {emojiAmount} emoji", + "regional_indicator": "Regional indicator {letter}" }, "errors": { "storage_unavailable": "Pleroma could not access browser storage. Your login or your local settings won't be saved and you might encounter unexpected issues. Try enabling cookies." @@ -187,10 +247,13 @@ "interactions": { "favs_repeats": "Repeats and favorites", "follows": "New follows", + "emoji_reactions": "Emoji Reactions", + "reports": "Reports", "moves": "User migrates", "load_older": "Load older interactions" }, "post_status": { + "edit_status": "Edit status", "new_status": "Post new status", "account_not_locked_warning": "Your account is not {0}. Anyone can follow you to view your follower-only posts.", "account_not_locked_warning_link": "locked", @@ -206,6 +269,8 @@ "default": "Just landed in L.A.", "direct_warning_to_all": "This post will be visible to all the mentioned users.", "direct_warning_to_first_only": "This post will only be visible to the mentioned users at the beginning of the message.", + "edit_remote_warning": "Other remote instances may not support editing and unable to receive the latest version of your post.", + "edit_unsupported_warning": "Pleroma does not support editing mentions or polls.", "posting": "Posting", "post": "Post", "preview": "Preview", @@ -225,8 +290,9 @@ } }, "registration": { - "bio": "Bio", + "bio_optional": "Bio (optional)", "email": "Email", + "email_optional": "Email (optional)", "fullname": "Display name", "password_confirm": "Password confirmation", "registration": "Registration", @@ -246,24 +312,37 @@ "password_required": "cannot be left blank", "password_confirmation_required": "cannot be left blank", "password_confirmation_match": "should be the same as password" - } + }, + "email_language": "In which language do you want to receive emails from the server?" }, "remote_user_resolver": { "remote_user_resolver": "Remote user resolver", "searching_for": "Searching for", "error": "Not found." }, + "report": { + "reporter": "Reporter:", + "reported_user": "Reported user:", + "reported_statuses": "Reported statuses:", + "notes": "Notes:", + "state": "State:", + "state_open": "Open", + "state_closed": "Closed", + "state_resolved": "Resolved" + }, "selectable_list": { "select_all": "Select all" }, "settings": { "app_name": "App name", + "expert_mode": "Show advanced", "save": "Save changes", "security": "Security", "setting_changed": "Setting is different from default", + "setting_server_side": "This setting is tied to your profile and affects all sessions and clients", "enter_current_password_to_confirm": "Enter your current password to confirm your identity", - "mentions_new_style": "Fancier mention links", - "mentions_new_place": "Put mentions on a separate line", + "post_look_feel": "Posts Look & Feel", + "mention_links": "Mention links", "mfa": { "otp": "OTP", "setup_otp": "Setup OTP", @@ -285,6 +364,7 @@ "desc": "To enable two-factor authentication, enter the code from your two-factor app:" } }, + "lists_navigation": "Show lists in navigation", "allow_following_move": "Allow auto-follow when following account moves", "attachmentRadius": "Attachments", "attachments": "Attachments", @@ -293,6 +373,7 @@ "avatarRadius": "Avatars", "background": "Background", "bio": "Bio", + "email_language": "Language for receiving emails from the server", "block_export": "Block export", "block_export_button": "Export your blocks to a csv file", "block_import": "Block import", @@ -304,6 +385,16 @@ "mute_import_error": "Error importing mutes", "mutes_imported": "Mutes imported! Processing them will take a while.", "import_mutes_from_a_csv_file": "Import mutes from a csv file", + "account_backup": "Account backup", + "account_backup_description": "This allows you to download an archive of your account information and your posts, but they cannot yet be imported into a Pleroma account.", + "account_backup_table_head": "Backup", + "download_backup": "Download", + "backup_not_ready": "This backup is not ready yet.", + "remove_backup": "Remove", + "list_backups_error": "Error fetching backup list: {error}", + "add_backup": "Create a new backup", + "added_backup": "Added a new backup.", + "add_backup_error": "Error adding a new backup: {error}", "blocks_tab": "Blocks", "bot": "This is a bot account", "btnRadius": "Buttons", @@ -329,6 +420,19 @@ "delete_account_description": "Permanently delete your data and deactivate your account.", "delete_account_error": "There was an issue deleting your account. If this persists please contact your instance administrator.", "delete_account_instructions": "Type your password in the input below to confirm account deletion.", + "account_alias": "Account aliases", + "account_alias_table_head": "Alias", + "list_aliases_error": "Error fetching aliases: {error}", + "hide_list_aliases_error_action": "Close", + "remove_alias": "Remove this alias", + "new_alias_target": "Add a new alias (e.g. {example})", + "added_alias": "Alias is added.", + "add_alias_error": "Error adding alias: {error}", + "move_account": "Move account", + "move_account_notes": "If you want to move the account somewhere else, you must go to your target account and add an alias pointing here.", + "move_account_target": "Target account (e.g. {example})", + "moved_account": "Account is moved.", + "move_account_error": "Error moving account: {error}", "discoverable": "Allow discovery of this account in search results and other services", "domain_mutes": "Domains", "avatar_size_instruction": "The recommended minimum size for avatar images is 150x150 pixels.", @@ -336,8 +440,9 @@ "emoji_reactions_on_timeline": "Show emoji reactions on timeline", "export_theme": "Save preset", "filtering": "Filtering", + "wordfilter": "Wordfilter", "filtering_explanation": "All statuses containing these words will be muted, one per line", - "word_filter": "Word filter", + "word_filter_and_more": "Word filter and more...", "follow_export": "Follow export", "follow_export_button": "Export your follows to a csv file", "follow_import": "Follow import", @@ -350,18 +455,23 @@ "hide_attachments_in_tl": "Hide attachments in timeline", "hide_media_previews": "Hide media previews", "hide_muted_posts": "Hide posts of muted users", + "mute_bot_posts": "Mute bot posts", + "hide_bot_indication": "Hide bot indication in posts", "hide_all_muted_posts": "Hide muted posts", - "max_thumbnails": "Maximum amount of thumbnails per post", + "max_thumbnails": "Maximum amount of thumbnails per post (empty = no limit)", "hide_isp": "Hide instance-specific panel", "hide_shoutbox": "Hide instance shoutbox", - "right_sidebar": "Show sidebar on the right side", + "right_sidebar": "Reverse order of columns", + "navbar_column_stretch": "Stretch navbar to columns width", "always_show_post_button": "Always show floating New Post button", "hide_wallpaper": "Hide instance wallpaper", "preload_images": "Preload images", "use_one_click_nsfw": "Open NSFW attachments with just one click", "hide_post_stats": "Hide post statistics (e.g. the number of favorites)", "hide_user_stats": "Hide user statistics (e.g. the number of followers)", - "hide_filtered_statuses": "Hide filtered statuses", + "hide_filtered_statuses": "Hide all filtered posts", + "hide_wordfiltered_statuses": "Hide word-filtered statuses", + "hide_muted_threads": "Hide muted threads", "import_blocks_from_a_csv_file": "Import blocks from a csv file", "import_followers_from_a_csv_file": "Import follows from a csv file", "import_theme": "Load preset", @@ -397,11 +507,14 @@ "name": "Label", "value": "Content" }, + "account_privacy": "Privacy", "use_contain_fit": "Don't crop the attachment in thumbnails", "name": "Name", "name_bio": "Name & bio", "new_email": "New email", "new_password": "New password", + "posts": "Posts", + "user_profiles": "User Profiles", "notification_visibility": "Types of notifications to show", "notification_visibility_follows": "Follows", "notification_visibility_likes": "Favorites", @@ -409,23 +522,25 @@ "notification_visibility_repeats": "Repeats", "notification_visibility_moves": "User Migrates", "notification_visibility_emoji_reactions": "Reactions", + "notification_visibility_polls": "Ends of polls you voted in", "no_rich_text_description": "Strip rich text formatting from all posts", "no_blocks": "No blocks", "no_mutes": "No mutes", + "hide_favorites_description": "Don't show list of my favorites (people still get notified)", "hide_follows_description": "Don't show who I'm following", "hide_followers_description": "Don't show who's following me", "hide_follows_count_description": "Don't show follow count", "hide_followers_count_description": "Don't show follower count", "show_admin_badge": "Show \"Admin\" badge in my profile", "show_moderator_badge": "Show \"Moderator\" badge in my profile", - "nsfw_clickthrough": "Enable clickthrough attachment and link preview image hiding for NSFW statuses", + "nsfw_clickthrough": "Hide sensitive/NSFW media", "oauth_tokens": "OAuth tokens", "token": "Token", "refresh_token": "Refresh token", "valid_until": "Valid until", "revoke_token": "Revoke", "panelRadius": "Panels", - "pause_on_unfocused": "Pause streaming when tab is not focused", + "pause_on_unfocused": "Pause when tab is not focused", "presets": "Presets", "profile_background": "Profile background", "profile_banner": "Profile banner", @@ -460,13 +575,36 @@ "subject_line_email": "Like email: \"re: subject\"", "subject_line_mastodon": "Like mastodon: copy as is", "subject_line_noop": "Do not copy", + "conversation_display": "Conversation display style", + "conversation_display_tree": "Tree-style", + "conversation_display_tree_quick": "Tree view", + "disable_sticky_headers": "Don't stick column headers to top of the screen", + "show_scrollbars": "Show side column's scrollbars", + "third_column_mode": "When there's enough space, show third column containing", + "third_column_mode_none": "Don't show third column at all", + "third_column_mode_notifications": "Notifications column", + "third_column_mode_postform": "Main post form and navigation", + "columns": "Columns", + "column_sizes": "Column sizes", + "column_sizes_sidebar": "Sidebar", + "column_sizes_content": "Content", + "column_sizes_notifs": "Notifications", + "tree_advanced": "Allow more flexible navigation in tree view", + "tree_fade_ancestors": "Display ancestors of the current status in faint text", + "conversation_display_linear": "Linear-style", + "conversation_display_linear_quick": "Linear view", + "conversation_other_replies_button": "Show the \"other replies\" button", + "conversation_other_replies_button_below": "Below statuses", + "conversation_other_replies_button_inside": "Inside statuses", + "max_depth_in_thread": "Maximum number of levels in thread to display by default", "post_status_content_type": "Post status content type", "sensitive_by_default": "Mark posts as sensitive by default", - "stop_gifs": "Play-on-hover GIFs", - "streaming": "Enable automatic streaming of new posts when scrolled to the top", + "stop_gifs": "Pause animated images until you hover on them", + "streaming": "Automatically show new posts when scrolled to the top", + "auto_update": "Show new posts automatically", "user_mutes": "Users", "useStreamingApi": "Receive posts and notifications real-time", - "useStreamingApiWarning": "(Not recommended, experimental, known to skip posts)", + "use_websockets": "Use websockets (Realtime updates)", "text": "Text", "theme": "Theme", "theme_help": "Use hex color codes (#rrggbb) to customize your color theme.", @@ -481,8 +619,24 @@ "true": "yes" }, "virtual_scrolling": "Optimize timeline rendering", + "use_at_icon": "Display {'@'} symbol as an icon instead of text", + "mention_link_display": "Display mention links", + "mention_link_display_short": "always as short names (e.g. {'@'}foo)", + "mention_link_display_full_for_remote": "as full names only for remote users (e.g. {'@'}foo{'@'}example.org)", + "mention_link_display_full": "always as full names (e.g. {'@'}foo{'@'}example.org)", + "mention_link_use_tooltip": "Show user card when clicking mention links", + "mention_link_show_avatar": "Show user avatar beside the link", + "mention_link_show_avatar_quick": "Show user avatar next to mentions", + "mention_link_fade_domain": "Fade domains (e.g. {'@'}example.org in {'@'}foo{'@'}example.org)", + "mention_link_bolden_you": "Highlight mention of you when you are mentioned", + "user_popover_avatar_action": "Popover avatar click action", + "user_popover_avatar_action_zoom": "Zoom the avatar", + "user_popover_avatar_action_close": "Close the popover", + "user_popover_avatar_action_open": "Open profile", + "user_popover_avatar_overlay": "Show user popover over user avatar", "fun": "Fun", "greentext": "Meme arrows", + "show_yous": "Show (You)s", "notifications": "Notifications", "notification_setting_filters": "Filters", "notification_setting_block_from_strangers": "Block notifications from users who you do not follow", @@ -645,38 +799,26 @@ } }, "time": { - "day": "{0} day", - "days": "{0} days", - "day_short": "{0}d", - "days_short": "{0}d", - "hour": "{0} hour", - "hours": "{0} hours", - "hour_short": "{0}h", - "hours_short": "{0}h", + "unit": { + "days": "{0} day | {0} days", + "days_short": "{0}d", + "hours": "{0} hour | {0} hours", + "hours_short": "{0}h", + "minutes": "{0} minute | {0} minutes", + "minutes_short": "{0}min", + "months": "{0} month | {0} months", + "months_short": "{0}mo", + "seconds": "{0} second | {0} seconds", + "seconds_short": "{0}s", + "weeks": "{0} week | {0} weeks", + "weeks_short": "{0}w", + "years": "{0} year | {0} years", + "years_short": "{0}y" + }, "in_future": "in {0}", "in_past": "{0} ago", - "minute": "{0} minute", - "minutes": "{0} minutes", - "minute_short": "{0}min", - "minutes_short": "{0}min", - "month": "{0} month", - "months": "{0} months", - "month_short": "{0}mo", - "months_short": "{0}mo", "now": "just now", - "now_short": "now", - "second": "{0} second", - "seconds": "{0} seconds", - "second_short": "{0}s", - "seconds_short": "{0}s", - "week": "{0} week", - "weeks": "{0} weeks", - "week_short": "{0}w", - "weeks_short": "{0}w", - "year": "{0} year", - "years": "{0} years", - "year_short": "{0}y", - "years_short": "{0}y" + "now_short": "now" }, "timeline": { "collapse": "Collapse", @@ -691,12 +833,16 @@ "no_more_statuses": "No more statuses", "no_statuses": "No statuses", "socket_reconnected": "Realtime connection established", - "socket_broke": "Realtime connection lost: CloseEvent code {0}" + "socket_broke": "Realtime connection lost: CloseEvent code {0}", + "quick_view_settings": "Quick view settings", + "quick_filter_settings": "Quick filter settings" }, "status": { "favorites": "Favorites", "repeats": "Repeats", "delete": "Delete status", + "edit": "Edit status", + "edited_at": "(last edited {time})", "pin": "Pin on profile", "unpin": "Unpin from profile", "pinned": "Pinned", @@ -706,6 +852,7 @@ "reply_to": "Reply to", "mentions": "Mentions", "replies_list": "Replies:", + "replies_list_with_others": "Replies (+{numReplies} other): | Replies (+{numReplies} others):", "mute_conversation": "Mute conversation", "unmute_conversation": "Unmute conversation", "status_unavailable": "Status unavailable", @@ -721,12 +868,36 @@ "nsfw": "NSFW", "expand": "Expand", "you": "(You)", - "plus_more": "+{number} more" + "plus_more": "+{number} more", + "many_attachments": "Post has {number} attachment(s)", + "collapse_attachments": "Collapse attachments", + "show_all_attachments": "Show all attachments", + "show_attachment_in_modal": "Show in media modal", + "show_attachment_description": "Preview description (open attachment for full description)", + "hide_attachment": "Hide attachment", + "remove_attachment": "Remove attachment", + "attachment_stop_flash": "Stop Flash player", + "move_up": "Shift attachment left", + "move_down": "Shift attachment right", + "open_gallery": "Open gallery", + "thread_hide": "Hide this thread", + "thread_show": "Show this thread", + "thread_show_full": "Show everything under this thread ({numStatus} status in total, max depth {depth}) | Show everything under this thread ({numStatus} statuses in total, max depth {depth})", + "thread_show_full_with_icon": "{icon} {text}", + "thread_follow": "See the remaining part of this thread ({numStatus} status in total) | See the remaining part of this thread ({numStatus} statuses in total)", + "thread_follow_with_icon": "{icon} {text}", + "ancestor_follow": "See {numReplies} other reply under this status | See {numReplies} other replies under this status", + "ancestor_follow_with_icon": "{icon} {text}", + "show_all_conversation_with_icon": "{icon} {text}", + "show_all_conversation": "Show full conversation ({numStatus} other status) | Show full conversation ({numStatus} other statuses)", + "show_only_conversation_under_this": "Only show replies to this status", + "status_history": "Status history" }, "user_card": { "approve": "Approve", "block": "Block", "blocked": "Blocked!", + "deactivated": "Deactivated", "deny": "Deny", "edit_profile": "Edit profile", "favorites": "Favorites", @@ -748,6 +919,7 @@ "muted": "Muted", "per_day": "per day", "remote_follow": "Remote follow", + "remove_follower": "Remove follower", "report": "Report", "statuses": "Statuses", "subscribe": "Subscribe", @@ -778,14 +950,19 @@ "disable_any_subscription": "Disallow following user at all", "quarantine": "Disallow user posts from federating", "delete_user": "Delete user", - "delete_user_confirmation": "Are you absolutely sure? This action cannot be undone." + "delete_user_data_and_deactivate_confirmation": "This will permanently delete the data from this account and deactivate it. Are you absolutely sure?" }, "highlight": { "disabled": "No highlight", "solid": "Solid bg", "striped": "Striped bg", "side": "Side stripe" - } + }, + "note": "Note", + "note_blank": "(None)", + "edit_note": "Edit note", + "edit_note_apply": "Apply", + "edit_note_cancel": "Cancel" }, "user_profile": { "timeline_title": "User timeline", @@ -814,7 +991,9 @@ "user_settings": "User Settings", "accept_follow_request": "Accept follow request", "reject_follow_request": "Reject follow request", - "bookmark": "Bookmark" + "bookmark": "Bookmark", + "toggle_expand": "Expand or collapse notification to show post in full", + "toggle_mute": "Expand or collapse notification to reveal muted content" }, "upload": { "error": { @@ -836,7 +1015,9 @@ "hashtags": "Hashtags", "person_talking": "{count} person talking", "people_talking": "{count} people talking", - "no_results": "No results" + "no_results": "No results", + "no_more_results": "No more results", + "load_more": "Load more results" }, "password_reset": { "forgot_password": "Forgot password?", @@ -863,6 +1044,27 @@ "error_sending_message": "Something went wrong when sending the message.", "empty_chat_list_placeholder": "You don't have any chats yet. Start a new chat!" }, + "lists": { + "lists": "Lists", + "new": "New List", + "title": "List title", + "search": "Search users", + "create": "Create", + "save": "Save changes", + "delete": "Delete list", + "following_only": "Limit to Following", + "manage_lists": "Manage lists", + "manage_members": "Manage list members", + "add_members": "Search for more users", + "remove_from_list": "Remove from list", + "add_to_list": "Add to list", + "is_in_list": "Already in list", + "editing_list": "Editing list {listTitle}", + "creating_list": "Creating new list", + "update_title": "Save Title", + "really_delete": "Really delete list?", + "error": "Error manipulating lists: {0}" + }, "file_type": { "audio": "Audio", "video": "Video", @@ -871,5 +1073,17 @@ }, "display_date": { "today": "Today" + }, + "update": { + "big_update_title": "Please bear with us", + "big_update_content": "We haven't had a release in a while, so things might look and feel different than what you're used to.", + "update_bugs": "Please report any issues and bugs on {pleromaGitlab}, as we have changed a lot, and although we test thoroughly and use development versions ourselves, we may have missed some things. We welcome your feedback and suggestions on issues you might encounter, or how to improve Pleroma and Pleroma-FE.", + "update_bugs_gitlab": "Pleroma GitLab", + "update_changelog": "For more details on what's changed, see {theFullChangelog}.", + "update_changelog_here": "the full changelog", + "art_by": "Art by {linkToArtist}" + }, + "unicode_domain_indicator": { + "tooltip": "This domain contains non-ascii characters." } } diff --git a/src/i18n/eo.json b/src/i18n/eo.json @@ -11,7 +11,8 @@ "title": "Funkcioj", "who_to_follow": "Kiun aboni", "pleroma_chat_messages": "Babilejo de Pleroma", - "upload_limit": "Limo de alŝutoj" + "upload_limit": "Limo de alŝutoj", + "shout": "Kriujo" }, "finder": { "error_fetching_user": "Eraris alporto de uzanto", @@ -42,7 +43,21 @@ }, "flash_content": "Klaku por montri enhavon de Flash per Ruffle. (Eksperimente, eble ne funkcios.)", "flash_security": "Sciu, ke tio povas esti danĝera, ĉar la enhavo de Flash ja estas arbitra programo.", - "flash_fail": "Malsukcesis enlegi enhavon de Flash; vidu detalojn en konzolo." + "flash_fail": "Malsukcesis enlegi enhavon de Flash; vidu detalojn en konzolo.", + "scope_in_timeline": { + "direct": "Persona", + "private": "Nur abonantoj", + "public": "Publika", + "unlisted": "Nelistigita" + }, + "generic_error_message": "Eraris: {0}", + "never_show_again": "Neniam remontri", + "undo": "Malfari", + "yes": "Jes", + "no": "Ne", + "unpin": "Malfiksi eron", + "pin": "Fiksi eron", + "scroll_to_top": "Rulumi supren" }, "image_cropper": { "crop_picture": "Tondi bildon", @@ -70,7 +85,9 @@ }, "media_modal": { "previous": "Antaŭa", - "next": "Sekva" + "next": "Sekva", + "counter": "{current} / {total}", + "hide": "Fermi vidilon de vidaŭdaĵoj" }, "nav": { "about": "Pri", @@ -79,9 +96,9 @@ "friend_requests": "Petoj pri abono", "mentions": "Mencioj", "dms": "Rektaj mesaĝoj", - "public_tl": "Publika historio", + "public_tl": "Loka historio", "timeline": "Historio", - "twkn": "Konata reto", + "twkn": "Federa historio", "user_search": "Serĉi uzantojn", "who_to_follow": "Kiun aboni", "preferences": "Agordoj", @@ -91,7 +108,11 @@ "administration": "Administrado", "bookmarks": "Legosignoj", "timelines": "Historioj", - "home_timeline": "Hejma historio" + "home_timeline": "Hejma historio", + "edit_pinned": "Redakti fiksitajn erojn", + "lists": "Listoj", + "edit_nav_mobile": "Adapti navigan breton", + "edit_finish": "Fini redakton" }, "notifications": { "broken_favorite": "Nekonata stato, serĉante ĝin…", @@ -105,7 +126,9 @@ "reacted_with": "reagis per {0}", "migrated_to": "migris al", "follow_request": "volas vin aboni", - "error": "Eraris akirado de sciigoj: {0}" + "error": "Eraris akirado de sciigoj: {0}", + "submitted_report": "sendis raporton", + "poll_ended": "enketo finiĝis" }, "post_status": { "new_status": "Afiŝi novan staton", @@ -129,7 +152,7 @@ "unlisted": "Nelistigita – ne afiŝi al publikaj historioj" }, "scope_notice": { - "unlisted": "Ĉi tiu afiŝo ne estos videbla en la Publika historio kaj La tuta konata reto", + "unlisted": "Ĉi tiu afiŝo ne estos videbla en la Loka historio kaj la Federa historio", "private": "Ĉi tiu afiŝo estos videbla nur al viaj abonantoj", "public": "Ĉi tiu afiŝo estos videbla al ĉiuj" }, @@ -140,7 +163,10 @@ "direct_warning_to_first_only": "Ĉi tiu afiŝo estas nur videbla al uzantoj menciitaj je la komenco de la mesaĝo.", "direct_warning_to_all": "Ĉi tiu afiŝo estos videbla al ĉiuj menciitaj uzantoj.", "media_description": "Priskribo de vidaŭdaĵo", - "post": "Afiŝo" + "post": "Afiŝo", + "edit_remote_warning": "Aliaj foraj nodoj eble ne subtenas redaktadon, kaj ne povos ricevi pli novan version de via afiŝo.", + "edit_unsupported_warning": "Pleroma ne subtenas redaktadon de mencioj aŭ enketoj.", + "edit_status": "Stato de redakto" }, "registration": { "bio": "Priskribo", @@ -164,7 +190,10 @@ }, "reason_placeholder": "Ĉi-node oni aprobas registriĝojn permane.\nSciigu la administrantojn kial vi volas registriĝi.", "reason": "Kialo registriĝi", - "register": "Registriĝi" + "register": "Registriĝi", + "bio_optional": "Prio (malnepra)", + "email_optional": "Retpoŝtadreso (malnepra)", + "email_language": "En kiu lingvo vi volus ricevi retleterojn de la servilo?" }, "settings": { "app_name": "Nomo de aplikaĵo", @@ -553,7 +582,87 @@ }, "right_sidebar": "Montri flankan breton dekstre", "save": "Konservi ŝanĝojn", - "hide_shoutbox": "Kaŝi kriujon de nodo" + "hide_shoutbox": "Kaŝi kriujon de nodo", + "always_show_post_button": "Ĉiam montri ŝvebantan butonon por nova afiŝo", + "mentions_new_style": "Pli mojosaj menciligiloj", + "mentions_new_place": "Meti menciojn sur apartan linion", + "lists_navigation": "Montri listojn en navigiloj", + "account_backup": "Savkopio de konto", + "account_backup_description": "Ĉi tio povigas vin elŝuti arĥivon de viaj afiŝoj kaj ĉiuj informoj pri via konto, sed ili ne jam povas enportiĝi en konton de Pleroma.", + "list_aliases_error": "Eraris akirado de kromnomoj: {error}", + "move_account_notes": "Se vi volas movi la konton aliloken, vi devas iri al via celata konto, kaj aldoni kromnomon ligitan al tie ĉi.", + "navbar_column_stretch": "Etendi navigan breton laŭ larĝeco de kolumnoj", + "posts": "Afiŝoj", + "notification_visibility_polls": "Finoj de enketoj kun via voĉo", + "conversation_display": "Aspekto de interparoloj", + "disable_sticky_headers": "Ne alglui kapojn de kolumnoj al supro de la ekrano", + "conversation_display_linear_quick": "Linia vido", + "use_websockets": "Uzi teĥnikaron «websockets» (tuja ĝisdatigo)", + "mention_link_display_full_for_remote": "plene nur je uzantoj foraj (ekz. {'@'}zozo{'@'}ekzemplo.org)", + "expert_mode": "Montri altnivelajn", + "setting_server_side": "Ĉi tiu agordo estas ligita al via profilo, kaj efektiviĝon en ĉiuj viaj salutoj kaj klientoj", + "post_look_feel": "Aspekto de afiŝoj", + "mention_links": "Menciaj ligiloj", + "email_language": "Lingvo de leteroj ricevotaj de la servilo", + "account_backup_table_head": "Savkopio", + "download_backup": "Elŝuti", + "backup_not_ready": "Ĉi tiu savkopio ne jam pretas.", + "remove_backup": "Forigi", + "list_backups_error": "Eraris akirado de listo de savkopioj: {error}", + "add_backup": "Fari novan savkopion", + "added_backup": "Aldonis novan savkopion.", + "add_backup_error": "Eraris aldono de nova savkopio: {error}", + "account_alias": "Kromnomoj de konto", + "account_alias_table_head": "Kromnomo", + "hide_list_aliases_error_action": "Fermi", + "remove_alias": "Forigi ĉi tiun kromnomon", + "new_alias_target": "Aldoni novan kromnomon (ekz. {example})", + "added_alias": "Kromnomo estas aldonita.", + "add_alias_error": "Eraris aldono de kromnomo: {error}", + "move_account": "Movi konton", + "move_account_target": "Celata konto (ekz. {example})", + "moved_account": "Konto moviĝis.", + "move_account_error": "Eraris movado de konto: {error}", + "wordfilter": "Vortofiltrado", + "word_filter_and_more": "Vortofiltrado kaj pli…", + "mute_bot_posts": "Silentigi afiŝojn de robotoj", + "hide_bot_indication": "Kaŝi markon de roboteco en afiŝoj", + "hide_wordfiltered_statuses": "Kaŝi vorte filtritajn statojn", + "hide_muted_threads": "Kaŝi silentigitajn fadenojn", + "account_privacy": "Privateco", + "user_profiles": "Profiloj de uzantoj", + "hide_favorites_description": "Ne montri liston de miaj ŝatatoj (oni tamen sciiĝas)", + "conversation_display_tree": "Arba stilo", + "conversation_display_tree_quick": "Arba vido", + "show_scrollbars": "Montri rulumajn bretojn de flankaj kolumnoj", + "third_column_mode_none": "Neniam montri trian kolumnon", + "third_column_mode_notifications": "Kolumno de sciigoj", + "columns": "Kolumnoj", + "column_sizes": "Grandeco de kolumnoj", + "column_sizes_sidebar": "Flanka breto", + "column_sizes_content": "Enhavo", + "column_sizes_notifs": "Sciigoj", + "tree_advanced": "Permesi pli flekseblan navigadon en arba vido", + "conversation_display_linear": "Linia stilo", + "conversation_other_replies_button": "Montri la butonon «aliaj respondoj»", + "conversation_other_replies_button_below": "Sub statoj", + "conversation_other_replies_button_inside": "En statoj", + "max_depth_in_thread": "Maksimuma nombro de niveloj implicite montrataj en fadeno", + "auto_update": "Montri novajn afiŝojn memage", + "use_at_icon": "Montri simbolon {'@'} kiel bildon anstataŭ teksto", + "mention_link_display": "Montri menciajn ligilojn", + "mention_link_display_short": "ĉiam mallonge (ekz. {'@'}zozo)", + "mention_link_display_full": "ĉiam plene (ekz. {'@'}zozo{'@'}ekzemplo.org)", + "mention_link_show_avatar": "Montri profilbildon de uzanto apud la ligilo", + "mention_link_show_avatar_quick": "Montri profilbildon de uzanto apud mencioj", + "mention_link_fade_domain": "Malvigligi retnomojn (ekz. {'@'}ekzemplo.org en {'@'}zozo{'@'}ekzemplo.org)", + "mention_link_bolden_you": "Emfazi vian mencion, se vi estas menciita", + "mention_link_use_tooltip": "Montri karton de uzanto per klako al mencia ligilo", + "user_popover_avatar_action_close": "Fermi la ŝprucaĵon", + "user_popover_avatar_action_open": "Malfermi la profilon", + "user_popover_avatar_overlay": "Aperigi ŝprucaĵon pri uzanto sur profilbildo", + "show_yous": "Montri la markon «(Vi)»", + "user_popover_avatar_action_zoom": "Zomi la profilbildon" }, "timeline": { "collapse": "Maletendi", @@ -603,7 +712,6 @@ "mention": "Mencio", "hidden": "Kaŝita", "admin_menu": { - "delete_user_confirmation": "Ĉu vi tute certas? Ĉi tiu ago ne estas malfarebla.", "delete_user": "Forigi uzanton", "quarantine": "Malpermesi federadon de afiŝoj de uzanto", "disable_any_subscription": "Malpermesi ĉian abonadon al uzanto", @@ -619,7 +727,8 @@ "grant_moderator": "Nomumi reguligiston", "revoke_admin": "Malnomumi administranton", "grant_admin": "Nomumi administranton", - "moderation": "Reguligado" + "moderation": "Reguligado", + "delete_user_data_and_deactivate_confirmation": "Tio ĉi por ĉiam forigos datumojn de tiu ĉi konto, kaj malaktivigos ĝin. Ĉu vi plene certas?" }, "show_repeats": "Montri ripetojn", "hide_repeats": "Kaŝi ripetojn", @@ -631,7 +740,11 @@ "striped": "Stria fono", "solid": "Unueca fono", "disabled": "Senemfaze" - } + }, + "edit_profile": "Redakti profilon", + "deactivated": "Malaktiva", + "follow_cancel": "Nuligi peton", + "remove_follower": "Forigi abonanton" }, "user_profile": { "timeline_title": "Historio de uzanto", @@ -677,7 +790,19 @@ "load_all": "Enlegante ĉiujn {emojiAmount} bildosignojn", "load_all_hint": "Enlegis la {saneAmount} unuajn bildosignojn; enlego de ĉiuj povus kaŭzi problemojn pri efikeco.", "unicode": "Unikoda bildosigno", - "custom": "Propra bildosigno" + "custom": "Propra bildosigno", + "unicode_groups": { + "activities": "Agado", + "animals-and-nature": "Bestoj kaj naturo", + "flags": "Flagoj", + "food-and-drink": "Manĝaĵoj kaj trinkaĵoj", + "objects": "Aĵoj", + "people-and-body": "Homoj kaj korpo", + "smileys-and-emotion": "Mienbildoj kaj sentoj", + "symbols": "Simboloj", + "travel-and-places": "Vojaĝoj kaj lokoj" + }, + "regional_indicator": "Regiona marko {letter}" }, "polls": { "not_enough_options": "Tro malmultaj unikaj elektebloj en la enketo", @@ -718,7 +843,7 @@ "media_nsfw": "Devige marki vidaŭdaĵojn konsternaj", "media_removal_desc": "Ĉi tiu nodo forigas vidaŭdaĵojn de afiŝoj el la jenaj nodoj:", "media_removal": "Forigo de vidaŭdaĵoj", - "ftl_removal": "Forigo el la historio de «Konata reto»", + "ftl_removal": "Forigo el la «Federa historio»", "quarantine_desc": "Ĉi tiu nodo sendos nur publikajn afiŝojn al la jenaj nodoj:", "quarantine": "Kvaranteno", "reject_desc": "Ĉi tiu nodo ne akceptos mesaĝojn de la jenaj nodoj:", @@ -726,14 +851,16 @@ "accept_desc": "Ĉi tiu nodo nur akceptas mesaĝojn de la jenaj nodoj:", "accept": "Akcepti", "simple_policies": "Specialaj politikoj de la nodo", - "ftl_removal_desc": "Ĉi tiu nodo forigas la jenajn nodojn el la historio de «Konata reto»:" + "ftl_removal_desc": "Ĉi tiu nodo forigas la jenajn nodojn el la «Federa historio»:", + "instance": "Nodo", + "reason": "Kialo" }, "mrf_policies": "Ŝaltis politikon de Mesaĝa ŝanĝilaro (MRF)", "keyword": { "is_replaced_by": "→", "replace": "Anstataŭigi", "reject": "Rifuzi", - "ftl_removal": "Forigo el la historio de «La tuta konata reto»", + "ftl_removal": "Forigo el la historio de la «Federa historio»", "keyword_policies": "Politiko pri ĉefvortoj" }, "federation": "Federado", @@ -752,7 +879,9 @@ "load_older": "Enlegi pli malnovajn interagojn", "moves": "Migrado de uzantoj", "follows": "Novaj abonoj", - "favs_repeats": "Ripetoj kaj ŝatoj" + "favs_repeats": "Ripetoj kaj ŝatoj", + "emoji_reactions": "Bildosignaj reagoj", + "reports": "Raportoj" }, "errors": { "storage_unavailable": "Pleroma ne povis aliri deponejon de la foliumilo. Via saluto kaj viaj lokaj agordoj ne estos konservitaj, kaj vi eble renkontos neatenditajn problemojn. Provu permesi kuketojn." @@ -782,7 +911,35 @@ "status_deleted": "Ĉi tiu afiŝo foriĝis", "nsfw": "Konsterna", "expand": "Etendi", - "external_source": "Ekstera fonto" + "external_source": "Ekstera fonto", + "mentions": "Mencioj", + "you": "(Vi)", + "plus_more": "+{number} pli", + "show_all_attachments": "Montri ĉiujn kunsendaĵojn", + "collapse_attachments": "Kaŝi iujn kunsendaĵojn", + "many_attachments": "Afiŝo havas {number} kunsendaĵo(j)n", + "show_attachment_in_modal": "Montri en vidilo de vidaŭdaĵoj", + "edit": "Redakti afiŝon", + "replies_list_with_others": "Respondoj (+{numReplies} alia): | Respondoj (+{numReplies} aliaj):", + "thread_show": "Malkaŝi ĉi tiun fadenon", + "thread_show_full": "Montri ĉion en ĉi tiu fadeno ({numStatus} afiŝon sume, maksimume en profundeco {depth}) | Montri ĉion en ĉi tiu fadeno ({numStatus} afiŝojn sume, maksimume en profundeco {depth})", + "show_all_conversation": "Montri plenan interparolon ({numStatus} alian afiŝon) | Montri plenan interparolon ({numStatus} aliajn afiŝojn)", + "edited_at": "(lastafoje redaktita je {time})", + "remove_attachment": "Forigi kunsendaĵon", + "show_attachment_description": "Antaŭvidi priskribon (malfermu kunsendaĵon por vidi plenan priskribon)", + "hide_attachment": "Kaŝi kunsendaĵon", + "attachment_stop_flash": "Ĉesigi ludilon de [Flash]", + "move_up": "Ŝovi kunsendaĵon antaŭen", + "move_down": "Ŝovi kunsendaĵon posten", + "thread_hide": "Kaŝi ĉi tiun fadenon", + "thread_show_full_with_icon": "{icon} {text}", + "thread_follow": "Montri ceteron de ĉi tiu fadeno ({numStatus} afiŝon sume) | Montri ceteron de ĉi tiu fadeno ({numStatus} afiŝojn sume)", + "thread_follow_with_icon": "{icon} {text}", + "ancestor_follow": "Vidi {numReplies} alian respondon sub ĉi tiu afiŝo | Vidi {numReplies} aliajn respondojn sub ĉi tiu afiŝo", + "ancestor_follow_with_icon": "{icon} {text}", + "show_all_conversation_with_icon": "{icon} {text}", + "show_only_conversation_under_this": "Montri nur respondojn al ĉi tiu afiŝo", + "status_history": "Historio de afiŝo" }, "time": { "years_short": "{0}j", @@ -816,7 +973,23 @@ "days_short": "{0}t", "day_short": "{0}t", "days": "{0} tagoj", - "day": "{0} tago" + "day": "{0} tago", + "unit": { + "days": "{0} tago | {0} tagoj", + "minutes": "{0} minuto | {0} minutoj", + "days_short": "{0}t", + "hours": "{0} horo | {0} horoj", + "hours_short": "{0}h", + "minutes_short": "{0}min", + "months": "{0} monato | {0} monatoj", + "months_short": "{0}mo", + "seconds": "{0} sekundo | {0} sekundoj", + "seconds_short": "{0}sek", + "weeks": "{0} semajno | {0} semajnoj", + "weeks_short": "{0}sem", + "years": "{0} jaro | {0} jaroj", + "years_short": "{0}j" + } }, "search": { "people": "Personoj", @@ -870,5 +1043,68 @@ }, "shoutbox": { "title": "Kriujo" + }, + "report": { + "reporter": "Raportinto:", + "reported_user": "Raportito:", + "reported_statuses": "Raportitaj statoj:", + "notes": "Notoj:", + "state": "Stato:", + "state_open": "Malfermita", + "state_closed": "Fermita", + "state_resolved": "Solvita" + }, + "lists": { + "editing_list": "Redaktado de listo {listTitle}", + "lists": "Listoj", + "new": "Nova listo", + "title": "Nomo de listo", + "search": "Serĉi uzantojn", + "create": "Krei", + "save": "Konservi ŝanĝojn", + "delete": "Forigi liston", + "following_only": "Limigi al abonatoj", + "manage_lists": "Mastrumi listojn", + "manage_members": "Mastrumi listanojn", + "add_members": "Serĉi pliajn uzantojn", + "remove_from_list": "Forigi de listo", + "add_to_list": "Aldoni al listo", + "is_in_list": "Jam en listo", + "creating_list": "Kreado de nova listo", + "update_title": "Konservi nomon", + "really_delete": "Ĉu vi certe volas forigi la liston?", + "error": "Eraris umado je listoj: {0}" + }, + "update": { + "big_update_content": "Ni longe ne eldonis novan version, kaj tial aferoj eble aspektos iom malsame, ol antaŭe.", + "update_bugs": "Bonvolu raporti problemojn kaj erarojn ĉe {pleromaGitlab}, ĉar ni ŝanĝis multon, kaj kvankam ni zorge testas kaj mem uzas la prilaboratajn versiojn, ni tamen povas preteratenti ion. Ni bonvenigas viajn rimarkojn kaj proponojn pri renkontitaj eraroj aŭ proponoj plibonigi Pleromon.", + "big_update_title": "Bonvolu pacienci", + "update_bugs_gitlab": "GitLab de Pleroma", + "update_changelog": "Por legi detalojn pri ŝanĝoj, vidu {theFullChangelog}.", + "update_changelog_here": "la plenan ŝanĝaron", + "art_by": "Arto de {linkToArtist}" + }, + "unicode_domain_indicator": { + "tooltip": "Ĉi tiu retnomo enhavas signojn ekster ASCII." + }, + "announcements": { + "all_day_prompt": "Ĉi tio estas tuttaga okazo", + "page_header": "Anoncoj", + "title": "Anonco", + "mark_as_read_action": "Marki legita", + "post_placeholder": "Entajpu vian anoncon tie ĉi…", + "post_action": "Afiŝi", + "post_error": "Eraro: {error}", + "close_error": "Fermi", + "delete_action": "Forigi", + "start_time_prompt": "Komenca tempo: ", + "end_time_prompt": "Fina tempo: ", + "published_time_display": "Publikigita je {time}", + "start_time_display": "Komenciĝas je {time}", + "end_time_display": "Finiĝas je {time}", + "edit_action": "Redakti", + "submit_edit_action": "Afiŝi", + "cancel_edit_action": "Nuligi", + "inactive_message": "Ĉi tiu anonco estas neaktiva" } } diff --git a/src/i18n/es.json b/src/i18n/es.json @@ -599,7 +599,10 @@ "backup_restore": "Copia de seguridad de la configuración" }, "hide_shoutbox": "Ocultar cuadro de diálogo de la instancia", - "right_sidebar": "Mostrar la barra lateral a la derecha" + "right_sidebar": "Mostrar la barra lateral a la derecha", + "always_show_post_button": "Muestra siempre el botón flotante de Nueva Plubicación", + "mentions_new_style": "Enlaces de menciones más elegantes", + "mentions_new_place": "Situa las menciones en una línea separada" }, "time": { "day": "{0} día", @@ -676,7 +679,10 @@ "status_deleted": "Esta publicación ha sido eliminada", "nsfw": "NSFW (No apropiado para el trabajo)", "expand": "Expandir", - "external_source": "Fuente externa" + "external_source": "Fuente externa", + "mentions": "Menciones", + "you": "(Tú)", + "plus_more": "+{number} más" }, "user_card": { "approve": "Aprobar", @@ -725,8 +731,7 @@ "disable_remote_subscription": "No permitir que usuarios de instancias remotas te siga", "disable_any_subscription": "No permitir que ningún usuario te siga", "quarantine": "No permitir publicaciones de usuarios de instancias remotas", - "delete_user": "Eliminar usuario", - "delete_user_confirmation": "¿Estás completamente seguro? Esta acción no se puede deshacer." + "delete_user": "Eliminar usuario" }, "show_repeats": "Mostrar repetidos", "hide_repeats": "Ocultar repetidos", diff --git a/src/i18n/eu.json b/src/i18n/eu.json @@ -609,8 +609,7 @@ "disable_remote_subscription": "Ez utzi istantzia kanpoko erabiltzaileak zuri jarraitzea", "disable_any_subscription": "Ez utzi beste erabiltzaileak zuri jarraitzea", "quarantine": "Ez onartu mezuak beste instantzietatik", - "delete_user": "Erabiltzailea ezabatu", - "delete_user_confirmation": "Erabat ziur zaude? Ekintza hau ezin da desegin." + "delete_user": "Erabiltzailea ezabatu" } }, "user_profile": { diff --git a/src/i18n/fi.json b/src/i18n/fi.json @@ -620,8 +620,7 @@ "sandbox": "Pakota viestit vain seuraajille", "disable_remote_subscription": "Estä seuraaminen ulkopuolisilta sivuilta", "quarantine": "Estä käyttäjän viestin federoituminen", - "delete_user": "Poista käyttäjä", - "delete_user_confirmation": "Oletko aivan varma? Tätä ei voi kumota." + "delete_user": "Poista käyttäjä" }, "favorites": "Tykkäykset", "mention": "Mainitse", diff --git a/src/i18n/fr.json b/src/i18n/fr.json @@ -15,7 +15,8 @@ "title": "Fonctionnalités", "who_to_follow": "Suggestions de suivis", "pleroma_chat_messages": "Chat Pleroma", - "upload_limit": "Limite de téléversement" + "upload_limit": "Limite de téléversement", + "shout": "Shoutbox" }, "finder": { "error_fetching_user": "Erreur lors de la recherche du compte", @@ -44,9 +45,23 @@ "moderator": "Modo'", "admin": "Admin" }, - "flash_content": "Clique pour afficher le contenu Flash avec Ruffle (Expérimental, peut ne pas fonctionner).", + "flash_content": "Cliquer pour afficher le contenu Flash avec Ruffle (Expérimental, peut ne pas fonctionner).", "flash_security": "Cela reste potentiellement dangereux, Flash restant du code arbitraire.", - "flash_fail": "Échec de chargement du contenu Flash, voir la console pour les détails." + "flash_fail": "Échec de chargement du contenu Flash, voir la console pour les détails.", + "scope_in_timeline": { + "direct": "Direct", + "public": "Publique", + "private": "Abonné⋅e⋅s seulement", + "unlisted": "Non-listé" + }, + "undo": "Défaire", + "yes": "Oui", + "no": "Non", + "unpin": "Dégrafer l'élément", + "scroll_to_top": "Défiler au début", + "pin": "Agrafer l'élément", + "generic_error_message": "Une erreur est apparue : {0}", + "never_show_again": "Ne plus afficher" }, "image_cropper": { "crop_picture": "Rogner l'image", @@ -79,7 +94,9 @@ }, "media_modal": { "previous": "Précédent", - "next": "Suivant" + "next": "Suivant", + "counter": "{current} / {total}", + "hide": "Fermer le visualiseur multimédia" }, "nav": { "about": "À propos", @@ -100,7 +117,14 @@ "chats": "Chats", "bookmarks": "Marques-Pages", "timelines": "Flux", - "home_timeline": "Flux personnel" + "home_timeline": "Flux personnel", + "edit_nav_mobile": "Personnaliser la barre de navigation", + "mobile_notifications": "Ouvrir les notifications (il y en a de nouvelles)", + "lists": "Listes", + "edit_pinned": "Éditer les éléments agrafés", + "edit_finish": "Édition terminée", + "mobile_sidebar": "(Dés)activer le panneau latéral", + "mobile_notifications_close": "Fermer les notifications" }, "notifications": { "broken_favorite": "Message inconnu, recherche en cours…", @@ -114,13 +138,17 @@ "migrated_to": "a migré à", "reacted_with": "a réagi avec {0}", "follow_request": "veut vous suivre", - "error": "Erreur de chargement des notifications : {0}" + "error": "Erreur de chargement des notifications : {0}", + "poll_ended": "Sondage terminé", + "submitted_report": "Rapport envoyé" }, "interactions": { "favs_repeats": "Partages et favoris", "follows": "Nouveaux suivis", "load_older": "Chargez d'anciennes interactions", - "moves": "Migrations de comptes" + "moves": "Migrations de comptes", + "emoji_reactions": "Émoticônes de réaction", + "reports": "Rapports" }, "post_status": { "new_status": "Poster un nouveau statut", @@ -154,7 +182,10 @@ "preview_empty": "Vide", "preview": "Prévisualisation", "media_description": "Description de la pièce-jointe", - "post": "Post" + "post": "Post", + "edit_status": "Éditer le status", + "edit_remote_warning": "Des instances distantes pourraient ne pas supporter l'édition et seront incapables de recevoir la nouvelle version de votre post.", + "edit_unsupported_warning": "Pleroma ne supporte pas l'édition de mentions ni de sondages." }, "registration": { "bio": "Biographie", @@ -178,7 +209,10 @@ }, "reason_placeholder": "Cette instance modère les inscriptions manuellement.\nExpliquer ce qui motive votre inscription à l'administration.", "reason": "Motivation d'inscription", - "register": "Enregistrer" + "register": "Enregistrer", + "email_language": "Dans quelle langue voulez-vous recevoir les emails du server ?", + "bio_optional": "Biographie (optionnelle)", + "email_optional": "Courriel (optionnel)" }, "selectable_list": { "select_all": "Tout selectionner" @@ -267,8 +301,8 @@ "import_theme": "Charger le thème", "inputRadius": "Champs de texte", "checkboxRadius": "Cases à cocher", - "instance_default": "(default : {value})", - "instance_default_simple": "(default)", + "instance_default": "(défaut : {value})", + "instance_default_simple": "(défaut)", "interface": "Interface", "interfaceLanguage": "Langue de l'interface", "invalid_theme_imported": "Le fichier sélectionné n'est pas un thème Pleroma pris en charge. Aucun changement n'a été apporté à votre thème.", @@ -570,7 +604,87 @@ "restore_settings": "Restaurer les paramètres depuis un fichier" }, "hide_shoutbox": "Cacher la shoutbox de l'instance", - "right_sidebar": "Afficher le paneau latéral à droite" + "right_sidebar": "Afficher le paneau latéral à droite", + "expert_mode": "Préférences Avancées", + "post_look_feel": "Affichage des messages", + "mention_links": "Liens des mentions", + "email_language": "Langue pour recevoir les emails du server", + "account_backup_table_head": "Sauvegarde", + "download_backup": "Télécharger", + "backup_not_ready": "La sauvegarde n'est pas encore prête.", + "remove_backup": "Supprimer", + "list_backups_error": "Erreur d'obtention de la liste des sauvegardes : {error}", + "add_backup": "Créer une nouvelle sauvegarde", + "added_backup": "Ajouter une nouvelle sauvegarde.", + "account_alias": "Alias du compte", + "account_alias_table_head": "Alias", + "list_aliases_error": "Erreur à l'obtention des alias : {error}", + "hide_list_aliases_error_action": "Fermer", + "remove_alias": "Supprimer cet alias", + "new_alias_target": "Ajouter un nouvel alias (ex. {example})", + "added_alias": "L'alias à été ajouté.", + "add_alias_error": "Erreur à l'ajout de l'alias : {error}", + "move_account_target": "Compte cible (ex. {example})", + "moved_account": "Compte déplacé.", + "move_account_error": "Erreur au déplacement du compte : {error}", + "wordfilter": "Filtrage de mots", + "mute_bot_posts": "Masquer les messages des robots", + "hide_bot_indication": "Cacher l'indication d'un robot avec les messages", + "always_show_post_button": "Toujours montrer le bouton flottant Nouveau Message", + "hide_muted_threads": "Cacher les fils masqués", + "account_privacy": "Intimité", + "posts": "Messages", + "disable_sticky_headers": "Ne pas coller les en-têtes des colonnes en haut de l'écran", + "show_scrollbars": "Montrer les ascenseurs des colonnes", + "third_column_mode_none": "Jamais afficher la troisième colonne", + "third_column_mode_notifications": "Colonne de notifications", + "third_column_mode_postform": "Édition de messages et navigation", + "tree_advanced": "Permettre une navigation plus flexible dans l'arborescence", + "conversation_display_linear": "Style linéaire", + "conversation_other_replies_button": "Montrer le bouton \"autres réponses\"", + "conversation_other_replies_button_below": "En-dessous des messages", + "conversation_other_replies_button_inside": "Dans les messages", + "max_depth_in_thread": "Profondeur maximum à afficher par défaut dans un fil", + "mention_link_display": "Afficher les mentions", + "mention_link_display_full_for_remote": "complet pour les comptes distants (ex. {'@'}foo{'@'}example.org)", + "mention_link_display_full": "toujours complet (ex. {'@'}foo{'@'}example.org)", + "mention_link_show_avatar": "Afficher les avatars à coté du lien", + "mention_link_fade_domain": "Estomper les domaines (ex. {'@'}example.org en {'@'}foo{'@'}example.org)", + "mention_link_bolden_you": "Surligner les mentions qui vous sont destinées", + "show_yous": "Afficher (Vous)", + "setting_server_side": "Cette préférence est liée au profile et affecte toutes les sessions et clients", + "account_backup": "Sauvegarde de compte", + "account_backup_description": "Ceci permet de télécharger une archive des informations du compte et vos messages, mais ils ne peuvent pas actuellement être importé dans un compte Pleroma.", + "add_backup_error": "Erreur à l'ajout d'une nouvelle sauvegarde : {error}", + "move_account": "Déplacer le compte", + "move_account_notes": "Si vous voulez déplacer le compte ailleurs, vous devez aller sur votre compte cible et y créer un alias pointant ici.", + "hide_wordfiltered_statuses": "Cacher les messages filtré par un mot", + "user_profiles": "Profils des utilisateur⋅ice⋅s", + "notification_visibility_polls": "Fins de sondage auquel vous avez voté·e", + "hide_favorites_description": "Ne pas montrer ma liste de favoris (les personnes sont quand même notifiés)", + "conversation_display": "Style d'affichage des conversations", + "conversation_display_tree": "Arborescence", + "third_column_mode": "Quand il-y-a assez d'espace, afficher une troisième colonne avec", + "tree_fade_ancestors": "Montrer les parents du message courant en texte léger", + "use_at_icon": "Montrer le symbole {'@'} comme une icône au lieu de textuelle", + "mention_link_display_short": "toujours raccourcies (ex. {'@'}foo)", + "mention_link_show_tooltip": "Montrer le nom complet pour les comptes distants dans une info-bulle", + "lists_navigation": "Afficher les listes dans la navigation", + "word_filter_and_more": "Filtrer par mots et plus ...", + "columns": "Colonnes", + "auto_update": "Afficher automatiquement les nouveaux posts", + "mention_link_use_tooltip": "Montrer le profil utilisateur en cliquant sur les liens de mentions", + "mention_link_show_avatar_quick": "Afficher l'avatar de l'utilisateur à côté des mentions", + "navbar_column_stretch": "Élargir la barre de navigation à la taille des colonnes", + "column_sizes": "Taille des colonnes", + "column_sizes_sidebar": "Panneau latéral", + "column_sizes_content": "Contenu", + "column_sizes_notifs": "Notifications", + "conversation_display_linear_quick": "Vue linéaire", + "use_websockets": "Utiliser les websockets (mises à jour en temps réel)", + "user_popover_avatar_action_zoom": "Zoomer sur l'avatar", + "user_popover_avatar_action_open": "Ouvrir le profil", + "conversation_display_tree_quick": "Vue arborescente" }, "timeline": { "collapse": "Fermer", @@ -586,7 +700,9 @@ "reload": "Recharger", "error": "Erreur lors de l'affichage du flux : {0}", "socket_broke": "Connexion temps-réel perdue : CloseEvent code {0}", - "socket_reconnected": "Connexion temps-réel établie" + "socket_reconnected": "Connexion temps-réel établie", + "quick_view_settings": "Afficher les réglages rapides", + "quick_filter_settings": "Afficher les filtres rapides" }, "status": { "favorites": "Favoris", @@ -613,7 +729,36 @@ "thread_muted": "Fil de discussion masqué", "external_source": "Source externe", "unbookmark": "Supprimer des favoris", - "bookmark": "Ajouter aux favoris" + "bookmark": "Ajouter aux favoris", + "plus_more": "plus +{number}", + "many_attachments": "Message avec {number} pièce(s)-jointe(s)", + "collapse_attachments": "Réduire les pièces jointes", + "show_attachment_in_modal": "Montrer dans le visionneur de médias", + "hide_attachment": "Cacher la pièce jointe", + "you": "(Vous)", + "attachment_stop_flash": "Arrêter Flash Player", + "move_down": "Décaler la pièce-jointe à droite", + "thread_hide": "Cacher ce fil", + "thread_show": "Montrer ce fil", + "thread_show_full_with_icon": "{icon} {text}", + "thread_follow": "Montrer le reste du fil ({numStatus} message) | Montrer le reste du fil ({numStatus} messages)", + "thread_follow_with_icon": "{icon} {text}", + "ancestor_follow": "Monter les {numReplies} autres réponses après ce message | Monter les {numReplies} autres réponses après ce message", + "ancestor_follow_with_icon": "{icon} {text}", + "show_all_conversation_with_icon": "{icon} {text}", + "show_only_conversation_under_this": "Montrer uniquement les réponses à ce message", + "mentions": "Mentions", + "replies_list_with_others": "Réponses (+{numReplies} autres) : | Réponses (+{numReplies} autres) :", + "show_all_attachments": "Montrer toutes les pièces jointes", + "show_attachment_description": "Prévisualiser la description (ouvrir la pièce-jointe pour la description complète)", + "remove_attachment": "Enlever la pièce jointe", + "move_up": "Décaler la pièce-jointe à gauche", + "open_gallery": "Ouvrir la galerie", + "thread_show_full": "Montrer tout le fil ({numStatus} message, {depth} niveaux maximum) | Montrer tout le fil ({numStatus} messages, {depth} niveaux maximum)", + "show_all_conversation": "Montrer tout le fil ({numStatus} autre message) | Montrer tout le fil ({numStatus} autre messages)", + "edit": "Éditer le status", + "edited_at": "(dernière édition {time})", + "status_history": "Historique du status" }, "user_card": { "approve": "Accepter", @@ -644,11 +789,11 @@ "unmute_progress": "Démasquage…", "mute_progress": "Masquage…", "admin_menu": { - "moderation": "Moderation", + "moderation": "Modération", "grant_admin": "Promouvoir Administrateur⋅ice", - "revoke_admin": "Dégrader Administrateur⋅ice", + "revoke_admin": "Dégrader L'administrateur⋅ice", "grant_moderator": "Promouvoir Modérateur⋅ice", - "revoke_moderator": "Dégrader Modérateur⋅ice", + "revoke_moderator": "Dégrader la·e modérateur⋅ice", "activate_account": "Activer le compte", "deactivate_account": "Désactiver le compte", "delete_account": "Supprimer le compte", @@ -660,7 +805,7 @@ "disable_any_subscription": "Interdir de s'abonner à l'utilisateur tout court", "quarantine": "Interdir les statuts de l'utilisateur à fédérer", "delete_user": "Supprimer l'utilisateur", - "delete_user_confirmation": "Êtes-vous absolument-sûr⋅e ? Cette action ne peut être annulée." + "delete_user_data_and_deactivate_confirmation": "Ceci va supprimer les données du compte de manière permanente et le désactivé. Êtes-vous vraiment sûr ?" }, "mention": "Mention", "hidden": "Caché", @@ -680,7 +825,10 @@ "striped": "Fond rayé" }, "bot": "Robot", - "edit_profile": "Éditer le profil" + "edit_profile": "Éditer le profil", + "deactivated": "Désactivé", + "follow_cancel": "Annuler la requête", + "remove_follower": "Retirer l'abonné·e" }, "user_profile": { "timeline_title": "Flux du compte", @@ -748,13 +896,16 @@ "media_removal_desc": "Cette instance supprime le contenu multimédia des instances suivantes :", "media_nsfw": "Force le contenu multimédia comme sensible", "ftl_removal": "Supprimées du flux fédéré", - "media_nsfw_desc": "Cette instance force les pièce-jointes comme sensible pour les messages des instances suivantes :" + "media_nsfw_desc": "Cette instance force les pièce-jointes comme sensible pour les messages des instances suivantes :", + "reason": "Raison", + "not_applicable": "N/A", + "instance": "Instance" }, "federation": "Fédération", "mrf_policies": "Politiques MRF actives", "mrf_policies_desc": "Les politiques MRF modifient la fédération entre les instances. Les politiques suivantes sont activées :" }, - "staff": "Staff" + "staff": "Équipe" }, "domain_mute_card": { "mute": "Masqué", @@ -787,7 +938,19 @@ "load_all": "Charger tout les {emojiAmount} émojis", "load_all_hint": "{saneAmount} émojis chargé, charger tout les émojis peuvent causer des problèmes de performances.", "stickers": "Stickers", - "keep_open": "Garder ouvert" + "keep_open": "Garder ouvert", + "unicode_groups": { + "activities": "Activités", + "animals-and-nature": "Animaux & nature", + "flags": "Drapeaux", + "food-and-drink": "Nourriture & boissons", + "objects": "Objets", + "people-and-body": "Personnes & Corps", + "smileys-and-emotion": "Emoticônes", + "symbols": "Symboles", + "travel-and-places": "Voyages & lieux" + }, + "regional_indicator": "Indicateur régional {letter}" }, "remote_user_resolver": { "error": "Non trouvé.", @@ -826,14 +989,32 @@ "year": "{0} année", "years": "{0} années", "year_short": "{0}a", - "years_short": "{0}a" + "years_short": "{0}a", + "unit": { + "years": "{0} année | {0} années", + "years_short": "{0}ans", + "days_short": "{0}j", + "hours": "{0} heure | {0} heures", + "hours_short": "{0}h", + "minutes": "{0} minute | {0} minutes", + "minutes_short": "{0}min", + "months_short": "{0}mois", + "seconds": "{0} seconde | {0} secondes", + "seconds_short": "{0}s", + "weeks": "{0} semaine | {0} semaines", + "days": "{0} jour | {0} jours", + "months": "{0} mois | {0} mois", + "weeks_short": "{0}semaine" + } }, "search": { "people": "Comptes", "person_talking": "{count} personnes discutant", "hashtags": "Mot-dièses", "people_talking": "{count} personnes discutant", - "no_results": "Aucun résultats" + "no_results": "Aucun résultats", + "no_more_results": "Pas de résultats supplémentaires", + "load_more": "Charger plus de résultats" }, "password_reset": { "forgot_password": "Mot de passe oublié ?", @@ -874,5 +1055,47 @@ "delete": "Effacer", "message_user": "Message à {nickname}", "you": "Vous :" + }, + "lists": { + "new": "Nouvelle liste", + "title": "Titre de la liste", + "create": "Créer", + "save": "Sauvegarder les changements", + "delete": "Supprimer la liste", + "following_only": "Limiter aux abonné·e·s", + "manage_lists": "Gérer les listes", + "add_members": "Rechercher plus d'utilisateurs", + "remove_from_list": "Retirer de la liste", + "add_to_list": "Ajouter à la liste", + "is_in_list": "Déjà dans la liste", + "editing_list": "Édition de la liste {listTitle}", + "creating_list": "Création d'une nouvelle liste", + "really_delete": "Êtes-vous sûr·e de vouloir supprimer la liste ?", + "error": "Erreur en manipulant les listes : {0}", + "lists": "Listes", + "search": "Rechercher des utilisateurs", + "manage_members": "Gérer les membres des listes", + "update_title": "Sauvegarder le titre" + }, + "update": { + "update_bugs_gitlab": "GitLab du projet Pleroma", + "update_changelog": "Pour plus de détails sur les changements, consultez {theFullChangelog}.", + "update_changelog_here": "Liste compète des changements", + "art_by": "Œuvre par {linkToArtist}", + "big_update_content": "Nous n'avons pas fait de nouvelle version depuis un moment, les choses peuvent vous paraitre différentes de vos habitudes.", + "update_bugs": "Veuillez rapporter les problèmes sur {pleromaGitlab}, comme beaucoup de changements on été fait, même si nous testons entièrement et utilisons la version de dévelopement nous-même, nous avons pu en louper. Les retours et suggestions sont bienvenues sur ce que vous avez pu rencontrer, ou sur comment améliorer Pleroma (BE) et Pleroma-FE." + }, + "unicode_domain_indicator": { + "tooltip": "Ce domaine contient des caractères non ascii." + }, + "report": { + "reporter": "Rapporteur·euse :", + "reported_user": "Compte rapporté :", + "reported_statuses": "Status rapportés :", + "notes": "Notes :", + "state": "Status :", + "state_open": "Ouvert", + "state_closed": "Fermé", + "state_resolved": "Résolut" } } diff --git a/src/i18n/he.json b/src/i18n/he.json @@ -347,8 +347,7 @@ "disable_remote_subscription": "אל תאפשר עקיבה של המשתמש מאינסטנס אחר", "disable_any_subscription": "אל תאפשר עקיבה של המשתמש בכלל", "quarantine": "אל תאפשר פדרציה של ההודעות של המשתמש", - "delete_user": "מחק משתמש", - "delete_user_confirmation": "בטוח? פעולה זו הינה בלתי הפיכה." + "delete_user": "מחק משתמש" } }, "user_profile": { diff --git a/src/i18n/id.json b/src/i18n/id.json @@ -208,7 +208,13 @@ "enable_web_push_notifications": "Aktifkan notifikasi push web", "more_settings": "Lebih banyak pengaturan", "reply_visibility_all": "Tampilkan semua balasan", - "reply_visibility_self": "Hanya tampilkan balasan yang ditujukan kepada saya" + "reply_visibility_self": "Hanya tampilkan balasan yang ditujukan kepada saya", + "hide_muted_posts": "Sembunyikan postingan-postingan dari pengguna yang dibisukan", + "import_blocks_from_a_csv_file": "Impor blokiran dari berkas csv", + "domain_mutes": "Domain", + "composing": "Menulis", + "no_blocks": "Tidak ada yang diblokir", + "no_mutes": "Tidak ada yang dibisukan" }, "about": { "mrf": { @@ -222,7 +228,9 @@ "reject_desc": "Instansi ini tidak akan menerima pesan dari instansi-instansi berikut:", "reject": "Tolak", "accept_desc": "Instansi ini hanya menerima pesan dari instansi-instansi berikut:", - "accept": "Terima" + "accept": "Terima", + "media_removal": "Penghapusan Media", + "media_removal_desc": "Instansi ini menghapus media dari postingan yang berasal dari instansi-instansi berikut:" }, "federation": "Federasi", "mrf_policies": "Kebijakan MRF yang diaktifkan" @@ -319,8 +327,7 @@ "delete_account": "Hapus akun", "force_nsfw": "Tandai semua postingan sebagai NSFW", "strip_media": "Hapus media dari postingan-postingan", - "delete_user": "Hapus pengguna", - "delete_user_confirmation": "Apakah Anda benar-benar yakin? Tindakan ini tidak dapat dibatalkan." + "delete_user": "Hapus pengguna" }, "follow_unfollow": "Berhenti mengikuti", "followees": "Mengikuti", @@ -334,7 +341,9 @@ "message": "Kirimkan pesan" }, "user_profile": { - "timeline_title": "Linimasa pengguna" + "timeline_title": "Linimasa pengguna", + "profile_does_not_exist": "Maaf, profil ini tidak ada.", + "profile_loading_error": "Maaf, terjadi kesalahan ketika memuat profil ini." }, "user_reporting": { "title": "Melaporkan {0}", diff --git a/src/i18n/it.json b/src/i18n/it.json @@ -448,7 +448,10 @@ "backup_restore": "Archiviazione impostazioni" }, "right_sidebar": "Mostra barra laterale a destra", - "hide_shoutbox": "Nascondi muro dei graffiti" + "hide_shoutbox": "Nascondi muro dei graffiti", + "mentions_new_style": "Menzioni abbreviate", + "mentions_new_place": "Segrega le menzioni", + "always_show_post_button": "Non nascondere il pulsante di composizione" }, "timeline": { "error_fetching": "Errore nell'aggiornamento", @@ -482,7 +485,6 @@ "deny": "Nega", "remote_follow": "Segui da remoto", "admin_menu": { - "delete_user_confirmation": "Ne sei completamente sicuro? Non potrai tornare indietro.", "delete_user": "Elimina utente", "quarantine": "I messaggi non arriveranno alle altre stanze", "disable_any_subscription": "Rendi utente non seguibile", @@ -757,7 +759,10 @@ "status_deleted": "Questo messagio è stato cancellato", "nsfw": "DISDICEVOLE", "external_source": "Vai all'origine", - "expand": "Espandi" + "expand": "Espandi", + "mentions": "Menzioni", + "you": "(Tu)", + "plus_more": "+{number} altri" }, "time": { "years_short": "{0} a", @@ -774,8 +779,8 @@ "second": "{0} secondo", "now_short": "adesso", "now": "adesso", - "months_short": "{0} ms", - "month_short": "{0} ms", + "months_short": "{0} mes", + "month_short": "{0} mes", "months": "{0} mesi", "month": "{0} mese", "minutes_short": "{0} min", diff --git a/src/i18n/ja_easy.json b/src/i18n/ja_easy.json @@ -608,8 +608,7 @@ "disable_remote_subscription": "ほかのインスタンスからフォローされないようにする", "disable_any_subscription": "フォローされないようにする", "quarantine": "ほかのインスタンスのユーザーのとうこうをとめる", - "delete_user": "ユーザーをけす", - "delete_user_confirmation": "あなたは、ほんとうに、きはたしかですか? これは、とりけすことが、できません。" + "delete_user": "ユーザーをけす" } }, "user_profile": { diff --git a/src/i18n/ja_pedantic.json b/src/i18n/ja_pedantic.json @@ -43,7 +43,10 @@ "role": { "moderator": "モデレーター", "admin": "管理者" - } + }, + "flash_security": "Flashコンテンツが任意の命令を実行させることにより、コンピューターが危険にさらされることがあります。", + "flash_fail": "Flashコンテンツの読み込みに失敗しました。コンソールで詳細を確認できます。", + "flash_content": "(試験的機能)クリックしてFlashコンテンツを再生します。" }, "image_cropper": { "crop_picture": "画像を切り抜く", @@ -586,14 +589,18 @@ "word_filter": "単語フィルタ", "file_export_import": { "errors": { - "invalid_file": "これはPleromaの設定をバックアップしたファイルではありません。" + "invalid_file": "これはPleromaの設定をバックアップしたファイルではありません。", + "file_slightly_new": "ファイルのマイナーバージョンが異なり、一部の設定が読み込まれないことがあります" }, "restore_settings": "設定をファイルから復元する", "backup_settings_theme": "テーマを含む設定をファイルにバックアップする", "backup_settings": "設定をファイルにバックアップする", "backup_restore": "設定をバックアップ" }, - "save": "変更を保存" + "save": "変更を保存", + "hide_shoutbox": "Shoutboxを表示しない", + "always_show_post_button": "投稿ボタンを常に表示", + "right_sidebar": "サイドバーを右に表示" }, "time": { "day": "{0}日", @@ -641,7 +648,9 @@ "no_more_statuses": "これで終わりです", "no_statuses": "ステータスはありません", "reload": "再読み込み", - "error": "タイムラインの読み込みに失敗しました: {0}" + "error": "タイムラインの読み込みに失敗しました: {0}", + "socket_reconnected": "リアルタイム接続が確立されました", + "socket_broke": "コード{0}によりリアルタイム接続が切断されました" }, "status": { "favorites": "お気に入り", @@ -668,7 +677,10 @@ "copy_link": "リンクをコピー", "status_unavailable": "利用できません", "unbookmark": "ブックマーク解除", - "bookmark": "ブックマーク" + "bookmark": "ブックマーク", + "mentions": "メンション", + "you": "(あなた)", + "plus_more": "ほか{number}件" }, "user_card": { "approve": "受け入れ", @@ -717,8 +729,7 @@ "disable_remote_subscription": "他のインスタンスからフォローされないようにする", "disable_any_subscription": "フォローされないようにする", "quarantine": "他のインスタンスからの投稿を止める", - "delete_user": "ユーザーを削除", - "delete_user_confirmation": "あなたの精神状態に何か問題はございませんか? この操作を取り消すことはできません。" + "delete_user": "ユーザーを削除" }, "roles": { "moderator": "モデレーター", @@ -734,7 +745,8 @@ "striped": "背景を縞模様にする", "side": "端に線を付ける", "disabled": "強調しない" - } + }, + "edit_profile": "プロフィールを編集" }, "user_profile": { "timeline_title": "ユーザータイムライン", diff --git a/src/i18n/ko.json b/src/i18n/ko.json @@ -4,14 +4,15 @@ }, "features_panel": { "chat": "챗", - "gopher": "고퍼", + "gopher": "Gopher", "media_proxy": "미디어 프록시", "scope_options": "범위 옵션", "text_limit": "텍스트 제한", "title": "기능", "who_to_follow": "팔로우 추천", "upload_limit": "최대 파일용량", - "pleroma_chat_messages": "Pleroma 채트" + "pleroma_chat_messages": "Pleroma 채팅", + "shout": "외치기" }, "finder": { "error_fetching_user": "사용자 정보 불러오기 실패", @@ -21,12 +22,12 @@ "apply": "적용", "submit": "보내기", "loading": "로딩중…", - "peek": "숨기기", + "peek": "살짝 보기", "close": "닫기", "verify": "검사", "confirm": "확인", - "enable": "유효", - "disable": "무효", + "enable": "활성화", + "disable": "비활성화", "cancel": "취소", "dismiss": "무시", "show_less": "접기", @@ -34,18 +35,35 @@ "optional": "필수 아님", "retry": "다시 시도하십시오", "error_retry": "다시 시도하십시오", - "generic_error": "잘못되었습니다", + "generic_error": "에러가 발생했습니다", "more": "더 보기", "role": { "moderator": "중재자", "admin": "관리자" - } + }, + "undo": "취소", + "yes": "네", + "no": "아니오", + "unpin": "고정 해제", + "pin": "고정", + "scope_in_timeline": { + "private": "팔로워 전용", + "public": "공개", + "unlisted": "비표시", + "direct": "다이렉트" + }, + "flash_content": "클릭해서 플래시 컨텐츠 보기 (Ruffle 사용, 작동하지 않을 수 있습니다).", + "flash_security": "플래시 컨텐츠는 임의 코드 실행이 아직도 가능할 수 있기에 위험할 수 있습니다.", + "flash_fail": "플래시를 로드하지 못했습니다, 콘솔로 자세한 내용을 확인하세요.", + "scroll_to_top": "맨 위로 올라가기", + "generic_error_message": "에러가 발생했습니다: {0}", + "never_show_again": "다시 보지 않기" }, "login": { "login": "로그인", "description": "OAuth로 로그인", "logout": "로그아웃", - "password": "암호", + "password": "패스워드", "placeholder": "예시: lain", "register": "가입", "username": "사용자 이름", @@ -57,7 +75,7 @@ "enter_two_factor_code": "2단계인증 코드를 입력하십시오", "enter_recovery_code": "복구 코드를 입력하십시오", "authentication_code": "인증 코드", - "hint": "로그인하여 대화에 참가합시다" + "hint": "로그인해서 대화에 참여" }, "nav": { "about": "인스턴스 소개", @@ -71,68 +89,86 @@ "twkn": "알려진 네트워크", "user_search": "사용자 검색", "preferences": "환경설정", - "chats": "채트", + "chats": "채팅", "timelines": "타임라인", "who_to_follow": "추천된 사용자", "search": "검색", "bookmarks": "북마크", - "interactions": "대화", + "interactions": "알림", "administration": "관리", - "home_timeline": "홈 타임라인" + "home_timeline": "홈 타임라인", + "mobile_notifications": "알림 열기 (읽지 않은 알림이 있습니다)", + "lists": "리스트", + "edit_nav_mobile": "네비게이션 바 커스텀하기", + "edit_pinned": "상단 고정 편집", + "edit_finish": "편집 종료", + "mobile_notifications_close": "알림 닫기", + "mobile_sidebar": "모바일 사이드바 토글", + "announcements": "공지사항" }, "notifications": { "broken_favorite": "알 수 없는 게시물입니다, 검색합니다…", - "favorited_you": "당신의 게시물을 즐겨찾기", - "followed_you": "당신을 팔로우", - "load_older": "오래 된 알림 불러오기", + "favorited_you": "관심을 가짐", + "followed_you": "팔로우함", + "load_older": "이전 알림 불러오기", "notifications": "알림", "read": "읽음!", - "repeated_you": "당신의 게시물을 리핏", + "repeated_you": "리핏함", "no_more_notifications": "알림이 없습니다", "migrated_to": "이사했습니다", "reacted_with": "{0} 로 반응했습니다", "error": "알림 불러오기 실패: {0}", - "follow_request": "당신에게 팔로우 신청" + "follow_request": "팔로우 요청", + "submitted_report": "신고 내용을 전송함", + "poll_ended": "투표가 끝남" }, "post_status": { "new_status": "새 게시물 게시", - "account_not_locked_warning": "당신의 계정은 {0} 상태가 아닙니다. 누구나 당신을 팔로우 하고 팔로워 전용 게시물을 볼 수 있습니다.", + "account_not_locked_warning": "계정이 {0} 상태가 아닙니다. 누구나 당신을 팔로우 하고 팔로워 전용 게시물을 볼 수 있습니다.", "account_not_locked_warning_link": "잠김", "attachments_sensitive": "첨부물을 민감함으로 설정", "content_type": { "text/plain": "평문", "text/bbcode": "BBCode", - "text/markdown": "Markdown", + "text/markdown": "마크다운", "text/html": "HTML" }, - "content_warning": "주제 (필수 아님)", + "content_warning": "제목 (선택)", "default": "인천공항에 도착했습니다.", "direct_warning": "이 게시물을 멘션 된 사용자들에게만 보여집니다", - "posting": "게시", + "posting": "게시 중", "scope": { "direct": "다이렉트 - 멘션 된 사용자들에게만", "private": "팔로워 전용 - 팔로워들에게만", "public": "공개 - 공개 타임라인으로", - "unlisted": "비공개 - 공개 타임라인에 게시 안 함" + "unlisted": "비표시 - 공개 타임라인에는 안 보이게" }, - "preview_empty": "아무것도 없습니다", + "preview_empty": "비어있음", "preview": "미리보기", "scope_notice": { - "public": "이 글은 누구나 볼 수 있습니다" + "public": "누구나 볼 수 있습니다", + "private": "팔로워에게만 보여집니다", + "unlisted": "공개 타임라인이나 알려진 네트워크에는 보여지지 않습니다" }, - "media_description_error": "파일을 올리지 못하였습니다. 다시한번 시도하여 주십시오", - "empty_status_error": "글을 입력하십시오", - "media_description": "첨부파일 설명" + "media_description_error": "파일을 올리지 못했습니다, 다시 시도해 보세요", + "empty_status_error": "게시물이 비어 있습니다", + "media_description": "첨부파일 설명", + "direct_warning_to_all": "멘션한 모든 사용자에게 보여집니다.", + "edit_unsupported_warning": "Pleroma는 멘션이나 투표를 수정하는 기능을 지원하지 않습니다.", + "edit_status": "수정", + "edit_remote_warning": "수정 기능이 없는 다른 인스턴스에서는 수정한 사항이 반영되지 않을 수 있습니다.", + "post": "게시", + "direct_warning_to_first_only": "맨 앞에 멘션한 사용자들에게만 보여집니다." }, "registration": { "bio": "소개", "email": "이메일", - "fullname": "표시 되는 이름", - "password_confirm": "암호 확인", + "fullname": "표시될 이름", + "password_confirm": "패스워드 확인", "registration": "가입하기", "token": "초대 토큰", "captcha": "캡차", - "new_captcha": "이미지를 클릭해서 새로운 캡차", + "new_captcha": "이미지를 클릭해서 새로운 캡차 가져오기", "validations": { "username_required": "공백으로 둘 수 없습니다", "fullname_required": "공백으로 둘 수 없습니다", @@ -142,25 +178,32 @@ "password_confirmation_match": "패스워드와 일치해야 합니다" }, "fullname_placeholder": "예: 김례인", - "username_placeholder": "예: lain" + "username_placeholder": "예: lain", + "bio_placeholder": "예시\n안녕하세요, 례인입니다.\n일본 시외에서 애니메이션 아이돌을 하고 있습니다. Wired에서 절 보셨을 거예요.", + "bio_optional": "소개 (선택)", + "email_optional": "이메일 (선택)", + "reason": "가입하려는 이유", + "reason_placeholder": "이 인스턴스는 수동으로 가입을 승인하고 있습니다.\n왜 가입하고 싶은지 관리자에게 알려주세요.", + "register": "가입", + "email_language": "무슨 언어로 이메일을 받길 원하시나요?" }, "settings": { "attachmentRadius": "첨부물", "attachments": "첨부물", - "avatar": "아바타", - "avatarAltRadius": "아바타 (알림)", - "avatarRadius": "아바타", + "avatar": "프로필 사진", + "avatarAltRadius": "프로필 사진 (알림창)", + "avatarRadius": "프로필 사진", "background": "배경", "bio": "소개", "btnRadius": "버튼", "cBlue": "파랑 (답글, 팔로우)", "cGreen": "초록 (리트윗)", - "cOrange": "주황 (즐겨찾기)", + "cOrange": "주황 (관심글)", "cRed": "빨강 (취소)", - "change_password": "암호 바꾸기", - "change_password_error": "암호를 바꾸는 데 몇 가지 문제가 있습니다.", - "changed_password": "암호를 바꾸었습니다!", - "collapse_subject": "주제를 가진 게시물 접기", + "change_password": "패스워드 바꾸기", + "change_password_error": "패스워드를 바꾸는 데 문제가 있습니다.", + "changed_password": "패스워드가 바뀌었습니다!", + "collapse_subject": "제목이 있는 게시물 접기", "composing": "작성", "confirm_new_password": "새 패스워드 확인", "current_avatar": "현재 아바타", @@ -169,27 +212,27 @@ "data_import_export_tab": "데이터 불러오기 / 내보내기", "default_vis": "기본 공개 범위", "delete_account": "계정 삭제", - "delete_account_description": "데이터가 영구히 삭제되고 계정이 불활성화됩니다.", + "delete_account_description": "데이터가 영구히 삭제되고 계정이 비활성화됩니다.", "delete_account_error": "계정을 삭제하는데 문제가 있습니다. 계속 발생한다면 인스턴스 관리자에게 문의하세요.", - "delete_account_instructions": "계정 삭제를 확인하기 위해 아래에 패스워드 입력.", + "delete_account_instructions": "아래 패스워드를 입력하시면 계정이 삭제됩니다.", "export_theme": "프리셋 저장", "filtering": "필터링", - "filtering_explanation": "아래의 단어를 가진 게시물들은 뮤트 됩니다, 한 줄에 하나씩 적으세요", + "filtering_explanation": "이 단어를 가진 게시물들은 뮤트됩니다, 한 줄에 하나씩 적으세요", "follow_export": "팔로우 내보내기", - "follow_export_button": "팔로우 목록을 csv로 내보내기", + "follow_export_button": "팔로우 목록을 CSV 파일로 내보내기", "follow_export_processing": "진행 중입니다, 곧 다운로드 가능해 질 것입니다", "follow_import": "팔로우 불러오기", "follow_import_error": "팔로우 불러오기 실패", "follows_imported": "팔로우 목록을 불러왔습니다! 처리에는 시간이 걸립니다.", - "foreground": "전경", + "foreground": "표면", "general": "일반", "hide_attachments_in_convo": "대화의 첨부물 숨기기", "hide_attachments_in_tl": "타임라인의 첨부물 숨기기", "hide_isp": "인스턴스 전용 패널 숨기기", "preload_images": "이미지 미리 불러오기", - "hide_post_stats": "게시물 통계 숨기기 (즐겨찾기 수 등)", + "hide_post_stats": "게시물 통계 숨기기 (관심글 수 등)", "hide_user_stats": "사용자 통계 숨기기 (팔로워 수 등)", - "import_followers_from_a_csv_file": "csv 파일에서 팔로우 목록 불러오기", + "import_followers_from_a_csv_file": "CSV 파일에서 팔로우 목록 불러오기", "import_theme": "프리셋 불러오기", "inputRadius": "입력 칸", "checkboxRadius": "체크박스", @@ -197,58 +240,58 @@ "instance_default_simple": "(기본)", "interface": "인터페이스", "interfaceLanguage": "인터페이스 언어", - "invalid_theme_imported": "선택한 파일은 지원하는 플레로마 테마가 아닙니다. 아무런 변경도 일어나지 않았습니다.", + "invalid_theme_imported": "해당 파일은 지원되지 않는 Pleroma 테마입니다. 아무 일도 일어나지 않았습니다.", "limited_availability": "이 브라우저에서 사용 불가", "links": "링크", - "lock_account_description": "계정을 승인 된 팔로워들로 제한", + "lock_account_description": "팔로워를 승인해서 받도록 제한", "loop_video": "비디오 반복재생", - "loop_video_silent_only": "소리가 없는 비디오만 반복 재생 (마스토돈의 \"gifs\" 같은 것들)", + "loop_video_silent_only": "소리가 없는 비디오만 반복 재생 (마스토돈의 \"GIF\" 같은 것들)", "name": "이름", "name_bio": "이름 & 소개", - "new_password": "새 암호", - "notification_visibility": "보여 줄 알림 종류", + "new_password": "새 패스워드", + "notification_visibility": "보여질 알림 종류", "notification_visibility_follows": "팔로우", - "notification_visibility_likes": "좋아함", + "notification_visibility_likes": "관심글", "notification_visibility_mentions": "멘션", - "notification_visibility_repeats": "반복", + "notification_visibility_repeats": "리핏", "no_rich_text_description": "모든 게시물의 서식을 지우기", - "hide_follows_description": "내가 팔로우하는 사람을 표시하지 않음", - "hide_followers_description": "나를 따르는 사람을 숨기기", - "nsfw_clickthrough": "NSFW 이미지 \"클릭해서 보이기\"를 활성화", + "hide_follows_description": "팔로우 중인 사람 숨기기", + "hide_followers_description": "팔로워 숨기기", + "nsfw_clickthrough": "민감한 이미지를 숨기기", "oauth_tokens": "OAuth 토큰", "token": "토큰", "refresh_token": "토큰 새로 고침", - "valid_until": "까지 유효하다", + "valid_until": "만료일", "revoke_token": "취소", "panelRadius": "패널", - "pause_on_unfocused": "탭이 활성 상태가 아닐 때 스트리밍 멈추기", + "pause_on_unfocused": "탭이 포커스되지 않았을 땐 멈추기", "presets": "프리셋", "profile_background": "프로필 배경", "profile_banner": "프로필 배너", "profile_tab": "프로필", "radii_help": "인터페이스 모서리 둥글기 (픽셀 단위)", - "replies_in_timeline": "답글을 타임라인에", + "replies_in_timeline": "타임라인의 답글", "reply_visibility_all": "모든 답글 보기", - "reply_visibility_following": "나에게 직접 오는 답글이나 내가 팔로우 중인 사람에게서 오는 답글만 표시", - "reply_visibility_self": "나에게 직접 전송 된 답글만 보이기", - "saving_err": "설정 저장 실패", + "reply_visibility_following": "나에게 직접 오거나 내가 팔로우 중인 사람이 보낸 답글만 보기", + "reply_visibility_self": "나에게 직접 온 답글만 보기", + "saving_err": "설정을 저장하는 데 에러가 발생했습니다", "saving_ok": "설정 저장 됨", "security_tab": "보안", - "scope_copy": "답글을 달 때 공개 범위 따라가리 (다이렉트 메시지는 언제나 따라감)", - "set_new_avatar": "새 아바타 설정", + "scope_copy": "답글을 달 때 공개 범위 따라가기 (다이렉트 메시지는 언제나 따라감)", + "set_new_avatar": "새 프로필 사진 설정", "set_new_profile_background": "새 프로필 배경 설정", "set_new_profile_banner": "새 프로필 배너 설정", "settings": "설정", - "subject_input_always_show": "항상 주제 칸 보이기", - "subject_line_behavior": "답글을 달 때 주제 복사하기", - "subject_line_email": "이메일처럼: \"re: 주제\"", + "subject_input_always_show": "항상 제목 입력창 보이기", + "subject_line_behavior": "답글을 달 때 제목 복사하기", + "subject_line_email": "이메일처럼: \"re: 제목\"", "subject_line_mastodon": "마스토돈처럼: 그대로 복사", "subject_line_noop": "복사 안 함", - "stop_gifs": "GIF파일에 마우스를 올려서 재생", - "streaming": "최상단에 도달하면 자동으로 새 게시물 스트리밍", + "stop_gifs": "마우스를 올려서 GIF 재생", + "streaming": "최상단에 도달하면 알아서 새 게시물 가져오기", "text": "텍스트", "theme": "테마", - "theme_help": "16진수 색상코드(#rrggbb)를 사용해 색상 테마를 커스터마이즈.", + "theme_help": "16진수 색상코드(#rrggbb)를 사용해 색상을 조정하세요.", "theme_help_v2_1": "체크박스를 통해 몇몇 컴포넌트의 색상과 불투명도를 조절 가능, \"모두 지우기\" 버튼으로 덮어 씌운 것을 모두 취소.", "theme_help_v2_2": "몇몇 입력칸 밑의 아이콘은 전경/배경 대비 관련 표시등입니다, 마우스를 올려 자세한 정보를 볼 수 있습니다. 투명도 대비 표시등이 가장 최악의 경우를 나타낸다는 것을 유의하세요.", "tooltipRadius": "툴팁/경고", @@ -265,25 +308,42 @@ "keep_shadows": "그림자 유지", "keep_opacity": "불투명도 유지", "keep_roundness": "둥글기 유지", - "keep_fonts": "글자체 유지", + "keep_fonts": "글꼴 유지", "save_load_hint": "\"유지\" 옵션들은 다른 테마를 고르거나 불러 올 때 현재 설정 된 옵션들을 건드리지 않게 합니다, 테마를 내보내기 할 때도 이 옵션에 따라 저장합니다. 아무 것도 체크 되지 않았다면 모든 설정을 내보냅니다.", "reset": "초기화", "clear_all": "모두 지우기", - "clear_opacity": "불투명도 지우기" + "clear_opacity": "불투명도 지우기", + "help": { + "upgraded_from_v2": "PleromaFE가 업그레이드 되었기에, 테마가 기억하시던 것과 조금 다를 수 있습니다.", + "v2_imported": "불러온 파일은 이곳보다 이전 버전의 FE에서 만들어졌습니다. 호환성을 유지하겠지만 깨진 부분이 있을 수 있습니다.", + "migration_snapshot_ok": "혹시나 싶어서, 테마 스냅샷을 불러왔습니다. 테마 데이터를 불러와도 됩니다.", + "snapshot_source_mismatch": "버전이 충돌됩니다: 아마 FE가 롤백되고 다시 업데이트 되어서일 건데, 이전 버전 FE로 테마를 수정했다면 이전 버전 FE를 써보시는 게 좋고, 아니면 새 버전을 쓰세요.", + "future_version_imported": "불러온 파일은 이곳보다 새 버전의 FE에서 만들어졌습니다.", + "older_version_imported": "불러온 파일은 이곳보다 이전 버전의 FE에서 만들어졌습니다.", + "snapshot_present": "테마 스냅샷이 있어서, 모든 값이 덮어 씌워졌습니다. 직접 테마의 실제 데이터를 대신 불러와도 됩니다.", + "snapshot_missing": "파일에 스냅샷이 없어서 원래 보였던 것보다 다르게 보일 수 있습니다.", + "fe_upgraded": "버전 업데이트로 PleromaFE의 테마 엔진이 업그레이드 되었습니다.", + "fe_downgraded": "PleromaFE의 버전이 롤백되었습니다.", + "migration_napshot_gone": "뭔 일인진 모르겠지만 스냅샷이 없어서, 몇몇 개가 기억하신 것과 달리 보일 수 있습니다." + }, + "load_theme": "테마 불러오기", + "keep_as_is": "그대로 두기", + "use_snapshot": "이전 버전", + "use_source": "새 버전" }, "common": { "color": "색상", "opacity": "불투명도", "contrast": { - "hint": "대비율이 {ratio}입니다, 이것은 {context} {level}", + "hint": "색상 대비율이 {ratio}입니다, {context} {level}", "level": { - "aa": "AA등급 가이드라인에 부합합니다 (최소한도)", - "aaa": "AAA등급 가이드라인에 부합합니다 (권장)", - "bad": "아무런 가이드라인 등급에도 미치지 못합니다" + "aa": "접근성 가이드라인 AA등급을 충족합니다 (최소)", + "aaa": "접근성 가이드라인 AAA등급을 충족합니다 (권장)", + "bad": "접근성 가이드라인을 충족하지 못합니다" }, "context": { "18pt": "큰 (18pt 이상) 텍스트에 대해", - "text": "텍스트에 대해" + "text": "일반 텍스트에 대해" } } }, @@ -307,13 +367,23 @@ "faint_text": "흐려진 텍스트", "chat": { "border": "경계선", - "outgoing": "송신", - "incoming": "수신" + "outgoing": "보냄", + "incoming": "받음" }, "selectedMenu": "선택된 메뉴 요소", "selectedPost": "선택된 글", "icons": "아이콘", - "alert_warning": "경고" + "alert_warning": "경고", + "alert_neutral": "중립적", + "post": "게시물 / 유저 소개", + "popover": "툴팁, 메뉴, 프로필 카드", + "disabled": "비활성화", + "wallpaper": "배경사진", + "poll": "투표 그래프", + "highlight": "강조 요소", + "pressed": "눌렸을 때", + "toggled": "토글됨", + "tabs": "탭" }, "radii": { "_tab_label": "둥글기" @@ -344,23 +414,24 @@ "button": "버튼", "buttonHover": "버튼 (마우스 올렸을 때)", "buttonPressed": "버튼 (눌렸을 때)", - "buttonPressedHover": "Button (마우스 올림 + 눌림)", + "buttonPressedHover": "버튼 (마우스 올림 + 눌림)", "input": "입력칸" - } + }, + "hintV3": "그림자의 경우 {0} 표기법으로 다른 컬러 슬롯을 사용할 수 있습니다." }, "fonts": { - "_tab_label": "글자체", - "help": "인터페이스의 요소에 사용 될 글자체를 고르세요. \"커스텀\"은 시스템에 있는 폰트 이름을 정확히 입력해야 합니다.", + "_tab_label": "글꼴", + "help": "화면에 적용할 글꼴을 고르세요. \"직접 입력\"은 시스템에 있는 글꼴 이름을 정확히 입력해야 합니다.", "components": { "interface": "인터페이스", "input": "입력칸", "post": "게시물 텍스트", "postCode": "게시물의 고정폭 텍스트 (서식 있는 텍스트)" }, - "family": "글자체 이름", + "family": "글꼴 이름", "size": "크기 (px 단위)", "weight": "굵기", - "custom": "커스텀" + "custom": "직접 입력" }, "preview": { "header": "미리보기", @@ -371,8 +442,8 @@ "mono": "내용", "input": "인천공항에 도착했습니다.", "faint_link": "도움 되는 설명서", - "fine_print": "우리의 {0} 를 읽고 도움 되지 않는 것들을 배우자!", - "header_faint": "이건 괜찮아", + "fine_print": "우리의 {0}를 읽고 도움 되지 않는 것들을 배우자!", + "header_faint": "괜찮은 텍스트", "checkbox": "나는 약관을 대충 훑어보았습니다", "link": "작고 귀여운 링크" } @@ -381,44 +452,224 @@ "mfa": { "scan": { "secret_code": "키", - "title": "스캔" + "title": "스캔", + "desc": "2단계 인증 앱을 통해 QR 코드를 찍거나 키를 입력하세요:" }, "authentication_methods": "인증 방법", - "waiting_a_recovery_codes": "예비 코드를 수신하고 있습니다…", + "waiting_a_recovery_codes": "복구 코드를 가져오고 있습니다…", "recovery_codes": "복구 코드.", - "generate_new_recovery_codes": "새로운 복구 코드를 작성", - "title": "2단계인증", - "confirm_and_enable": "OTP 확인과 활성화", - "setup_otp": "OTP 설치", - "otp": "OTP" + "generate_new_recovery_codes": "새 복구 코드 생성", + "title": "2단계 인증", + "confirm_and_enable": "확인 & OTP 활성화", + "setup_otp": "OTP 설정", + "otp": "OTP", + "warning_of_generate_new_codes": "새 복구 코드를 생성하면, 이전 코드는 작동하지 않게 됩니다.", + "recovery_codes_warning": "복구 코드를 어딘가 안전한 곳에 적어 놓으세요 - 더 이상 이 코드를 보실 순 없습니다. 만약 2단계 인증 앱과 복구 코드 둘 다 접근할 수 없게 된다면 계정에 로그인할 수 없게 됩니다.", + "verify": { + "desc": "활성화하려면 2단계 인증 앱에서 받은 코드를 입력하세요:" + } }, "security": "보안", - "emoji_reactions_on_timeline": "이모지 반응을 타임라인으로 표시", - "avatar_size_instruction": "크기를 150x150 이상으로 설정할 것을 추장합니다.", + "emoji_reactions_on_timeline": "에모지 반응을 타임라인에 표시", + "avatar_size_instruction": "최소 150x150 픽셀보다 큰 사진을 업로드하시면 좋습니다.", "blocks_tab": "차단", "notification_setting_privacy": "보안", "user_mutes": "사용자", "notification_visibility_emoji_reactions": "반응", "profile_fields": { - "value": "내용" + "value": "내용", + "label": "프로필 추가정보", + "add_field": "필드 추가", + "name": "라벨" }, - "mutes_and_blocks": "침묵과 차단", - "chatMessageRadius": "챗 메시지", - "change_email": "메일주소 바꾸기", - "changed_email": "메일주소가 갱신되었습니다!", - "bot": "이 계정은 bot입니다", - "mutes_tab": "침묵", - "app_name": "앱 이름" + "mutes_and_blocks": "뮤트와 차단", + "chatMessageRadius": "채팅 메시지", + "change_email": "메일 주소 바꾸기", + "changed_email": "메일 주소가 바뀌었습니다!", + "bot": "이 계정은 자동 봇입니다", + "mutes_tab": "뮤트", + "app_name": "앱 이름", + "notification_setting_block_from_strangers": "팔로하지 않은 계정에서 보내는 알림 차단", + "autohide_floating_post_button": "알아서 새 게시물 버튼 숨기기 (모바일)", + "blocks_imported": "차단 목록을 가져왔습니다! 처리하는 데에 시간이 걸릴 수 있습니다.", + "mutes_imported": "뮤트 목록을 가져왔습니다! 처리하는 데에 시간이 걸릴 수 있습니다.", + "account_backup_description": "내 계정 정보와 게시물이 담긴 아카이브를 다운로드 받을 수 있지만, 아직 Pleroma로 다시 불러오는 기능은 지원하지 않습니다.", + "move_account_notes": "계정을 다른 곳으로 이사하려면, 이사 갈 계정으로 가셔서 별칭이 이 계정을 가리키도록 하세요.", + "hide_bot_indication": "게시물에서 봇 알림 숨기기", + "navbar_column_stretch": "상단 바를 컬럼 너비만큼 늘리기", + "show_admin_badge": "내 프로필에 \"관리자\" 배지 달기", + "sensitive_by_default": "게시물을 민감함으로 기본 설정", + "notification_mutes": "특정 사용자의 알림을 받지 않으려면, 뮤트를 사용하세요.", + "mention_link_fade_domain": "흐려진 도메인 (예: {'@'}foo{'@'}example.org 에서의 {'@'}example.org)", + "notification_blocks": "사용자를 차단하면 알림을 받지 않는데다 구독까지 취소하게 됩니다.", + "conversation_display_tree": "트리", + "save": "변경 사항을 저장", + "allow_following_move": "팔로우 중인 계정이 이사를 하면 자동으로 팔로우하기", + "expert_mode": "고급 설정 보기", + "setting_changed": "기본 설정과 다릅니다", + "setting_server_side": "이 설정은 계정과 묶여 있으며 연결된 모든 세션과 클라이언트에 영향을 줍니다", + "enter_current_password_to_confirm": "본인 확인을 위해 현재 패스워드를 입력하세요", + "post_look_feel": "게시물 모양새", + "mention_links": "멘션 링크", + "lists_navigation": "메뉴에 리스트 보이기", + "email_language": "서버로부터 이메일을 받을 언어", + "block_import": "차단 목록 가져오기", + "block_export_button": "차단 목록을 CSV 파일로 내보내기", + "block_import_error": "차단 목록을 가져오는 데에 문제가 발생했습니다", + "mute_export": "뮤트 목록 내보내기", + "mute_export_button": "뮤트 목록을 CSV 파일로 내보내기", + "mute_import": "뮤트 목록 가져오기", + "mute_import_error": "뮤트 목록을 가져오는 데에 문제가 발생했습니다", + "import_mutes_from_a_csv_file": "뮤트 목록을 CSV 파일에서 가져오기", + "account_backup": "계정 백업", + "account_backup_table_head": "백업", + "download_backup": "다운로드", + "backup_not_ready": "백업이 아직 준비되지 않았습니다.", + "remove_backup": "삭제", + "list_backups_error": "백업 리스트를 가져오는 데 에러가 발생했습니다: {error}", + "add_backup": "새 백업 만들기", + "added_backup": "새 백업 추가됨.", + "add_backup_error": "새 백업을 추가하는 데 에러가 발생했습니다: {error}", + "change_email_error": "메일 주소를 바꾸는 데 문제가 있습니다.", + "account_alias": "계정 별칭", + "always_show_post_button": "항상 떠다니는 게시물 작성 버튼 보기", + "mute_bot_posts": "봇 게시물 뮤트하기", + "hide_all_muted_posts": "뮤트한 게시물 숨기기", + "account_alias_table_head": "별칭", + "hide_list_aliases_error_action": "닫기", + "remove_alias": "이 별칭 삭제", + "new_alias_target": "새 별칭 추가 (예시. {example})", + "added_alias": "별칭이 추가되었습니다.", + "move_account": "계정 이사", + "move_account_target": "이사 갈 계정 (예시. {example})", + "moved_account": "계정을 이사했습니다.", + "discoverable": "검색 결과나 다른 서비스들에서 이 계정을 찾을 수 있도록 허용", + "pad_emoji": "에모지를 선택창에서 고를 때 띄어쓰기를 집어넣기", + "wordfilter": "단어 필터", + "word_filter_and_more": "단어 필터 그리고 더보기...", + "accent": "강조", + "hide_media_previews": "미디어 미리보기 숨기기", + "max_thumbnails": "게시물 하나 당 최대로 보여질 섬네일 개수 (비워두면 제한을 두지 않습니다)", + "hide_shoutbox": "인스턴스 외치기 숨기기", + "right_sidebar": "컬럼 순서 뒤집기", + "hide_wallpaper": "인스턴스 배경화면 가리기", + "use_one_click_nsfw": "민감한 첨부물을 클릭 한 번으로 열기", + "move_account_error": "계정을 이사하는 데 에러가 발생했습니다: {error}", + "hide_muted_posts": "뮤트한 사용자의 게시물 숨기기", + "hide_filtered_statuses": "필터된 모든 게시물 숨기기", + "hide_wordfiltered_statuses": "단어 필터된 게시물 숨기기", + "use_contain_fit": "첨부파일의 섬네일을 자르지 않음", + "hide_muted_threads": "뮤트한 스레드 숨기기", + "import_blocks_from_a_csv_file": "CSV 파일에서 차단 목록 불러오기", + "play_videos_in_modal": "팝업 프레임에서 비디오를 재생", + "file_export_import": { + "backup_restore": "설정 백업", + "backup_settings": "설정을 파일로 백업", + "backup_settings_theme": "설정과 테마를 파일로 백업", + "restore_settings": "파일에서 설정 복구하기", + "errors": { + "invalid_file": "해당 파일은 지원되지 않는 Pleroma 백업입니다. 아무 일도 일어나지 않았습니다.", + "file_too_new": "호환되지 않는 버전: {fileMajor}, 이 PleromaFE (설정 버전 {feMajor}) 가 너무 낡아서 처리할 수 없습니다", + "file_too_old": "호환되지 않는 버전: {fileMajor}, 파일 버전이 너무 낡아서 처리할 수 없습니다 (지원되는 최소 설정 버전 {feMajor})", + "file_slightly_new": "파일 마이너 버전이 달라서, 몇몇 설정들이 적용되지 않았을 수 있습니다" + } + }, + "account_privacy": "사생활 보안", + "new_email": "새 메일 주소", + "hide_favorites_description": "내 관심글을 보이지 않음 (알림은 갑니다)", + "hide_follows_count_description": "팔로우 중 숫자 숨기기", + "hide_followers_count_description": "팔로워 숫자 숨기기", + "no_mutes": "뮤트 없음", + "search_user_to_block": "차단할 사람 검색하기", + "search_user_to_mute": "뮤트할 사람 검색하기", + "posts": "게시물", + "notification_visibility_moves": "계정 이사", + "notification_visibility_polls": "참여한 투표가 끝남", + "no_blocks": "차단 없음", + "reply_visibility_self_short": "내 답글만 보기", + "reply_visibility_following_short": "팔로우 중인 사람들끼리의 답글 보기", + "user_profiles": "사용자 프로필", + "show_moderator_badge": "내 프로필에 \"중재자\" 배지 달기", + "type_domains_to_mute": "뮤트할 도메인 검색하기", + "disable_sticky_headers": "컬럼 헤더를 화면 상단에 고정하지 않음", + "auto_update": "알아서 새 게시물 가져오기", + "minimal_scopes_mode": "공개 범위 선택지 줄이기", + "reset_avatar": "프로필 사진 초기화", + "reset_avatar_confirm": "정말 프로필 사진을 초기화할까요?", + "reset_profile_background": "프로필 배경 초기화", + "reset_profile_banner": "프로필 배너 초기화", + "reset_banner_confirm": "정말 프로필 배너를 초기화할까요?", + "reset_background_confirm": "정말 프로필 배경을 초기화할까요?", + "useStreamingApi": "실시간으로 게시물과 알림 받기", + "use_websockets": "웹소켓 사용 (실시간 업데이트)", + "upload_a_photo": "사진 업로드", + "conversation_display": "대화 표시 모양", + "conversation_display_tree_quick": "트리 뷰", + "show_scrollbars": "측면 컬럼의 스크롤바 보기", + "conversation_other_replies_button_inside": "게시물 안에 놓기", + "notification_setting_hide_notification_contents": "푸시 알림에서 보낸 사람과 내용을 숨김", + "virtual_scrolling": "타임라인 렌더링 최적화", + "use_at_icon": "{'@'} 문자를 텍스트 대신 아이콘으로 표시", + "mention_link_display": "멘션에 링크 표시", + "mention_link_display_short": "항상 짧은 이름 사용 (예: {'@'}foo)", + "mention_link_display_full_for_remote": "다른 인스턴스 사용자만 이름 전부 보기 (예: {'@'}foo{'@'}example.org)", + "mention_link_display_full": "항상 이름 전부 보기 (예: {'@'}foo{'@'}example.org)", + "mention_link_use_tooltip": "멘션 링크를 누르면 사용자 카드 보기", + "mention_link_show_avatar": "링크 옆에 프로필 사진 보기", + "mention_link_bolden_you": "누가 날 멘션했을 때 멘션을 강조 표시", + "user_popover_avatar_action_zoom": "사진 키우기", + "greentext": "밈 화살표", + "show_yous": "\"(당신)\" 보이기", + "notification_setting_filters": "필터", + "more_settings": "추가 설정", + "user_popover_avatar_action_open": "프로필 열기", + "version": { + "frontend_version": "프론트엔드 버전", + "title": "버전", + "backend_version": "백엔드 버전" + }, + "fun": "즐겁다", + "domain_mutes": "도메인", + "third_column_mode": "공간이 충분하면, 세 번째 컬럼 채우기", + "third_column_mode_none": "세 번째 컬럼 안 보기", + "third_column_mode_notifications": "알림 컬럼", + "third_column_mode_postform": "게시물 편집창과 내비게이션", + "columns": "컬럼", + "column_sizes": "컬럼 크기", + "column_sizes_sidebar": "사이드바", + "column_sizes_content": "내용", + "column_sizes_notifs": "알림", + "tree_advanced": "트리 뷰에서 더 유연한 탐색을 허용", + "tree_fade_ancestors": "현재 게시물보다 상단의 게시물들을 흐린 텍스트로 표시", + "conversation_display_linear": "선형", + "conversation_display_linear_quick": "선형 뷰", + "conversation_other_replies_button": "\"답글 더 보기\" 버튼을", + "conversation_other_replies_button_below": "게시물 아래에 놓기", + "max_depth_in_thread": "기본적으로 보일 최대 깊이", + "user_popover_avatar_action": "프로필 카드의 사진 클릭 시", + "user_popover_avatar_action_close": "카드 닫기", + "user_popover_avatar_overlay": "프로필 카드를 프로필 사진 위에 띄우기", + "post_status_content_type": "게시물 내용 형식", + "list_aliases_error": "별칭을 가져오는 중 에러 발생: {error}", + "add_alias_error": "별칭을 추가하는 중 에러 발생: {error}", + "mention_link_show_avatar_quick": "멘션 옆에 유저 프로필 사진을 보임" }, "timeline": { "collapse": "접기", "conversation": "대화", "error_fetching": "업데이트 불러오기 실패", - "load_older": "더 오래 된 게시물 불러오기", - "no_retweet_hint": "팔로워 전용, 다이렉트 메시지는 반복할 수 없습니다", - "repeated": "반복 됨", - "show_new": "새로운 것 보기", - "up_to_date": "최신 상태" + "load_older": "이전 게시물 불러오기", + "no_retweet_hint": "팔로워 전용 게시물과 다이렉트 메시지는 리핏할 수 없습니다", + "repeated": "리핏함", + "show_new": "새 게시물 보기", + "up_to_date": "최신", + "error": "타임라인을 가져오지 못했습니다: {0}", + "reload": "새로고침", + "no_statuses": "게시물 없음", + "no_more_statuses": "새 게시물 없음", + "socket_reconnected": "실시간 연결 됨", + "socket_broke": "실시간 연결이 끊어짐: CloseEvent 코드 {0}", + "quick_filter_settings": "빠른 필터 설정" }, "user_card": { "approve": "승인", @@ -426,22 +677,70 @@ "blocked": "차단 됨!", "deny": "거부", "follow": "팔로우", - "follow_sent": "요청 보내짐!", + "follow_sent": "요청 보냄!", "follow_progress": "요청 중…", - "follow_unfollow": "팔로우 중지", + "follow_unfollow": "언팔로우", "followees": "팔로우 중", "followers": "팔로워", "following": "팔로우 중!", - "follows_you": "당신을 팔로우 합니다!", + "follows_you": "나를 팔로우 합니다!", "its_you": "당신입니다!", - "mute": "침묵", - "muted": "침묵 됨", - "per_day": "/ 하루", - "remote_follow": "원격 팔로우", - "statuses": "게시물" + "mute": "뮤트", + "muted": "뮤트 됨", + "per_day": "개 / 일", + "remote_follow": "다른 인스턴스에서 팔로우", + "statuses": "게시물", + "unmute_progress": "뮤트 해제 중…", + "unblock_progress": "차단 해제 중…", + "admin_menu": { + "revoke_moderator": "중재자 탄핵", + "sandbox": "게시물 공개 범위를 팔로워 전용으로 강제", + "disable_any_subscription": "누구도 팔로우를 못하도록 막기", + "delete_user_data_and_deactivate_confirmation": "영구적으로 이 계정의 데이터가 삭제되고 비활성화 됩니다. 정말로 괜찮겠습니까?", + "moderation": "관리", + "grant_admin": "관리자로 임명", + "grant_moderator": "중재자로 임명", + "disable_remote_subscription": "다른 인스턴스에서 팔로우하지 못하도록 막기", + "activate_account": "계정 활성화", + "deactivate_account": "계정 비활성화", + "delete_account": "계정 삭제", + "force_nsfw": "모든 게시물을 민감한 내용으로 표시", + "strip_media": "게시물에서 미디어 제거", + "revoke_admin": "관리자 탄핵", + "force_unlisted": "게시물 공개 범위를 비표시로 강제", + "quarantine": "연합 타임라인에서 사용자 게시물 비허용", + "delete_user": "사용자 삭제" + }, + "deactivated": "비활성화됨", + "edit_profile": "프로필 편집", + "favorites": "관심글", + "follow_cancel": "팔로우 요청 취소", + "unmute": "뮤트 해제", + "mute_progress": "뮤트 중…", + "hidden": "숨겨짐", + "media": "미디어", + "mention": "멘션", + "message": "메시지", + "remove_follower": "팔로워 삭제", + "report": "신고", + "subscribe": "구독", + "unsubscribe": "구독 해제", + "unblock": "차단 해제", + "block_progress": "차단 중…", + "hide_repeats": "리핏 숨기기", + "show_repeats": "리핏 보기", + "bot": "봇", + "highlight": { + "disabled": "강조 표시 없음", + "striped": "줄무늬 배경", + "solid": "단색 배경", + "side": "옆트임" + } }, "user_profile": { - "timeline_title": "사용자 타임라인" + "timeline_title": "사용자 타임라인", + "profile_does_not_exist": "죄송하지만, 이 프로필은 존재하지 않습니다.", + "profile_loading_error": "죄송하지만, 프로필을 불러오는 데 에러가 발생했습니다." }, "who_to_follow": { "more": "더 보기", @@ -449,38 +748,60 @@ }, "tool_tip": { "media_upload": "미디어 업로드", - "repeat": "반복", + "repeat": "리핏", "reply": "답글", - "favorite": "즐겨찾기", - "user_settings": "사용자 설정" + "favorite": "관심글", + "user_settings": "사용자 설정", + "add_reaction": "반응 추가", + "accept_follow_request": "팔로우 요청 승인", + "reject_follow_request": "팔로우 요청 거절", + "bookmark": "북마크" }, "upload": { "error": { "base": "업로드 실패.", "file_too_big": "파일이 너무 커요 [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]", - "default": "잠시 후에 다시 시도해 보세요" + "default": "잠시 후에 다시 시도해 보세요", + "message": "업로드 실패: {0}" }, "file_size_units": { - "B": "바이트", - "KiB": "키비바이트", - "MiB": "메비바이트", - "GiB": "기비바이트", - "TiB": "테비바이트" + "B": "B", + "KiB": "KiB", + "MiB": "MiB", + "GiB": "GiB", + "TiB": "TiB" } }, "interactions": { "follows": "새 팔로워", - "favs_repeats": "반복과 즐겨찾기", - "moves": "계정 통합" + "favs_repeats": "리핏과 관심", + "moves": "계정 이동", + "emoji_reactions": "에모지 반응", + "reports": "신고", + "load_older": "이전 반응 불러오기" }, "emoji": { - "load_all": "전체 {emojiAmount} 이모지 불러오기", - "unicode": "Unicode 이모지", - "custom": "전용 이모지", - "add_emoji": "이모지 넣기", - "search_emoji": "이모지 검색", - "emoji": "이모지", - "stickers": "스티커" + "load_all": "전체 {emojiAmount}개의 에모지 불러오기", + "unicode": "Unicode 에모지", + "custom": "전용 에모지", + "add_emoji": "에모지 넣기", + "search_emoji": "에모지 검색", + "emoji": "에모지", + "stickers": "스티커", + "load_all_hint": "첫 {saneAmount}개의 에모지를 불러왔습니다, 에모지를 전부 불러오면 성능 저하가 있을 수 있습니다.", + "unicode_groups": { + "people-and-body": "사람 & 몸", + "smileys-and-emotion": "웃는 얼굴 & 감정", + "travel-and-places": "여행 & 장소", + "activities": "활동", + "animals-and-nature": "동물 & 자연", + "flags": "깃발", + "food-and-drink": "음식 & 음료", + "objects": "사물", + "symbols": "기호" + }, + "keep_open": "열린 채로 두기", + "regional_indicator": "지역 표시기 {letter}" }, "polls": { "add_poll": "투표를 추가", @@ -491,11 +812,18 @@ "votes_count": "{count} 표 | {count} 표", "people_voted_count": "{count} 명 투표 | {count} 명 투표", "option": "선택지", - "add_option": "선택지 추가" + "add_option": "선택지 추가", + "expired": "투표는 {0} 전에 마감되었습니다", + "expires_in": "투표는 {0}에 마감됩니다", + "single_choice": "하나만 선택", + "multiple_choices": "여러 개 선택", + "not_enough_options": "선택지가 너무 적습니다" }, "media_modal": { "next": "다음", - "previous": "이전" + "previous": "이전", + "counter": "{current} / {total}", + "hide": "미디어 뷰어 닫기" }, "importer": { "error": "이 파일을 가져올 때 오류가 발생하였습니다.", @@ -509,14 +837,14 @@ "crop_picture": "사진 자르기" }, "exporter": { - "processing": "처리중입니다, 처리가 끝나면 파일을 다운로드하라는 지시가 있겠습니다", + "processing": "처리중입니다, 곧 파일을 다운로드할 수 있습니다", "export": "내보내기" }, "domain_mute_card": { - "unmute_progress": "침묵을 해제중…", - "unmute": "침묵 해제", - "mute_progress": "침묵으로 설정중…", - "mute": "침묵" + "unmute_progress": "뮤트 해제 중…", + "unmute": "뮤트 해제", + "mute_progress": "뮤트 중…", + "mute": "뮤트" }, "about": { "staff": "운영자", @@ -534,21 +862,25 @@ "accept_desc": "이 인스턴스에서는 아래의 인스턴스로부터 보내온 투고만이 접수됩니다:", "reject": "거부", "accept": "허가", - "simple_policies": "인스턴스 특유의 폴리시" + "simple_policies": "인스턴스 특유의 정책", + "instance": "인스턴스", + "reason": "사유", + "not_applicable": "없음" }, - "mrf_policies": "사용되는 MRF 폴리시", + "mrf_policies": "사용되는 MRF 정책", "keyword": { "is_replaced_by": "→", "replace": "바꾸기", "reject": "거부", "ftl_removal": "\"알려진 모든 네트워크\" 타임라인에서 제외", - "keyword_policies": "단어 폴리시" + "keyword_policies": "단어 정책" }, - "federation": "연합" + "federation": "연합", + "mrf_policies_desc": "MRF 정책은 이 인스턴스의 페더레이션 동작을 제어하고 있습니다. 적용되고 있는 정책은 다음과 같습니다:" } }, "shoutbox": { - "title": "Shoutbox" + "title": "외치기" }, "time": { "years_short": "{0} 년", @@ -563,8 +895,8 @@ "second_short": "{0} 초", "seconds": "{0} 초", "second": "{0} 초", - "now_short": "방금", - "now": "방끔", + "now_short": "지금", + "now": "방금", "months_short": "{0} 달 전", "month_short": "{0} 달 전", "months": "{0} 달 전", @@ -581,13 +913,205 @@ "days_short": "{0} 일", "day_short": "{0} 일", "days": "{0} 일", - "day": "{0} 일" + "day": "{0} 일", + "unit": { + "weeks": "{0}주 | {0}주", + "minutes": "{0}분 | {0}분", + "seconds": "{0}초 | {0}초", + "seconds_short": "{0}초", + "weeks_short": "{0}주", + "years": "{0}년 | {0}년", + "years_short": "{0}년", + "days": "{0}일 | {0}일", + "days_short": "{0}일", + "hours": "{0}시간 | {0}시간", + "hours_short": "{0}시간", + "minutes_short": "{0}분", + "months": "{0}달 | {0}달", + "months_short": "{0}달" + }, + "in_future": "{0} 후" }, "remote_user_resolver": { "error": "찾을 수 없습니다.", - "searching_for": "검색중" + "searching_for": "검색:", + "remote_user_resolver": "다른 인스턴스 사용자 안내기" }, "selectable_list": { "select_all": "모두 선택" + }, + "lists": { + "title": "리스트 제목", + "search": "사용자 검색하기", + "lists": "리스트", + "new": "리스트 만들기", + "create": "만들기", + "delete": "리스트 삭제", + "following_only": "팔로우 중인 사람들만", + "manage_lists": "리스트 관리", + "manage_members": "멤버 관리", + "remove_from_list": "리스트에서 제거", + "add_to_list": "리스트에 추가", + "is_in_list": "리스트에 이미 있음", + "editing_list": "{listTitle} 리스트 편집", + "update_title": "제목 저장", + "really_delete": "리스트를 삭제하시겠어요?", + "save": "변경 사항을 저장", + "creating_list": "새 리스트 만들기", + "add_members": "사용자 추가", + "error": "리스트를 조작하는 데 오류가 발생했습니다: {0}" + }, + "search": { + "no_more_results": "결과 더 없음", + "load_more": "결과 더 불러오기", + "people": "사람", + "hashtags": "해시태그", + "person_talking": "{count}명이 말하는 중", + "people_talking": "{count}명이 말하는 중", + "no_results": "결과 없음" + }, + "password_reset": { + "forgot_password": "패스워드를 잊으셨나요?", + "password_reset": "패스워드 재설정", + "placeholder": "이메일 주소 또는 사용자 이름", + "password_reset_required_but_mailer_is_disabled": "패스워드 초기화를 하셔야 하지만, 못 하게 막혀 있습니다. 인스턴스 관리자에게 문의해주세요.", + "check_email": "패스워드 초기화를 위해 이메일을 확인해주세요.", + "return_home": "홈으로 돌아가기", + "password_reset_required": "로그인하려면 패스워드를 초기화해야 합니다.", + "password_reset_disabled": "패스워드 초기화를 못 하게 되어 있습니다. 인스턴스 관리자에게 문의해주세요.", + "instruction": "이메일 주소 또는 사용자 이름을 입력하세요. 패스워드 초기화 링크를 메일로 보내드립니다.", + "too_many_requests": "너무 많은 시도를 했습니다, 나중에 다시 해주세요." + }, + "chats": { + "you": "당신:", + "delete": "삭제", + "new": "새 채팅", + "chats": "채팅", + "empty_message_error": "메시지가 비어 있습니다", + "more": "더 보기", + "error_loading_chat": "왜인진 모르겠는데 채팅을 불러오지 못했습니다.", + "error_sending_message": "왜인진 모르겠는데 메시지를 전송하지 못했습니다.", + "delete_confirm": "이 메시지를 정말 지울까요?", + "empty_chat_list_placeholder": "채팅이 없네요. 새 채팅을 시작해보세요!", + "message_user": "{nickname}에게 메시지" + }, + "file_type": { + "audio": "오디오", + "video": "영상", + "image": "사진", + "file": "파일" + }, + "display_date": { + "today": "오늘" + }, + "update": { + "big_update_title": "양해해주세요", + "update_bugs_gitlab": "Pleroma GitLab", + "update_changelog_here": "변경 내역", + "update_changelog": "무엇이 바뀌었는지 자세히 알아보시려면, {theFullChangelog}을 참조하세요.", + "big_update_content": "저희가 한동안 릴리즈를 안 해서, 익숙하셨던 생김새나 경험과 많이 달라졌을 수 있습니다.", + "update_bugs": "저희가 비록 테스트를 많이 하고 직접 개발 버전을 쓰기도 하지만, 많이 바꾸기도 했고, 몇몇 가지 놓친 점들이 있을 터이니, 사용하면서 불편한 점이나 문제는 {pleromaGitlab}에 제보해주시면 감사하겠습니다. 저희는 겪으신 문제점이나 Pleroma와 Pleroma-FE에 대한 피드백과 제안을 환영합니다." + }, + "unicode_domain_indicator": { + "tooltip": "이 도메인은 아스키 문자가 아닌 문자를 포함하고 있습니다." + }, + "status": { + "mute_conversation": "대화 뮤트", + "thread_muted_and_words": ", 단어 포함:", + "unpin": "프로필에서 고정 해제", + "replies_list_with_others": "답글 (+{numReplies}개): | 답글 (+{numReplies}개):", + "show_attachment_in_modal": "미디어 모달에서 보기", + "thread_hide": "이 스레드 숨기기", + "show_attachment_description": "설명 미리보기 (첨부물을 열어서 전체 설명 보기)", + "thread_show_full": "이 스레드를 전부 들춰보기 (총 {numStatus}개 있음, 최대 깊이 {depth}) | 이 스레드를 전부 들춰보기 (총 {numStatus}개 있음, 최대 깊이 {depth})", + "thread_follow": "이 스레드의 나머지 부분 보기 (총 {numStatus}개) | 이 스레드의 나머지 부분 보기 (총 {numStatus}개)", + "status_history": "게시물 이력", + "show_all_conversation": "전체 대화 보기 ({numStatus}개 더 있음) | 전체 대화 보기 ({numStatus}개 더 있음)", + "repeats": "리핏", + "delete": "삭제", + "edit": "수정", + "favorites": "관심글", + "edited_at": "({time}에 마지막으로 수정됨)", + "pin": "프로필에 고정", + "pinned": "고정됨", + "bookmark": "북마크", + "unbookmark": "북마크 해제", + "delete_confirm": "정말 지우시겠어요?", + "reply_to": "답글", + "mentions": "멘션", + "replies_list": "답글:", + "unmute_conversation": "대화 뮤트 해제", + "thread_muted": "스레드 뮤트됨", + "status_unavailable": "게시물 접근 불가", + "copy_link": "게시물 링크 복사", + "external_source": "원본 페이지", + "show_full_subject": "전체 제목 보기", + "hide_full_subject": "전체 제목 숨기기", + "show_content": "내용 보기", + "hide_content": "내용 숨기기", + "status_deleted": "지워진 게시물입니다", + "nsfw": "민감한 내용", + "expand": "펼치기", + "you": "(당신)", + "plus_more": "+{number}개 더 있음", + "many_attachments": "{number}개의 첨부물을 가짐", + "show_all_attachments": "첨부물 전부 보이기", + "hide_attachment": "첨부물 숨기기", + "collapse_attachments": "첨부물 접기", + "remove_attachment": "첨부물 지우기", + "attachment_stop_flash": "플래시 플레이어 정지", + "move_up": "첨부물 왼쪽으로 밀기", + "move_down": "첨부물 오른쪽으로 밀기", + "open_gallery": "갤러리 열기", + "thread_show": "이 스레드 보이기", + "thread_show_full_with_icon": "{icon} {text}", + "thread_follow_with_icon": "{icon} {text}", + "ancestor_follow_with_icon": "{icon} {text}", + "show_all_conversation_with_icon": "{icon} {text}", + "ancestor_follow": "이 게시물 아래 {numReplies}개 답글 더 보기 | 이 게시물 아래 {numReplies}개 답글 더 보기", + "show_only_conversation_under_this": "이 게시물의 답글만 보기" + }, + "errors": { + "storage_unavailable": "Pleroma가 브라우저 저장소에 접근할 수 없습니다. 로그인이 풀리거나 로컬 설정이 초기화 되는 등 예상치 못한 문제를 겪을 수 있습니다. 쿠키를 활성화 해보세요." + }, + "report": { + "reporter": "신고자:", + "reported_statuses": "신고된 게시물:", + "notes": "기타:", + "state": "상태:", + "state_open": "열림", + "state_closed": "닫힘", + "reported_user": "신고된 사용자:", + "state_resolved": "해결됨" + }, + "user_reporting": { + "title": "{0} 신고", + "add_comment_description": "이 신고서는 내 인스턴스의 중재자에게 전달됩니다. 왜 이 계정을 신고하려는지 좀 더 자세히 알려주세요:", + "additional_comments": "추가 설명", + "forward_description": "이 계정은 다른 서버에 있는 계정입니다. 그쪽으로도 신고를 보낼까요?", + "forward_to": "{0}로 전달하기", + "submit": "전송", + "generic_error": "요청을 처리하는 중 오류가 발생했습니다." + }, + "announcements": { + "end_time_prompt": "끝나는 시각: ", + "page_header": "공지사항", + "title": "공지사항", + "mark_as_read_action": "읽음으로 표시", + "post_form_header": "공지사항 작성", + "post_placeholder": "공지사항 내용을 작성하세요...", + "post_error": "오류: {error}", + "close_error": "닫기", + "delete_action": "삭제", + "post_action": "게시", + "start_time_prompt": "시작 시각: ", + "all_day_prompt": "온종일 있는 이벤트입니다", + "published_time_display": "{time}에 게시함", + "start_time_display": "{time}에 시작함", + "end_time_display": "{time}에 끝남", + "edit_action": "편집", + "submit_edit_action": "수정본 반영", + "cancel_edit_action": "취소", + "inactive_message": "이 공지사항은 비활성화 되었습니다" } } diff --git a/src/i18n/languages.js b/src/i18n/languages.js @@ -0,0 +1,53 @@ + +const languages = [ + 'ar', + 'ca', + 'cs', + 'de', + 'eo', + 'en', + 'es', + 'et', + 'eu', + 'fi', + 'fr', + 'ga', + 'he', + 'hu', + 'it', + 'ja', + 'ja_easy', + 'ko', + 'nb', + 'nl', + 'oc', + 'pl', + 'pt', + 'ro', + 'ru', + 'sk', + 'te', + 'uk', + 'zh', + 'zh_Hant' +] + +const specialJsonName = { + ja: 'ja_pedantic' +} + +const langCodeToJsonName = (code) => specialJsonName[code] || code + +const langCodeToCldrName = (code) => code + +const ensureFinalFallback = codes => { + const codeList = Array.isArray(codes) ? codes : [codes] + return codeList.includes('en') ? codeList : codeList.concat(['en']) +} + +module.exports = { + languages, + langCodeToJsonName, + langCodeToCldrName, + ensureFinalFallback +} diff --git a/src/i18n/messages.js b/src/i18n/messages.js @@ -7,46 +7,27 @@ // sed -i -e "s/'//gm" -e 's/"/\\"/gm' -re 's/^( +)(.+?): ((.+?))?(,?)(\{?)$/\1"\2": "\4"/gm' -e 's/\"\{\"/{/g' -e 's/,"$/",/g' file.json // There's only problem that apostrophe character ' gets replaced by \\ so you have to fix it manually, sorry. -const loaders = { - ar: () => import('./ar.json'), - ca: () => import('./ca.json'), - cs: () => import('./cs.json'), - de: () => import('./de.json'), - eo: () => import('./eo.json'), - es: () => import('./es.json'), - et: () => import('./et.json'), - eu: () => import('./eu.json'), - fi: () => import('./fi.json'), - fr: () => import('./fr.json'), - ga: () => import('./ga.json'), - he: () => import('./he.json'), - hu: () => import('./hu.json'), - it: () => import('./it.json'), - ja: () => import('./ja_pedantic.json'), - ja_easy: () => import('./ja_easy.json'), - ko: () => import('./ko.json'), - nb: () => import('./nb.json'), - nl: () => import('./nl.json'), - oc: () => import('./oc.json'), - pl: () => import('./pl.json'), - pt: () => import('./pt.json'), - ro: () => import('./ro.json'), - ru: () => import('./ru.json'), - te: () => import('./te.json'), - uk: () => import('./uk.json'), - zh: () => import('./zh.json'), - zh_Hant: () => import('./zh_Hant.json') +import { languages, langCodeToJsonName } from './languages.js' + +const hasLanguageFile = (code) => languages.includes(code) + +const loadLanguageFile = (code) => { + return import( + /* webpackInclude: /\.json$/ */ + /* webpackChunkName: "i18n/[request]" */ + `./${langCodeToJsonName(code)}.json` + ) } const messages = { - languages: ['en', ...Object.keys(loaders)], + languages, default: { - en: require('./en.json') + en: require('./en.json').default }, setLanguage: async (i18n, language) => { - if (loaders[language]) { - let messages = await loaders[language]() - i18n.setLocaleMessage(language, messages) + if (hasLanguageFile(language)) { + const messages = await loadLanguageFile(language) + i18n.setLocaleMessage(language, messages.default) } i18n.locale = language } diff --git a/src/i18n/nb.json b/src/i18n/nb.json @@ -553,8 +553,7 @@ "disable_remote_subscription": "Fjern mulighet til å følge brukeren fra andre instanser", "disable_any_subscription": "Fjern mulighet til å følge brukeren", "quarantine": "Gjør at statuser fra brukeren ikke kan sendes til andre instanser", - "delete_user": "Slett bruker", - "delete_user_confirmation": "Er du helt sikker? Denne handlingen kan ikke omgjøres." + "delete_user": "Slett bruker" } }, "user_profile": { diff --git a/src/i18n/nl.json b/src/i18n/nl.json @@ -8,10 +8,11 @@ "media_proxy": "Mediaproxy", "scope_options": "Zichtbaarheidsopties", "text_limit": "Tekstlimiet", - "title": "Kenmerken", + "title": "Functies", "who_to_follow": "Wie te volgen", "upload_limit": "Upload limiet", - "pleroma_chat_messages": "Pleroma Chat" + "pleroma_chat_messages": "Pleroma Chat", + "shout": "Shoutbox" }, "finder": { "error_fetching_user": "Fout tijdens ophalen gebruiker", @@ -39,6 +40,15 @@ "role": { "moderator": "Moderator", "admin": "Beheerder" + }, + "flash_content": "Klik om Flash-content te laten zien met Ruffle (Experimenteel, werkt mogelijk niet).", + "flash_security": "Let op: Flash-inhoud is niet gescreend en kan malware bevatten.", + "flash_fail": "Laden van Flash-content is mislukt, zie console voor details.", + "scope_in_timeline": { + "direct": "Privé", + "private": "Alleen-volgers", + "public": "Openbaar", + "unlisted": "Niet-openbaar" } }, "login": { @@ -60,7 +70,7 @@ } }, "nav": { - "about": "Over", + "about": "Over ons", "back": "Terug", "chat": "Lokale Chat", "friend_requests": "Volgverzoeken", @@ -68,7 +78,7 @@ "dms": "Privéberichten", "public_tl": "Openbare tijdlijn", "timeline": "Tijdlijn", - "twkn": "Bekende Netwerk", + "twkn": "Globale Netwerk", "user_search": "Gebruiker Zoeken", "who_to_follow": "Wie te volgen", "preferences": "Voorkeuren", @@ -81,22 +91,23 @@ "bookmarks": "Bladwijzers" }, "notifications": { - "broken_favorite": "Onbekende status, aan het zoeken…", - "favorited_you": "vond je status leuk", + "broken_favorite": "Onbekend bericht, aan het zoeken…", + "favorited_you": "vond je bericht leuk", "followed_you": "volgt jou", "load_older": "Oudere meldingen laden", "notifications": "Meldingen", "read": "Gelezen!", - "repeated_you": "herhaalde je status", + "repeated_you": "herhaalde je bericht", "no_more_notifications": "Geen meldingen meer", "migrated_to": "is gemigreerd naar", "follow_request": "wil je volgen", "reacted_with": "reageerde met {0}", - "error": "Fout bij ophalen van meldingen: {0}" + "error": "Fout bij ophalen van meldingen: {0}", + "poll_ended": "peiling is beëindigd" }, "post_status": { - "new_status": "Nieuwe status plaatsen", - "account_not_locked_warning": "Je account is niet {0}. Iedereen kan je volgen om je alleen-volgers-berichten te lezen.", + "new_status": "Nieuw bericht plaatsen", + "account_not_locked_warning": "Je account is niet {0}. Iedereen kan je volgen om je alleen-volgersberichten te lezen.", "account_not_locked_warning_link": "gesloten", "attachments_sensitive": "Bijlagen als gevoelig markeren", "content_type": { @@ -108,10 +119,10 @@ "content_warning": "Onderwerp (optioneel)", "default": "Tijd voor anime!", "direct_warning": "Deze post zal enkel zichtbaar zijn voor de personen die genoemd zijn.", - "posting": "Plaatsen", + "posting": "Aan het plaatsen", "scope": { "direct": "Privé - bericht enkel naar vermelde gebruikers sturen", - "private": "Enkel volgers - bericht enkel naar volgers sturen", + "private": "Alleen-volgers - bericht is enkel leesbaar voor volgers", "public": "Openbaar - bericht op openbare tijdlijnen plaatsen", "unlisted": "Niet vermelden - niet tonen op openbare tijdlijnen" }, @@ -119,11 +130,11 @@ "direct_warning_to_first_only": "Dit bericht zal alleen zichtbaar zijn voor de vermelde gebruikers aan het begin van het bericht.", "scope_notice": { "public": "Dit bericht zal voor iedereen zichtbaar zijn", - "unlisted": "Dit bericht zal niet zichtbaar zijn in de Openbare Tijdlijn en Het Geheel Bekende Netwerk", + "unlisted": "Dit bericht zal niet zichtbaar zijn in de Openbare Tijdlijn en Het Globale Netwerk", "private": "Dit bericht zal voor alleen je volgers zichtbaar zijn" }, - "post": "Bericht", - "empty_status_error": "Kan geen lege status zonder bijlagen plaatsen", + "post": "Plaatsen", + "empty_status_error": "Kan geen leeg bericht zonder bijlagen plaatsen", "preview_empty": "Leeg", "preview": "Voorbeeld", "media_description": "Mediaomschrijving", @@ -149,13 +160,14 @@ "username_placeholder": "bijv. lain", "fullname_placeholder": "bijv. Lain Iwakura", "bio_placeholder": "bijv.\nHallo, ik ben Lain.\nIk ben een animemeisje woonachtig in een buitenwijk in Japan. Je kent me misschien van the Wired.", - "reason_placeholder": "Deze instantie keurt registraties handmatig goed.\nLaat de beheerder weten waarom je wilt registreren.", + "reason_placeholder": "Deze instantie keurt registraties handmatig goed.\nLaat de beheerder weten waarom je je wilt registreren.", "reason": "Reden voor registratie", - "register": "Registreren" + "register": "Registreren", + "email_language": "In welke taal wil je e-mails ontvangen van de server?" }, "settings": { - "attachmentRadius": "Bijlages", - "attachments": "Bijlages", + "attachmentRadius": "Bijlagen", + "attachments": "Bijlagen", "avatar": "Avatar", "avatarAltRadius": "Avatars (meldingen)", "avatarRadius": "Avatars", @@ -169,7 +181,7 @@ "change_password": "Wachtwoord wijzigen", "change_password_error": "Er is een fout opgetreden bij het wijzigen van je wachtwoord.", "changed_password": "Wachtwoord succesvol gewijzigd!", - "collapse_subject": "Klap berichten met een onderwerp in", + "collapse_subject": "Berichten met een onderwerp inklappen", "composing": "Opstellen", "confirm_new_password": "Nieuw wachtwoord bevestigen", "current_avatar": "Je huidige avatar", @@ -181,9 +193,9 @@ "delete_account_description": "Permanent je gegevens verwijderen en account deactiveren.", "delete_account_error": "Er is een fout opgetreden bij het verwijderen van je account. Indien dit probleem zich voor blijft doen, neem dan contact op met de beheerder van deze instantie.", "delete_account_instructions": "Voer je wachtwoord in het onderstaande invoerveld in om het verwijderen van je account te bevestigen.", - "export_theme": "Voorinstelling opslaan", + "export_theme": "Preset opslaan", "filtering": "Filtering", - "filtering_explanation": "Alle statussen die deze woorden bevatten worden genegeerd, één filter per regel", + "filtering_explanation": "Alle berichten die deze woorden bevatten worden genegeerd, één filter per regel", "follow_export": "Volgers exporteren", "follow_export_button": "Exporteer je volgers naar een csv-bestand", "follow_export_processing": "Aan het verwerken, binnen enkele ogenblikken wordt je gevraagd je bestand te downloaden", @@ -192,13 +204,13 @@ "follows_imported": "Volgers geïmporteerd! Het kan even duren voordat deze verwerkt zijn.", "foreground": "Voorgrond", "general": "Algemeen", - "hide_attachments_in_convo": "Bijlagen in conversaties verbergen", + "hide_attachments_in_convo": "Bijlagen in gesprekken verbergen", "hide_attachments_in_tl": "Bijlagen in tijdlijn verbergen", "hide_isp": "Instantie-specifiek paneel verbergen", "preload_images": "Afbeeldingen vooraf laden", - "hide_post_stats": "Bericht statistieken verbergen (bijv. het aantal favorieten)", + "hide_post_stats": "Bericht-statistieken verbergen (bijv. het aantal favorieten)", "hide_user_stats": "Gebruikers-statistieken verbergen (bijv. het aantal volgers)", - "import_followers_from_a_csv_file": "Gevolgden uit een csv bestand importeren", + "import_followers_from_a_csv_file": "Gevolgde gebruikers uit een csv bestand importeren", "import_theme": "Preset laden", "inputRadius": "Invoervelden", "checkboxRadius": "Checkboxen", @@ -216,13 +228,13 @@ "name_bio": "Naam & bio", "new_password": "Nieuw wachtwoord", "notification_visibility": "Type meldingen die getoond worden", - "notification_visibility_follows": "Gevolgden", + "notification_visibility_follows": "Gevolgde gebruikers", "notification_visibility_likes": "Favorieten", "notification_visibility_mentions": "Vermeldingen", "notification_visibility_repeats": "Herhalingen", "no_rich_text_description": "Verwijder rich text formattering van alle berichten", "hide_network_description": "Toon niet wie mij volgt en wie ik volg.", - "nsfw_clickthrough": "Doorklikbaar verbergen van gevoelige bijlages en link voorbeelden inschakelen", + "nsfw_clickthrough": "Gevoelige media verbergen", "oauth_tokens": "OAuth-tokens", "token": "Token", "refresh_token": "Token vernieuwen", @@ -249,15 +261,15 @@ "settings": "Instellingen", "subject_input_always_show": "Altijd onderwerpveld tonen", "subject_line_behavior": "Onderwerp kopiëren bij beantwoorden", - "subject_line_email": "Zoals email: \"re: onderwerp\"", - "subject_line_mastodon": "Zoals mastodon: kopieer zoals het is", + "subject_line_email": "Zoals e-mail: \"re: onderwerp\"", + "subject_line_mastodon": "Zoals mastodon: kopiëren zoals het is", "subject_line_noop": "Niet kopiëren", - "stop_gifs": "GIFs afspelen bij zweven", + "stop_gifs": "Geanimeerde afbeeldingen afspelen bij zweven", "streaming": "Automatisch streamen van nieuwe berichten inschakelen wanneer tot boven gescrold is", "text": "Tekst", "theme": "Thema", "theme_help": "Hex kleur codes (#rrggbb) gebruiken om je kleur thema te wijzigen.", - "theme_help_v2_1": "Je kan ook de kleur en transparantie van bepaalde componenten overschrijven door de checkbox aan te vinken, gebruik de \"Alles wissen\" knop om alle overschrijvingen te annuleren.", + "theme_help_v2_1": "Je kan ook de kleur en transparantie van bepaalde componenten overschrijven door de checkbox aan te vinken, gebruik de \"Alles wissen\" knop om alle overschrijvingen te herstellen.", "theme_help_v2_2": "Iconen onder sommige onderdelen zijn achtergrond/tekst contrast indicatoren, zweef er over voor gedetailleerde info. Hou er rekening mee dat bij doorzichtigheid de ergst mogelijke situatie wordt weer gegeven.", "tooltipRadius": "Tooltips/alarmen", "user_settings": "Gebruikersinstellingen", @@ -275,10 +287,10 @@ "keep_roundness": "Rondingen behouden", "keep_fonts": "Lettertypes behouden", "save_load_hint": "\"Behoud\" opties behouden de momenteel ingestelde opties bij het selecteren of laden van thema's, maar slaan ook de genoemde opties op bij het exporteren van een thema. Wanneer alle selectievakjes zijn uitgeschakeld, zal het exporteren van thema's alles opslaan.", - "reset": "Reset", + "reset": "Herstellen", "clear_all": "Alles wissen", "clear_opacity": "Transparantie wissen", - "keep_as_is": "Hou zoals het is", + "keep_as_is": "Houden zoals het is", "use_snapshot": "Oude versie", "use_source": "Nieuwe versie", "help": { @@ -289,7 +301,7 @@ "snapshot_source_mismatch": "Versie conflict: waarschijnlijk was FE terug gerold en opnieuw bijgewerkt, indien je het thema aangepast hebt met de oudere versie van FE wil je waarschijnlijk de oude versie gebruiken, gebruik anders de nieuwe versie.", "migration_napshot_gone": "Voor een onduidelijke reden mist de momentopname, dus sommige dingen kunnen anders uitzien dan je gewend bent.", "migration_snapshot_ok": "Voor de zekerheid is een momentopname van het thema geladen. Je kunt proberen om de thema gegevens te laden.", - "fe_downgraded": "PleromaFE's versie is terug gerold.", + "fe_downgraded": "PleromaFE's versie is terug gezet.", "fe_upgraded": "De thema-engine van PleromaFE is bijgewerkt na de versie update.", "snapshot_missing": "Het bestand bevat geen thema momentopname, dus het thema kan anders uitzien dan je oorspronkelijk bedacht had.", "snapshot_present": "Thema momentopname is geladen, alle waarden zijn overschreven. Je kunt in plaats daarvan ook de daadwerkelijke data van het thema laden." @@ -315,7 +327,7 @@ "common_colors": { "_tab_label": "Algemeen", "main": "Algemene kleuren", - "foreground_hint": "Zie \"Geavanceerd\" tab voor meer gedetailleerde controle", + "foreground_hint": "Zie \"Geavanceerd\" tab voor meer gedetailleerde opties", "rgbo": "Iconen, accenten, badges" }, "advanced_colors": { @@ -336,9 +348,9 @@ "selectedMenu": "Geselecteerd menu item", "selectedPost": "Geselecteerd bericht", "pressed": "Ingedrukt", - "highlight": "Gemarkeerde elementen", + "highlight": "Uitgelichte elementen", "icons": "Iconen", - "poll": "Poll grafiek", + "poll": "Peiling grafiek", "underlay": "Onderlaag", "popover": "Tooltips, menu's, popovers", "post": "Berichten / Gebruiker bios", @@ -352,7 +364,7 @@ "wallpaper": "Achtergrond" }, "radii": { - "_tab_label": "Rondheid" + "_tab_label": "Rondingen" }, "shadows": { "_tab_label": "Schaduw en belichting", @@ -374,8 +386,8 @@ "panel": "Paneel", "panelHeader": "Paneel koptekst", "topBar": "Top balk", - "avatar": "Gebruikers avatar (in profiel weergave)", - "avatarStatus": "Gebruikers avatar (in bericht weergave)", + "avatar": "Gebruikers-avatar (in profiel weergave)", + "avatarStatus": "Gebruikers-avatar (in bericht weergave)", "popup": "Popups en tooltips", "button": "Knop", "buttonHover": "Knop (zweven)", @@ -386,7 +398,7 @@ "hintV3": "Voor schaduwen kun je ook de {0} notatie gebruiken om de andere kleur invoer te gebruiken." }, "fonts": { - "_tab_label": "Lettertypes", + "_tab_label": "Lettertypen", "help": "Selecteer het lettertype om te gebruiken voor elementen van de UI. Voor \"aangepast\" dien je de exacte naam van het lettertype in te voeren zoals die in het systeem wordt weergegeven.", "components": { "interface": "Interface", @@ -426,10 +438,10 @@ "wait_pre_setup_otp": "OTP voorinstellen", "confirm_and_enable": "Bevestig en schakel OTP in", "title": "Twee-factorauthenticatie", - "generate_new_recovery_codes": "Genereer nieuwe herstelcodes", + "generate_new_recovery_codes": "Nieuwe herstelcodes genereren", "recovery_codes": "Herstelcodes.", "waiting_a_recovery_codes": "Back-upcodes ontvangen…", - "authentication_methods": "Authenticatiemethodes", + "authentication_methods": "Authenticatiemethoden", "scan": { "title": "Scannen", "desc": "Scan de QR-code of voer een sleutel in met je twee-factorapplicatie:", @@ -441,39 +453,39 @@ "warning_of_generate_new_codes": "Wanneer je nieuwe herstelcodes genereert, zullen je oude codes niet langer werken.", "recovery_codes_warning": "Schrijf de codes op of sla ze op een veilige locatie op - anders kun je ze niet meer inzien. Als je toegang tot je 2FA-app en herstelcodes verliest, zal je buitengesloten zijn van je account." }, - "allow_following_move": "Automatisch volgen toestaan wanneer een gevolgd account migreert", - "block_export": "Blokkades exporteren", - "block_import": "Blokkades importeren", - "blocks_imported": "Blokkades geïmporteerd! Het kan even duren voordat deze verwerkt zijn.", - "blocks_tab": "Blokkades", + "allow_following_move": "Automatisch volgen toestaan wanneer een gevolgd account verhuist", + "block_export": "Geblokkeerde gebruikers exporteren", + "block_import": "Geblokkeerde gebruikers importeren", + "blocks_imported": "Geblokkeerde gebruikers geïmporteerd! Het kan even duren voordat deze verwerkt zijn.", + "blocks_tab": "Geblokkeerde gebruikers", "change_email": "E-mail wijzigen", "change_email_error": "Er is een fout opgetreden tijdens het wijzigen van je e-mailadres.", "changed_email": "E-mailadres succesvol gewijzigd!", "domain_mutes": "Domeinen", "avatar_size_instruction": "De aangeraden minimale afmeting voor avatar-afbeeldingen is 150x150 pixels.", - "pad_emoji": "Vul emoji aan met spaties wanneer deze met de picker ingevoegd worden", + "pad_emoji": "Emoji aan met spaties aanvullen wanneer deze met de picker ingevoegd worden", "emoji_reactions_on_timeline": "Toon emoji-reacties op de tijdlijn", "accent": "Accent", "hide_muted_posts": "Berichten van genegeerde gebruikers verbergen", "max_thumbnails": "Maximaal aantal miniaturen per bericht", "use_one_click_nsfw": "Gevoelige bijlagen met slechts één klik openen", - "hide_filtered_statuses": "Gefilterde statussen verbergen", - "import_blocks_from_a_csv_file": "Blokkades van een csv bestand importeren", - "mutes_tab": "Genegeerden", - "play_videos_in_modal": "Video's in een popup frame afspelen", + "hide_filtered_statuses": "Gefilterde berichten verbergen", + "import_blocks_from_a_csv_file": "Geblokkeerde gebruikers van een csv bestand importeren", + "mutes_tab": "Genegeerde gebruikers", + "play_videos_in_modal": "Video's in een popup venster afspelen", "new_email": "Nieuwe e-mail", "notification_visibility_emoji_reactions": "Reacties", - "no_blocks": "Geen blokkades", - "no_mutes": "Geen genegeerden", + "no_blocks": "Geen geblokkeerde gebruikers", + "no_mutes": "Geen genegeerde gebruikers", "hide_followers_description": "Niet tonen wie mij volgt", "hide_followers_count_description": "Niet mijn volgers aantal tonen", - "hide_follows_count_description": "Niet mijn gevolgde aantal tonen", + "hide_follows_count_description": "Niet mijn gevolgden aantal tonen", "show_admin_badge": "\"Beheerder\" badge in mijn profiel tonen", - "autohide_floating_post_button": "Nieuw Bericht knop automatisch verbergen (mobiel)", + "autohide_floating_post_button": "\"Bericht opstellen\"-knop automatisch verbergen (mobiel)", "search_user_to_block": "Zoek wie je wilt blokkeren", "search_user_to_mute": "Zoek wie je wilt negeren", "minimal_scopes_mode": "Bericht bereik-opties minimaliseren", - "post_status_content_type": "Bericht status content type", + "post_status_content_type": "Standaard bericht content type", "user_mutes": "Gebruikers", "useStreamingApi": "Berichten en meldingen in real-time ontvangen", "useStreamingApiWarning": "(Afgeraden, experimenteel, kan berichten overslaan)", @@ -482,7 +494,7 @@ "fun": "Plezier", "greentext": "Meme pijlen", "block_export_button": "Exporteer je geblokkeerde gebruikers naar een csv-bestand", - "block_import_error": "Fout bij importeren blokkades", + "block_import_error": "Fout bij importeren geblokkeerde gebruikers", "discoverable": "Sta toe dat dit account ontdekt kan worden in zoekresultaten en andere diensten", "use_contain_fit": "Bijlage in miniaturen niet bijsnijden", "notification_visibility_moves": "Gebruiker Migraties", @@ -495,7 +507,7 @@ "backend_version": "Backend versie", "title": "Versie" }, - "mutes_and_blocks": "Negeringen en Blokkades", + "mutes_and_blocks": "Negeren en Blokkeren", "profile_fields": { "value": "Inhoud", "name": "Label", @@ -508,15 +520,15 @@ "hide_media_previews": "Media voorbeelden verbergen", "word_filter": "Woord filter", "chatMessageRadius": "Chatbericht", - "mute_export": "Genegeerden export", - "mute_export_button": "Exporteer je genegeerden naar een csv-bestand", - "mute_import_error": "Fout tijdens het importeren van genegeerden", - "mute_import": "Genegeerden import", - "mutes_imported": "Genegeerden geïmporteerd! Het kan even duren voordat deze verwerkt zijn.", + "mute_export": "Genegeerde gebruikers export", + "mute_export_button": "Genegeerde gebruikers naar een csv-bestand exporteren", + "mute_import_error": "Fout tijdens het importeren van genegeerde gebruikers", + "mute_import": "Genegeerde gebruikers import", + "mutes_imported": "Genegeerde gebruikers geïmporteerd! Het kan even duren voordat deze verwerkt zijn.", "more_settings": "Meer instellingen", - "notification_setting_hide_notification_contents": "Afzender en inhoud van push meldingen verbergen", + "notification_setting_hide_notification_contents": "Afzender en inhoud van push-meldingen verbergen", "notification_setting_block_from_strangers": "Meldingen van gebruikers die je niet volgt blokkeren", - "virtual_scrolling": "Tijdlijn rendering optimaliseren", + "virtual_scrolling": "Tijdlijn weergave optimaliseren", "sensitive_by_default": "Berichten standaard als gevoelig markeren", "reset_avatar_confirm": "Wil je echt de avatar herstellen?", "reset_banner_confirm": "Wil je echt de banner herstellen?", @@ -528,7 +540,7 @@ "reply_visibility_following_short": "Antwoorden naar mijn gevolgden tonen", "file_export_import": { "errors": { - "file_slightly_new": "Bestand minor versie is verschillend, sommige instellingen kunnen mogelijk niet worden geladen", + "file_slightly_new": "Minor versie van bestand is verschillend, sommige instellingen kunnen mogelijk niet worden geladen", "file_too_old": "Incompatibele hoofdversie: {fileMajor}, bestandsversie is te oud en wordt niet ondersteund (minimale versie {feMajor})", "file_too_new": "Incompatibele hoofdversie: {fileMajor}, deze PleromaFE (instellingen versie {feMajor}) is te oud om deze te ondersteunen", "invalid_file": "Het geselecteerde bestand is niet een door Pleroma ondersteunde instellingen back-up. Er zijn geen wijzigingen gemaakt." @@ -536,27 +548,95 @@ "restore_settings": "Instellingen uit bestand herstellen", "backup_settings_theme": "Instellingen en thema naar bestand back-uppen", "backup_settings": "Instellingen naar bestand back-uppen", - "backup_restore": "Instellingen backup" + "backup_restore": "Instellingen back-up" }, - "hide_wallpaper": "Instantie achtergrond verbergen", + "hide_wallpaper": "Achtergrond-afbeelding verbergen", "hide_all_muted_posts": "Genegeerde berichten verbergen", - "import_mutes_from_a_csv_file": "Importeer genegeerden van een csv bestand" + "import_mutes_from_a_csv_file": "Genegeerde gebruikers uit een csv bestand importeren", + "added_alias": "Alias is toegevoegd.", + "add_alias_error": "Fout bij het toevoegen van alias: {error}", + "move_account": "Account verhuizen", + "move_account_notes": "Indien je het account ergens anders heen wilt verplaatsen, dien je eerst een alias naar dit account te maken in het nieuwe account.", + "move_account_target": "Doelwit account (b.v. {example})", + "moved_account": "Het account is verhuisd.", + "move_account_error": "Fout tijdens account verhuizen: {error}", + "wordfilter": "Woordfilter", + "third_column_mode": "Indien er genoeg plaats is, derde kolom tonen met", + "third_column_mode_none": "Géén derde kolom tonen", + "third_column_mode_notifications": "Meldingen", + "third_column_mode_postform": "Berichtformulier en navigatie", + "tree_advanced": "Flexibelere navigatie toestaan in boom weergave", + "tree_fade_ancestors": "Ouders van huidige bericht met gedempte tekst tonen", + "conversation_display_linear": "Lineaire weergave", + "mention_link_display_full_for_remote": "als volledige namen alleen voor externe gebruikers (b.v. {'@'}foo{'@'}example.org)", + "mention_link_display_full": "altijd als volledige namen (b.v. {'@'}foo{'@'}example.org)", + "mention_link_show_avatar": "Profielfoto naast link tonen", + "mention_link_fade_domain": "Domeinen vervagen (b.v. {'@'}example.org in {'@'}foo{'@'}example.org)", + "mention_link_bolden_you": "Vermeldingen naar jezelf uitlichten", + "expert_mode": "Geavanceerde opties tonen", + "setting_server_side": "Deze instelling is gebonden aan je profiel en beïnvloed alle sessies en clients", + "post_look_feel": "Berichten Look & Feel", + "mention_links": "Vermelding-links", + "email_language": "Taal voor e-mails van de server", + "account_backup": "Account back-up", + "account_backup_description": "Hiermee kun je een archief van je account gegevens en berichten downloaden, maar deze kunnen nog niet geïmporteerd worden in een Pleroma account.", + "account_backup_table_head": "Back-up", + "download_backup": "Downloaden", + "backup_not_ready": "Deze back-up is nog niet gereed.", + "remove_backup": "Verwijderen", + "list_backups_error": "Fout bij het ophalen van back-ups: {error}", + "add_backup": "Nieuwe back-up aanmaken", + "added_backup": "Nieuwe back-up is toegevoegd.", + "add_backup_error": "Fout bij het maken van back-up: {error}", + "account_alias": "Account aliassen", + "account_alias_table_head": "Alias", + "list_aliases_error": "Fout bij het ophalen van aliassen: {error}", + "hide_list_aliases_error_action": "Sluiten", + "remove_alias": "Deze alias verwijderen", + "new_alias_target": "Nieuwe alias toevoegen (b.v. {example})", + "mute_bot_posts": "Bot-berichten negeren", + "hide_bot_indication": "Bot-indicatie in berichten verbergen", + "hide_shoutbox": "Shoutbox verbergen", + "right_sidebar": "Kolom-volgorde omdraaien", + "always_show_post_button": "Altijd de zwevende \"Bericht opstellen\"-knop tonen", + "hide_wordfiltered_statuses": "Berichten met gefilterde woorden verbergen", + "hide_muted_threads": "Genegeerde gesprekken verbergen", + "account_privacy": "Privacy", + "posts": "Berichten", + "user_profiles": "Gebruikersprofielen", + "notification_visibility_polls": "Einde van peilingen waar je in gestemd hebt", + "hide_favorites_description": "Lijst van favorieten verbergen (mensen krijgen wel nog meldingen)", + "conversation_display": "Gespreksweergave stijl", + "conversation_display_tree": "Boom weergave", + "disable_sticky_headers": "Kolomkopteksten niet bovenaan het scherm plakken", + "show_scrollbars": "Scrollbalk tonen in zijkolommen", + "conversation_other_replies_button": "\"Andere antwoorden\"-knop tonen", + "conversation_other_replies_button_below": "Onder berichten", + "conversation_other_replies_button_inside": "Binnen in berichten", + "max_depth_in_thread": "Maximum lagen van een gesprek welke standaard getoond dienen te worden", + "use_at_icon": "{'@'} symbool als icoon tonen in plaats van tekst", + "mention_link_display": "Vermelding-links tonen", + "mention_link_display_short": "altijd als korte namen (b.v. {'@'}foo)", + "mention_link_use_tooltip": "Volledige namen in tooltip tonen voor externe gebruikers", + "show_yous": "(Jij)'s tonen", + "user_popover_avatar_zoom": "Gebruikers-avatar inzoomen wanneer hier op geklikt wordt in een popover in plaats van de popover te sluiten", + "user_popover_avatar_overlay": "Gebruikers-popover tonen over gebruikers-avatar" }, "timeline": { - "collapse": "Inklappen", - "conversation": "Conversatie", + "collapse": "Invouwen", + "conversation": "Gesprek", "error_fetching": "Fout bij ophalen van updates", - "load_older": "Oudere statussen laden", - "no_retweet_hint": "Bericht is gemarkeerd als enkel volgers of direct en kan niet worden herhaald", + "load_older": "Oudere berichten laden", + "no_retweet_hint": "Bericht is gemarkeerd als enkel-volgers of privé en kan niet worden herhaald of geciteerd", "repeated": "herhaalde", "show_new": "Nieuwe tonen", "up_to_date": "Up-to-date", - "no_statuses": "Geen statussen", - "no_more_statuses": "Geen statussen meer", + "no_statuses": "Geen berichten", + "no_more_statuses": "Geen verdere berichten", "socket_broke": "Realtime verbinding verloren: CloseEvent code {0}", "socket_reconnected": "Realtime verbinding opgezet", "reload": "Verversen", - "error": "Fout tijdens het ophalen van tijdlijn: {0}" + "error": "Fout bij het ophalen van tijdlijn: {0}" }, "user_card": { "approve": "Goedkeuren", @@ -565,28 +645,27 @@ "deny": "Weigeren", "favorites": "Favorieten", "follow": "Volgen", - "follow_cancel": "Aanvraag annuleren", - "follow_sent": "Aanvraag verzonden!", + "follow_cancel": "Verzoek annuleren", + "follow_sent": "Verzoek verzonden!", "follow_progress": "Aanvragen…", - "follow_unfollow": "Stop volgen", - "followees": "Aan het volgen", + "follow_unfollow": "Ontvolgen", + "followees": "Volgen", "followers": "Volgers", - "following": "Aan het volgen!", + "following": "Gevolgd!", "follows_you": "Volgt jou!", "its_you": "'t is jij!", "mute": "Negeren", "muted": "Genegeerd", "per_day": "per dag", - "remote_follow": "Volg vanop afstand", - "statuses": "Statussen", + "remote_follow": "Van afstand volgen", + "statuses": "Berichten", "admin_menu": { - "delete_user_confirmation": "Weet je het heel zeker? Deze uitvoering kan niet ongedaan worden gemaakt.", "delete_user": "Gebruiker verwijderen", - "quarantine": "Federeren van gebruikers berichten verbieden", + "quarantine": "Federeren van berichten verbieden", "disable_any_subscription": "Volgen van gebruiker in zijn geheel verbieden", "disable_remote_subscription": "Volgen van gebruiker vanaf andere instanties verbieden", "sandbox": "Berichten forceren om alleen voor volgers zichtbaar te zijn", - "force_unlisted": "Berichten forceren om niet publiekelijk getoond te worden", + "force_unlisted": "Berichten forceren om niet openbaar getoond te worden", "strip_media": "Media van berichten verwijderen", "force_nsfw": "Alle berichten als gevoelig markeren", "delete_account": "Account verwijderen", @@ -596,30 +675,33 @@ "grant_moderator": "Moderatorsrechten toekennen", "revoke_admin": "Beheerdersrechten intrekken", "grant_admin": "Beheerdersrechten toekennen", - "moderation": "Moderatie" + "moderation": "Moderatie", + "delete_user_data_and_deactivate_confirmation": "Dit zal permanent alle data van dit account verwijderen en het account deactiveren. Weet je het zeker?" }, "show_repeats": "Herhalingen tonen", "hide_repeats": "Herhalingen verbergen", "mute_progress": "Negeren…", - "unmute_progress": "Negering opheffen…", - "unmute": "Negering opheffen", + "unmute_progress": "Negeren opheffen…", + "unmute": "Negeren opheffen", "block_progress": "Blokkeren…", - "unblock_progress": "Blokkade opheffen…", - "unblock": "Blokkade opheffen", + "unblock_progress": "Blokkeren opheffen…", + "unblock": "Blokkeren opheffen", "unsubscribe": "Abonnement opzeggen", "subscribe": "Abonneren", - "report": "Aangeven", - "mention": "Vermelding", + "report": "Rapporteren", + "mention": "Vermelden", "media": "Media", "hidden": "Verborgen", "highlight": { "side": "Zijstreep", "striped": "Gestreepte achtergrond", "solid": "Effen achtergrond", - "disabled": "Geen highlight" + "disabled": "Geen uitlichting" }, "bot": "Bot", - "message": "Bericht" + "message": "Bericht", + "edit_profile": "Profiel wijzigen", + "deactivated": "Gedeactiveerd" }, "user_profile": { "timeline_title": "Gebruikerstijdlijn", @@ -635,11 +717,11 @@ "repeat": "Herhalen", "reply": "Beantwoorden", "favorite": "Favoriet maken", - "user_settings": "Gebruikers Instellingen", - "reject_follow_request": "Volg-verzoek afwijzen", - "accept_follow_request": "Volg-aanvraag accepteren", + "user_settings": "Gebruikersinstellingen", + "reject_follow_request": "Volgverzoek afwijzen", + "accept_follow_request": "Volgverzoek accepteren", "add_reaction": "Reactie toevoegen", - "bookmark": "Bladwijzer" + "bookmark": "Bladwijzer maken" }, "upload": { "error": { @@ -664,27 +746,27 @@ "replace": "Vervangen", "is_replaced_by": "→", "keyword_policies": "Zoekwoordbeleid", - "ftl_removal": "Verwijdering van \"Het Geheel Bekende Netwerk\" Tijdlijn" + "ftl_removal": "Verwijderen van \"Het Globale Netwerk\" Tijdlijn" }, "mrf_policies_desc": "MRF-regels beïnvloeden het federatiegedrag van de instantie. De volgende regels zijn ingeschakeld:", "mrf_policies": "Ingeschakelde MRF-regels", "simple": { - "simple_policies": "Instantiespecifieke regels", + "simple_policies": "Instantie-specifieke regels", "instance": "Instantie", "reason": "Reden", "not_applicable": "n.v.t.", "accept": "Accepteren", "accept_desc": "Deze instantie accepteert alleen berichten van de volgende instanties:", "reject": "Afwijzen", - "reject_desc": "Deze instantie zal geen berichten accepteren van de volgende instanties:", + "reject_desc": "Deze instantie zal géén berichten accepteren van de volgende instanties:", "quarantine": "Quarantaine", - "quarantine_desc": "Deze instantie zal alleen openbare berichten sturen naar de volgende instanties:", - "ftl_removal_desc": "Deze instantie verwijdert de volgende instanties van \"Bekende Netwerk\" tijdlijn:", + "quarantine_desc": "Deze instantie zal géén berichten sturen naar de volgende instanties:", + "ftl_removal_desc": "Deze instantie verwijdert de volgende instanties van \"Globale Netwerk\" tijdlijn:", "media_removal_desc": "Deze instantie verwijdert media van berichten van de volgende instanties:", - "media_nsfw_desc": "Deze instantie stelt media in als gevoelig in berichten van de volgende instanties:", - "ftl_removal": "Verwijderen van \"Bekende Netwerk\" Tijdlijn", - "media_removal": "Mediaverwijdering", - "media_nsfw": "Forceer media als gevoelig" + "media_nsfw_desc": "Deze instantie markeert media als gevoelig in berichten van de volgende instanties:", + "ftl_removal": "Verwijderen van \"Globale Netwerk\" Tijdlijn", + "media_removal": "Verwijderen van media", + "media_nsfw": "Media als gevoelig markeren" } }, "staff": "Personeel" @@ -692,8 +774,8 @@ "domain_mute_card": { "mute": "Negeren", "mute_progress": "Negeren…", - "unmute": "Negering opheffen", - "unmute_progress": "Negering wordt opgeheven…" + "unmute": "Negeren opheffen", + "unmute_progress": "Negeren wordt opgeheven…" }, "exporter": { "export": "Exporteren", @@ -712,21 +794,23 @@ }, "media_modal": { "previous": "Vorige", - "next": "Volgende" + "next": "Volgende", + "counter": "{current} / {total}", + "hide": "Media venster sluiten" }, "polls": { - "add_poll": "Poll toevoegen", + "add_poll": "Peiling toevoegen", "add_option": "Optie toevoegen", "option": "Optie", "votes": "stemmen", - "vote": "Stem", + "vote": "Stemmen", "single_choice": "Enkele keuze", "multiple_choices": "Meerkeuze", - "expiry": "Poll leeftijd", - "expires_in": "Poll eindigt in {0}", - "expired": "Poll is {0} geleden beëindigd", - "not_enough_options": "Te weinig opties in poll", - "type": "Poll-type", + "expiry": "Peiling tijdsduur", + "expires_in": "Peiling eindigt in {0}", + "expired": "Peiling is {0} geleden beëindigd", + "not_enough_options": "Te weinig opties in peiling", + "type": "Peiling-type", "votes_count": "{count} stem | {count} stemmen", "people_voted_count": "{count} persoon heeft gestemd | {count} personen hebben gestemd" }, @@ -743,28 +827,41 @@ }, "interactions": { "favs_repeats": "Herhalingen en favorieten", - "follows": "Nieuwe gevolgden", + "follows": "Nieuwe volgs", "moves": "Gebruikermigraties", + "emoji_reactions": "Emoji Reacties", + "reports": "Rapportages", "load_older": "Oudere interacties laden" }, "remote_user_resolver": { "searching_for": "Zoeken naar", "error": "Niet gevonden.", - "remote_user_resolver": "Externe gebruikers-zoeker" - }, + "remote_user_resolver": "Externe gebruiker zoeker" + }, + "report": { + "reporter": "Reporteerder:", + "reported_user": "Gerapporteerde gebruiker:", + "reported_statuses": "Gerapporteerde statussen:", + "notes": "Notas:", + "state": "Status:", + "state_open": "Open", + "state_closed": "Gesloten", + "state_resolved": "Opgelost" + }, + "selectable_list": { "select_all": "Alles selecteren" }, "password_reset": { - "password_reset_required_but_mailer_is_disabled": "Je dient je wachtwoord opnieuw in te stellen, maar wachtwoord reset is uitgeschakeld. Neem contact op met de beheerder van deze instantie.", + "password_reset_required_but_mailer_is_disabled": "Je dient je wachtwoord opnieuw in te stellen, maar wachtwoordherstel is uitgeschakeld. Neem contact op met de beheerder van deze instantie.", "password_reset_required": "Je dient je wachtwoord opnieuw in te stellen om in te kunnen loggen.", - "password_reset_disabled": "Wachtwoord reset is uitgeschakeld. Neem contact op met de beheerder van deze instantie.", + "password_reset_disabled": "Wachtwoordherstel is uitgeschakeld. Neem contact op met de beheerder van deze instantie.", "too_many_requests": "Je hebt het maximaal aantal pogingen bereikt, probeer het later opnieuw.", "return_home": "Terugkeren naar de home pagina", "check_email": "Controleer je email inbox voor een link om je wachtwoord opnieuw in te stellen.", "placeholder": "Je email of gebruikersnaam", "instruction": "Voer je email adres of gebruikersnaam in. We sturen je een link om je wachtwoord opnieuw in te stellen.", - "password_reset": "Wachtwoord opnieuw instellen", + "password_reset": "Wachtwoord herstellen", "forgot_password": "Wachtwoord vergeten?" }, "search": { @@ -780,26 +877,26 @@ "forward_to": "Doorsturen naar {0}", "forward_description": "Dit account hoort bij een andere server. Wil je een kopie van het rapport ook daarheen sturen?", "additional_comments": "Aanvullende opmerkingen", - "add_comment_description": "Het rapport zal naar de moderators van de instantie worden verstuurd. Je kunt hieronder uitleg bijvoegen waarom je dit account wilt aangeven:", - "title": "{0} aangeven" + "add_comment_description": "Het rapport zal naar de moderators van de instantie worden verstuurd. Je kunt hieronder uitleg bijvoegen waarom je dit account wilt rapporteren:", + "title": "{0} rapporteren" }, "status": { - "copy_link": "Link naar status kopiëren", - "status_unavailable": "Status niet beschikbaar", - "unmute_conversation": "Conversatie niet meer negeren", - "mute_conversation": "Conversatie negeren", + "copy_link": "Link naar bericht kopiëren", + "status_unavailable": "Bericht niet beschikbaar", + "unmute_conversation": "Gesprek niet meer negeren", + "mute_conversation": "Gesprek negeren", "replies_list": "Antwoorden:", "reply_to": "Antwoorden aan", - "delete_confirm": "Wil je echt deze status verwijderen?", + "delete_confirm": "Wil je echt dit bericht verwijderen?", "pin": "Aan profiel vastmaken", "pinned": "Vastgezet", "unpin": "Van profiel losmaken", - "delete": "Status verwijderen", + "delete": "Bericht verwijderen", "repeats": "Herhalingen", "favorites": "Favorieten", "thread_muted_and_words": ", heeft woorden:", - "thread_muted": "Thread genegeerd", - "expand": "Uitklappen", + "thread_muted": "Gesprek genegeerd", + "expand": "Uitvouwen", "nsfw": "Gevoelig", "status_deleted": "Dit bericht is verwijderd", "hide_content": "Inhoud verbergen", @@ -808,7 +905,33 @@ "show_full_subject": "Volledig onderwerp tonen", "external_source": "Externe bron", "unbookmark": "Bladwijzer verwijderen", - "bookmark": "Bladwijzer toevoegen" + "bookmark": "Bladwijzer toevoegen", + "show_attachment_description": "Voorbeeld beschrijving (open bijlage om de volledige beschrijving te zien)", + "remove_attachment": "Bijlage verwijderen", + "attachment_stop_flash": "Flash speler stoppen", + "move_up": "Bijlage naar links schuiven", + "move_down": "Bijlage naar rechts schuiven", + "open_gallery": "Gallerij openen", + "thread_hide": "Gesprek verbergen", + "thread_show": "Gesprek tonen", + "show_all_conversation": "Volledig gesprek tonen ({numStatus} ander bericht) | Volledig gesprek tonen ({numStatus} andere berichten)", + "show_only_conversation_under_this": "Alleen antwoorden op dit bericht tonen", + "mentions": "Vermeldingen", + "replies_list_with_others": "Antwoorden (+{numReplies} andere): | Antwoorden (+{numReplies} anderen):", + "you": "(Jij)", + "plus_more": "+{number} meer", + "many_attachments": "Bericht heeft {number} bijlage | Bericht heeft {number} bijlagen", + "collapse_attachments": "Bijlagen invouwen", + "show_all_attachments": "Alle bijlagen tonen", + "show_attachment_in_modal": "In media venster tonen", + "hide_attachment": "Bijlage verbergen", + "thread_show_full": "Alle berichten in dit gesprek tonen ({numStatus} bericht in totaal, max. diepte {depth}) | Alle berichten in dit gesprek tonen ({numStatus} berichten in totaal, max. diepte {depth})", + "thread_show_full_with_icon": "{icon} {text}", + "thread_follow": "Rest van gesprek tonen ({numStatus} bericht in totaal) | Rest van gesprek tonen ({numStatus} berichten in totaal)", + "thread_follow_with_icon": "{icon} {text}", + "ancestor_follow": "{numReplies} ander antwoord onder dit bericht tonen | {numReplies} andere antwoorden onder dit bericht tonen", + "ancestor_follow_with_icon": "{icon} {text}", + "show_all_conversation_with_icon": "{icon} {text}" }, "time": { "years_short": "{0}j", @@ -842,13 +965,29 @@ "days_short": "{0}d", "day_short": "{0}d", "days": "{0} dagen", - "day": "{0} dag" + "day": "{0} dag", + "unit": { + "months": "{0} maand | {0} maanden", + "months_short": "{0}ma", + "seconds": "{0} seconde | {0} seconden", + "seconds_short": "{0}s", + "weeks": "{0} week | {0} weken", + "weeks_short": "{0}w", + "years": "{0} jaar | {0} jaren", + "years_short": "{0}j", + "days": "{0} dag | {0} dagen", + "days_short": "{0}d", + "hours": "{0} uur | {0} uren", + "hours_short": "{0}u", + "minutes": "{0} minuut | {0} minuten", + "minutes_short": "{0}min" + } }, "shoutbox": { "title": "Shoutbox" }, "errors": { - "storage_unavailable": "Pleroma kon browseropslag niet benaderen. Je login of lokale instellingen worden niet opgeslagen en je kunt onverwachte problemen ondervinden. Probeer cookies te accepteren." + "storage_unavailable": "Pleroma kan de browseropslag niet benaderen. Je login of lokale instellingen worden niet opgeslagen en je kunt onverwachte problemen ondervinden. Probeer cookies te accepteren." }, "display_date": { "today": "Vandaag" diff --git a/src/i18n/oc.json b/src/i18n/oc.json @@ -501,8 +501,7 @@ "disable_remote_subscription": "Desactivar lo seguiment d’utilizaire d’instàncias alonhadas", "disable_any_subscription": "Desactivar tot seguiment", "quarantine": "Defendre la federacion de las publicacions de l’utilizaire", - "delete_user": "Suprimir l’utilizaire", - "delete_user_confirmation": "Volètz vertadièrament far aquò ? Aquesta accion se pòt pas anullar." + "delete_user": "Suprimir l’utilizaire" } }, "user_profile": { diff --git a/src/i18n/pl.json b/src/i18n/pl.json @@ -762,8 +762,7 @@ "disable_remote_subscription": "Zakaż obserwowania użytkownika ze zdalnych instancji", "disable_any_subscription": "Zakaż całkowicie obserwowania użytkownika", "quarantine": "Zakaż federowania postów od tego użytkownika", - "delete_user": "Usuń użytkownika", - "delete_user_confirmation": "Czy jesteś absolutnie pewny(-a)? Ta operacja nie może być cofnięta." + "delete_user": "Usuń użytkownika" }, "message": "Napisz", "edit_profile": "Edytuj profil", diff --git a/src/i18n/pt.json b/src/i18n/pt.json @@ -594,7 +594,6 @@ "unmute_progress": "A retirar silêncio…", "mute_progress": "A silenciar…", "admin_menu": { - "delete_user_confirmation": "Tens a certeza? Esta ação não pode ser revertida.", "delete_user": "Eliminar utilizador", "quarantine": "Não permitir publicações de utilizadores de instâncias remotas", "disable_any_subscription": "Não permitir que nenhum utilizador te siga", diff --git a/src/i18n/ru.json b/src/i18n/ru.json @@ -456,6 +456,15 @@ "subject_line_mastodon": "Как в Mastodon: скопировать как есть", "subject_line_email": "Как в электронной почте: \"re: тема\"", "subject_line_behavior": "Копировать тему в ответах", + "third_column_mode": "Когда недостаточно места, показывать третью колонку содержащую", + "third_column_mode_none": "Не показывать третью колонку совсем", + "third_column_mode_notifications": "Колонку уведомлений", + "third_column_mode_postform": "Форму отправки сообщения и навигацию", + "columns": "Колонки", + "column_sizes": "Размеры колонок", + "column_sizes_sidebar": "Боковой", + "column_sizes_content": "Содержимого", + "column_sizes_notifs": "Уведомлений", "no_mutes": "Нет игнорируемых", "no_blocks": "Нет блокировок", "notification_visibility_emoji_reactions": "Реакции", @@ -576,8 +585,7 @@ "disable_remote_subscription": "Запретить читать с других узлов", "disable_any_subscription": "Запретить читать пользователя", "quarantine": "Не федерировать статусы пользователя", - "delete_user": "Удалить пользователя", - "delete_user_confirmation": "Вы уверены? Это действие нельзя отменить." + "delete_user": "Удалить пользователя" }, "media": "С вложениями", "mention": "Упомянуть", diff --git a/src/i18n/service_worker_messages.js b/src/i18n/service_worker_messages.js @@ -27,6 +27,7 @@ const messages = { pt: require('../lib/notification-i18n-loader.js!./pt.json'), ro: require('../lib/notification-i18n-loader.js!./ro.json'), ru: require('../lib/notification-i18n-loader.js!./ru.json'), + sk: require('../lib/notification-i18n-loader.js!./sk.json'), te: require('../lib/notification-i18n-loader.js!./te.json'), zh: require('../lib/notification-i18n-loader.js!./zh.json'), en: require('../lib/notification-i18n-loader.js!./en.json') diff --git a/src/i18n/sk.json b/src/i18n/sk.json @@ -0,0 +1,512 @@ +{ + "about": { + "mrf": { + "federation": "Federácia", + "keyword": { + "keyword_policies": "Pravidlá pre kľúčové slová", + "ftl_removal": "Odstránenie z časovej osy \"Celej známej siete\"", + "reject": "Odmietni", + "replace": "Nahraď", + "is_replaced_by": "→" + }, + "mrf_policies": "Povoliť MRF pravidlá", + "mrf_policies_desc": "MRF pravidlá upravujú správanie servera v rámci federácie s inými. Nasledovné pravidlá sú aktívne:", + "simple": { + "simple_policies": "Pravidlá špecifické pre tento server", + "instance": "Server", + "reason": "Dôvod", + "not_applicable": "N/A", + "accept": "Prijať", + "accept_desc": "Tento server preberá správy len z nasledovných serverov:", + "reject": "Odmietnuť", + "reject_desc": "Tento server preberá správy spravy z nasledovných serverov:", + "quarantine": "Karanténa", + "quarantine_desc": "Tento server posiela verejné oznamy len na nasledovné servre:", + "ftl_removal": "Odstránenie časovej osy \"Známa sieť\"", + "ftl_removal_desc": "Tento server odstraňuje nasledovné serverov zo svojej časovej osy \"Známa sieť\":", + "media_removal": "Odstránenie médií", + "media_removal_desc": "Tento server odstraňuje médiá zo správ nasledovných serverov:", + "media_nsfw": "Označenie médií ako citlivých", + "media_nsfw_desc": "Tento server označuje média ako citlivé v správach z nasledovných serverov:" + } + }, + "staff": "Personál" + }, + "shoutbox": { + "title": "Verejné fórum" + }, + "domain_mute_card": { + "mute": "Utíš", + "mute_progress": "Utišujem…", + "unmute": "Povoľ oznamy", + "unmute_progress": "Povoľujem oznamy…" + }, + "exporter": { + "export": "Export", + "processing": "Spracováva sa, čoskoro sa ti ponúknu na stiahnutie súbory s dátami exportu" + }, + "features_panel": { + "shout": "Verejné fórum", + "pleroma_chat_messages": "Pleroma Chat", + "gopher": "Gopher", + "media_proxy": "Proxy pre médiá", + "scope_options": "Nastavenia rámca", + "text_limit": "Limit počtu znakov", + "title": "Vlastnosti", + "who_to_follow": "Koho nasledovať", + "upload_limit": "Limit nahrávania" + }, + "finder": { + "error_fetching_user": "Chyba načítavania užívateľa", + "find_user": "Nájsť užívateľa" + }, + "general": { + "apply": "Použiť", + "submit": "Odoslať", + "more": "Viac", + "loading": "Nahrávam…", + "generic_error": "Nastala chyba", + "error_retry": "Zopakuj znova, prosím", + "retry": "Zopakuj znova", + "optional": "nepovinné", + "show_more": "Zobraz viac", + "show_less": "Zobraz menej", + "dismiss": "Zahoď", + "cancel": "Zruš", + "disable": "Vypni", + "enable": "Zapni", + "confirm": "Potvrdiť", + "verify": "Overiť", + "close": "Zatvoriť", + "peek": "Vybrať", + "role": { + "admin": "Správca", + "moderator": "Moderátor" + }, + "flash_content": "Klikni pre zobrazenie Flash obsahu prostredníctvom Ruffle (experimentálne, nemusí fungovať).", + "flash_security": "Flash obsah je potencionálne nebezpečný, keďže je to produkt s uzatvoreným kódom.", + "flash_fail": "Nepodarilo sa nahrať Flash obsah, pre detaily pozri konzolu prehliadača.", + "scope_in_timeline": { + "direct": "Priame", + "private": "Len pre nasledovníkov", + "public": "Verejné", + "unlisted": "Nezaradené" + } + }, + "image_cropper": { + "crop_picture": "Orezať obrázok", + "save": "Uložiť", + "save_without_cropping": "Ulož bez orezania", + "cancel": "Zrušiť" + }, + "importer": { + "submit": "Odoslať", + "success": "Úspečne naimportované.", + "error": "Pri importe súboru nastala chyba." + }, + "login": { + "login": "Prihlásiť sa", + "description": "Prihlásiť pomocou OAuth", + "logout": "Odhlásiť sa", + "password": "Heslo", + "placeholder": "napr. peter", + "register": "Registrácia", + "username": "Meno užívateľa", + "hint": "Prihlás sa, aby si sa mohol zúčastniť konverzácie", + "authentication_code": "Autentifikačný kód", + "enter_recovery_code": "Zadaj kód obnovenia", + "enter_two_factor_code": "Zadaj 2-fázový validačný kód", + "recovery_code": "Kód obnovenia", + "heading": { + "totp": "2-fázové overenie", + "recovery": "2-fázové obnova" + } + }, + "media_modal": { + "previous": "Predchádzajúce", + "next": "Nasledujúce", + "counter": "{current} / {total}", + "hide": "Zatvoriť prehliadač médií" + }, + "nav": { + "about": "O stránke", + "administration": "Administrácia", + "back": "Späť", + "friend_requests": "Žiadosti o priateľstvo", + "mentions": "Zmienky", + "interactions": "Interakcie", + "dms": "Priame správy", + "public_tl": "Verejná časová os", + "timeline": "Časová os", + "home_timeline": "Domáca časová os", + "twkn": "Známa sieť", + "bookmarks": "Záložky", + "user_search": "Hľadanie užívateľa", + "search": "Hladať", + "who_to_follow": "Koho nasledovať", + "preferences": "Nastavenia", + "timelines": "Časové osy", + "chats": "Chaty" + }, + "notifications": { + "broken_favorite": "Neznáma správa, dohľadávam ju…", + "error": "Chyba získavania upozornení: {0}", + "favorited_you": "si obľúbil tvoju správu", + "followed_you": "ťa nasleduje", + "follow_request": "ťa chce nasledovať", + "load_older": "Nahrať staršie upozornenia", + "notifications": "Upozornenia", + "read": "Prečítané!", + "repeated_you": "zopakoval tvoju správu", + "no_more_notifications": "Žiadne ďalšie upozornenia", + "migrated_to": "sa presťahoval na", + "reacted_with": "reagoval nasledovne {0}" + }, + "polls": { + "add_poll": "Pridať anketu", + "add_option": "Pridať možnosť", + "option": "Možnosť", + "votes": "hlasy", + "people_voted_count": "{count} volič | {count} voličov", + "votes_count": "{count} hlas | {count} hlasov", + "vote": "Hlas", + "type": "Typ ankety", + "single_choice": "Výber jednej možnosti", + "multiple_choices": "Výber viacerých možností", + "expiry": "Vek ankety", + "expires_in": "Anketa končí za {0}", + "expired": "Anketa skončila pre {0}", + "not_enough_options": "Príliš málo jedinečných možností v ankete" + }, + "emoji": { + "stickers": "Nálepka", + "emoji": "Emotikon", + "keep_open": "Ponechaj okno výberu otvorené", + "search_emoji": "Vyhladať emotikon", + "add_emoji": "Vložiť emotikon", + "custom": "Vlastný emotikon", + "unicode": "Unicode emotikon", + "load_all_hint": "Nahralo sa prvých {saneAmount} emotikonov, nahranie všetkých by mohlo spôsobiť zníženie výkonu.", + "load_all": "Nahrať všetkých {emojiAmount} emotikonov" + }, + "errors": { + "storage_unavailable": "Pleroma nemôže používať úložisko prehliadača. Tvoje prihlasovacie meno a lokálne nastavenia nebudú uchované a môžu sa vyskytnúť neočakávané chyby. Skús povoliť cookie." + }, + "interactions": { + "favs_repeats": "Zopakovania a obľúbené", + "follows": "Nový nasledovatelia", + "moves": "Užívateľ sa sťahuje", + "load_older": "Nahrať staršiu komunikáciu" + }, + "post_status": { + "new_status": "Poslať novú správu", + "account_not_locked_warning": "Tvoj účen nie je {0}. Ktokoľvek ťa môže začať nasledovať a tak vidieť správy určené len pre nasledovateľov.", + "account_not_locked_warning_link": "uzamknuté", + "attachments_sensitive": "Označiť prílohy ako citlivé", + "media_description": "Popis média", + "content_type": { + "text/plain": "Obyčajný text", + "text/html": "HTML", + "text/markdown": "Markdown", + "text/bbcode": "BBCode" + }, + "content_warning": "Nadpis (nepovinné)", + "default": "Práve som ...", + "direct_warning_to_all": "Túto správu bude vidieť každý užívateľ, ktorého v nej spomenieš.", + "direct_warning_to_first_only": "Táto správa bude viditeľná len pre užívateľov, ktorých vymenuješ na začiatku správy.", + "posting": "Posielanie", + "post": "Poslať", + "preview": "Náhľad", + "preview_empty": "Prázdne", + "empty_status_error": "Nie je možné odoslať prázdnu správu bez priložených súborov", + "media_description_error": "Nepodarilo sa aktualizovať média, skús znova", + "scope_notice": { + "public": "Túto správu bude vidieť každý", + "private": "Túto správu budú vidieť len tvoji nasledovníci", + "unlisted": "Táto správa nebude viditeľná na verejnej časovej osi a v celej známej sieti" + }, + "scope": { + "direct": "Priama správa - zobrazí sa len užívateľom spomenutým v správe", + "private": "Pre nasledovníkov - zobrazí sa len tvojim nasledovníkom", + "public": "Verejné - zobrazí sa vo všetkých časových osiach", + "unlisted": "Nezaradené - nezobrazí sa v žiadnej časovej osy" + } + }, + "registration": { + "bio": "Životopis", + "email": "Email", + "fullname": "Zobrazované meno", + "password_confirm": "Potvrdenie hesla", + "registration": "Registrácia", + "token": "Pozývací kód", + "captcha": "CAPTCHA", + "new_captcha": "Klikni na obrázok a vnikne nová captcha", + "username_placeholder": "napr. peter", + "fullname_placeholder": "napr. Peter Kukurica", + "bio_placeholder": "e.g.\nHi, I'm Lain.\nI’m an anime girl living in suburban Japan. You may know me from the Wired.", + "reason": "Dôvod registrácie", + "reason_placeholder": "Tento server schvaľuje registrácie manuálne.\nZanechaj správcom dôvod, prečo máš záujem vytvoriť si tu účet.", + "register": "Registrácia", + "validations": { + "username_required": "nemôže byť prázdne", + "fullname_required": "nemôže byť prázdne", + "email_required": "nemôže byť prázdne", + "password_required": "nemôže byť prázdne", + "password_confirmation_required": "nemôže byť prázdne", + "password_confirmation_match": "musí byť rovnaké ako heslo" + } + }, + "remote_user_resolver": { + "remote_user_resolver": "Vzdialené overenie užívateľa", + "searching_for": "Hľadám...", + "error": "Nenájdené." + }, + "selectable_list": { + "select_all": "Vybrať všetko" + }, + "time": { + "day": "{0} deň", + "days": "{0} dní", + "day_short": "{0}d", + "days_short": "{0}d", + "hour": "{0} hodina", + "hours": "{0} hodín", + "hour_short": "{0}h", + "hours_short": "{0}h", + "in_future": "za {0}", + "in_past": "pred {0}", + "minute": "{0} minúta", + "minutes": "{0} minút", + "minute_short": "{0}min", + "minutes_short": "{0}min", + "month": "{0} mesiac", + "months": "{0} mesiacov", + "month_short": "{0}mes", + "months_short": "{0}mes", + "now": "práve teraz", + "now_short": "teraz", + "second": "{0} sekunda", + "seconds": "{0} sekúnd", + "second_short": "{0}s", + "seconds_short": "{0}s", + "week": "{0} týždeň", + "weeks": "{0} týždňov", + "week_short": "{0}t", + "weeks_short": "{0}t", + "year": "{0} rok", + "years": "{0} rokov", + "year_short": "{0}r", + "years_short": "{0}r" + }, + "timeline": { + "collapse": "Zbaliť", + "conversation": "Konverzácia", + "error": "Chyba pri nahrávaní časovej správy: {0}", + "load_older": "Nahrať staršie správy", + "no_retweet_hint": "Správa je označená ako len-pre-nasledovateľov alebo ako priama a nemôže byť zopakovaná na tvojej časovej osy.", + "repeated": "zopakované", + "show_new": "Zobraziť nové", + "reload": "Znovu nahrať", + "up_to_date": "Aktuálne", + "no_more_statuses": "Žiadne ďalšie správy", + "no_statuses": "Žiadne správy", + "socket_reconnected": "Prepojenie v reálnom čase bolo úspešne vytvorené", + "socket_broke": "Strata prepojenia v reálnom čase: chyba CloseEvent kód {0}" + }, + "status": { + "favorites": "Obľúbené", + "repeats": "Opakovania", + "delete": "Zmazať správu", + "pin": "Pripnúť na stránku užívateľa", + "unpin": "Odopnúť zo stránky užívateľa", + "pinned": "Pripnuté", + "bookmark": "Vytvoriť záložku", + "unbookmark": "Zmazať záložku", + "delete_confirm": "Skutočne chceš zmazať túto správu?", + "reply_to": "Odpovedať komu", + "mentions": "Spomenutia", + "replies_list": "Odpovede:", + "replies_list_with_others": "Odpoveď (+{numReplies} iný): | Odpoveď (+{numReplies} iných):", + "mute_conversation": "Stíšiť konverzáciu", + "unmute_conversation": "Oznamovať konverzáciu", + "status_unavailable": "Neznámy status", + "copy_link": "Skopírovať odkaz do správy", + "external_source": "Vzdialený zdroj", + "thread_muted": "Konverzácia stíšená", + "thread_muted_and_words": ", má slová:", + "show_full_subject": "Zobraziť celý nadpis", + "hide_full_subject": "Skry celý nadpis", + "show_content": "Zobraziť obsah", + "hide_content": "Skryť obsah", + "status_deleted": "Táto správa bola zmazaná", + "nsfw": "NSFW", + "expand": "Rozbaliť správu", + "you": "(ty)", + "plus_more": "+{number} ďalších", + "many_attachments": "Správa má {number} príloh", + "collapse_attachments": "Zabaliť médiá", + "show_all_attachments": "Zobraz všetky prílohy", + "show_attachment_in_modal": "Zobraz médiá modálne", + "show_attachment_description": "Náhľad popisku (otvor prílohu pre zobrazenie celého popisku)", + "hide_attachment": "Skryť prílohy", + "remove_attachment": "Odstrániť prílohy", + "attachment_stop_flash": "Zastaviť prehrávač Flashu", + "move_up": "Presuň prílohu doľava", + "move_down": "Presuň prílohu doprava", + "open_gallery": "Otvoriť galériu", + "thread_hide": "Skry túto konverzáciu", + "thread_show": "Zobraz túto konverzáciu", + "thread_show_full": "Zobraz všetko pod touto konverzáciou (celkovo {numStatus} správa, max hĺbka {depth}) | Zobraz všetko pod touto konverzáciou (celkovo {numStatus} správ, max hĺbka {depth})", + "thread_show_full_with_icon": "{icon} {text}", + "thread_follow": "Zobraz zvyšnú časť tejto konverzácie (celkovo {numStatus} správa) | Zobraz zvyšnú časť tejto konverzácie (celkovo {numStatus} správ)", + "thread_follow_with_icon": "{icon} {text}", + "ancestor_follow": "Pozri {numReplies} ďalšiu odpoveď pod touto správou | Pozri {numReplies} ďalších odpovedí pod touto správou", + "ancestor_follow_with_icon": "{icon} {text}", + "show_all_conversation_with_icon": "{icon} {text}", + "show_all_conversation": "Zobraz celú konverzáciu ({numStatus} iná správa) | Zobraz celú konverzáciu ({numStatus} iných správ)", + "show_only_conversation_under_this": "Zobraz len správy súvisiace s touto správou" + }, + "user_card": { + "approve": "Schváliť", + "block": "Zablokovať", + "blocked": "Blokované!", + "deactivated": "Neaktívne", + "deny": "Zakázané", + "edit_profile": "Uraviť profil", + "favorites": "Obľúbené", + "follow": "Nasledovať", + "follow_cancel": "Požiadavka zrušená", + "follow_sent": "Požiadavka zaslaná!", + "follow_progress": "Žiadam o povolenie…", + "follow_unfollow": "Prestať sledovať", + "followees": "Nasleduje", + "followers": "Nasledovatelia", + "following": "Nasleduješ!", + "follows_you": "Nasleduje teba!", + "hidden": "Skryté", + "its_you": "To si ty!", + "media": "Média", + "mention": "Spomenul", + "message": "Správa", + "mute": "Stíšiť", + "muted": "Stíšené", + "per_day": "za deň", + "remote_follow": "Nasledovanie z ďaleka", + "report": "Nahlásiť", + "statuses": "Vytvorených správ", + "subscribe": "Prihlásiť k odberu", + "unsubscribe": "Odhlásiť z odberu", + "unblock": "Odblokovať", + "unblock_progress": "Oblokováva sa…", + "block_progress": "Blokujem…", + "unmute": "Povoliť oznamy", + "unmute_progress": "Povoľujem oznamy…", + "mute_progress": "Stišujem…", + "hide_repeats": "Skry zopakovania", + "show_repeats": "Zobraz zopakovania", + "bot": "Robot", + "admin_menu": { + "moderation": "Moderovanie", + "grant_admin": "Povoliť spravovanie", + "revoke_admin": "Zakázať spravovanie", + "grant_moderator": "Povoliť moderovanie", + "revoke_moderator": "Zakázať moderovanie", + "activate_account": "Aktivovať účet", + "deactivate_account": "Deaktivovať účet", + "delete_account": "Zmazať účet", + "force_nsfw": "Označ všetky správy ako NSFW", + "strip_media": "Odstrániť média zo správy", + "force_unlisted": "Vynúť, aby správy neboli zobrazované", + "sandbox": "Vynúť, aby správy boli len pre nasledovateľov", + "disable_remote_subscription": "Odstrániť prístup k serveru nasledovnému vzdialenému užívateľovi", + "disable_any_subscription": "Zakázať nasledovanie užívateľov", + "quarantine": "Zakázať federáciu správ užívateľa", + "delete_user": "Zmazať užívateľa", + "delete_user_confirmation": "Si si úplne istý? Táto akcia sa nedá zobrať späť." + }, + "highlight": { + "disabled": "Bez zvýraznenia", + "solid": "Jednoliate pozadie", + "striped": "Šrafované pozadie", + "side": "Pásik na boku" + } + }, + "user_profile": { + "timeline_title": "Časová os užívateľa", + "profile_does_not_exist": "Prepáč, tento profil neexistuje.", + "profile_loading_error": "Prepáč, nastala chyba pri nahrávaní profilu." + }, + "user_reporting": { + "title": "Nahlásení {0}", + "add_comment_description": "Hlásnenie bude zaslané moderátorom servera. Nižšie môžeš napísať dôvod prečo tento účet nahlasuješ:", + "additional_comments": "Ďalšie poznámky", + "forward_description": "Účet je z iného servera. Poslať kópiu tohto hlásenia aj tam?", + "forward_to": "Preposlať komu {0}", + "submit": "Odoslať", + "generic_error": "Nastala chyba pri vykonaní tvojej požiadavky." + }, + "who_to_follow": { + "more": "Viac", + "who_to_follow": "Koho nasledovať" + }, + "tool_tip": { + "media_upload": "Nahrať médium", + "repeat": "Zopakovať", + "reply": "Odpovedať", + "favorite": "Obľúbené", + "add_reaction": "Reagovať", + "user_settings": "Nastavenia užívateľa", + "accept_follow_request": "Prijať požiadavku nasledovníka", + "reject_follow_request": "Odmietnuť požiadavku nasledovníka", + "bookmark": "Záložka" + }, + "upload": { + "error": { + "base": "Nahrávanie bolo neúspešné.", + "message": "Nahrávanie bolo neúspešné: {0}", + "file_too_big": "Súbor je príliš veľký [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]", + "default": "Vyskúšaj opäť neskôr" + } + }, + "search": { + "people": "Ľudia", + "hashtags": "Haštagy", + "person_talking": "{count} človek hovorí", + "people_talking": "{count} ľudí hovorí", + "no_results": "Žiadne výsledky" + }, + "password_reset": { + "forgot_password": "Zabudol si heslo?", + "password_reset": "Obnovenie hesla", + "instruction": "Zadaj svoju emailovú adresu alebo užívateľské meno. Pošleme ti odkaz pomocou, ktorého môžeš obnoviť svoje heslo.", + "placeholder": "Tvoj email alebo užívateľské meno", + "check_email": "V novom emaile ti bol doručený odkaz na spôsob, ako obnovíš svoje heslo.", + "return_home": "Návrat na domácu stránku", + "too_many_requests": "Prekročil si limit pokusov, skús znova neskôr.", + "password_reset_disabled": "Obnova hesla je vypnutá. Kontaktuj, prosím, správcu tohto servera.", + "password_reset_required": "Musíš najskôr obnoviť heslo, ak sa chceš prihlásiť.", + "password_reset_required_but_mailer_is_disabled": "Musíš obnoviť svoje heslo, ale obnova hesla je na serveri vypnutá. Kontaktuj, prosím, správcu tohto servera." + }, + "chats": { + "you": "Ty:", + "message_user": "Správa {nickname}", + "delete": "Zmazať", + "chats": "Rozhovor", + "new": "Nový rozhovor", + "empty_message_error": "Nie je možné odoslať prázdnu správu", + "more": "Viac", + "delete_confirm": "Skutočne chceš zmazať túto správu?", + "error_loading_chat": "Nastala chyba pri nahrávaní rozhovoru.", + "error_sending_message": "Nastala chyba pri odosielaní správ.", + "empty_chat_list_placeholder": "Nemáš za sebou žiadne rozhovory. Začni nový rozhovor!" + }, + "file_type": { + "audio": "Audio", + "video": "Video", + "image": "Obrázok", + "file": "Súbor" + }, + "display_date": { + "today": "Dnes" + } +} diff --git a/src/i18n/te.json b/src/i18n/te.json @@ -49,7 +49,7 @@ "notifications.repeated_you": "మీ స్థితిని పునరావృతం చేసారు", "notifications.no_more_notifications": "ఇక నోటిఫికేషన్లు లేవు", "post_status.new_status": "క్రొత్త స్థితిని పోస్ట్ చేయండి", - "post_status.account_not_locked_warning": "మీ ఖాతా {౦} కాదు. ఎవరైనా మిమ్మల్ని అనుసరించి అనుచరులకు మాత్రమే ఉద్దేశించిన పోస్టులను చూడవచ్చు.", + "post_status.account_not_locked_warning": "మీ ఖాతా {0} కాదు. ఎవరైనా మిమ్మల్ని అనుసరించి అనుచరులకు మాత్రమే ఉద్దేశించిన పోస్టులను చూడవచ్చు.", "post_status.account_not_locked_warning_link": "తాళం వేయబడినది", "post_status.attachments_sensitive": "జోడింపులను సున్నితమైనవిగా గుర్తించండి", "post_status.content_type.text/plain": "సాధారణ అక్షరాలు", diff --git a/src/i18n/uk.json b/src/i18n/uk.json @@ -24,7 +24,15 @@ }, "flash_content": "Натисніть для перегляду змісту Flash за допомогою Ruffle (експериментально, може не працювати).", "flash_security": "Ця функція може становити ризик, оскільки Flash-вміст все ще є потенційно небезпечним.", - "flash_fail": "Не вдалося завантажити Flash-вміст, докладнішу інформацію дивись у консолі." + "flash_fail": "Не вдалося завантажити Flash-вміст, докладнішу інформацію дивись у консолі.", + "generic_error_message": "Виникла помилка: {0}", + "never_show_again": "Ніколи не показувати знову", + "scope_in_timeline": { + "direct": "Приватне", + "private": "Лише читачі", + "public": "Публічне", + "unlisted": "Непублічне" + } }, "finder": { "error_fetching_user": "Користувача не знайдено", @@ -39,7 +47,8 @@ "scope_options": "Параметри обсягу", "media_proxy": "Посередник медіа-даних", "text_limit": "Ліміт символів", - "upload_limit": "Обмеження завантажень" + "upload_limit": "Обмеження завантажень", + "shout": "Оголошення" }, "exporter": { "processing": "Опрацьовую, скоро ви зможете завантажити файл", @@ -70,7 +79,9 @@ "accept": "Прийняти", "reject": "Відхилити", "accept_desc": "Поточний інстанс приймає повідомлення тільки з перелічених інстансів:", - "simple_policies": "Правила поточного інстансу" + "simple_policies": "Правила поточного інстансу", + "reason": "Причина", + "not_applicable": "н/в" }, "mrf_policies_desc": "Правила MRF розповсюджуються на даний інстанс. Наступні правила активні:", "mrf_policies": "Активувати правила MRF (модуль переписування повідомлень)", @@ -141,7 +152,8 @@ "followed_you": "підписався(-лась) на вас", "favorited_you": "вподобав(-ла) ваш допис", "broken_favorite": "Невідомий допис, шукаю його…", - "error": "Помилка при оновленні сповіщень: {0}" + "error": "Помилка при оновленні сповіщень: {0}", + "poll_ended": "опитування закінчено" }, "nav": { "chats": "Чати", @@ -161,11 +173,14 @@ "mentions": "Згадування", "back": "Назад", "administration": "Адміністрування", - "home_timeline": "Домашня стрічка" + "home_timeline": "Домашня стрічка", + "lists": "Списки" }, "media_modal": { "next": "Наступна", - "previous": "Попередня" + "previous": "Попередня", + "counter": "{current} / {total}", + "hide": "Закрити медіапереглядач" }, "password_reset": { "instruction": "Введіть свою адресу електронної пошти або ім’я користувача. Ми надішлемо вам посилання для скидання пароля.", @@ -205,7 +220,8 @@ "load_older": "Завантажити давніші взаємодії", "follows": "Нові підписки", "favs_repeats": "Поширення та вподобайки", - "moves": "Міграції користувачів" + "moves": "Міграції користувачів", + "emoji_reactions": "Емоджі реакції" }, "errors": { "storage_unavailable": "Pleroma не змогла отримати доступ до сховища браузеру. Ваша сесія та налаштування не будуть збережені, це може спричинити непередбачувані проблеми. Спробуйте увімкнути cookie." @@ -638,7 +654,35 @@ "backup_restore": "Резервне копіювання налаштувань" }, "right_sidebar": "Показувати бокову панель справа", - "hide_shoutbox": "Приховати оголошення інстансу" + "hide_shoutbox": "Приховати оголошення інстансу", + "setting_server_side": "Цей параметр прив’язаний до вашого профілю та впливає на всі сеанси та клієнти", + "lists_navigation": "Показувати списки в навігації", + "account_backup": "Резервне копіювання облікового запису", + "account_backup_description": "Це дозволяє завантажити архів даних вашого облікового запису та ваших дописів, але їх ще не можна імпортувати в обліковий запис Pleroma.", + "add_backup_error": "Не вдалося додати нову резервну копію: {error}", + "account_alias": "Псевдоніми облікового запису", + "new_alias_target": "Додати новий псевдонім (напр. {example})", + "move_account_notes": "Якщо ви хочете перемістити обліковий запис на інший інстанс, вам потрібно перейти до свого цільового облікового запису та додати псевдонім, що вказує цей обліковий запис.", + "added_backup": "Додано нову резервну копію.", + "expert_mode": "Показати додаткові параметри", + "post_look_feel": "Відображення дописів", + "email_language": "Мова для отримання електронних листів від сервера", + "account_backup_table_head": "Резервне копіювання", + "download_backup": "Завантажити", + "backup_not_ready": "Резервна копія ще не готова.", + "remove_backup": "Видалити", + "list_backups_error": "Помилка під час отримання списку резервних копій: {error}", + "add_backup": "Створити нову резервну копію", + "account_alias_table_head": "Псевдонім", + "list_aliases_error": "Помилка під час отримання псевдонімів: {error}", + "hide_list_aliases_error_action": "Закрити", + "remove_alias": "Видалити цей псевдонім", + "added_alias": "Псевдонім додано.", + "add_alias_error": "Помилка під час додавання псевдоніма: {error}", + "move_account": "Перемістити обліковий запис", + "move_account_target": "Цільовий обліковий запис (напр. {example})", + "moved_account": "Обліковий запис переміщено.", + "move_account_error": "Помилка під час переміщення облікового запису: {error}" }, "selectable_list": { "select_all": "Вибрати все" @@ -670,7 +714,10 @@ "captcha": "CAPTCHA", "register": "Зареєструватися", "reason_placeholder": "Цей інстанс обробляє запити на реєстрацію вручну.\nРозкажіть адміністрації чому ви хочете зареєструватися.", - "reason": "Причина реєстрації" + "reason": "Причина реєстрації", + "bio_optional": "Біографія (необов'язково)", + "email_language": "Якою мовою ви бажаєте отримувати електронні листи від сервера?", + "email_optional": "Ел. пошта (необов'язково)" }, "who_to_follow": { "who_to_follow": "На кого підписатися", @@ -755,7 +802,6 @@ "deactivate_account": "Деактивувати обліковий запис", "delete_account": "Видалити обліковий запис", "moderation": "Модерація", - "delete_user_confirmation": "Ви абсолютно впевнені? Цю дію неможливо буде скасовувати.", "delete_user": "Видалити обліковий запис", "strip_media": "Вилучити медіа з дописів користувача", "force_nsfw": "Позначити всі дописи як NSFW", @@ -861,5 +907,12 @@ "profile_loading_error": "Вибачте, під час завантаження цього профілю виникла помилка.", "profile_does_not_exist": "Вибачте, цей профіль більше не існує.", "timeline_title": "Стрічка користувача" + }, + "report": { + "notes": "Примітки:", + "state": "Статус:", + "state_open": "відкритий", + "state_closed": "закритий", + "state_resolved": "вирішений" } } diff --git a/src/i18n/vi.json b/src/i18n/vi.json @@ -51,7 +51,7 @@ "scope_options": "Đa dạng kiểu đăng" }, "finder": { - "error_fetching_user": "Lỗi người dùng", + "error_fetching_user": "Lỗi khi nạp người dùng", "find_user": "Tìm người dùng" }, "shoutbox": { @@ -149,7 +149,7 @@ "no_more_notifications": "Không còn thông báo nào", "migrated_to": "chuyển sang", "reacted_with": "chạm tới {0}", - "error": "Lỗi xử lý thông báo: {0}" + "error": "Lỗi khi nạp thông báo {0}" }, "polls": { "add_poll": "Tạo bình chọn", @@ -197,7 +197,7 @@ "text/bbcode": "BBCode" }, "content_warning": "Tiêu đề (tùy chọn)", - "default": "Just landed in L.A.", + "default": "Đời người con gái không muốn yêu ai được không?", "direct_warning_to_first_only": "Người đầu tiên được nhắc đến mới có thể thấy tút này.", "posting": "Đang đăng tút", "post": "Đăng", @@ -427,9 +427,445 @@ "no_rich_text_description": "Không hiện rich text trong các tút", "hide_follows_count_description": "Ẩn số lượng người tôi theo dõi", "nsfw_clickthrough": "Cho phép nhấn vào xem các tút nhạy cảm", - "reply_visibility_following": "Chỉ hiện những trả lời có nhắc tới tôi hoặc từ những người mà tôi theo dõi" + "reply_visibility_following": "Chỉ hiện những trả lời có nhắc tới tôi hoặc từ những người mà tôi theo dõi", + "autohide_floating_post_button": "Ẩn nút viết tút khi xem bảng tin (di động)", + "saving_err": "Thiết lập lỗi lưu", + "saving_ok": "Đã lưu các thay đổi", + "search_user_to_block": "Tìm người bạn muốn chặn", + "search_user_to_mute": "Tìm người bạn muốn ẩn", + "security_tab": "Bảo mật", + "scope_copy": "Chép phạm vi khi trả lời (tin nhắn luôn được chép sẵn)", + "minimal_scopes_mode": "Tùy chọn thu nhỏ phạm vi tút", + "set_new_avatar": "Đổi ảnh đại diện", + "set_new_profile_background": "Đổi ảnh nền", + "set_new_profile_banner": "Đổi ảnh bìa", + "reset_profile_background": "Đặt lại ảnh nền", + "reset_profile_banner": "Đặt lại ảnh bìa", + "reset_banner_confirm": "Bạn có chắc chắn muốn đặt lại ảnh bìa?", + "reset_background_confirm": "Bạn có chắc chắn muốn đặt lại ảnh nền?", + "settings": "Cài đặt", + "subject_input_always_show": "Luôn hiện vùng tiêu đề", + "subject_line_behavior": "Chép tiêu đề khi trả lời", + "subject_line_email": "Giống email: \"re: subject\"", + "subject_line_mastodon": "Giống Mastodon: copy as is", + "subject_line_noop": "Đừng chép", + "sensitive_by_default": "Mặc định tút là nhạy cảm", + "stop_gifs": "Chỉ phát GIF khi chạm vào", + "streaming": "Tự động tải tút mới khi cuộn lên trên", + "user_mutes": "Người dùng", + "useStreamingApiWarning": "(Tính năng thử nghiệm, không đề xuất sử dụng)", + "text": "Văn bản", + "theme": "Theme", + "theme_help": "Dùng mã màu hex (#rrggbb) để tự chế theme.", + "tooltipRadius": "Tooltips/alerts", + "type_domains_to_mute": "Tìm máy chủ để ẩn", + "upload_a_photo": "Tải ảnh lên", + "user_settings": "Thiết lập người dùng", + "values": { + "false": "không", + "true": "có" + }, + "virtual_scrolling": "Render bảng tin", + "fun": "Vui nhộn", + "greentext": "Mũi tên meme", + "notifications": "Thông báo", + "notification_setting_filters": "Bộ lọc", + "notification_setting_block_from_strangers": "Chặn thông báo từ những người bạn không theo dõi", + "notification_setting_privacy": "Riêng tư", + "notification_setting_hide_notification_contents": "Ẩn người gửi và nội dung thông báo đẩy", + "notification_mutes": "Sử dụng ẩn nếu muốn dừng nhận thông báo từ một người cụ thể.", + "notification_blocks": "Chặn một người ngừng toàn bộ thông báo cũng giống như hủy đăng ký họ.", + "more_settings": "Cài đặt khác", + "style": { + "switcher": { + "keep_shadows": "Giữ bóng đổ", + "keep_color": "Giữ màu", + "keep_opacity": "Giữ trong suốt", + "keep_roundness": "Giữ bo tròn góc", + "reset": "Đặt lại", + "clear_all": "Xóa hết", + "clear_opacity": "Xóa trong suốt", + "load_theme": "Tải theme", + "keep_as_is": "Giữ như là", + "use_snapshot": "Bản cũ", + "use_source": "Bản mới", + "help": { + "upgraded_from_v2": "PleromaFE đã được nâng cấp, theme có thể khác hơn một chút so với bản cũ.", + "v2_imported": "Tập tin bạn nhập là từ phiên bản PleromaFE cũ. Chúng tôi sẽ cố làm nó tương thích nhưng có thể sẽ có xung đột.", + "older_version_imported": "Tập tin bạn vừa nhập được tạo ra từ phiên bản PleromaFE cũ.", + "snapshot_present": "Đã tải theme snapshot, mọi giá trị sẽ bị chép đè. Thay vào đó, bạn có thể tải dữ liệu chắc chắn của theme.", + "fe_upgraded": "Theme của PleromaFE được nâng cấp sau mỗi phiên bản.", + "fe_downgraded": "Theme của phiên bản PleromaFE đã được hạ cấp.", + "migration_snapshot_ok": "Theme snapshot đã tải xong. Bạn có thể thử tải dữ liệu theme.", + "migration_napshot_gone": "Nếu thiếu snapshot, một số thứ sẽ khác với ban đầu.", + "future_version_imported": "Tập tin bạn vừa nhập được tạo ra từ phiên bản PleromaFE mới.", + "snapshot_missing": "Không có theme snapshot trong tập tin cho nên có thể nó sẽ khác với bản gốc đôi chút.", + "snapshot_source_mismatch": "Xung đột phiên bản: hầu hết Pleroma FE đã hạ cấp và cập nhật lại, nếu bạn đổi theme sử dụng phiên bản cũ hơn của FE, bạn gần như muốn sử dụng phiên bản cũ, thay vào đó sử dụng phiên bản mới." + }, + "keep_fonts": "Giữ phông chữ", + "save_load_hint": "Giúp giữ nguyên các tùy chọn hiện tại khi chọn hoặc tải theme khác, nó cũng lưu trữ các tùy chọn đã nói khi xuất một theme. Khi tất cả các hộp kiểm bị bỏ trống, việc xuất theme sẽ lưu mọi thứ." + }, + "common": { + "color": "Màu sắc", + "opacity": "Trong suốt", + "contrast": { + "hint": "Tỉ lệ tương phản là {ratio}, nó {level} {context}", + "level": { + "aa": "đạt mức AA (tối thiểu)", + "aaa": "đạt mức AAA (đề xuất)", + "bad": "không đạt yêu cầu" + }, + "context": { + "18pt": "cỡ chữ lớn (18pt+)", + "text": "cho chữ" + } + } + }, + "common_colors": { + "_tab_label": "Chung", + "main": "Màu sắc chung", + "foreground_hint": "Mở tab \"Nâng cao\" để có nhiều tùy chọn hơn", + "rgbo": "Icons, accents, badges" + }, + "advanced_colors": { + "_tab_label": "Nâng cao", + "alert": "Nền cảnh báo", + "alert_error": "Lỗi", + "alert_warning": "Cảnh báo", + "alert_neutral": "Neutral", + "post": "Tút/Tiểu sử", + "badge": "Nền huy hiệu", + "popover": "Tooltips, menus, popovers", + "badge_notification": "Thông báo", + "panel_header": "Tiêu đề panel", + "top_bar": "Thanh trên cùng", + "borders": "Đường biên", + "buttons": "Nút bấm", + "faint_text": "Chữ mờ", + "underlay": "Lớp dưới", + "wallpaper": "Wallpaper", + "poll": "Biểu đồ cuộc bình chọn", + "icons": "Biểu tượng", + "highlight": "Những thành phần nổi bật", + "pressed": "Khi nhấn xuống", + "selectedPost": "Chọn tút", + "selectedMenu": "Chọn menu", + "toggled": "Toggled", + "tabs": "Tab", + "chat": { + "incoming": "Tin nhắn đến", + "outgoing": "Tin nhắn đi", + "border": "Đường biên" + }, + "inputs": "Khung soạn thảo", + "disabled": "Vô hiệu hóa" + }, + "radii": { + "_tab_label": "Góc bo tròn" + }, + "shadows": { + "component": "Thành phần", + "shadow_id": "Đổ bóng #{value}", + "blur": "Làm mờ", + "spread": "Mở rộng", + "inset": "Thu vào", + "filter_hint": { + "always_drop_shadow": "Chú ý, màu bóng đổ này luôn sử dụng {0} nếu trình duyệt hỗ trợ.", + "drop_shadow_syntax": "{0} không hỗ trợ {1} phần và từ khóa {2}.", + "spread_zero": "Bóng đổ > 0 sẽ xuất hiện nếu chọn nó thành không", + "inset_classic": "Bóng đổ inset sẽ sử dụng {0}", + "avatar_inset": "Nếu trộn lẫn bóng đổ inset và non-inset trên ảnh đại diện có thể khiến ảnh đại diện biến thành trong suốt." + }, + "components": { + "panel": "Panel", + "panelHeader": "Panel ảnh bìa", + "topBar": "Thanh trên cùng", + "avatar": "Ảnh đại diện (ở trang cá nhân)", + "avatarStatus": "Ảnh đại diện (ở tút)", + "popup": "Popups và tooltips", + "button": "Nút bấm", + "buttonHover": "Nút bấm (khi rê chuột)", + "buttonPressed": "Nút bấm (khi nhấn chuột)", + "buttonPressedHover": "Nút bấm (khi nhấn+giữ)", + "input": "Khung soạn thảo" + }, + "_tab_label": "Đổ bóng và tô sáng", + "override": "Chép đè", + "hintV3": "Với bóng đổ, bạn có thể sử dụng ký hiệu {0} để dùng slot màu khác." + }, + "fonts": { + "_tab_label": "Phông chữ", + "components": { + "interface": "Giao diện chung", + "input": "Khung soạn thảo", + "post": "Tút", + "postCode": "Chữ monospaced (rich text)" + }, + "family": "Tên phông", + "size": "Kích cỡ (px)", + "weight": "Độ đậm", + "custom": "Tùy chỉnh", + "help": "Chọn phông chữ hiển thị. Để \"tùy chọn\", bạn phải nhập chính xác tên phông chữ trên hệ thống." + }, + "preview": { + "header": "Xem trước", + "content": "Nội dung", + "error": "Lỗi mẫu ví dụ", + "button": "Nút bấm", + "text": "Một đống {0} và {1}", + "mono": "nội dung", + "input": "Đời người con gái không muốn yêu ai được không?", + "faint_link": "tài liệu hướng dẫn", + "checkbox": "Tôi đã đọc lướt qua quy tắc và chính sách bảo mật", + "link": "Link đẹp đó em yêu", + "fine_print": "Đọc {0} để tìm hiểu thêm!", + "header_faint": "OK nè" + } + }, + "version": { + "title": "Phiên bản", + "frontend_version": "Frontend", + "backend_version": "Backend" + }, + "reset_avatar": "Đặt lại ảnh đại diện", + "reset_avatar_confirm": "Bạn có chắc chắn muốn đặt lại ảnh đại diện?", + "post_status_content_type": "Loại tút đăng", + "useStreamingApi": "Nhận tút và thông báo theo thời gian thực", + "theme_help_v2_1": "Bạn cũng có thể xóa hết màu thành phần và làm theme trong suốt, chọn nút \"Xóa hết\".", + "theme_help_v2_2": "Các biểu tượng bên dưới các mục có độ tương phản nền/văn bản, hãy rê chuột qua để biết thông tin chi tiết. Xin lưu ý rằng, khi sử dụng các độ tương phản trong suốt có thể khiến đọc chữ không ra.", + "enable_web_push_notifications": "Cho phép thông báo đẩy trên web", + "mentions_new_style": "Lượt nhắc màu mè", + "mentions_new_place": "Đặt lượt nhắc ở dòng riêng", + "always_show_post_button": "Luôn hiện nút viết tút mới" }, "errors": { "storage_unavailable": "Pleroma không thể truy cập lưu trữ trình duyệt. Thông tin đăng nhập và những thiết lập tạm thời sẽ bị mất. Hãy cho phép cookies." + }, + "time": { + "day": "{0} ngày", + "days": "{0} ngày", + "day_short": "{0} ngày", + "days_short": "{0} ngày", + "hour": "{0} giờ", + "hours": "{0} giờ", + "hour_short": "{0} giờ", + "hours_short": "{0} giờ", + "in_future": "lúc {0}", + "in_past": "{0} trước", + "minute": "{0} phút", + "minutes": "{0} phút", + "minute_short": "{0} phút", + "minutes_short": "{0} phút", + "month": "{0} tháng", + "months": "{0} tháng", + "month_short": "{0} tháng", + "months_short": "{0} tháng", + "now": "vừa xong", + "second": "{0} giây", + "seconds": "{0} giây", + "second_short": "{0}s", + "seconds_short": "{0}s", + "week": "{0} tuần", + "weeks": "{0} tuần", + "week_short": "{0} tuần", + "weeks_short": "{0} tuần", + "year": "{0} năm", + "years": "{0} năm", + "year_short": "{0} năm", + "years_short": "{0} năm", + "now_short": "vừa xong" + }, + "timeline": { + "collapse": "Thu gọn", + "error": "Lỗi khi nạp bảng tin {0}", + "load_older": "Xem tút cũ hơn", + "repeated": "chia sẻ", + "show_new": "Hiện mới", + "reload": "Tải lại", + "up_to_date": "Đã tải những tút mới nhất", + "no_more_statuses": "Không còn tút nào", + "no_statuses": "Trống trơn!", + "socket_reconnected": "Thiết lập kết nối thời gian thực", + "conversation": "Thảo luận", + "no_retweet_hint": "Không thể chia sẻ tin nhắn và những tút riêng tư", + "socket_broke": "Mất kết nối thời gian thực: CloseEvent {0}" + }, + "status": { + "repeats": "Chia sẻ", + "delete": "Xóa tút", + "unpin": "Bỏ ghim trên trang cá nhân", + "pin": "Ghim trên trang cá nhân", + "pinned": "Tút được ghim", + "bookmark": "Lưu", + "unbookmark": "Bỏ lưu", + "reply_to": "Trả lời", + "replies_list": "Những trả lời:", + "mute_conversation": "Không quan tâm nữa", + "unmute_conversation": "Quan tâm", + "status_unavailable": "Không tìm thấy tút", + "copy_link": "Sao chép URL", + "external_source": "Nguồn bên ngoài", + "thread_muted": "Đã ẩn chủ đề", + "thread_muted_and_words": ", có từ:", + "hide_full_subject": "Ẩn tiêu đề", + "show_content": "Hiện nội dung", + "hide_content": "Ẩn nội dung", + "status_deleted": "Tút này đã bị xóa", + "nsfw": "Nhạy cảm", + "expand": "Xem nguyên văn", + "favorites": "Thích", + "delete_confirm": "Bạn có chắc chắn muốn xóa tút này?", + "show_full_subject": "Hiện đầy đủ tiêu đề", + "you": "(Bạn)", + "mentions": "Lượt nhắc", + "plus_more": "+{number} nhiều hơn" + }, + "user_card": { + "approve": "Chấp nhận", + "block": "Chặn", + "blocked": "Đã chặn!", + "deny": "Từ chối", + "edit_profile": "Chỉnh sửa trang cá nhân", + "favorites": "Thích", + "follow": "Theo dõi", + "follow_progress": "Đang yêu cầu…", + "follow_again": "Gửi lại yêu cầu?", + "follow_unfollow": "Ngưng theo dõi", + "followees": "Đang theo dõi", + "followers": "Người theo dõi", + "following": "Đang theo dõi!", + "follows_you": "Theo dõi bạn!", + "hidden": "Ẩn", + "media": "Media", + "mention": "Lượt nhắc", + "message": "Tin nhắn", + "mute": "Ẩn", + "muted": "Đã ẩn", + "per_day": "tút mỗi ngày", + "remote_follow": "Theo dõi từ xa", + "report": "Báo cáo", + "statuses": "Tút", + "subscribe": "Đăng ký", + "unsubscribe": "Hủy đăng ký", + "unblock": "Bỏ chặn", + "unblock_progress": "Đang bỏ chặn…", + "block_progress": "Đang chặn…", + "unmute": "Bỏ ẩn", + "unmute_progress": "Đang bỏ ẩn…", + "mute_progress": "Đang ẩn…", + "hide_repeats": "Ẩn lượt chia sẻ", + "show_repeats": "Hiện lượt chia sẻ", + "bot": "Bot", + "admin_menu": { + "moderation": "Kiểm duyệt", + "grant_admin": "Chỉ định Quản trị viên", + "revoke_admin": "Gỡ bỏ Quản trị viên", + "grant_moderator": "Chỉ định Kiểm duyệt viên", + "activate_account": "Xác thực người dùng", + "deactivate_account": "Vô hiệu hóa người dùng", + "delete_account": "Xóa người dùng", + "force_nsfw": "Đánh dấu tất cả tút là nhạy cảm", + "strip_media": "Gỡ bỏ media trong tút", + "sandbox": "Đánh dấu tất cả tút là riêng tư", + "disable_remote_subscription": "Không cho phép theo dõi từ máy chủ khác", + "disable_any_subscription": "Không cho phép theo dõi bất cứ ai", + "quarantine": "Không cho phép tút liên hợp", + "delete_user": "Xóa người dùng", + "revoke_moderator": "Gỡ bỏ Quản trị viên", + "force_unlisted": "Đánh dấu tất cả tút là hạn chế" + }, + "highlight": { + "disabled": "Không nổi bật", + "solid": "Nền 1 màu", + "striped": "Nền 2 màu", + "side": "Sọc bên" + }, + "follow_sent": "Đã gửi yêu cầu!", + "its_you": "Đó là bạn!" + }, + "user_profile": { + "timeline_title": "Bảng tin người dùng", + "profile_does_not_exist": "Xin lỗi, tài khoản này không tồn tại.", + "profile_loading_error": "Xin lỗi, có lỗi xảy ra khi xem trang cá nhân này." + }, + "user_reporting": { + "title": "Báo cáo {0}", + "additional_comments": "Ghi chú", + "forward_description": "Người này thuộc máy chủ khác. Gửi một báo cáo ẩn danh tới máy chủ đó?", + "forward_to": "Chuyển cho {0}", + "submit": "Gửi", + "generic_error": "Có lỗi xảy ra khi xử lý yêu cầu của bạn.", + "add_comment_description": "Hãy cho quản trị viên biết lý do vì sao bạn báo cáo người này:" + }, + "who_to_follow": { + "more": "Nhiều hơn nữa", + "who_to_follow": "Những người dùng nổi bật" + }, + "tool_tip": { + "media_upload": "Tải lên media", + "repeat": "Chia sẻ", + "reply": "Trả lời", + "favorite": "Thích", + "add_reaction": "Thêm tương tác", + "accept_follow_request": "Phê duyệt yêu cầu theo dõi", + "reject_follow_request": "Từ chối yêu cầu theo dõi", + "bookmark": "Lưu", + "user_settings": "Thiết lập người dùng" + }, + "upload": { + "error": { + "base": "Tải lên thất bại.", + "message": "Tải lên thất bại: {0}", + "file_too_big": "Tập tin quá lớn [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]", + "default": "Hãy thử lại sau" + }, + "file_size_units": { + "KiB": "KB", + "MiB": "MB", + "GiB": "GB", + "B": "byte", + "TiB": "TB" + } + }, + "search": { + "people": "Người", + "hashtags": "Hashtag", + "person_talking": "{count} người đang trò chuyện", + "people_talking": "{count} người đang trò chuyện", + "no_results": "Không tìm thấy" + }, + "password_reset": { + "forgot_password": "Quên mật khẩu", + "password_reset": "Đổi mật khẩu", + "placeholder": "Email hoặc tên người dùng", + "check_email": "Kiểm tra email của bạn.", + "return_home": "Quay lại Pleroma", + "too_many_requests": "Bạn đã vượt giới hạn cho phép, hãy thử lại sau.", + "password_reset_disabled": "Reset mật khẩu bị tắt. Hãy liên hệ quản trị viên máy chủ.", + "password_reset_required": "Bạn phải đổi mật khẩu để đăng nhập.", + "instruction": "Nhập email hoặc tên người dùng. Chúng tôi sẽ gửi email reset mật khẩu cho bạn.", + "password_reset_required_but_mailer_is_disabled": "Bạn cần phải đổi mật khẩu, nhưng tính năng bị tắt. Hãy liên hệ quản trị viên máy chủ." + }, + "chats": { + "you": "Bạn:", + "message_user": "Nhắn tin {nickname}", + "delete": "Xóa", + "chats": "Chat", + "new": "Chat mới", + "empty_message_error": "Không thể gửi tin nhắn trống", + "more": "Nhiều hơn", + "delete_confirm": "Bạn có chắc chắn muốn xóa tin nhắn này?", + "error_loading_chat": "Có vấn đề khi tải giao diện chat.", + "error_sending_message": "Có vấn đề khi gửi tin nhắn.", + "empty_chat_list_placeholder": "Bạn không có tin nhắn. Hãy bắt đầu nhắn cho ai đó!" + }, + "file_type": { + "audio": "Âm thanh", + "video": "Video", + "image": "Hình ảnh", + "file": "Tập tin" + }, + "display_date": { + "today": "Hôm nay" } } diff --git a/src/i18n/zh.json b/src/i18n/zh.json @@ -15,7 +15,8 @@ "title": "功能", "who_to_follow": "推荐关注", "pleroma_chat_messages": "Pleroma 聊天", - "upload_limit": "上传限制" + "upload_limit": "上传限制", + "shout": "留言板" }, "finder": { "error_fetching_user": "获取用户时发生错误", @@ -46,7 +47,13 @@ }, "flash_content": "点击以使用 Ruffle 显示 Flash 内容(实验性,可能无效)。", "flash_security": "注意这可能有潜在的危险,因为 Flash 内容仍然是任意的代码。", - "flash_fail": "Flash 内容加载失败,请在控制台查看详情。" + "flash_fail": "Flash 内容加载失败,请在控制台查看详情。", + "scope_in_timeline": { + "public": "公开", + "direct": "私讯", + "private": "仅关注者", + "unlisted": "列外" + } }, "image_cropper": { "crop_picture": "裁剪图片", @@ -79,7 +86,9 @@ }, "media_modal": { "previous": "往前", - "next": "往后" + "next": "往后", + "hide": "关闭媒体查看器", + "counter": "{current} / {total}" }, "nav": { "about": "关于", @@ -114,7 +123,8 @@ "reacted_with": "作出了 {0} 的反应", "migrated_to": "迁移到了", "follow_request": "想要关注你", - "error": "取得通知时发生错误:{0}" + "error": "取得通知时发生错误:{0}", + "poll_ended": "投票结束了" }, "polls": { "add_poll": "增加投票", @@ -197,7 +207,8 @@ }, "reason_placeholder": "此实例的注册需要手动批准。\n请让管理员知道您为什么想要注册。", "reason": "注册理由", - "register": "注册" + "register": "注册", + "email_language": "你想从服务器收到什么语言的邮件?" }, "selectable_list": { "select_all": "选择全部" @@ -589,7 +600,38 @@ "backup_restore": "设置备份" }, "right_sidebar": "在右侧显示侧边栏", - "hide_shoutbox": "隐藏实例留言板" + "hide_shoutbox": "隐藏实例留言板", + "expert_mode": "显示高级", + "download_backup": "下载", + "mention_links": "提及链接", + "account_backup": "账号备份", + "account_backup_table_head": "备份", + "remove_backup": "移除", + "list_backups_error": "获取备份列表出错:{error}", + "add_backup": "创建一个新备份", + "added_backup": "创建了一个新备份。", + "account_alias": "账号别名", + "account_alias_table_head": "别名", + "list_aliases_error": "获取别名时出错:{error}", + "hide_list_aliases_error_action": "关闭", + "remove_alias": "移除这个别名", + "new_alias_target": "添加一个新别名(例如 {example})", + "added_alias": "别名添加好了。", + "move_account": "移动账号", + "move_account_target": "目标账号(例如 {example})", + "moved_account": "账号移动好了。", + "move_account_error": "移动账号时出错:{error}", + "setting_server_side": "这个设置是捆绑到你的个人资料的,能影响所有会话和客户端", + "post_look_feel": "文章的样子跟感受", + "email_language": "从服务器收邮件的语言", + "account_backup_description": "这个允许你下载一份账号信息和文章的存档,但是现在还不能导入到 Pleroma 账号里。", + "backup_not_ready": "备份还没准备好。", + "add_backup_error": "添加新备份时出错:{error}", + "add_alias_error": "添加别名时出错:{error}", + "move_account_notes": "如果你想把账号移动到别的地方,你必须去目标账号,然后加一个指向这里的别名。", + "wordfilter": "词语过滤器", + "user_profiles": "用户资料", + "third_column_mode_notifications": "消息栏" }, "time": { "day": "{0} 天", @@ -623,7 +665,23 @@ "year": "{0} 年", "years": "{0} 年", "year_short": "{0}y", - "years_short": "{0}y" + "years_short": "{0}y", + "unit": { + "days_short": "{0} 天", + "hours": "{0} 小时", + "hours_short": "{0} 时", + "minutes": "{0} 分", + "minutes_short": "{0} 分", + "months": "{0} 个月", + "months_short": "{0} 月", + "seconds": "{0} 秒", + "seconds_short": "{0} 秒", + "weeks_short": "{0} 周", + "years": "{0} 年", + "years_short": "{0} 年", + "weeks": "{0} 周", + "days": "{0} 天" + } }, "timeline": { "collapse": "折叠", @@ -666,7 +724,32 @@ "status_deleted": "该状态已被删除", "nsfw": "NSFW", "external_source": "外部来源", - "expand": "展开" + "expand": "展开", + "you": "(你)", + "plus_more": "还有 {number} 个", + "many_attachments": "文章有 {number} 个附件", + "collapse_attachments": "折起附件", + "show_all_attachments": "显示所有附件", + "show_attachment_description": "预览描述(打开附件能看完整描述)", + "hide_attachment": "隐藏附件", + "remove_attachment": "移除附件", + "attachment_stop_flash": "停止 Flash 播放器", + "move_up": "把附件左移", + "open_gallery": "打开图库", + "thread_hide": "隐藏这个线索", + "thread_show": "显示这个线索", + "thread_show_full_with_icon": "{icon} {text}", + "thread_follow": "查看这个线索的剩余部分(一共有 {numStatus} 个状态)", + "thread_follow_with_icon": "{icon} {text}", + "ancestor_follow": "查看这个状态下的别的 {numReplies} 个回复", + "ancestor_follow_with_icon": "{icon} {text}", + "show_all_conversation_with_icon": "{icon} {text}", + "show_all_conversation": "显示完整对话(还有 {numStatus} 个状态)", + "mentions": "提及", + "replies_list_with_others": "回复(另外 +{numReplies} 个):", + "move_down": "把附件右移", + "thread_show_full": "显示这个线索下的所有东西(一共有 {numStatus} 个状态,最大深度 {depth})", + "show_only_conversation_under_this": "只显示这个状态的回复" }, "user_card": { "approve": "核准", @@ -714,8 +797,7 @@ "disable_remote_subscription": "禁止从远程实例关注用户", "disable_any_subscription": "完全禁止关注用户", "quarantine": "从联合实例中禁止用户帖子", - "delete_user": "删除用户", - "delete_user_confirmation": "你确定吗?此操作无法撤销。" + "delete_user": "删除用户" }, "hidden": "已隐藏", "show_repeats": "显示转发", @@ -825,7 +907,10 @@ "media_nsfw": "强制设置媒体为敏感内容", "media_removal_desc": "本实例移除来自以下实例的媒体内容:", "ftl_removal_desc": "该实例在从“已知网络”时间线上移除了下列实例:", - "ftl_removal": "从“已知网络”时间线上移除" + "ftl_removal": "从“已知网络”时间线上移除", + "reason": "理由", + "not_applicable": "无", + "instance": "实例" }, "mrf_policies_desc": "MRF 策略会影响本实例的互通行为。以下策略已启用:", "mrf_policies": "已启用的 MRF 策略", diff --git a/src/i18n/zh_Hant.json b/src/i18n/zh_Hant.json @@ -747,7 +747,6 @@ "admin_menu": { "delete_account": "刪除賬號", "delete_user": "刪除用戶", - "delete_user_confirmation": "你確認嗎?此操作無法撤銷。", "moderation": "調停", "grant_admin": "賦予管理權限", "revoke_admin": "撤銷管理權限", diff --git a/src/lib/notification-i18n-loader.js b/src/lib/notification-i18n-loader.js @@ -3,8 +3,8 @@ // meant to be used to load the partial i18n we need for // the service worker. module.exports = function (source) { - var object = JSON.parse(source) - var smol = { + const object = JSON.parse(source) + const smol = { notifications: object.notifications || {} } diff --git a/src/lib/persisted_state.js b/src/lib/persisted_state.js @@ -1,20 +1,23 @@ import merge from 'lodash.merge' import localforage from 'localforage' -import { each, get, set } from 'lodash' +import { each, get, set, cloneDeep } from 'lodash' let loaded = false const defaultReducer = (state, paths) => ( - paths.length === 0 ? state : paths.reduce((substate, path) => { - set(substate, path, get(state, path)) - return substate - }, {}) + paths.length === 0 + ? state + : paths.reduce((substate, path) => { + set(substate, path, get(state, path)) + return substate + }, {}) ) const saveImmedeatelyActions = [ 'markNotificationsAsSeen', 'clearCurrentUser', 'setCurrentUser', + 'setServerSideStorage', 'setHighlight', 'setOption', 'setClientData', @@ -30,7 +33,7 @@ export default function createPersistedState ({ key = 'vuex-lz', paths = [], getState = (key, storage) => { - let value = storage.getItem(key) + const value = storage.getItem(key) return value }, setState = (key, state, storage) => { @@ -69,7 +72,7 @@ export default function createPersistedState ({ subscriber(store)((mutation, state) => { try { if (saveImmedeatelyActions.includes(mutation.type)) { - setState(key, reducer(state, paths), storage) + setState(key, reducer(cloneDeep(state), paths), storage) .then(success => { if (typeof success !== 'undefined') { if (mutation.type === 'setOption' || mutation.type === 'setCurrentUser') { diff --git a/src/main.js b/src/main.js @@ -1,6 +1,4 @@ -import Vue from 'vue' -import VueRouter from 'vue-router' -import Vuex from 'vuex' +import { createStore } from 'vuex' import 'custom-event-polyfill' import './lib/event_target_polyfill.js' @@ -8,9 +6,12 @@ import './lib/event_target_polyfill.js' import interfaceModule from './modules/interface.js' import instanceModule from './modules/instance.js' import statusesModule from './modules/statuses.js' +import listsModule from './modules/lists.js' import usersModule from './modules/users.js' import apiModule from './modules/api.js' import configModule from './modules/config.js' +import serverSideConfigModule from './modules/serverSideConfig.js' +import serverSideStorageModule from './modules/serverSideStorage.js' import shoutModule from './modules/shout.js' import oauthModule from './modules/oauth.js' import authFlowModule from './modules/auth_flow.js' @@ -19,36 +20,24 @@ import oauthTokensModule from './modules/oauth_tokens.js' import reportsModule from './modules/reports.js' import pollsModule from './modules/polls.js' import postStatusModule from './modules/postStatus.js' +import editStatusModule from './modules/editStatus.js' +import statusHistoryModule from './modules/statusHistory.js' + import chatsModule from './modules/chats.js' +import announcementsModule from './modules/announcements.js' -import VueI18n from 'vue-i18n' +import { createI18n } from 'vue-i18n' import createPersistedState from './lib/persisted_state.js' import pushNotifications from './lib/push_notifications_plugin.js' import messages from './i18n/messages.js' -import VueClickOutside from 'v-click-outside' -import PortalVue from 'portal-vue' -import VBodyScrollLock from './directives/body_scroll_lock' - -import { FontAwesomeIcon, FontAwesomeLayers } from '@fortawesome/vue-fontawesome' - import afterStoreSetup from './boot/after_store.js' const currentLocale = (window.navigator.language || 'en').split('-')[0] -Vue.use(Vuex) -Vue.use(VueRouter) -Vue.use(VueI18n) -Vue.use(VueClickOutside) -Vue.use(PortalVue) -Vue.use(VBodyScrollLock) - -Vue.component('FAIcon', FontAwesomeIcon) -Vue.component('FALayers', FontAwesomeLayers) - -const i18n = new VueI18n({ +const i18n = createI18n({ // By default, use the browser locale, we will update it if neccessary locale: 'en', fallbackLocale: 'en', @@ -59,6 +48,7 @@ messages.setLanguage(i18n, currentLocale) const persistedStateOptions = { paths: [ + 'serverSideStorage.cache', 'config', 'users.lastLoginName', 'oauth' @@ -75,19 +65,23 @@ const persistedStateOptions = { console.error(e) storageError = true } - const store = new Vuex.Store({ + const store = createStore({ modules: { i18n: { getters: { - i18n: () => i18n + i18n: () => i18n.global } }, interface: interfaceModule, instance: instanceModule, - statuses: statusesModule, + // TODO refactor users/statuses modules, they depend on each other users: usersModule, + statuses: statusesModule, + lists: listsModule, api: apiModule, config: configModule, + serverSideConfig: serverSideConfigModule, + serverSideStorage: serverSideStorageModule, shout: shoutModule, oauth: oauthModule, authFlow: authFlowModule, @@ -96,7 +90,10 @@ const persistedStateOptions = { reports: reportsModule, polls: pollsModule, postStatus: postStatusModule, - chats: chatsModule + editStatus: editStatusModule, + statusHistory: statusHistoryModule, + chats: chatsModule, + announcements: announcementsModule }, plugins, strict: false // Socket modifies itself, let's ignore this for now. diff --git a/src/modules/announcements.js b/src/modules/announcements.js @@ -0,0 +1,135 @@ +const FETCH_ANNOUNCEMENT_INTERVAL_MS = 1000 * 60 * 5 + +export const defaultState = { + announcements: [], + supportsAnnouncements: true, + fetchAnnouncementsTimer: undefined +} + +export const mutations = { + setAnnouncements (state, announcements) { + state.announcements = announcements + }, + setAnnouncementRead (state, { id, read }) { + const index = state.announcements.findIndex(a => a.id === id) + + if (index < 0) { + return + } + + state.announcements[index].read = read + }, + setFetchAnnouncementsTimer (state, timer) { + state.fetchAnnouncementsTimer = timer + }, + setSupportsAnnouncements (state, supportsAnnouncements) { + state.supportsAnnouncements = supportsAnnouncements + } +} + +export const getters = { + unreadAnnouncementCount (state, _getters, rootState) { + if (!rootState.users.currentUser) { + return 0 + } + + const unread = state.announcements.filter(announcement => !(announcement.inactive || announcement.read)) + return unread.length + } +} + +const announcements = { + state: defaultState, + mutations, + getters, + actions: { + fetchAnnouncements (store) { + if (!store.state.supportsAnnouncements) { + return Promise.resolve() + } + + const currentUser = store.rootState.users.currentUser + const isAdmin = currentUser && currentUser.role === 'admin' + + const getAnnouncements = async () => { + if (!isAdmin) { + return store.rootState.api.backendInteractor.fetchAnnouncements() + } + + const all = await store.rootState.api.backendInteractor.adminFetchAnnouncements() + const visible = await store.rootState.api.backendInteractor.fetchAnnouncements() + const visibleObject = visible.reduce((a, c) => { + a[c.id] = c + return a + }, {}) + const getWithinVisible = announcement => visibleObject[announcement.id] + + all.forEach(announcement => { + const visibleAnnouncement = getWithinVisible(announcement) + if (!visibleAnnouncement) { + announcement.inactive = true + } else { + announcement.read = visibleAnnouncement.read + } + }) + + return all + } + + return getAnnouncements() + .then(announcements => { + store.commit('setAnnouncements', announcements) + }) + .catch(error => { + // If and only if backend does not support announcements, it would return 404. + // In this case, silently ignores it. + if (error && error.statusCode === 404) { + store.commit('setSupportsAnnouncements', false) + } else { + throw error + } + }) + }, + markAnnouncementAsRead (store, id) { + return store.rootState.api.backendInteractor.dismissAnnouncement({ id }) + .then(() => { + store.commit('setAnnouncementRead', { id, read: true }) + }) + }, + startFetchingAnnouncements (store) { + if (store.state.fetchAnnouncementsTimer) { + return + } + + const interval = setInterval(() => store.dispatch('fetchAnnouncements'), FETCH_ANNOUNCEMENT_INTERVAL_MS) + store.commit('setFetchAnnouncementsTimer', interval) + + return store.dispatch('fetchAnnouncements') + }, + stopFetchingAnnouncements (store) { + const interval = store.state.fetchAnnouncementsTimer + store.commit('setFetchAnnouncementsTimer', undefined) + clearInterval(interval) + }, + postAnnouncement (store, { content, startsAt, endsAt, allDay }) { + return store.rootState.api.backendInteractor.postAnnouncement({ content, startsAt, endsAt, allDay }) + .then(() => { + return store.dispatch('fetchAnnouncements') + }) + }, + editAnnouncement (store, { id, content, startsAt, endsAt, allDay }) { + return store.rootState.api.backendInteractor.editAnnouncement({ id, content, startsAt, endsAt, allDay }) + .then(() => { + return store.dispatch('fetchAnnouncements') + }) + }, + deleteAnnouncement (store, id) { + return store.rootState.api.backendInteractor.deleteAnnouncement({ id }) + .then(() => { + return store.dispatch('fetchAnnouncements') + }) + } + } +} + +export default announcements diff --git a/src/modules/api.js b/src/modules/api.js @@ -15,6 +15,9 @@ const api = { mastoUserSocketStatus: null, followRequests: [] }, + getters: { + followRequestCount: state => state.followRequests.length + }, mutations: { setBackendInteractor (state, backendInteractor) { state.backendInteractor = backendInteractor @@ -100,6 +103,13 @@ const api = { showImmediately: timelineData.visibleStatuses.length === 0, timeline: 'friends' }) + } else if (message.event === 'status.update') { + dispatch('addNewStatuses', { + statuses: [message.status], + userId: false, + showImmediately: message.status.id in timelineData.visibleStatusesObject, + timeline: 'friends' + }) } else if (message.event === 'delete') { dispatch('deleteStatusById', message.id) } else if (message.event === 'pleroma:chat_update') { @@ -191,12 +201,13 @@ const api = { startFetchingTimeline (store, { timeline = 'friends', tag = false, - userId = false + userId = false, + listId = false }) { if (store.state.fetchers[timeline]) return const fetcher = store.state.backendInteractor.startFetchingTimeline({ - timeline, store, userId, tag + timeline, store, userId, listId, tag }) store.commit('addFetcher', { fetcherName: timeline, fetcher }) }, @@ -205,7 +216,7 @@ const api = { if (!fetcher) return store.commit('removeFetcher', { fetcherName: timeline, fetcher }) }, - fetchTimeline (store, timeline, { ...rest }) { + fetchTimeline (store, { timeline, ...rest }) { store.state.backendInteractor.fetchTimeline({ store, timeline, @@ -233,7 +244,7 @@ const api = { // Follow requests startFetchingFollowRequests (store) { - if (store.state.fetchers['followRequests']) return + if (store.state.fetchers.followRequests) return const fetcher = store.state.backendInteractor.startFetchingFollowRequests({ store }) store.commit('addFetcher', { fetcherName: 'followRequests', fetcher }) @@ -244,10 +255,22 @@ const api = { store.commit('removeFetcher', { fetcherName: 'followRequests', fetcher }) }, removeFollowRequest (store, request) { - let requests = store.state.followRequests.filter((it) => it !== request) + const requests = store.state.followRequests.filter((it) => it !== request) store.commit('setFollowRequests', requests) }, + // Lists + startFetchingLists (store) { + if (store.state.fetchers.lists) return + const fetcher = store.state.backendInteractor.startFetchingLists({ store }) + store.commit('addFetcher', { fetcherName: 'lists', fetcher }) + }, + stopFetchingLists (store) { + const fetcher = store.state.fetchers.lists + if (!fetcher) return + store.commit('removeFetcher', { fetcherName: 'lists', fetcher }) + }, + // Pleroma websocket setWsToken (store, token) { store.commit('setWsToken', token) diff --git a/src/modules/chats.js b/src/modules/chats.js @@ -1,4 +1,4 @@ -import Vue from 'vue' +import { reactive } from 'vue' import { find, omitBy, orderBy, sumBy } from 'lodash' import chatService from '../services/chat_service/chat_service.js' import { parseChat, parseChatMessage } from '../services/entity_normalizer/entity_normalizer.service.js' @@ -13,8 +13,8 @@ const emptyChatList = () => ({ const defaultState = { chatList: emptyChatList(), chatListFetcher: null, - openedChats: {}, - openedChatMessageServices: {}, + openedChats: reactive({}), + openedChatMessageServices: reactive({}), fetcher: undefined, currentChatId: null, lastReadMessageId: null @@ -137,10 +137,10 @@ const chats = { }, addOpenedChat (state, { _dispatch, chat }) { state.currentChatId = chat.id - Vue.set(state.openedChats, chat.id, chat) + state.openedChats[chat.id] = chat if (!state.openedChatMessageServices[chat.id]) { - Vue.set(state.openedChatMessageServices, chat.id, chatService.empty(chat.id)) + state.openedChatMessageServices[chat.id] = chatService.empty(chat.id) } }, setCurrentChatId (state, { chatId }) { @@ -160,7 +160,7 @@ const chats = { } } else { state.chatList.data.push(updatedChat) - Vue.set(state.chatList.idStore, updatedChat.id, updatedChat) + state.chatList.idStore[updatedChat.id] = updatedChat } }) }, @@ -172,7 +172,7 @@ const chats = { chat.updated_at = updatedChat.updated_at } if (!chat) { state.chatList.data.unshift(updatedChat) } - Vue.set(state.chatList.idStore, updatedChat.id, updatedChat) + state.chatList.idStore[updatedChat.id] = updatedChat }, deleteChat (state, { _dispatch, id, _rootGetters }) { state.chats.data = state.chats.data.filter(conversation => @@ -186,8 +186,8 @@ const chats = { commit('setChatListFetcher', { fetcher: undefined }) for (const chatId in state.openedChats) { chatService.clear(state.openedChatMessageServices[chatId]) - Vue.delete(state.openedChats, chatId) - Vue.delete(state.openedChatMessageServices, chatId) + delete state.openedChats[chatId] + delete state.openedChatMessageServices[chatId] } }, setChatsLoading (state, { value }) { @@ -215,8 +215,8 @@ const chats = { for (const chatId in state.openedChats) { if (currentChatId !== chatId) { chatService.clear(state.openedChatMessageServices[chatId]) - Vue.delete(state.openedChats, chatId) - Vue.delete(state.openedChatMessageServices, chatId) + delete state.openedChats[chatId] + delete state.openedChatMessageServices[chatId] } } }, diff --git a/src/modules/config.js b/src/modules/config.js @@ -1,6 +1,9 @@ -import { set, delete as del } from 'vue' -import { setPreset, applyTheme } from '../services/style_setter/style_setter.js' +import Cookies from 'js-cookie' +import { setPreset, applyTheme, applyConfig } from '../services/style_setter/style_setter.js' import messages from '../i18n/messages' +import localeService from '../services/locale/locale.service.js' + +const BACKEND_LANGUAGE_COOKIE_NAME = 'userLanguage' const browserLocale = (window.navigator.language || 'en').split('-')[0] @@ -11,10 +14,15 @@ const browserLocale = (window.navigator.language || 'en').split('-')[0] */ export const multiChoiceProperties = [ 'postContentType', - 'subjectLineBehavior' + 'subjectLineBehavior', + 'conversationDisplay', // tree | linear + 'conversationOtherRepliesButton', // below | inside + 'mentionLinkDisplay', // short | full_for_remote | full + 'userPopoverAvatarAction' // close | zoom | open ] export const defaultState = { + expertLevel: 0, // used to track which settings to show and hide colors: {}, theme: undefined, customTheme: undefined, @@ -24,6 +32,9 @@ export const defaultState = { hideShoutbox: false, // bad name: actually hides posts of muted USERS hideMutedPosts: undefined, // instance default + hideMutedThreads: undefined, // instance default + hideWordFilteredPosts: undefined, // instance default + muteBotStatuses: undefined, // instance default collapseMessageWithSubject: undefined, // instance default padEmoji: true, hideAttachments: false, @@ -38,8 +49,9 @@ export const defaultState = { alwaysShowNewPostButton: false, autohideFloatingPostButton: false, pauseOnUnfocused: true, - stopGifs: false, + stopGifs: true, replyVisibility: 'all', + thirdColumnMode: 'notifications', notificationVisibility: { follows: true, mentions: true, @@ -48,7 +60,9 @@ export const defaultState = { moves: true, emojiReactions: true, followRequest: true, - chatMention: true + reports: true, + chatMention: true, + polls: true }, webPushNotifications: false, muteWords: [], @@ -66,12 +80,33 @@ export const defaultState = { hideFilteredStatuses: undefined, // instance default playVideosInModal: false, useOneClickNsfw: false, - useContainFit: false, + useContainFit: true, + disableStickyHeaders: false, + showScrollbars: false, + userPopoverAvatarAction: 'open', + userPopoverOverlay: false, + sidebarColumnWidth: '25rem', + contentColumnWidth: '45rem', + notifsColumnWidth: '25rem', + navbarColumnStretch: false, greentext: undefined, // instance default + useAtIcon: undefined, // instance default + mentionLinkDisplay: undefined, // instance default + mentionLinkShowTooltip: undefined, // instance default + mentionLinkShowAvatar: undefined, // instance default + mentionLinkFadeDomain: undefined, // instance default + mentionLinkShowYous: undefined, // instance default + mentionLinkBoldenYou: undefined, // instance default hidePostStats: undefined, // instance default + hideBotIndication: undefined, // instance default hideUserStats: undefined, // instance default virtualScrolling: undefined, // instance default - sensitiveByDefault: undefined // instance default + sensitiveByDefault: undefined, // instance default + conversationDisplay: undefined, // instance default + conversationTreeAdvanced: undefined, // instance default + conversationOtherRepliesButton: undefined, // instance default + conversationTreeFadeAncestors: undefined, // instance default + maxDepthInThread: undefined // instance default } // caching the instance default properties @@ -102,14 +137,14 @@ const config = { }, mutations: { setOption (state, { name, value }) { - set(state, name, value) + state[name] = value }, setHighlight (state, { user, color, type }) { const data = this.state.config.highlight[user] if (color || type) { - set(state.highlight, user, { color: color || data.color, type: type || data.type }) + state.highlight[user] = { color: color || data.color, type: type || data.type } } else { - del(state.highlight, user) + delete state.highlight[user] } } }, @@ -118,7 +153,7 @@ const config = { const knownKeys = new Set(Object.keys(defaultState)) const presentKeys = new Set(Object.keys(data)) const intersection = new Set() - for (let elem of presentKeys) { + for (const elem of presentKeys) { if (knownKeys.has(elem)) { intersection.add(elem) } @@ -131,18 +166,28 @@ const config = { setHighlight ({ commit, dispatch }, { user, color, type }) { commit('setHighlight', { user, color, type }) }, - setOption ({ commit, dispatch }, { name, value }) { + setOption ({ commit, dispatch, state }, { name, value }) { commit('setOption', { name, value }) switch (name) { case 'theme': setPreset(value) break + case 'sidebarColumnWidth': + case 'contentColumnWidth': + case 'notifsColumnWidth': + applyConfig(state) + break case 'customTheme': case 'customThemeSource': applyTheme(value) break case 'interfaceLanguage': messages.setLanguage(this.getters.i18n, value) + dispatch('loadUnicodeEmojiData', value) + Cookies.set(BACKEND_LANGUAGE_COOKIE_NAME, localeService.internalToBackendLocale(value)) + break + case 'thirdColumnMode': + dispatch('setLayoutWidth', undefined) break } } diff --git a/src/modules/editStatus.js b/src/modules/editStatus.js @@ -0,0 +1,25 @@ +const editStatus = { + state: { + params: null, + modalActivated: false + }, + mutations: { + openEditStatusModal (state, params) { + state.params = params + state.modalActivated = true + }, + closeEditStatusModal (state) { + state.modalActivated = false + } + }, + actions: { + openEditStatusModal ({ commit }, params) { + commit('openEditStatusModal', params) + }, + closeEditStatusModal ({ commit }) { + commit('closeEditStatusModal') + } + } +} + +export default editStatus diff --git a/src/modules/errors.js b/src/modules/errors.js @@ -2,8 +2,8 @@ import { capitalize } from 'lodash' export function humanizeErrors (errors) { return Object.entries(errors).reduce((errs, [k, val]) => { - let message = val.reduce((acc, message) => { - let key = capitalize(k.replace(/_/g, ' ')) + const message = val.reduce((acc, message) => { + const key = capitalize(k.replace(/_/g, ' ')) return acc + [key, message].join(' ') + '. ' }, '') return [...errs, message] diff --git a/src/modules/instance.js b/src/modules/instance.js @@ -1,8 +1,42 @@ -import { set } from 'vue' import { getPreset, applyTheme } from '../services/style_setter/style_setter.js' import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js' import apiService from '../services/api/api.service.js' import { instanceDefaultProperties } from './config.js' +import { langCodeToCldrName, ensureFinalFallback } from '../i18n/languages.js' + +const SORTED_EMOJI_GROUP_IDS = [ + 'smileys-and-emotion', + 'people-and-body', + 'animals-and-nature', + 'food-and-drink', + 'travel-and-places', + 'activities', + 'objects', + 'symbols', + 'flags' +] + +const REGIONAL_INDICATORS = (() => { + const start = 0x1F1E6 + const end = 0x1F1FF + const A = 'A'.codePointAt(0) + const res = new Array(end - start + 1) + for (let i = start; i <= end; ++i) { + const letter = String.fromCodePoint(A + i - start) + res[i - start] = { + replacement: String.fromCodePoint(i), + imageUrl: false, + displayText: 'regional_indicator_' + letter, + displayTextI18n: { + key: 'emoji.regional_indicator', + args: { letter } + } + } + } + return res +})() + +const REMOTE_INTERACTION_URL = '/main/ostatus' const defaultState = { // Stuff from apiConfig @@ -20,16 +54,29 @@ const defaultState = { background: '/static/aurora_borealis.jpg', collapseMessageWithSubject: false, greentext: false, + useAtIcon: false, + mentionLinkDisplay: 'short', + mentionLinkShowTooltip: true, + mentionLinkShowAvatar: false, + mentionLinkFadeDomain: true, + mentionLinkShowYous: false, + mentionLinkBoldenYou: true, hideFilteredStatuses: false, + // bad name: actually hides posts of muted USERS hideMutedPosts: false, + hideMutedThreads: true, + hideWordFilteredPosts: false, hidePostStats: false, + hideBotIndication: false, hideSitename: false, hideUserStats: false, + muteBotStatuses: false, loginMethod: 'password', logo: '/static/logo.svg', logoMargin: '.2em', logoMask: true, logoLeft: false, + disableUpdateNotification: false, minimalScopesMode: false, nsfwCensorImage: undefined, postContentType: 'text/plain', @@ -43,12 +90,18 @@ const defaultState = { theme: 'pleroma-dark', virtualScrolling: true, sensitiveByDefault: false, + conversationDisplay: 'linear', + conversationTreeAdvanced: false, + conversationOtherRepliesButton: 'below', + conversationTreeFadeAncestors: false, + maxDepthInThread: 6, // Nasty stuff customEmoji: [], customEmojiFetched: false, - emoji: [], + emoji: {}, emojiFetched: false, + unicodeEmojiAnnotations: {}, pleromaBackend: true, postFormats: [], restrictedNicknames: [], @@ -80,16 +133,44 @@ const defaultState = { } } +const loadAnnotations = (lang) => { + return import( + /* webpackChunkName: "emoji-annotations/[request]" */ + `@kazvmoe-infra/unicode-emoji-json/annotations/${langCodeToCldrName(lang)}.json` + ) + .then(k => k.default) +} + +const injectAnnotations = (emoji, annotations) => { + const availableLangs = Object.keys(annotations) + + return { + ...emoji, + annotations: availableLangs.reduce((acc, cur) => { + acc[cur] = annotations[cur][emoji.replacement] + return acc + }, {}) + } +} + +const injectRegionalIndicators = groups => { + groups.symbols.push(...REGIONAL_INDICATORS) + return groups +} + const instance = { state: defaultState, mutations: { setInstanceOption (state, { name, value }) { if (typeof value !== 'undefined') { - set(state, name, value) + state[name] = value } }, setKnownDomains (state, domains) { state.knownDomains = domains + }, + setUnicodeEmojiAnnotations (state, { lang, annotations }) { + state.unicodeEmojiAnnotations[lang] = annotations } }, getters: { @@ -97,6 +178,56 @@ const instance = { return instanceDefaultProperties .map(key => [key, state[key]]) .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}) + }, + groupedCustomEmojis (state) { + const packsOf = emoji => { + return emoji.tags + .filter(k => k.startsWith('pack:')) + .map(k => k.slice(5)) // remove 'pack:' prefix + } + + return state.customEmoji + .reduce((res, emoji) => { + packsOf(emoji).forEach(packName => { + const packId = `custom-${packName}` + if (!res[packId]) { + res[packId] = ({ + id: packId, + text: packName, + image: emoji.imageUrl, + emojis: [] + }) + } + res[packId].emojis.push(emoji) + }) + return res + }, {}) + }, + standardEmojiList (state) { + return SORTED_EMOJI_GROUP_IDS + .map(groupId => (state.emoji[groupId] || []).map(k => injectAnnotations(k, state.unicodeEmojiAnnotations))) + .reduce((a, b) => a.concat(b), []) + }, + standardEmojiGroupList (state) { + return SORTED_EMOJI_GROUP_IDS.map(groupId => ({ + id: groupId, + emojis: (state.emoji[groupId] || []).map(k => injectAnnotations(k, state.unicodeEmojiAnnotations)) + })) + }, + instanceDomain (state) { + return new URL(state.server).hostname + }, + remoteInteractionLink (state) { + const server = state.server.endsWith('/') ? state.server.slice(0, -1) : state.server + const link = server + REMOTE_INTERACTION_URL + + return ({ statusId, nickname }) => { + if (statusId) { + return `${link}?status_id=${statusId}` + } else { + return `${link}?nickname=${nickname}` + } + } } }, actions: { @@ -118,32 +249,52 @@ const instance = { }, async getStaticEmoji ({ commit }) { try { - const res = await window.fetch('/static/emoji.json') - if (res.ok) { - const values = await res.json() - const emoji = Object.keys(values).map((key) => { - return { - displayText: key, - imageUrl: false, - replacement: values[key] - } - }).sort((a, b) => a.name > b.name ? 1 : -1) - commit('setInstanceOption', { name: 'emoji', value: emoji }) - } else { - throw (res) - } + const values = (await import(/* webpackChunkName: 'emoji' */ '../../static/emoji.json')).default + + const emoji = Object.keys(values).reduce((res, groupId) => { + res[groupId] = values[groupId].map(e => ({ + displayText: e.slug, + imageUrl: false, + replacement: e.emoji + })) + return res + }, {}) + commit('setInstanceOption', { name: 'emoji', value: injectRegionalIndicators(emoji) }) } catch (e) { console.warn("Can't load static emoji") console.warn(e) } }, + loadUnicodeEmojiData ({ commit, state }, language) { + const langList = ensureFinalFallback(language) + + return Promise.all( + langList + .map(async lang => { + if (!state.unicodeEmojiAnnotations[lang]) { + const annotations = await loadAnnotations(lang) + commit('setUnicodeEmojiAnnotations', { lang, annotations }) + } + })) + }, + async getCustomEmoji ({ commit, state }) { try { const res = await window.fetch('/api/pleroma/emoji.json') if (res.ok) { const result = await res.json() const values = Array.isArray(result) ? Object.assign({}, ...result) : result + const caseInsensitiveStrCmp = (a, b) => { + const la = a.toLowerCase() + const lb = b.toLowerCase() + return la > lb ? 1 : (la < lb ? -1 : 0) + } + const byPackThenByName = (a, b) => { + const packOf = emoji => (emoji.tags.filter(k => k.startsWith('pack:'))[0] || '').slice(5) + return caseInsensitiveStrCmp(packOf(a), packOf(b)) || caseInsensitiveStrCmp(a.displayText, b.displayText) + } + const emoji = Object.entries(values).map(([key, value]) => { const imageUrl = value.image_url return { @@ -154,7 +305,7 @@ const instance = { } // Technically could use tags but those are kinda useless right now, // should have been "pack" field, that would be more useful - }).sort((a, b) => a.displayText.toLowerCase() > b.displayText.toLowerCase() ? 1 : -1) + }).sort(byPackThenByName) commit('setInstanceOption', { name: 'customEmoji', value: emoji }) } else { throw (res) diff --git a/src/modules/interface.js b/src/modules/interface.js @@ -1,5 +1,3 @@ -import { set, delete as del } from 'vue' - const defaultState = { settingsModalState: 'hidden', settingsModalLoaded: false, @@ -15,7 +13,7 @@ const defaultState = { window.CSS.supports('-webkit-filter', 'drop-shadow(0 0)') ) }, - mobileLayout: false, + layoutType: 'normal', globalNotices: [], layoutHeight: 0, lastTimeline: null @@ -29,18 +27,17 @@ const interfaceMod = { if (state.noticeClearTimeout) { clearTimeout(state.noticeClearTimeout) } - set(state.settings, 'currentSaveStateNotice', { error: false, data: success }) - set(state.settings, 'noticeClearTimeout', - setTimeout(() => del(state.settings, 'currentSaveStateNotice'), 2000)) + state.settings.currentSaveStateNotice = { error: false, data: success } + state.settings.noticeClearTimeout = setTimeout(() => delete state.settings.currentSaveStateNotice, 2000) } else { - set(state.settings, 'currentSaveStateNotice', { error: true, errorData: error }) + state.settings.currentSaveStateNotice = { error: true, errorData: error } } }, setNotificationPermission (state, permission) { state.notificationPermission = permission }, - setMobileLayout (state, value) { - state.mobileLayout = value + setLayoutType (state, value) { + state.layoutType = value }, closeSettingsModal (state) { state.settingsModalState = 'hidden' @@ -75,6 +72,9 @@ const interfaceMod = { setLayoutHeight (state, value) { state.layoutHeight = value }, + setLayoutWidth (state, value) { + state.layoutWidth = value + }, setLastTimeline (state, value) { state.lastTimeline = value } @@ -89,9 +89,6 @@ const interfaceMod = { setNotificationPermission ({ commit }, permission) { commit('setNotificationPermission', permission) }, - setMobileLayout ({ commit }, value) { - commit('setMobileLayout', value) - }, closeSettingsModal ({ commit }) { commit('closeSettingsModal') }, @@ -109,7 +106,7 @@ const interfaceMod = { commit('openSettingsModal') }, pushGlobalNotice ( - { commit, dispatch }, + { commit, dispatch, state }, { messageKey, messageArgs = {}, @@ -121,11 +118,14 @@ const interfaceMod = { messageArgs, level } + commit('pushGlobalNotice', notice) + // Adding a new element to array wraps it in a Proxy, which breaks the comparison + // TODO: Generate UUID or something instead or relying on !== operator? + const newNotice = state.globalNotices[state.globalNotices.length - 1] if (timeout) { - setTimeout(() => dispatch('removeGlobalNotice', notice), timeout) + setTimeout(() => dispatch('removeGlobalNotice', newNotice), timeout) } - commit('pushGlobalNotice', notice) - return notice + return newNotice }, removeGlobalNotice ({ commit }, notice) { commit('removeGlobalNotice', notice) @@ -133,6 +133,24 @@ const interfaceMod = { setLayoutHeight ({ commit }, value) { commit('setLayoutHeight', value) }, + // value is optional, assuming it was cached prior + setLayoutWidth ({ commit, state, rootGetters, rootState }, value) { + let width = value + if (value !== undefined) { + commit('setLayoutWidth', value) + } else { + width = state.layoutWidth + } + const mobileLayout = width <= 800 + const normalOrMobile = mobileLayout ? 'mobile' : 'normal' + const { thirdColumnMode } = rootGetters.mergedConfig + if (thirdColumnMode === 'none' || !rootState.users.currentUser) { + commit('setLayoutType', normalOrMobile) + } else { + const wideLayout = width >= 1300 + commit('setLayoutType', wideLayout ? 'wide' : normalOrMobile) + } + }, setLastTimeline ({ commit }, value) { commit('setLastTimeline', value) } diff --git a/src/modules/lists.js b/src/modules/lists.js @@ -0,0 +1,130 @@ +import { remove, find } from 'lodash' + +export const defaultState = { + allLists: [], + allListsObject: {} +} + +export const mutations = { + setLists (state, value) { + state.allLists = value + }, + setList (state, { listId, title }) { + if (!state.allListsObject[listId]) { + state.allListsObject[listId] = { accountIds: [] } + } + state.allListsObject[listId].title = title + + const entry = find(state.allLists, { id: listId }) + if (!entry) { + state.allLists.push({ id: listId, title }) + } else { + entry.title = title + } + }, + setListAccounts (state, { listId, accountIds }) { + if (!state.allListsObject[listId]) { + state.allListsObject[listId] = { accountIds: [] } + } + state.allListsObject[listId].accountIds = accountIds + }, + addListAccount (state, { listId, accountId }) { + if (!state.allListsObject[listId]) { + state.allListsObject[listId] = { accountIds: [] } + } + state.allListsObject[listId].accountIds.push(accountId) + }, + removeListAccount (state, { listId, accountId }) { + if (!state.allListsObject[listId]) { + state.allListsObject[listId] = { accountIds: [] } + } + const { accountIds } = state.allListsObject[listId] + const set = new Set(accountIds) + set.delete(accountId) + state.allListsObject[listId].accountIds = [...set] + }, + deleteList (state, { listId }) { + delete state.allListsObject[listId] + remove(state.allLists, list => list.id === listId) + } +} + +const actions = { + setLists ({ commit }, value) { + commit('setLists', value) + }, + createList ({ rootState, commit }, { title }) { + return rootState.api.backendInteractor.createList({ title }) + .then((list) => { + commit('setList', { listId: list.id, title }) + return list + }) + }, + fetchList ({ rootState, commit }, { listId }) { + return rootState.api.backendInteractor.getList({ listId }) + .then((list) => commit('setList', { listId: list.id, title: list.title })) + }, + fetchListAccounts ({ rootState, commit }, { listId }) { + return rootState.api.backendInteractor.getListAccounts({ listId }) + .then((accountIds) => commit('setListAccounts', { listId, accountIds })) + }, + setList ({ rootState, commit }, { listId, title }) { + rootState.api.backendInteractor.updateList({ listId, title }) + commit('setList', { listId, title }) + }, + setListAccounts ({ rootState, commit }, { listId, accountIds }) { + const saved = rootState.lists.allListsObject[listId].accountIds || [] + const added = accountIds.filter(id => !saved.includes(id)) + const removed = saved.filter(id => !accountIds.includes(id)) + commit('setListAccounts', { listId, accountIds }) + if (added.length > 0) { + rootState.api.backendInteractor.addAccountsToList({ listId, accountIds: added }) + } + if (removed.length > 0) { + rootState.api.backendInteractor.removeAccountsFromList({ listId, accountIds: removed }) + } + }, + addListAccount ({ rootState, commit }, { listId, accountId }) { + return rootState + .api + .backendInteractor + .addAccountsToList({ listId, accountIds: [accountId] }) + .then((result) => { + commit('addListAccount', { listId, accountId }) + return result + }) + }, + removeListAccount ({ rootState, commit }, { listId, accountId }) { + return rootState + .api + .backendInteractor + .removeAccountsFromList({ listId, accountIds: [accountId] }) + .then((result) => { + commit('removeListAccount', { listId, accountId }) + return result + }) + }, + deleteList ({ rootState, commit }, { listId }) { + rootState.api.backendInteractor.deleteList({ listId }) + commit('deleteList', { listId }) + } +} + +export const getters = { + findListTitle: state => id => { + if (!state.allListsObject[id]) return + return state.allListsObject[id].title + }, + findListAccounts: state => id => { + return [...state.allListsObject[id].accountIds] + } +} + +const lists = { + state: defaultState, + mutations, + actions, + getters +} + +export default lists diff --git a/src/modules/media_viewer.js b/src/modules/media_viewer.js @@ -1,4 +1,5 @@ import fileTypeService from '../services/file_type/file_type.service.js' +const supportedTypes = new Set(['image', 'video', 'audio', 'flash']) const mediaViewer = { state: { @@ -10,7 +11,7 @@ const mediaViewer = { setMedia (state, media) { state.media = media }, - setCurrent (state, index) { + setCurrentMedia (state, index) { state.activated = true state.currentIndex = index }, @@ -22,13 +23,13 @@ const mediaViewer = { setMedia ({ commit }, attachments) { const media = attachments.filter(attachment => { const type = fileTypeService.fileType(attachment.mimetype) - return type === 'image' || type === 'video' || type === 'audio' + return supportedTypes.has(type) }) commit('setMedia', media) }, - setCurrent ({ commit, state }, current) { + setCurrentMedia ({ commit, state }, current) { const index = state.media.indexOf(current) - commit('setCurrent', index || 0) + commit('setCurrentMedia', index || 0) }, closeMediaViewer ({ commit }) { commit('close') diff --git a/src/modules/oauth.js b/src/modules/oauth.js @@ -1,5 +1,3 @@ -import { delete as del } from 'vue' - const oauth = { state: { clientId: false, @@ -29,7 +27,7 @@ const oauth = { state.userToken = false // state.token is userToken with older name, coming from persistent state // let's clear it as well, since it is being used as a fallback of state.userToken - del(state, 'token') + delete state.token } }, getters: { diff --git a/src/modules/polls.js b/src/modules/polls.js @@ -1,5 +1,4 @@ import { merge } from 'lodash' -import { set } from 'vue' const polls = { state: { @@ -13,25 +12,25 @@ const polls = { // Make expired-state change trigger re-renders properly poll.expired = Date.now() > Date.parse(poll.expires_at) if (existingPoll) { - set(state.pollsObject, poll.id, merge(existingPoll, poll)) + state.pollsObject[poll.id] = merge(existingPoll, poll) } else { - set(state.pollsObject, poll.id, poll) + state.pollsObject[poll.id] = poll } }, trackPoll (state, pollId) { const currentValue = state.trackedPolls[pollId] if (currentValue) { - set(state.trackedPolls, pollId, currentValue + 1) + state.trackedPolls[pollId] = currentValue + 1 } else { - set(state.trackedPolls, pollId, 1) + state.trackedPolls[pollId] = 1 } }, untrackPoll (state, pollId) { const currentValue = state.trackedPolls[pollId] if (currentValue) { - set(state.trackedPolls, pollId, currentValue - 1) + state.trackedPolls[pollId] = currentValue - 1 } else { - set(state.trackedPolls, pollId, 0) + state.trackedPolls[pollId] = 0 } } }, diff --git a/src/modules/reports.js b/src/modules/reports.js @@ -2,20 +2,29 @@ import filter from 'lodash/filter' const reports = { state: { - userId: null, - statuses: [], - preTickedIds: [], - modalActivated: false + reportModal: { + userId: null, + statuses: [], + preTickedIds: [], + activated: false + }, + reports: {} }, mutations: { openUserReportingModal (state, { userId, statuses, preTickedIds }) { - state.userId = userId - state.statuses = statuses - state.preTickedIds = preTickedIds - state.modalActivated = true + state.reportModal.userId = userId + state.reportModal.statuses = statuses + state.reportModal.preTickedIds = preTickedIds + state.reportModal.activated = true }, closeUserReportingModal (state) { - state.modalActivated = false + state.reportModal.activated = false + }, + setReportState (reportsState, { id, state }) { + reportsState.reports[id].state = state + }, + addReport (state, report) { + state.reports[report.id] = report } }, actions: { @@ -31,6 +40,23 @@ const reports = { }, closeUserReportingModal ({ commit }) { commit('closeUserReportingModal') + }, + setReportState ({ commit, dispatch, rootState }, { id, state }) { + const oldState = rootState.reports.reports[id].state + commit('setReportState', { id, state }) + rootState.api.backendInteractor.setReportState({ id, state }).catch(e => { + console.error('Failed to set report state', e) + dispatch('pushGlobalNotice', { + level: 'error', + messageKey: 'general.generic_error_message', + messageArgs: [e.message], + timeout: 5000 + }) + commit('setReportState', { id, state: oldState }) + }) + }, + addReport ({ commit }, report) { + commit('addReport', report) } } } diff --git a/src/modules/serverSideConfig.js b/src/modules/serverSideConfig.js @@ -0,0 +1,140 @@ +import { get, set } from 'lodash' + +const defaultApi = ({ rootState, commit }, { path, value }) => { + const params = {} + set(params, path, value) + return rootState + .api + .backendInteractor + .updateProfile({ params }) + .then(result => { + commit('addNewUsers', [result]) + commit('setCurrentUser', result) + }) +} + +const notificationsApi = ({ rootState, commit }, { path, value, oldValue }) => { + const settings = {} + set(settings, path, value) + return rootState + .api + .backendInteractor + .updateNotificationSettings({ settings }) + .then(result => { + if (result.status === 'success') { + commit('confirmServerSideOption', { name, value }) + } else { + commit('confirmServerSideOption', { name, value: oldValue }) + } + }) +} + +/** + * Map that stores relation between path for reading (from user profile), + * for writing (into API) an what API to use. + * + * Shorthand - instead of { get, set, api? } object it's possible to use string + * in case default api is used and get = set + * + * If no api is specified, defaultApi is used (see above) + */ +export const settingsMap = { + defaultScope: 'source.privacy', + defaultNSFW: 'source.sensitive', // BROKEN: pleroma/pleroma#2837 + stripRichContent: { + get: 'source.pleroma.no_rich_text', + set: 'no_rich_text' + }, + // Privacy + locked: 'locked', + acceptChatMessages: { + get: 'pleroma.accepts_chat_messages', + set: 'accepts_chat_messages' + }, + allowFollowingMove: { + get: 'pleroma.allow_following_move', + set: 'allow_following_move' + }, + discoverable: { + get: 'source.pleroma.discoverable', + set: 'discoverable' + }, + hideFavorites: { + get: 'pleroma.hide_favorites', + set: 'hide_favorites' + }, + hideFollowers: { + get: 'pleroma.hide_followers', + set: 'hide_followers' + }, + hideFollows: { + get: 'pleroma.hide_follows', + set: 'hide_follows' + }, + hideFollowersCount: { + get: 'pleroma.hide_followers_count', + set: 'hide_followers_count' + }, + hideFollowsCount: { + get: 'pleroma.hide_follows_count', + set: 'hide_follows_count' + }, + // NotificationSettingsAPIs + webPushHideContents: { + get: 'pleroma.notification_settings.hide_notification_contents', + set: 'hide_notification_contents', + api: notificationsApi + }, + blockNotificationsFromStrangers: { + get: 'pleroma.notification_settings.block_from_strangers', + set: 'block_from_strangers', + api: notificationsApi + } +} + +export const defaultState = Object.fromEntries(Object.keys(settingsMap).map(key => [key, null])) + +const serverSideConfig = { + state: { ...defaultState }, + mutations: { + confirmServerSideOption (state, { name, value }) { + set(state, name, value) + }, + wipeServerSideOption (state, { name }) { + set(state, name, null) + }, + wipeAllServerSideOptions (state) { + Object.keys(settingsMap).forEach(key => { + set(state, key, null) + }) + }, + // Set the settings based on their path location + setCurrentUser (state, user) { + Object.entries(settingsMap).forEach((map) => { + const [name, value] = map + const { get: path = value } = value + set(state, name, get(user._original, path)) + }) + } + }, + actions: { + setServerSideOption ({ rootState, state, commit, dispatch }, { name, value }) { + const oldValue = get(state, name) + const map = settingsMap[name] + if (!map) throw new Error('Invalid server-side setting') + const { set: path = map, api = defaultApi } = map + commit('wipeServerSideOption', { name }) + + api({ rootState, commit }, { path, value, oldValue }) + .catch((e) => { + console.warn('Error setting server-side option:', e) + commit('confirmServerSideOption', { name, value: oldValue }) + }) + }, + logout ({ commit }) { + commit('wipeAllServerSideOptions') + } + } +} + +export default serverSideConfig diff --git a/src/modules/serverSideStorage.js b/src/modules/serverSideStorage.js @@ -0,0 +1,436 @@ +import { toRaw } from 'vue' +import { isEqual, cloneDeep, set, get, clamp, flatten, groupBy, findLastIndex, takeRight, uniqWith } from 'lodash' +import { CURRENT_UPDATE_COUNTER } from 'src/components/update_notification/update_notification.js' + +export const VERSION = 1 +export const NEW_USER_DATE = new Date('2022-08-04') // date of writing this, basically + +export const COMMAND_TRIM_FLAGS = 1000 +export const COMMAND_TRIM_FLAGS_AND_RESET = 1001 + +export const defaultState = { + // do we need to update data on server? + dirty: false, + // storage of flags - stuff that can only be set and incremented + flagStorage: { + updateCounter: 0, // Counter for most recent update notification seen + reset: 0 // special flag that can be used to force-reset all flags, debug purposes only + // special reset codes: + // 1000: trim keys to those known by currently running FE + // 1001: same as above + reset everything to 0 + }, + prefsStorage: { + _journal: [], + simple: { + dontShowUpdateNotifs: false, + collapseNav: false + }, + collections: { + pinnedNavItems: ['home', 'dms', 'chats'] + } + }, + // raw data + raw: null, + // local cache + cache: null +} + +export const newUserFlags = { + ...defaultState.flagStorage, + updateCounter: CURRENT_UPDATE_COUNTER // new users don't need to see update notification +} + +export const _moveItemInArray = (array, value, movement) => { + const oldIndex = array.indexOf(value) + const newIndex = oldIndex + movement + const newArray = [...array] + // remove old + newArray.splice(oldIndex, 1) + // add new + newArray.splice(clamp(newIndex, 0, newArray.length + 1), 0, value) + return newArray +} + +const _wrapData = (data, userName) => ({ + ...data, + _user: userName, + _timestamp: Date.now(), + _version: VERSION +}) + +const _checkValidity = (data) => data._timestamp > 0 && data._version > 0 + +const _verifyPrefs = (state) => { + state.prefsStorage = state.prefsStorage || { + simple: {}, + collections: {} + } + Object.entries(defaultState.prefsStorage.simple).forEach(([k, v]) => { + if (typeof v === 'number' || typeof v === 'boolean') return + console.warn(`Preference simple.${k} as invalid type, reinitializing`) + set(state.prefsStorage.simple, k, defaultState.prefsStorage.simple[k]) + }) + Object.entries(defaultState.prefsStorage.collections).forEach(([k, v]) => { + if (Array.isArray(v)) return + console.warn(`Preference collections.${k} as invalid type, reinitializing`) + set(state.prefsStorage.collections, k, defaultState.prefsStorage.collections[k]) + }) +} + +export const _getRecentData = (cache, live) => { + const result = { recent: null, stale: null, needUpload: false } + const cacheValid = _checkValidity(cache || {}) + const liveValid = _checkValidity(live || {}) + if (!liveValid && cacheValid) { + result.needUpload = true + console.debug('Nothing valid stored on server, assuming cache to be source of truth') + result.recent = cache + result.stale = live + } else if (!cacheValid && liveValid) { + console.debug('Valid storage on server found, no local cache found, using live as source of truth') + result.recent = live + result.stale = cache + } else if (cacheValid && liveValid) { + console.debug('Both sources have valid data, figuring things out...') + if (live._timestamp === cache._timestamp && live._version === cache._version) { + console.debug('Same version/timestamp on both source, source of truth irrelevant') + result.recent = cache + result.stale = live + } else { + console.debug('Different timestamp, figuring out which one is more recent') + if (live._timestamp < cache._timestamp) { + result.recent = cache + result.stale = live + } else { + result.recent = live + result.stale = cache + } + } + } else { + console.debug('Both sources are invalid, start from scratch') + result.needUpload = true + } + return result +} + +export const _getAllFlags = (recent, stale) => { + return Array.from(new Set([ + ...Object.keys(toRaw((recent || {}).flagStorage || {})), + ...Object.keys(toRaw((stale || {}).flagStorage || {})) + ])) +} + +export const _mergeFlags = (recent, stale, allFlagKeys) => { + if (!stale.flagStorage) return recent.flagStorage + if (!recent.flagStorage) return stale.flagStorage + return Object.fromEntries(allFlagKeys.map(flag => { + const recentFlag = recent.flagStorage[flag] + const staleFlag = stale.flagStorage[flag] + // use flag that is of higher value + return [flag, Number((recentFlag > staleFlag ? recentFlag : staleFlag) || 0)] + })) +} + +const _mergeJournal = (...journals) => { + // Ignore invalid journal entries + const allJournals = flatten( + journals.map(j => Array.isArray(j) ? j : []) + ).filter(entry => + Object.prototype.hasOwnProperty.call(entry, 'path') && + Object.prototype.hasOwnProperty.call(entry, 'operation') && + Object.prototype.hasOwnProperty.call(entry, 'args') && + Object.prototype.hasOwnProperty.call(entry, 'timestamp') + ) + const grouped = groupBy(allJournals, 'path') + const trimmedGrouped = Object.entries(grouped).map(([path, journal]) => { + // side effect + journal.sort((a, b) => a.timestamp > b.timestamp ? 1 : -1) + + if (path.startsWith('collections')) { + const lastRemoveIndex = findLastIndex(journal, ({ operation }) => operation === 'removeFromCollection') + // everything before last remove is unimportant + let remainder + if (lastRemoveIndex > 0) { + remainder = journal.slice(lastRemoveIndex) + } else { + // everything else doesn't need trimming + remainder = journal + } + return uniqWith(remainder, (a, b) => { + if (a.path !== b.path) { return false } + if (a.operation !== b.operation) { return false } + if (a.operation === 'addToCollection') { + return a.args[0] === b.args[0] + } + return false + }) + } else if (path.startsWith('simple')) { + // Only the last record is important + return takeRight(journal) + } else { + return journal + } + }) + return flatten(trimmedGrouped) + .sort((a, b) => a.timestamp > b.timestamp ? 1 : -1) +} + +export const _mergePrefs = (recent, stale, allFlagKeys) => { + if (!stale) return recent + if (!recent) return stale + const { _journal: recentJournal, ...recentData } = recent + const { _journal: staleJournal } = stale + /** Journal entry format: + * path: path to entry in prefsStorage + * timestamp: timestamp of the change + * operation: operation type + * arguments: array of arguments, depends on operation type + * + * currently only supported operation type is "set" which just sets the value + * to requested one. Intended only to be used with simple preferences (boolean, number) + * shouldn't be used with collections! + */ + const resultOutput = { ...recentData } + const totalJournal = _mergeJournal(staleJournal, recentJournal) + totalJournal.forEach(({ path, timestamp, operation, command, args }) => { + if (path.startsWith('_')) { + console.error(`journal contains entry to edit internal (starts with _) field '${path}', something is incorrect here, ignoring.`) + return + } + switch (operation) { + case 'set': + set(resultOutput, path, args[0]) + break + case 'addToCollection': + set(resultOutput, path, Array.from(new Set(get(resultOutput, path)).add(args[0]))) + break + case 'removeFromCollection': { + const newSet = new Set(get(resultOutput, path)) + newSet.delete(args[0]) + set(resultOutput, path, Array.from(newSet)) + break + } + case 'reorderCollection': { + const [value, movement] = args + set(resultOutput, path, _moveItemInArray(get(resultOutput, path), value, movement)) + break + } + default: + console.error(`Unknown journal operation: '${operation}', did we forget to run reverse migrations beforehand?`) + } + }) + return { ...resultOutput, _journal: totalJournal } +} + +export const _resetFlags = (totalFlags, knownKeys = defaultState.flagStorage) => { + let result = { ...totalFlags } + const allFlagKeys = Object.keys(totalFlags) + // flag reset functionality + if (totalFlags.reset >= COMMAND_TRIM_FLAGS && totalFlags.reset <= COMMAND_TRIM_FLAGS_AND_RESET) { + console.debug('Received command to trim the flags') + const knownKeysSet = new Set(Object.keys(knownKeys)) + + // Trim + result = {} + allFlagKeys.forEach(flag => { + if (knownKeysSet.has(flag)) { + result[flag] = totalFlags[flag] + } + }) + + // Reset + if (totalFlags.reset === COMMAND_TRIM_FLAGS_AND_RESET) { + // 1001 - and reset everything to 0 + console.debug('Received command to reset the flags') + Object.keys(knownKeys).forEach(flag => { result[flag] = 0 }) + } + } else if (totalFlags.reset > 0 && totalFlags.reset < 9000) { + console.debug('Received command to reset the flags') + allFlagKeys.forEach(flag => { result[flag] = 0 }) + } + result.reset = 0 + return result +} + +export const _doMigrations = (cache) => { + if (!cache) return cache + + if (cache._version < VERSION) { + console.debug('Local cached data has older version, seeing if there any migrations that can be applied') + + // no migrations right now since we only have one version + console.debug('No migrations found') + } + + if (cache._version > VERSION) { + console.debug('Local cached data has newer version, seeing if there any reverse migrations that can be applied') + + // no reverse migrations right now but we leave a possibility of loading a hotpatch if need be + if (window._PLEROMA_HOTPATCH) { + if (window._PLEROMA_HOTPATCH.reverseMigrations) { + console.debug('Found hotpatch migration, applying') + return window._PLEROMA_HOTPATCH.reverseMigrations.call({}, 'serverSideStorage', { from: cache._version, to: VERSION }, cache) + } + } + } + + return cache +} + +export const mutations = { + clearServerSideStorage (state, userData) { + state = { ...cloneDeep(defaultState) } + }, + setServerSideStorage (state, userData) { + const live = userData.storage + state.raw = live + let cache = state.cache + if (cache && cache._user !== userData.fqn) { + console.warn('cache belongs to another user! reinitializing local cache!') + cache = null + } + + cache = _doMigrations(cache) + + let { recent, stale, needsUpload } = _getRecentData(cache, live) + + const userNew = userData.created_at > NEW_USER_DATE + const flagsTemplate = userNew ? newUserFlags : defaultState.flagStorage + let dirty = false + + if (recent === null) { + console.debug(`Data is empty, initializing for ${userNew ? 'new' : 'existing'} user`) + recent = _wrapData({ + flagStorage: { ...flagsTemplate }, + prefsStorage: { ...defaultState.prefsStorage } + }) + } + + if (!needsUpload && recent && stale) { + console.debug('Checking if data needs merging...') + // discarding timestamps and versions + const { _timestamp: _0, _version: _1, ...recentData } = recent + const { _timestamp: _2, _version: _3, ...staleData } = stale + dirty = !isEqual(recentData, staleData) + console.debug(`Data ${dirty ? 'needs' : 'doesn\'t need'} merging`) + } + + const allFlagKeys = _getAllFlags(recent, stale) + let totalFlags + let totalPrefs + if (dirty) { + // Merge the flags + console.debug('Merging the data...') + totalFlags = _mergeFlags(recent, stale, allFlagKeys) + _verifyPrefs(recent) + _verifyPrefs(stale) + totalPrefs = _mergePrefs(recent.prefsStorage, stale.prefsStorage) + } else { + totalFlags = recent.flagStorage + totalPrefs = recent.prefsStorage + } + + totalFlags = _resetFlags(totalFlags) + + recent.flagStorage = { ...flagsTemplate, ...totalFlags } + recent.prefsStorage = { ...defaultState.prefsStorage, ...totalPrefs } + + state.dirty = dirty || needsUpload + state.cache = recent + // set local timestamp to smaller one if we don't have any changes + if (stale && recent && !state.dirty) { + state.cache._timestamp = Math.min(stale._timestamp, recent._timestamp) + } + state.flagStorage = state.cache.flagStorage + state.prefsStorage = state.cache.prefsStorage + }, + setFlag (state, { flag, value }) { + state.flagStorage[flag] = value + state.dirty = true + }, + setPreference (state, { path, value }) { + if (path.startsWith('_')) { + console.error(`tried to edit internal (starts with _) field '${path}', ignoring.`) + return + } + set(state.prefsStorage, path, value) + state.prefsStorage._journal = [ + ...state.prefsStorage._journal, + { operation: 'set', path, args: [value], timestamp: Date.now() } + ] + state.dirty = true + }, + addCollectionPreference (state, { path, value }) { + if (path.startsWith('_')) { + console.error(`tried to edit internal (starts with _) field '${path}', ignoring.`) + return + } + const collection = new Set(get(state.prefsStorage, path)) + collection.add(value) + set(state.prefsStorage, path, [...collection]) + state.prefsStorage._journal = [ + ...state.prefsStorage._journal, + { operation: 'addToCollection', path, args: [value], timestamp: Date.now() } + ] + state.dirty = true + }, + removeCollectionPreference (state, { path, value }) { + if (path.startsWith('_')) { + console.error(`tried to edit internal (starts with _) field '${path}', ignoring.`) + return + } + const collection = new Set(get(state.prefsStorage, path)) + collection.delete(value) + set(state.prefsStorage, path, [...collection]) + state.prefsStorage._journal = [ + ...state.prefsStorage._journal, + { operation: 'removeFromCollection', path, args: [value], timestamp: Date.now() } + ] + state.dirty = true + }, + reorderCollectionPreference (state, { path, value, movement }) { + if (path.startsWith('_')) { + console.error(`tried to edit internal (starts with _) field '${path}', ignoring.`) + return + } + const collection = get(state.prefsStorage, path) + const newCollection = _moveItemInArray(collection, value, movement) + set(state.prefsStorage, path, newCollection) + state.prefsStorage._journal = [ + ...state.prefsStorage._journal, + { operation: 'arrangeCollection', path, args: [value], timestamp: Date.now() } + ] + state.dirty = true + }, + updateCache (state, { username }) { + state.prefsStorage._journal = _mergeJournal(state.prefsStorage._journal) + state.cache = _wrapData({ + flagStorage: toRaw(state.flagStorage), + prefsStorage: toRaw(state.prefsStorage) + }, username) + } +} + +const serverSideStorage = { + state: { + ...cloneDeep(defaultState) + }, + mutations, + actions: { + pushServerSideStorage ({ state, rootState, commit }, { force = false } = {}) { + const needPush = state.dirty || force + console.log(needPush) + if (!needPush) return + commit('updateCache', { username: rootState.users.currentUser.fqn }) + const params = { pleroma_settings_store: { 'pleroma-fe': state.cache } } + rootState.api.backendInteractor + .updateProfile({ params }) + .then((user) => { + commit('setServerSideStorage', user) + state.dirty = false + }) + } + } +} + +export default serverSideStorage diff --git a/src/modules/shout.js b/src/modules/shout.js @@ -1,7 +1,8 @@ const shout = { state: { messages: [], - channel: { state: '' } + channel: { state: '' }, + joined: false }, mutations: { setChannel (state, channel) { @@ -13,11 +14,23 @@ const shout = { }, setMessages (state, messages) { state.messages = messages.slice(-19, 20) + }, + setJoined (state, joined) { + state.joined = joined } }, actions: { initializeShout (store, socket) { const channel = socket.channel('chat:public') + channel.joinPush.receive('ok', () => { + store.commit('setJoined', true) + }) + channel.onClose(() => { + store.commit('setJoined', false) + }) + channel.onError(() => { + store.commit('setJoined', false) + }) channel.on('new_msg', (msg) => { store.commit('addMessage', msg) }) diff --git a/src/modules/statusHistory.js b/src/modules/statusHistory.js @@ -0,0 +1,25 @@ +const statusHistory = { + state: { + params: {}, + modalActivated: false + }, + mutations: { + openStatusHistoryModal (state, params) { + state.params = params + state.modalActivated = true + }, + closeStatusHistoryModal (state) { + state.modalActivated = false + } + }, + actions: { + openStatusHistoryModal ({ commit }, params) { + commit('openStatusHistoryModal', params) + }, + closeStatusHistoryModal ({ commit }) { + commit('closeStatusHistoryModal') + } + } +} + +export default statusHistory diff --git a/src/modules/statuses.js b/src/modules/statuses.js @@ -12,7 +12,6 @@ import { isArray, omitBy } from 'lodash' -import { set } from 'vue' import { isStatusNotification, isValidNotification, @@ -63,7 +62,8 @@ export const defaultState = () => ({ friends: emptyTl(), tag: emptyTl(), dms: emptyTl(), - bookmarks: emptyTl() + bookmarks: emptyTl(), + list: emptyTl() } }) @@ -92,7 +92,7 @@ const mergeOrAdd = (arr, obj, item) => { // This is a new item, prepare it prepareStatus(item) arr.push(item) - set(obj, item.id, item) + obj[item.id] = item return { item, new: true } } } @@ -131,7 +131,7 @@ const addStatusToGlobalStorage = (state, data) => { if (conversationsObject[conversationId]) { conversationsObject[conversationId].push(status) } else { - set(conversationsObject, conversationId, [status]) + conversationsObject[conversationId] = [status] } } return result @@ -246,10 +246,13 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us } const processors = { - 'status': (status) => { + status: (status) => { addStatus(status, showImmediately) }, - 'retweet': (status) => { + edit: (status) => { + addStatus(status, showImmediately) + }, + retweet: (status) => { // RetweetedStatuses are never shown immediately const retweetedStatus = addStatus(status.retweeted_status, false, false) @@ -271,7 +274,7 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us retweet.retweeted_status = retweetedStatus }, - 'favorite': (favorite) => { + favorite: (favorite) => { // Only update if this is a new favorite. // Ignore our own favorites because we get info about likes as response to like request if (!state.favorites.has(favorite.id)) { @@ -279,7 +282,7 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us favoriteStatus(favorite) } }, - 'deletion': (deletion) => { + deletion: (deletion) => { const uri = deletion.uri const status = find(allStatuses, { uri }) if (!status) { @@ -293,10 +296,10 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us remove(timelineObject.visibleStatuses, { uri }) } }, - 'follow': (follow) => { + follow: (follow) => { // NOOP, it is known status but we don't do anything about it for now }, - 'default': (unknown) => { + default: (unknown) => { console.log('unknown status type') console.log(unknown) } @@ -304,7 +307,7 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us each(statuses, (status) => { const type = status.type - const processor = processors[type] || processors['default'] + const processor = processors[type] || processors.default processor(status) }) @@ -337,11 +340,16 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot notification.status = notification.status && addStatusToGlobalStorage(state, notification.status).item } + if (notification.type === 'pleroma:report') { + dispatch('addReport', notification.report) + } + if (notification.type === 'pleroma:emoji_reaction') { dispatch('fetchEmojiReactionsBy', notification.status.id) } // Only add a new notification if we don't have one for the same action + // eslint-disable-next-line no-prototype-builtins if (!state.notifications.idStore.hasOwnProperty(notification.id)) { updateNotificationsMinMaxId(state, notification) @@ -523,7 +531,7 @@ export const mutations = { }, addEmojiReactionsBy (state, { id, emojiReactions, currentUser }) { const status = state.allStatusesObject[id] - set(status, 'emoji_reactions', emojiReactions) + status.emoji_reactions = emojiReactions }, addOwnReaction (state, { id, emoji, currentUser }) { const status = state.allStatusesObject[id] @@ -542,9 +550,9 @@ export const mutations = { // Update count of existing reaction if it exists, otherwise append at the end if (reactionIndex >= 0) { - set(status.emoji_reactions, reactionIndex, newReaction) + status.emoji_reactions[reactionIndex] = newReaction } else { - set(status, 'emoji_reactions', [...status.emoji_reactions, newReaction]) + status.emoji_reactions = [...status.emoji_reactions, newReaction] } }, removeOwnReaction (state, { id, emoji, currentUser }) { @@ -563,9 +571,9 @@ export const mutations = { } if (newReaction.count > 0) { - set(status.emoji_reactions, reactionIndex, newReaction) + status.emoji_reactions[reactionIndex] = newReaction } else { - set(status, 'emoji_reactions', status.emoji_reactions.filter(r => r.name !== emoji)) + status.emoji_reactions = status.emoji_reactions.filter(r => r.name !== emoji) } }, updateStatusWithPoll (state, { id, poll }) { @@ -601,6 +609,12 @@ const statuses = { return rootState.api.backendInteractor.fetchStatus({ id }) .then((status) => dispatch('addNewStatuses', { statuses: [status] })) }, + fetchStatusSource ({ rootState, dispatch }, status) { + return apiService.fetchStatusSource({ id: status.id, credentials: rootState.users.currentUser.credentials }) + }, + fetchStatusHistory ({ rootState, dispatch }, status) { + return apiService.fetchStatusHistory({ status }) + }, deleteStatus ({ rootState, commit }, status) { commit('setDeleted', { status }) apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials }) @@ -747,8 +761,8 @@ const statuses = { rootState.api.backendInteractor.fetchRebloggedByUsers({ id }) .then(rebloggedByUsers => commit('addRepeats', { id, rebloggedByUsers, currentUser: rootState.users.currentUser })) }, - search (store, { q, resolve, limit, offset, following }) { - return store.rootState.api.backendInteractor.search2({ q, resolve, limit, offset, following }) + search (store, { q, resolve, limit, offset, following, type }) { + return store.rootState.api.backendInteractor.search2({ q, resolve, limit, offset, following, type }) .then((data) => { store.commit('addNewUsers', data.accounts) store.commit('addNewStatuses', { statuses: data.statuses }) diff --git a/src/modules/users.js b/src/modules/users.js @@ -1,7 +1,7 @@ import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js' +import { windowWidth, windowHeight } from '../services/window_utils/window_utils' import oauthApi from '../services/new_api/oauth.js' import { compact, map, each, mergeWith, last, concat, uniq, isArray } from 'lodash' -import { set } from 'vue' import { registerPushNotifications, unregisterPushNotifications } from '../services/push/push.js' // TODO: Unify with mergeOrAdd in statuses.js @@ -15,10 +15,7 @@ export const mergeOrAdd = (arr, obj, item) => { } else { // This is a new item, prepare it arr.push(item) - set(obj, item.id, item) - if (item.screen_name && !item.screen_name.includes('@')) { - set(obj, item.screen_name.toLowerCase(), item) - } + obj[item.id] = item return { item, new: true } } } @@ -54,6 +51,16 @@ const unblockUser = (store, id) => { .then((relationship) => store.commit('updateUserRelationship', [relationship])) } +const removeUserFromFollowers = (store, id) => { + return store.rootState.api.backendInteractor.removeUserFromFollowers({ id }) + .then((relationship) => store.commit('updateUserRelationship', [relationship])) +} + +const editUserNote = (store, { id, comment }) => { + return store.rootState.api.backendInteractor.editUserNote({ id, comment }) + .then((relationship) => store.commit('updateUserRelationship', [relationship])) +} + const muteUser = (store, id) => { const predictedRelationship = store.state.relationships[id] || { id } predictedRelationship.muting = true @@ -103,23 +110,23 @@ export const mutations = { const user = state.usersObject[id] const tags = user.tags || [] const newTags = tags.concat([tag]) - set(user, 'tags', newTags) + user.tags = newTags }, untagUser (state, { user: { id }, tag }) { const user = state.usersObject[id] const tags = user.tags || [] const newTags = tags.filter(t => t !== tag) - set(user, 'tags', newTags) + user.tags = newTags }, updateRight (state, { user: { id }, right, value }) { const user = state.usersObject[id] - let newRights = user.rights + const newRights = user.rights newRights[right] = value - set(user, 'rights', newRights) + user.rights = newRights }, updateActivationStatus (state, { user: { id }, deactivated }) { const user = state.usersObject[id] - set(user, 'deactivated', deactivated) + user.deactivated = deactivated }, setCurrentUser (state, user) { state.lastLoginName = user.screen_name @@ -148,28 +155,35 @@ export const mutations = { clearFriends (state, userId) { const user = state.usersObject[userId] if (user) { - set(user, 'friendIds', []) + user.friendIds = [] } }, clearFollowers (state, userId) { const user = state.usersObject[userId] if (user) { - set(user, 'followerIds', []) + user.followerIds = [] } }, addNewUsers (state, users) { each(users, (user) => { if (user.relationship) { - set(state.relationships, user.relationship.id, user.relationship) + state.relationships[user.relationship.id] = user.relationship + } + const res = mergeOrAdd(state.users, state.usersObject, user) + const item = res.item + if (res.new && item.screen_name && !item.screen_name.includes('@')) { + state.usersByNameObject[item.screen_name.toLowerCase()] = item } - mergeOrAdd(state.users, state.usersObject, user) }) }, updateUserRelationship (state, relationships) { relationships.forEach((relationship) => { - set(state.relationships, relationship.id, relationship) + state.relationships[relationship.id] = relationship }) }, + updateUserInLists (state, { id, inLists }) { + state.usersObject[id].inLists = inLists + }, saveBlockIds (state, blockIds) { state.currentUser.blockIds = blockIds }, @@ -222,7 +236,7 @@ export const mutations = { }, setColor (state, { user: { id }, highlighted }) { const user = state.usersObject[id] - set(user, 'highlight', highlighted) + user.highlight = highlighted }, signUpPending (state) { state.signUpPending = true @@ -239,12 +253,10 @@ export const mutations = { export const getters = { findUser: state => query => { - const result = state.usersObject[query] - // In case it's a screen_name, we can try searching case-insensitive - if (!result && typeof query === 'string') { - return state.usersObject[query.toLowerCase()] - } - return result + return state.usersObject[query] + }, + findUserByName: state => query => { + return state.usersByNameObject[query.toLowerCase()] }, findUserByUrl: state => query => { return state.users @@ -263,6 +275,7 @@ export const defaultState = { currentUser: false, users: [], usersObject: {}, + usersByNameObject: {}, signUpPending: false, signUpErrors: [], relationships: {} @@ -285,12 +298,25 @@ const users = { return user }) }, + fetchUserByName (store, name) { + return store.rootState.api.backendInteractor.fetchUserByName({ name }) + .then((user) => { + store.commit('addNewUsers', [user]) + return user + }) + }, fetchUserRelationship (store, id) { if (store.state.currentUser) { store.rootState.api.backendInteractor.fetchUserRelationship({ id }) .then((relationships) => store.commit('updateUserRelationship', relationships)) } }, + fetchUserInLists (store, id) { + if (store.state.currentUser) { + store.rootState.api.backendInteractor.fetchUserInLists({ id }) + .then((inLists) => store.commit('updateUserInLists', { id, inLists })) + } + }, fetchBlocks (store) { return store.rootState.api.backendInteractor.fetchBlocks() .then((blocks) => { @@ -305,12 +331,18 @@ const users = { unblockUser (store, id) { return unblockUser(store, id) }, + removeUserFromFollowers (store, id) { + return removeUserFromFollowers(store, id) + }, blockUsers (store, ids = []) { return Promise.all(ids.map(id => blockUser(store, id))) }, unblockUsers (store, ids = []) { return Promise.all(ids.map(id => unblockUser(store, id))) }, + editUserNote (store, args) { + return editUserNote(store, args) + }, fetchMutes (store) { return store.rootState.api.backendInteractor.fetchMutes() .then((mutes) => { @@ -393,7 +425,7 @@ const users = { toggleActivationStatus ({ rootState, commit }, { user }) { const api = user.deactivated ? rootState.api.backendInteractor.activateUser : rootState.api.backendInteractor.deactivateUser api({ user }) - .then(({ deactivated }) => commit('updateActivationStatus', { user, deactivated })) + .then((user) => { const deactivated = !user.is_active; commit('updateActivationStatus', { user, deactivated }) }) }, registerPushNotifications (store) { const token = store.state.currentUser.credentials @@ -457,17 +489,17 @@ const users = { async signUp (store, userInfo) { store.commit('signUpPending') - let rootState = store.rootState + const rootState = store.rootState try { - let data = await rootState.api.backendInteractor.register( + const data = await rootState.api.backendInteractor.register( { params: { ...userInfo } } ) store.commit('signUpSuccess') store.commit('setToken', data.access_token) store.dispatch('loginUser', data.access_token) } catch (e) { - let errors = e.message + const errors = e.message store.commit('signUpFailure', errors) throw e } @@ -502,11 +534,15 @@ const users = { store.dispatch('stopFetchingTimeline', 'friends') store.commit('setBackendInteractor', backendInteractorService(store.getters.getToken())) store.dispatch('stopFetchingNotifications') + store.dispatch('stopFetchingLists') store.dispatch('stopFetchingFollowRequests') store.commit('clearNotifications') store.commit('resetStatuses') store.dispatch('resetChats') store.dispatch('setLastTimeline', 'public-timeline') + store.dispatch('setLayoutWidth', windowWidth()) + store.dispatch('setLayoutHeight', windowHeight()) + store.commit('clearServerSideStorage') }) }, loginUser (store, accessToken) { @@ -523,6 +559,7 @@ const users = { user.muteIds = [] user.domainMutes = [] commit('setCurrentUser', user) + commit('setServerSideStorage', user) commit('addNewUsers', [user]) store.dispatch('fetchEmoji') @@ -532,6 +569,7 @@ const users = { // Set our new backend interactor commit('setBackendInteractor', backendInteractorService(accessToken)) + store.dispatch('pushServerSideStorage') if (user.token) { store.dispatch('setWsToken', user.token) @@ -551,8 +589,14 @@ const users = { store.dispatch('startFetchingChats') } + store.dispatch('startFetchingLists') + + if (user.locked) { + store.dispatch('startFetchingFollowRequests') + } + if (store.getters.mergedConfig.useStreamingApi) { - store.dispatch('fetchTimeline', 'friends', { since: null }) + store.dispatch('fetchTimeline', { timeline: 'friends', since: null }) store.dispatch('fetchNotifications', { since: null }) store.dispatch('enableMastoSockets', true).catch((error) => { console.error('Failed initializing MastoAPI Streaming socket', error) @@ -567,6 +611,9 @@ const users = { // Get user mutes store.dispatch('fetchMutes') + store.dispatch('setLayoutWidth', windowWidth()) + store.dispatch('setLayoutHeight', windowHeight()) + // Fetch our friends store.rootState.api.backendInteractor.fetchFriends({ id: user.id }) .then((friends) => commit('addNewUsers', friends)) diff --git a/src/panel.scss b/src/panel.scss @@ -0,0 +1,240 @@ +.panel { + position: relative; + display: flex; + flex-direction: column; + background-color: $fallback--bg; + background-color: var(--bg, $fallback--bg); + + &::after, + & { + border-radius: $fallback--panelRadius; + border-radius: var(--panelRadius, $fallback--panelRadius); + } + + &::after { + content: ''; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + z-index: 5; + box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.6); + box-shadow: var(--panelShadow); + pointer-events: none; + } +} + +.panel-body { + padding: var(--panel-body-padding, 0); + + &:empty::before { + content: "¯\\_(ツ)_/¯"; // Could use words but it'd require translations + display: block; + margin: 1em; + text-align: center; + } + + > p { + line-height: 1.3; + padding: 1em; + margin: 0; + } +} + +.panel-heading, +.panel-footer { + --panel-heading-height-padding: 0.6em; + --__panel-heading-gap: 0.5em; + --__panel-heading-height: 3.2em; + --__panel-heading-height-inner: calc(var(--__panel-heading-height) - 2 * var(--panel-heading-height-padding, 0)); + + position: relative; + box-sizing: border-box; + display: grid; + grid-auto-flow: column; + grid-template-columns: minmax(50%, 1fr); + grid-auto-columns: auto; + grid-column-gap: var(--__panel-heading-gap); + flex: none; + background-size: cover; + padding: var(--panel-heading-height-padding); + height: var(--__panel-heading-height); + line-height: var(--__panel-heading-height-inner); + z-index: 4; + + &.-flexible-height { + --__panel-heading-height: auto; + + &::after, + &::before { + display: none; + } + } + + &.-stub { + &, + &::after { + border-radius: $fallback--panelRadius; + border-radius: var(--panelRadius, $fallback--panelRadius); + } + } + + &.-sticky { + position: sticky; + top: var(--navbar-height); + } + + &::after, + &::before { + content: ''; + position: absolute; + top: 0; + bottom: 0; + right: 0; + left: 0; + pointer-events: none; + } + + .title { + font-size: 1.3em; + } + + .alert { + white-space: nowrap; + text-overflow: ellipsis; + overflow-x: hidden; + } + + &:not(.-flexible-height) { + > .button-default, + > .alert { + height: var(--__panel-heading-height-inner); + min-height: 0; + box-sizing: border-box; + margin: 0; + min-width: 1px; + padding-top: 0; + padding-bottom: 0; + align-self: stretch; + } + } +} + +// TODO Should refactor panels into separate component and utilize slots + +.panel-heading { + border-radius: $fallback--panelRadius $fallback--panelRadius 0 0; + border-radius: var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius) 0 0; + border-width: 0 0 1px 0; + align-items: start; + // panel theme + color: var(--panelText); + background-color: $fallback--bg; + background-color: var(--bg, $fallback--bg); + + &::after { + background-color: $fallback--fg; + background-color: var(--panel, $fallback--fg); + z-index: -2; + border-radius: $fallback--panelRadius $fallback--panelRadius 0 0; + border-radius: var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius) 0 0; + box-shadow: var(--panelHeaderShadow); + } + + a, + .-link { + color: $fallback--link; + color: var(--panelLink, $fallback--link); + } + + .button-unstyled:hover, + a:hover { + i[class*=icon-], + .svg-inline--fa, + .iconLetter { + color: var(--panelText); + } + } + + .faint { + background-color: transparent; + color: $fallback--faint; + color: var(--panelFaint, $fallback--faint); + } + + .faint-link { + color: $fallback--faint; + color: var(--faintLink, $fallback--faint); + } + + &:not(.-flexible-height) { + > .button-default { + flex-shrink: 0; + + &, + i[class*=icon-] { + color: $fallback--text; + color: var(--btnPanelText, $fallback--text); + } + + &:active { + background-color: $fallback--fg; + background-color: var(--btnPressedPanel, $fallback--fg); + color: $fallback--text; + color: var(--btnPressedPanelText, $fallback--text); + } + + &:disabled { + color: $fallback--text; + color: var(--btnDisabledPanelText, $fallback--text); + } + + &.toggled { + color: $fallback--text; + color: var(--btnToggledPanelText, $fallback--text); + } + } + } + + .rightside-button { + align-self: stretch; + text-align: center; + width: var(--__panel-heading-height); + height: var(--__panel-heading-height); + margin: calc(-1 * var(--panel-heading-height-padding)) 0; + margin-right: calc(-1 * var(--__panel-heading-gap)); + + > button { + box-sizing: border-box; + padding: calc(1 * var(--panel-heading-height-padding)) 0; + height: 100%; + width: 100%; + text-align: center; + + svg { + font-size: 1.2em; + } + } + } + + .rightside-icon { + align-self: stretch; + text-align: center; + width: var(--__panel-heading-height); + margin-right: calc(-1 * var(--__panel-heading-gap)); + + svg { + font-size: 1.2em; + } + } +} + +.panel-footer { + border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius; + border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius); + align-items: center; + border-width: 1px 0 0 0; + border-style: solid; + border-color: var(--border, $fallback--border); +} diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js @@ -1,5 +1,5 @@ import { each, map, concat, last, get } from 'lodash' -import { parseStatus, parseUser, parseNotification, parseAttachment, parseChat, parseLinkHeaderPagination } from '../entity_normalizer/entity_normalizer.service.js' +import { parseStatus, parseSource, parseUser, parseNotification, parseAttachment, parseChat, parseLinkHeaderPagination } from '../entity_normalizer/entity_normalizer.service.js' import { RegistrationError, StatusCodeError } from '../errors/errors' /* eslint-env browser */ @@ -9,6 +9,8 @@ const FOLLOW_IMPORT_URL = '/api/pleroma/follow_import' const DELETE_ACCOUNT_URL = '/api/pleroma/delete_account' const CHANGE_EMAIL_URL = '/api/pleroma/change_email' const CHANGE_PASSWORD_URL = '/api/pleroma/change_password' +const MOVE_ACCOUNT_URL = '/api/pleroma/move_account' +const ALIASES_URL = '/api/pleroma/aliases' const TAG_USER_URL = '/api/pleroma/admin/users/tag' const PERMISSION_GROUP_URL = (screenName, right) => `/api/pleroma/admin/users/${screenName}/permission_group/${right}` const ACTIVATE_USER_URL = '/api/pleroma/admin/users/activate' @@ -47,9 +49,16 @@ const MASTODON_PUBLIC_TIMELINE = '/api/v1/timelines/public' const MASTODON_USER_HOME_TIMELINE_URL = '/api/v1/timelines/home' const MASTODON_STATUS_URL = id => `/api/v1/statuses/${id}` const MASTODON_STATUS_CONTEXT_URL = id => `/api/v1/statuses/${id}/context` +const MASTODON_STATUS_SOURCE_URL = id => `/api/v1/statuses/${id}/source` +const MASTODON_STATUS_HISTORY_URL = id => `/api/v1/statuses/${id}/history` const MASTODON_USER_URL = '/api/v1/accounts' +const MASTODON_USER_LOOKUP_URL = '/api/v1/accounts/lookup' const MASTODON_USER_RELATIONSHIPS_URL = '/api/v1/accounts/relationships' const MASTODON_USER_TIMELINE_URL = id => `/api/v1/accounts/${id}/statuses` +const MASTODON_USER_IN_LISTS = id => `/api/v1/accounts/${id}/lists` +const MASTODON_LIST_URL = id => `/api/v1/lists/${id}` +const MASTODON_LIST_TIMELINE_URL = id => `/api/v1/timelines/list/${id}` +const MASTODON_LIST_ACCOUNTS_URL = id => `/api/v1/lists/${id}/accounts` const MASTODON_TAG_TIMELINE_URL = tag => `/api/v1/timelines/tag/${tag}` const MASTODON_BOOKMARK_TIMELINE_URL = '/api/v1/bookmarks' const MASTODON_USER_BLOCKS_URL = '/api/v1/blocks/' @@ -58,8 +67,10 @@ const MASTODON_BLOCK_USER_URL = id => `/api/v1/accounts/${id}/block` const MASTODON_UNBLOCK_USER_URL = id => `/api/v1/accounts/${id}/unblock` const MASTODON_MUTE_USER_URL = id => `/api/v1/accounts/${id}/mute` const MASTODON_UNMUTE_USER_URL = id => `/api/v1/accounts/${id}/unmute` +const MASTODON_REMOVE_USER_FROM_FOLLOWERS = id => `/api/v1/accounts/${id}/remove_from_followers` const MASTODON_SUBSCRIBE_USER = id => `/api/v1/pleroma/accounts/${id}/subscribe` const MASTODON_UNSUBSCRIBE_USER = id => `/api/v1/pleroma/accounts/${id}/unsubscribe` +const MASTODON_USER_NOTE_URL = id => `/api/v1/accounts/${id}/note` const MASTODON_BOOKMARK_STATUS_URL = id => `/api/v1/statuses/${id}/bookmark` const MASTODON_UNBOOKMARK_STATUS_URL = id => `/api/v1/statuses/${id}/unbookmark` const MASTODON_POST_STATUS_URL = '/api/v1/statuses' @@ -74,23 +85,32 @@ const MASTODON_PIN_OWN_STATUS = id => `/api/v1/statuses/${id}/pin` const MASTODON_UNPIN_OWN_STATUS = id => `/api/v1/statuses/${id}/unpin` const MASTODON_MUTE_CONVERSATION = id => `/api/v1/statuses/${id}/mute` const MASTODON_UNMUTE_CONVERSATION = id => `/api/v1/statuses/${id}/unmute` -const MASTODON_SEARCH_2 = `/api/v2/search` +const MASTODON_SEARCH_2 = '/api/v2/search' const MASTODON_USER_SEARCH_URL = '/api/v1/accounts/search' const MASTODON_DOMAIN_BLOCKS_URL = '/api/v1/domain_blocks' +const MASTODON_LISTS_URL = '/api/v1/lists' const MASTODON_STREAMING = '/api/v1/streaming' const MASTODON_KNOWN_DOMAIN_LIST_URL = '/api/v1/instance/peers' +const MASTODON_ANNOUNCEMENTS_URL = '/api/v1/announcements' +const MASTODON_ANNOUNCEMENTS_DISMISS_URL = id => `/api/v1/announcements/${id}/dismiss` const PLEROMA_EMOJI_REACTIONS_URL = id => `/api/v1/pleroma/statuses/${id}/reactions` const PLEROMA_EMOJI_REACT_URL = (id, emoji) => `/api/v1/pleroma/statuses/${id}/reactions/${emoji}` const PLEROMA_EMOJI_UNREACT_URL = (id, emoji) => `/api/v1/pleroma/statuses/${id}/reactions/${emoji}` -const PLEROMA_CHATS_URL = `/api/v1/pleroma/chats` +const PLEROMA_CHATS_URL = '/api/v1/pleroma/chats' const PLEROMA_CHAT_URL = id => `/api/v1/pleroma/chats/by-account-id/${id}` const PLEROMA_CHAT_MESSAGES_URL = id => `/api/v1/pleroma/chats/${id}/messages` const PLEROMA_CHAT_READ_URL = id => `/api/v1/pleroma/chats/${id}/read` const PLEROMA_DELETE_CHAT_MESSAGE_URL = (chatId, messageId) => `/api/v1/pleroma/chats/${chatId}/messages/${messageId}` +const PLEROMA_ADMIN_REPORTS = '/api/pleroma/admin/reports' +const PLEROMA_BACKUP_URL = '/api/v1/pleroma/backups' +const PLEROMA_ANNOUNCEMENTS_URL = '/api/v1/pleroma/admin/announcements' +const PLEROMA_POST_ANNOUNCEMENT_URL = '/api/v1/pleroma/admin/announcements' +const PLEROMA_EDIT_ANNOUNCEMENT_URL = id => `/api/v1/pleroma/admin/announcements/${id}` +const PLEROMA_DELETE_ANNOUNCEMENT_URL = id => `/api/v1/pleroma/admin/announcements/${id}` const oldfetch = window.fetch -let fetch = (url, options) => { +const fetch = (url, options) => { options = options || {} const baseUrl = '' const fullUrl = baseUrl + url @@ -102,7 +122,7 @@ const promisedRequest = ({ method, url, params, payload, credentials, headers = const options = { method, headers: { - 'Accept': 'application/json', + Accept: 'application/json', 'Content-Type': 'application/json', ...headers } @@ -151,9 +171,15 @@ const updateNotificationSettings = ({ credentials, settings }) => { }).then((data) => data.json()) } -const updateProfileImages = ({ credentials, avatar = null, banner = null, background = null }) => { +const updateProfileImages = ({ credentials, avatar = null, avatarName = null, banner = null, background = null }) => { const form = new FormData() - if (avatar !== null) form.append('avatar', avatar) + if (avatar !== null) { + if (avatarName !== null) { + form.append('avatar', avatar, avatarName) + } else { + form.append('avatar', avatar) + } + } if (banner !== null) form.append('header', banner) if (background !== null) form.append('pleroma_background_image', background) return fetch(MASTODON_PROFILE_UPDATE_URL, { @@ -191,6 +217,7 @@ const updateProfile = ({ credentials, params }) => { // homepage // location // token +// language const register = ({ params, credentials }) => { const { nickname, ...rest } = params return fetch(MASTODON_REGISTRATION_URL, { @@ -219,16 +246,16 @@ const getCaptcha = () => fetch('/api/pleroma/captcha').then(resp => resp.json()) const authHeaders = (accessToken) => { if (accessToken) { - return { 'Authorization': `Bearer ${accessToken}` } + return { Authorization: `Bearer ${accessToken}` } } else { return { } } } const followUser = ({ id, credentials, ...options }) => { - let url = MASTODON_FOLLOW_URL(id) + const url = MASTODON_FOLLOW_URL(id) const form = {} - if (options.reblogs !== undefined) { form['reblogs'] = options.reblogs } + if (options.reblogs !== undefined) { form.reblogs = options.reblogs } return fetch(url, { body: JSON.stringify(form), headers: { @@ -240,13 +267,20 @@ const followUser = ({ id, credentials, ...options }) => { } const unfollowUser = ({ id, credentials }) => { - let url = MASTODON_UNFOLLOW_URL(id) + const url = MASTODON_UNFOLLOW_URL(id) return fetch(url, { headers: authHeaders(credentials), method: 'POST' }).then((data) => data.json()) } +const fetchUserInLists = ({ id, credentials }) => { + const url = MASTODON_USER_IN_LISTS(id) + return fetch(url, { + headers: authHeaders(credentials) + }).then((data) => data.json()) +} + const pinOwnStatus = ({ id, credentials }) => { return promisedRequest({ url: MASTODON_PIN_OWN_STATUS(id), credentials, method: 'POST' }) .then((data) => parseStatus(data)) @@ -281,8 +315,26 @@ const unblockUser = ({ id, credentials }) => { }).then((data) => data.json()) } +const removeUserFromFollowers = ({ id, credentials }) => { + return fetch(MASTODON_REMOVE_USER_FROM_FOLLOWERS(id), { + headers: authHeaders(credentials), + method: 'POST' + }).then((data) => data.json()) +} + +const editUserNote = ({ id, credentials, comment }) => { + return promisedRequest({ + url: MASTODON_USER_NOTE_URL(id), + credentials, + payload: { + comment + }, + method: 'POST' + }) +} + const approveUser = ({ id, credentials }) => { - let url = MASTODON_APPROVE_USER_URL(id) + const url = MASTODON_APPROVE_USER_URL(id) return fetch(url, { headers: authHeaders(credentials), method: 'POST' @@ -290,7 +342,7 @@ const approveUser = ({ id, credentials }) => { } const denyUser = ({ id, credentials }) => { - let url = MASTODON_DENY_USER_URL(id) + const url = MASTODON_DENY_USER_URL(id) return fetch(url, { headers: authHeaders(credentials), method: 'POST' @@ -298,13 +350,32 @@ const denyUser = ({ id, credentials }) => { } const fetchUser = ({ id, credentials }) => { - let url = `${MASTODON_USER_URL}/${id}` + const url = `${MASTODON_USER_URL}/${id}` return promisedRequest({ url, credentials }) .then((data) => parseUser(data)) } +const fetchUserByName = ({ name, credentials }) => { + return promisedRequest({ + url: MASTODON_USER_LOOKUP_URL, + credentials, + params: { acct: name } + }) + .then(data => data.id) + .catch(error => { + if (error && error.statusCode === 404) { + // Either the backend does not support lookup endpoint, + // or there is no user with such name. Fallback and treat name as id. + return name + } else { + throw error + } + }) + .then(id => fetchUser({ id, credentials })) +} + const fetchUserRelationship = ({ id, credentials }) => { - let url = `${MASTODON_USER_RELATIONSHIPS_URL}/?id=${id}` + const url = `${MASTODON_USER_RELATIONSHIPS_URL}/?id=${id}` return fetch(url, { headers: authHeaders(credentials) }) .then((response) => { return new Promise((resolve, reject) => response.json() @@ -323,7 +394,7 @@ const fetchFriends = ({ id, maxId, sinceId, limit = 20, credentials }) => { maxId && `max_id=${maxId}`, sinceId && `since_id=${sinceId}`, limit && `limit=${limit}`, - `with_relationships=true` + 'with_relationships=true' ].filter(_ => _).join('&') url = url + (args ? '?' + args : '') @@ -333,6 +404,7 @@ const fetchFriends = ({ id, maxId, sinceId, limit = 20, credentials }) => { } const exportFriends = ({ id, credentials }) => { + // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve, reject) => { try { let friends = [] @@ -358,7 +430,7 @@ const fetchFollowers = ({ id, maxId, sinceId, limit = 20, credentials }) => { maxId && `max_id=${maxId}`, sinceId && `since_id=${sinceId}`, limit && `limit=${limit}`, - `with_relationships=true` + 'with_relationships=true' ].filter(_ => _).join('&') url += args ? '?' + args : '' @@ -374,8 +446,83 @@ const fetchFollowRequests = ({ credentials }) => { .then((data) => data.map(parseUser)) } +const fetchLists = ({ credentials }) => { + const url = MASTODON_LISTS_URL + return fetch(url, { headers: authHeaders(credentials) }) + .then((data) => data.json()) +} + +const createList = ({ title, credentials }) => { + const url = MASTODON_LISTS_URL + const headers = authHeaders(credentials) + headers['Content-Type'] = 'application/json' + + return fetch(url, { + headers, + method: 'POST', + body: JSON.stringify({ title }) + }).then((data) => data.json()) +} + +const getList = ({ listId, credentials }) => { + const url = MASTODON_LIST_URL(listId) + return fetch(url, { headers: authHeaders(credentials) }) + .then((data) => data.json()) +} + +const updateList = ({ listId, title, credentials }) => { + const url = MASTODON_LIST_URL(listId) + const headers = authHeaders(credentials) + headers['Content-Type'] = 'application/json' + + return fetch(url, { + headers, + method: 'PUT', + body: JSON.stringify({ title }) + }) +} + +const getListAccounts = ({ listId, credentials }) => { + const url = MASTODON_LIST_ACCOUNTS_URL(listId) + return fetch(url, { headers: authHeaders(credentials) }) + .then((data) => data.json()) + .then((data) => data.map(({ id }) => id)) +} + +const addAccountsToList = ({ listId, accountIds, credentials }) => { + const url = MASTODON_LIST_ACCOUNTS_URL(listId) + const headers = authHeaders(credentials) + headers['Content-Type'] = 'application/json' + + return fetch(url, { + headers, + method: 'POST', + body: JSON.stringify({ account_ids: accountIds }) + }) +} + +const removeAccountsFromList = ({ listId, accountIds, credentials }) => { + const url = MASTODON_LIST_ACCOUNTS_URL(listId) + const headers = authHeaders(credentials) + headers['Content-Type'] = 'application/json' + + return fetch(url, { + headers, + method: 'DELETE', + body: JSON.stringify({ account_ids: accountIds }) + }) +} + +const deleteList = ({ listId, credentials }) => { + const url = MASTODON_LIST_URL(listId) + return fetch(url, { + method: 'DELETE', + headers: authHeaders(credentials) + }) +} + const fetchConversation = ({ id, credentials }) => { - let urlContext = MASTODON_STATUS_CONTEXT_URL(id) + const urlContext = MASTODON_STATUS_CONTEXT_URL(id) return fetch(urlContext, { headers: authHeaders(credentials) }) .then((data) => { if (data.ok) { @@ -391,7 +538,7 @@ const fetchConversation = ({ id, credentials }) => { } const fetchStatus = ({ id, credentials }) => { - let url = MASTODON_STATUS_URL(id) + const url = MASTODON_STATUS_URL(id) return fetch(url, { headers: authHeaders(credentials) }) .then((data) => { if (data.ok) { @@ -403,6 +550,31 @@ const fetchStatus = ({ id, credentials }) => { .then((data) => parseStatus(data)) } +const fetchStatusSource = ({ id, credentials }) => { + const url = MASTODON_STATUS_SOURCE_URL(id) + return fetch(url, { headers: authHeaders(credentials) }) + .then((data) => { + if (data.ok) { + return data + } + throw new Error('Error fetching source', data) + }) + .then((data) => data.json()) + .then((data) => parseSource(data)) +} + +const fetchStatusHistory = ({ status, credentials }) => { + const url = MASTODON_STATUS_HISTORY_URL(status.id) + return promisedRequest({ url, credentials }) + .then((data) => { + data.reverse() + return data.map((item) => { + item.originalStatus = status + return parseStatus(item) + }) + }) +} + const tagUser = ({ tag, credentials, user }) => { const screenName = user.screen_name const form = { @@ -415,7 +587,7 @@ const tagUser = ({ tag, credentials, user }) => { return fetch(TAG_USER_URL, { method: 'PUT', - headers: headers, + headers, body: JSON.stringify(form) }) } @@ -432,7 +604,7 @@ const untagUser = ({ tag, credentials, user }) => { return fetch(TAG_USER_URL, { method: 'DELETE', - headers: headers, + headers, body: JSON.stringify(body) }) } @@ -485,7 +657,7 @@ const deleteUser = ({ credentials, user }) => { return fetch(`${ADMIN_USERS_URL}?nickname=${screenName}`, { method: 'DELETE', - headers: headers + headers }) } @@ -495,18 +667,21 @@ const fetchTimeline = ({ since = false, until = false, userId = false, + listId = false, tag = false, withMuted = false, - replyVisibility = 'all' + replyVisibility = 'all', + includeTypes = [] }) => { const timelineUrls = { public: MASTODON_PUBLIC_TIMELINE, friends: MASTODON_USER_HOME_TIMELINE_URL, dms: MASTODON_DIRECT_MESSAGES_TIMELINE_URL, notifications: MASTODON_USER_NOTIFICATIONS_URL, - 'publicAndExternal': MASTODON_PUBLIC_TIMELINE, + publicAndExternal: MASTODON_PUBLIC_TIMELINE, user: MASTODON_USER_TIMELINE_URL, media: MASTODON_USER_TIMELINE_URL, + list: MASTODON_LIST_TIMELINE_URL, favorites: MASTODON_USER_FAVORITES_TIMELINE_URL, tag: MASTODON_TAG_TIMELINE_URL, bookmarks: MASTODON_BOOKMARK_TIMELINE_URL @@ -520,6 +695,10 @@ const fetchTimeline = ({ url = url(userId) } + if (timeline === 'list') { + url = url(listId) + } + if (since) { params.push(['since_id', since]) } @@ -544,6 +723,11 @@ const fetchTimeline = ({ if (replyVisibility !== 'all') { params.push(['reply_visibility', replyVisibility]) } + if (includeTypes.length > 0) { + includeTypes.forEach(type => { + params.push(['include_types[]', type]) + }) + } params.push(['limit', 20]) @@ -678,7 +862,7 @@ const postStatus = ({ form.append('preview', 'true') } - let postHeaders = authHeaders(credentials) + const postHeaders = authHeaders(credentials) if (idempotencyKey) { postHeaders['idempotency-key'] = idempotencyKey } @@ -694,6 +878,54 @@ const postStatus = ({ .then((data) => data.error ? data : parseStatus(data)) } +const editStatus = ({ + id, + credentials, + status, + spoilerText, + sensitive, + poll, + mediaIds = [], + contentType +}) => { + const form = new FormData() + const pollOptions = poll.options || [] + + form.append('status', status) + if (spoilerText) form.append('spoiler_text', spoilerText) + if (sensitive) form.append('sensitive', sensitive) + if (contentType) form.append('content_type', contentType) + mediaIds.forEach(val => { + form.append('media_ids[]', val) + }) + + if (pollOptions.some(option => option !== '')) { + const normalizedPoll = { + expires_in: poll.expiresIn, + multiple: poll.multiple + } + Object.keys(normalizedPoll).forEach(key => { + form.append(`poll[${key}]`, normalizedPoll[key]) + }) + + pollOptions.forEach(option => { + form.append('poll[options][]', option) + }) + } + + const putHeaders = authHeaders(credentials) + + return fetch(MASTODON_STATUS_URL(id), { + body: form, + method: 'PUT', + headers: putHeaders + }) + .then((response) => { + return response.json() + }) + .then((data) => data.error ? data : parseStatus(data)) +} + const deleteStatus = ({ id, credentials }) => { return fetch(MASTODON_DELETE_URL(id), { headers: authHeaders(credentials), @@ -782,6 +1014,49 @@ const changeEmail = ({ credentials, email, password }) => { .then((response) => response.json()) } +const moveAccount = ({ credentials, password, targetAccount }) => { + const form = new FormData() + + form.append('password', password) + form.append('target_account', targetAccount) + + return fetch(MOVE_ACCOUNT_URL, { + body: form, + method: 'POST', + headers: authHeaders(credentials) + }) + .then((response) => response.json()) +} + +const addAlias = ({ credentials, alias }) => { + return promisedRequest({ + url: ALIASES_URL, + method: 'PUT', + credentials, + payload: { alias } + }) +} + +const deleteAlias = ({ credentials, alias }) => { + return promisedRequest({ + url: ALIASES_URL, + method: 'DELETE', + credentials, + payload: { alias } + }) +} + +const listAliases = ({ credentials }) => { + return promisedRequest({ + url: ALIASES_URL, + method: 'GET', + credentials, + params: { + _cacheBooster: (new Date()).getTime() + } + }) +} + const changePassword = ({ credentials, password, newPassword, newPasswordConfirmation }) => { const form = new FormData() @@ -868,6 +1143,25 @@ const fetchBlocks = ({ credentials }) => { .then((users) => users.map(parseUser)) } +const addBackup = ({ credentials }) => { + return promisedRequest({ + url: PLEROMA_BACKUP_URL, + method: 'POST', + credentials + }) +} + +const listBackups = ({ credentials }) => { + return promisedRequest({ + url: PLEROMA_BACKUP_URL, + method: 'GET', + credentials, + params: { + _cacheBooster: (new Date()).getTime() + } + }) +} + const fetchOAuthTokens = ({ credentials }) => { const url = '/api/oauth_tokens.json' @@ -921,7 +1215,7 @@ const vote = ({ pollId, choices, credentials }) => { method: 'POST', credentials, payload: { - choices: choices + choices } }) } @@ -981,8 +1275,8 @@ const reportUser = ({ credentials, userId, statusIds, comment, forward }) => { url: MASTODON_REPORT_USER_URL, method: 'POST', payload: { - 'account_id': userId, - 'status_ids': statusIds, + account_id: userId, + status_ids: statusIds, comment, forward }, @@ -1002,9 +1296,9 @@ const searchUsers = ({ credentials, query }) => { .then((data) => data.map(parseUser)) } -const search2 = ({ credentials, q, resolve, limit, offset, following }) => { +const search2 = ({ credentials, q, resolve, limit, offset, following, type }) => { let url = MASTODON_SEARCH_2 - let params = [] + const params = [] if (q) { params.push(['q', encodeURIComponent(q)]) @@ -1026,9 +1320,13 @@ const search2 = ({ credentials, q, resolve, limit, offset, following }) => { params.push(['following', true]) } + if (type) { + params.push(['following', type]) + } + params.push(['with_relationships', true]) - let queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&') + const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&') url += `?${queryString}` return fetch(url, { headers: authHeaders(credentials) }) @@ -1081,6 +1379,66 @@ const dismissNotification = ({ credentials, id }) => { }) } +const adminFetchAnnouncements = ({ credentials }) => { + return promisedRequest({ url: PLEROMA_ANNOUNCEMENTS_URL, credentials }) +} + +const fetchAnnouncements = ({ credentials }) => { + return promisedRequest({ url: MASTODON_ANNOUNCEMENTS_URL, credentials }) +} + +const dismissAnnouncement = ({ id, credentials }) => { + return promisedRequest({ + url: MASTODON_ANNOUNCEMENTS_DISMISS_URL(id), + credentials, + method: 'POST' + }) +} + +const announcementToPayload = ({ content, startsAt, endsAt, allDay }) => { + const payload = { content } + + if (typeof startsAt !== 'undefined') { + payload.starts_at = startsAt ? new Date(startsAt).toISOString() : null + } + + if (typeof endsAt !== 'undefined') { + payload.ends_at = endsAt ? new Date(endsAt).toISOString() : null + } + + if (typeof allDay !== 'undefined') { + payload.all_day = allDay + } + + return payload +} + +const postAnnouncement = ({ credentials, content, startsAt, endsAt, allDay }) => { + return promisedRequest({ + url: PLEROMA_POST_ANNOUNCEMENT_URL, + credentials, + method: 'POST', + payload: announcementToPayload({ content, startsAt, endsAt, allDay }) + }) +} + +const editAnnouncement = ({ id, credentials, content, startsAt, endsAt, allDay }) => { + return promisedRequest({ + url: PLEROMA_EDIT_ANNOUNCEMENT_URL(id), + credentials, + method: 'PATCH', + payload: announcementToPayload({ content, startsAt, endsAt, allDay }) + }) +} + +const deleteAnnouncement = ({ id, credentials }) => { + return promisedRequest({ + url: PLEROMA_DELETE_ANNOUNCEMENT_URL(id), + credentials, + method: 'DELETE' + }) +} + export const getMastodonSocketURI = ({ credentials, stream, args = {} }) => { return Object.entries({ ...(credentials @@ -1098,7 +1456,8 @@ const MASTODON_STREAMING_EVENTS = new Set([ 'update', 'notification', 'delete', - 'filters_changed' + 'filters_changed', + 'status.update' ]) const PLEROMA_STREAMING_EVENTS = new Set([ @@ -1170,6 +1529,8 @@ export const handleMastoWS = (wsEvent) => { const data = payload ? JSON.parse(payload) : null if (event === 'update') { return { event, status: parseStatus(data) } + } else if (event === 'status.update') { + return { event, status: parseStatus(data) } } else if (event === 'notification') { return { event, notification: parseNotification(data) } } else if (event === 'pleroma:chat_update') { @@ -1182,12 +1543,12 @@ export const handleMastoWS = (wsEvent) => { } export const WSConnectionStatus = Object.freeze({ - 'JOINED': 1, - 'CLOSED': 2, - 'ERROR': 3, - 'DISABLED': 4, - 'STARTING': 5, - 'STARTING_INITIAL': 6 + JOINED: 1, + CLOSED: 2, + ERROR: 3, + DISABLED: 4, + STARTING: 5, + STARTING_INITIAL: 6 }) const chats = ({ credentials }) => { @@ -1225,11 +1586,11 @@ const chatMessages = ({ id, credentials, maxId, sinceId, limit = 20 }) => { const sendChatMessage = ({ id, content, mediaId = null, idempotencyKey, credentials }) => { const payload = { - 'content': content + content } if (mediaId) { - payload['media_id'] = mediaId + payload.media_id = mediaId } const headers = {} @@ -1241,7 +1602,7 @@ const sendChatMessage = ({ id, content, mediaId = null, idempotencyKey, credenti return promisedRequest({ url: PLEROMA_CHAT_MESSAGES_URL(id), method: 'POST', - payload: payload, + payload, credentials, headers }) @@ -1252,7 +1613,7 @@ const readChat = ({ id, lastReadId, credentials }) => { url: PLEROMA_CHAT_READ_URL(id), method: 'POST', payload: { - 'last_read_id': lastReadId + last_read_id: lastReadId }, credentials }) @@ -1266,12 +1627,46 @@ const deleteChatMessage = ({ chatId, messageId, credentials }) => { }) } +const setReportState = ({ id, state, credentials }) => { + // TODO: Can't use promisedRequest because on OK this does not return json + // See https://git.pleroma.social/pleroma/pleroma-fe/-/merge_requests/1322 + return fetch(PLEROMA_ADMIN_REPORTS, { + headers: { + ...authHeaders(credentials), + Accept: 'application/json', + 'Content-Type': 'application/json' + }, + method: 'PATCH', + body: JSON.stringify({ + reports: [{ + id, + state + }] + }) + }) + .then(data => { + if (data.status >= 500) { + throw Error(data.statusText) + } else if (data.status >= 400) { + return data.json() + } + return data + }) + .then(data => { + if (data.errors) { + throw Error(data.errors[0].message) + } + }) +} + const apiService = { verifyCredentials, fetchTimeline, fetchPinnedStatuses, fetchConversation, fetchStatus, + fetchStatusSource, + fetchStatusHistory, fetchFriends, exportFriends, fetchFollowers, @@ -1283,7 +1678,10 @@ const apiService = { unmuteConversation, blockUser, unblockUser, + removeUserFromFollowers, + editUserNote, fetchUser, + fetchUserByName, fetchUserRelationship, favorite, unfavorite, @@ -1292,6 +1690,7 @@ const apiService = { bookmarkStatus, unbookmarkStatus, postStatus, + editStatus, deleteStatus, uploadMedia, setMediaDescription, @@ -1319,13 +1718,27 @@ const apiService = { importFollows, deleteAccount, changeEmail, + moveAccount, + addAlias, + deleteAlias, + listAliases, changePassword, settingsMFA, mfaDisableOTP, generateMfaBackupCodes, mfaSetupOTP, mfaConfirmOTP, + addBackup, + listBackups, fetchFollowRequests, + fetchLists, + createList, + getList, + updateList, + getListAccounts, + addAccountsToList, + removeAccountsFromList, + deleteList, approveUser, denyUser, suggestions, @@ -1351,7 +1764,15 @@ const apiService = { chatMessages, sendChatMessage, readChat, - deleteChatMessage + deleteChatMessage, + setReportState, + fetchUserInLists, + fetchAnnouncements, + dismissAnnouncement, + postAnnouncement, + editAnnouncement, + deleteAnnouncement, + adminFetchAnnouncements } export default apiService diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js @@ -2,10 +2,11 @@ import apiService, { getMastodonSocketURI, ProcessedWS } from '../api/api.servic import timelineFetcher from '../timeline_fetcher/timeline_fetcher.service.js' import notificationsFetcher from '../notifications_fetcher/notifications_fetcher.service.js' import followRequestFetcher from '../../services/follow_request_fetcher/follow_request_fetcher.service' +import listsFetcher from '../../services/lists_fetcher/lists_fetcher.service.js' const backendInteractorService = credentials => ({ - startFetchingTimeline ({ timeline, store, userId = false, tag }) { - return timelineFetcher.startFetching({ timeline, store, credentials, userId, tag }) + startFetchingTimeline ({ timeline, store, userId = false, listId = false, tag }) { + return timelineFetcher.startFetching({ timeline, store, credentials, userId, listId, tag }) }, fetchTimeline (args) { @@ -24,6 +25,10 @@ const backendInteractorService = credentials => ({ return followRequestFetcher.startFetching({ store, credentials }) }, + startFetchingLists ({ store }) { + return listsFetcher.startFetching({ store, credentials }) + }, + startUserSocket ({ store }) { const serv = store.rootState.instance.server.replace('http', 'ws') const url = serv + getMastodonSocketURI({ credentials, stream: 'user' }) diff --git a/src/services/chat_service/chat_service.js b/src/services/chat_service/chat_service.js @@ -7,7 +7,7 @@ const empty = (chatId) => { messages: [], newMessageCount: 0, lastSeenMessageId: '0', - chatId: chatId, + chatId, minId: undefined, maxId: undefined } @@ -101,7 +101,7 @@ const add = (storage, { messages: newMessages, updateMaxId = true }) => { storage.messages = storage.messages.filter(msg => msg.id !== message.id) } Object.assign(fakeMessage, message, { error: false }) - delete fakeMessage['fakeId'] + delete fakeMessage.fakeId storage.idIndex[fakeMessage.id] = fakeMessage delete storage.idIndex[message.fakeId] @@ -178,7 +178,7 @@ const getView = (storage) => { id: date.getTime().toString() }) - previousMessage['isTail'] = true + previousMessage.isTail = true currentMessageChainId = undefined afterDate = true } @@ -193,15 +193,15 @@ const getView = (storage) => { // end a message chian if ((nextMessage && nextMessage.account_id) !== message.account_id) { - object['isTail'] = true + object.isTail = true currentMessageChainId = undefined } // start a new message chain if ((previousMessage && previousMessage.data && previousMessage.data.account_id) !== message.account_id || afterDate) { currentMessageChainId = _.uniqueId() - object['isHead'] = true - object['messageChainId'] = currentMessageChainId + object.isHead = true + object.messageChainId = currentMessageChainId } result.push(object) diff --git a/src/services/chat_utils/chat_utils.js b/src/services/chat_utils/chat_utils.js @@ -25,7 +25,7 @@ export const buildFakeMessage = ({ content, chatId, attachments, userId, idempot chat_id: chatId, created_at: new Date(), id: `${new Date().getTime()}`, - attachments: attachments, + attachments, account_id: userId, idempotency_key: idempotencyKey, emojis: [], diff --git a/src/services/color_convert/color_convert.js b/src/services/color_convert/color_convert.js @@ -144,11 +144,13 @@ export const invert = (rgb) => { */ export const hex2rgb = (hex) => { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) - return result ? { - r: parseInt(result[1], 16), - g: parseInt(result[2], 16), - b: parseInt(result[3], 16) - } : null + return result + ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16) + } + : null } /** diff --git a/src/services/completion/completion.js b/src/services/completion/completion.js @@ -35,7 +35,7 @@ export const addPositionToWords = (words) => { } export const splitByWhitespaceBoundary = (str) => { - let result = [] + const result = [] let currentWord = '' for (let i = 0; i < str.length; i++) { const currentChar = str[i] diff --git a/src/services/date_utils/date_utils.js b/src/services/date_utils/date_utils.js @@ -10,31 +10,29 @@ export const relativeTime = (date, nowThreshold = 1) => { if (typeof date === 'string') date = Date.parse(date) const round = Date.now() > date ? Math.floor : Math.ceil const d = Math.abs(Date.now() - date) - let r = { num: round(d / YEAR), key: 'time.years' } + const r = { num: round(d / YEAR), key: 'time.unit.years' } if (d < nowThreshold * SECOND) { r.num = 0 r.key = 'time.now' } else if (d < MINUTE) { r.num = round(d / SECOND) - r.key = 'time.seconds' + r.key = 'time.unit.seconds' } else if (d < HOUR) { r.num = round(d / MINUTE) - r.key = 'time.minutes' + r.key = 'time.unit.minutes' } else if (d < DAY) { r.num = round(d / HOUR) - r.key = 'time.hours' + r.key = 'time.unit.hours' } else if (d < WEEK) { r.num = round(d / DAY) - r.key = 'time.days' + r.key = 'time.unit.days' } else if (d < MONTH) { r.num = round(d / WEEK) - r.key = 'time.weeks' + r.key = 'time.unit.weeks' } else if (d < YEAR) { r.num = round(d / MONTH) - r.key = 'time.months' + r.key = 'time.unit.months' } - // Remove plural form when singular - if (r.num === 1) r.key = r.key.slice(0, -1) return r } diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js @@ -39,14 +39,17 @@ const qvitterStatusType = (status) => { export const parseUser = (data) => { const output = {} - const masto = data.hasOwnProperty('acct') + const masto = Object.prototype.hasOwnProperty.call(data, 'acct') // case for users in "mentions" property for statuses in MastoAPI - const mastoShort = masto && !data.hasOwnProperty('avatar') + const mastoShort = masto && !Object.prototype.hasOwnProperty.call(data, 'avatar') + output.inLists = null output.id = String(data.id) + output._original = data // used for server-side settings if (masto) { output.screen_name = data.acct + output.fqn = data.fqn output.statusnet_profile_url = data.url // There's nothing else to get @@ -89,6 +92,9 @@ export const parseUser = (data) => { output.bot = data.bot if (data.pleroma) { + if (data.pleroma.settings_store) { + output.storage = data.pleroma.settings_store['pleroma-fe'] + } const relationship = data.pleroma.relationship output.background_image = data.pleroma.background_image @@ -118,6 +124,34 @@ export const parseUser = (data) => { } else { output.role = 'member' } + + if (data.pleroma.privileges) { + output.privileges = data.pleroma.privileges + } else if (data.pleroma.is_admin) { + output.privileges = [ + 'users_read', + 'users_manage_invites', + 'users_manage_activation_state', + 'users_manage_tags', + 'users_manage_credentials', + 'users_delete', + 'messages_read', + 'messages_delete', + 'instances_delete', + 'reports_manage_reports', + 'moderation_log_read', + 'announcements_manage_announcements', + 'emoji_manage_emoji', + 'statistics_read' + ] + } else if (data.pleroma.is_moderator) { + output.privileges = [ + 'messages_delete', + 'reports_manage_reports' + ] + } else { + output.privileges = [] + } } if (data.source) { @@ -210,12 +244,14 @@ export const parseUser = (data) => { output.screen_name_ui = output.screen_name if (output.screen_name && output.screen_name.includes('@')) { const parts = output.screen_name.split('@') - let unicodeDomain = punycode.toUnicode(parts[1]) + const unicodeDomain = punycode.toUnicode(parts[1]) if (unicodeDomain !== parts[1]) { // Add some identifier so users can potentially spot spoofing attempts: // lain.com and xn--lin-6cd.com would appear identical otherwise. - unicodeDomain = '🌏' + unicodeDomain + output.screen_name_ui_contains_non_ascii = true output.screen_name_ui = [parts[0], unicodeDomain].join('@') + } else { + output.screen_name_ui_contains_non_ascii = false } } @@ -224,7 +260,7 @@ export const parseUser = (data) => { export const parseAttachment = (data) => { const output = {} - const masto = !data.hasOwnProperty('oembed') + const masto = !Object.prototype.hasOwnProperty.call(data, 'oembed') if (masto) { // Not exactly same... @@ -243,9 +279,19 @@ export const parseAttachment = (data) => { return output } +export const parseSource = (data) => { + const output = {} + + output.text = data.text + output.spoiler_text = data.spoiler_text + output.content_type = data.content_type + + return output +} + export const parseStatus = (data) => { const output = {} - const masto = data.hasOwnProperty('account') + const masto = Object.prototype.hasOwnProperty.call(data, 'account') if (masto) { output.favorited = data.favourited @@ -264,6 +310,8 @@ export const parseStatus = (data) => { output.tags = data.tags + output.edited_at = data.edited_at + if (data.pleroma) { const { pleroma } = data output.text = pleroma.content ? data.pleroma.content['text/plain'] : data.content @@ -365,15 +413,19 @@ export const parseStatus = (data) => { output.favoritedBy = [] output.rebloggedBy = [] + if (Object.prototype.hasOwnProperty.call(data, 'originalStatus')) { + Object.assign(output, data.originalStatus) + } + return output } export const parseNotification = (data) => { const mastoDict = { - 'favourite': 'like', - 'reblog': 'repeat' + favourite: 'like', + reblog: 'repeat' } - const masto = !data.hasOwnProperty('ntype') + const masto = !Object.prototype.hasOwnProperty.call(data, 'ntype') const output = {} if (masto) { @@ -386,6 +438,13 @@ export const parseNotification = (data) => { : parseUser(data.target) output.from_profile = parseUser(data.account) output.emoji = data.emoji + if (data.report) { + output.report = data.report + output.report.content = data.report.content + output.report.acct = parseUser(data.report.account) + output.report.actor = parseUser(data.report.actor) + output.report.statuses = data.report.statuses.map(parseStatus) + } } else { const parsedNotice = parseStatus(data.notice) output.type = data.ntype diff --git a/src/services/errors/errors.js b/src/services/errors/errors.js @@ -26,6 +26,7 @@ export class RegistrationError extends Error { // the error is probably a JSON object with a single key, "errors", whose value is another JSON object containing the real errors if (typeof error === 'string') { error = JSON.parse(error) + // eslint-disable-next-line if (error.hasOwnProperty('error')) { error = JSON.parse(error.error) } diff --git a/src/services/export_import/export_import.js b/src/services/export_import/export_import.js @@ -1,9 +1,11 @@ +import utf8 from 'utf8' + export const newExporter = ({ filename = 'data', getExportedObject }) => ({ exportData () { - const stringified = JSON.stringify(getExportedObject(), null, 2) // Pretty-print and indent with 2 spaces + const stringified = utf8.encode(JSON.stringify(getExportedObject(), null, 2)) // Pretty-print and indent with 2 spaces // Create an invisible link with a data url and simulate a click const e = document.createElement('a') diff --git a/src/services/file_size_format/file_size_format.js b/src/services/file_size_format/file_size_format.js @@ -1,15 +1,14 @@ -const fileSizeFormat = (num) => { - var exponent - var unit - var units = ['B', 'KiB', 'MiB', 'GiB', 'TiB'] +const fileSizeFormat = (numArg) => { + const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB'] + let num = numArg if (num < 1) { return num + ' ' + units[0] } - exponent = Math.min(Math.floor(Math.log(num) / Math.log(1024)), units.length - 1) + const exponent = Math.min(Math.floor(Math.log(num) / Math.log(1024)), units.length - 1) num = (num / Math.pow(1024, exponent)).toFixed(2) * 1 - unit = units[exponent] - return { num: num, unit: unit } + const unit = units[exponent] + return { num, unit } } const fileSizeFormatService = { fileSizeFormat diff --git a/src/services/gesture_service/gesture_service.js b/src/services/gesture_service/gesture_service.js @@ -4,9 +4,15 @@ const DIRECTION_RIGHT = [1, 0] const DIRECTION_UP = [0, -1] const DIRECTION_DOWN = [0, 1] +const BUTTON_LEFT = 0 + const deltaCoord = (oldCoord, newCoord) => [newCoord[0] - oldCoord[0], newCoord[1] - oldCoord[1]] -const touchEventCoord = e => ([e.touches[0].screenX, e.touches[0].screenY]) +const touchCoord = touch => [touch.screenX, touch.screenY] + +const touchEventCoord = e => touchCoord(e.touches[0]) + +const pointerEventCoord = e => [e.clientX, e.clientY] const vectorLength = v => Math.sqrt(v[0] * v[0] + v[1] * v[1]) @@ -61,6 +67,132 @@ const updateSwipe = (event, gesture) => { gesture._swiping = false } +class SwipeAndClickGesture { + // swipePreviewCallback(offsets: Array[Number]) + // offsets: the offset vector which the underlying component should move, from the starting position + // swipeEndCallback(sign: 0|-1|1) + // sign: if the swipe does not meet the threshold, 0 + // if the swipe meets the threshold in the positive direction, 1 + // if the swipe meets the threshold in the negative direction, -1 + constructor ({ + direction, + // swipeStartCallback + swipePreviewCallback, + swipeEndCallback, + swipeCancelCallback, + swipelessClickCallback, + threshold = 30, + perpendicularTolerance = 1.0, + disableClickThreshold = 1 + }) { + const nop = () => {} + this.direction = direction + this.swipePreviewCallback = swipePreviewCallback || nop + this.swipeEndCallback = swipeEndCallback || nop + this.swipeCancelCallback = swipeCancelCallback || nop + this.swipelessClickCallback = swipelessClickCallback || nop + this.threshold = typeof threshold === 'function' ? threshold : () => threshold + this.disableClickThreshold = typeof disableClickThreshold === 'function' ? disableClickThreshold : () => disableClickThreshold + this.perpendicularTolerance = perpendicularTolerance + this._reset() + } + + _reset () { + this._startPos = [0, 0] + this._pointerId = -1 + this._swiping = false + this._swiped = false + this._preventNextClick = false + } + + start (event) { + // Only handle left click + if (event.button !== BUTTON_LEFT) { + return + } + + this._startPos = pointerEventCoord(event) + this._pointerId = event.pointerId + this._swiping = true + this._swiped = false + } + + move (event) { + if (this._swiping && this._pointerId === event.pointerId) { + this._swiped = true + + const coord = pointerEventCoord(event) + const delta = deltaCoord(this._startPos, coord) + + this.swipePreviewCallback(delta) + } + } + + cancel (event) { + if (!this._swiping || this._pointerId !== event.pointerId) { + return + } + + this.swipeCancelCallback() + } + + end (event) { + if (!this._swiping) { + return + } + + if (this._pointerId !== event.pointerId) { + return + } + + this._swiping = false + + // movement too small + const coord = pointerEventCoord(event) + const delta = deltaCoord(this._startPos, coord) + + const sign = (() => { + if (vectorLength(delta) < this.threshold()) { + return 0 + } + // movement is opposite from direction + const isPositive = dotProduct(delta, this.direction) > 0 + + // movement perpendicular to direction is too much + const towardsDir = project(delta, this.direction) + const perpendicularDir = perpendicular(this.direction) + const towardsPerpendicular = project(delta, perpendicularDir) + if ( + vectorLength(towardsDir) * this.perpendicularTolerance < + vectorLength(towardsPerpendicular) + ) { + return 0 + } + + return isPositive ? 1 : -1 + })() + + if (this._swiped) { + this.swipeEndCallback(sign) + } + this._reset() + // Only a mouse will fire click event when + // the end point is far from the starting point + // so for other kinds of pointers do not check + // whether we have swiped + if (vectorLength(delta) >= this.disableClickThreshold() && event.pointerType === 'mouse') { + this._preventNextClick = true + } + } + + click (event) { + if (!this._preventNextClick) { + this.swipelessClickCallback() + } + this._reset() + } +} + const GestureService = { DIRECTION_LEFT, DIRECTION_RIGHT, @@ -68,7 +200,8 @@ const GestureService = { DIRECTION_DOWN, swipeGesture, beginSwipe, - updateSwipe + updateSwipe, + SwipeAndClickGesture } export default GestureService diff --git a/src/services/html_converter/html_line_converter.service.js b/src/services/html_converter/html_line_converter.service.js @@ -46,7 +46,7 @@ export const convertHtmlToLines = (html = '') => { // All block-level elements that aren't empty elements, i.e. not <hr> const nonEmptyElements = new Set(visualLineElements) // Difference - for (let elem of emptyElements) { + for (const elem of emptyElements) { nonEmptyElements.delete(elem) } @@ -56,7 +56,7 @@ export const convertHtmlToLines = (html = '') => { ...emptyElements.values() ]) - let buffer = [] // Current output buffer + const buffer = [] // Current output buffer const level = [] // How deep we are in tags and which tags were there let textBuffer = '' // Current line content let tagBuffer = null // Current tag buffer, if null = we are not currently reading a tag diff --git a/src/services/html_converter/html_tree_converter.service.js b/src/services/html_converter/html_tree_converter.service.js @@ -1,4 +1,5 @@ import { getTagName } from './utility.service.js' +import { unescape } from 'lodash' /** * This is a not-so-tiny purpose-built HTML parser/processor. This parses html @@ -49,7 +50,7 @@ export const convertHtmlToTree = (html = '') => { const handleOpen = (tag) => { const curBuf = getCurrentBuffer() - const newLevel = [tag, []] + const newLevel = [unescape(tag), []] levels.push(newLevel) curBuf.push(newLevel) } diff --git a/src/services/html_converter/utility.service.js b/src/services/html_converter/utility.service.js @@ -16,7 +16,7 @@ export const getTagName = (tag) => { * @return {Object} - map of attributes key = attribute name, value = attribute value * attributes without values represented as boolean true */ -export const getAttrs = tag => { +export const getAttrs = (tag, filter) => { const innertag = tag .substring(1, tag.length - 1) .replace(new RegExp('^' + getTagName(tag)), '') @@ -28,7 +28,15 @@ export const getAttrs = tag => { if (!v) return [k, true] return [k, v.substring(1, v.length - 1)] }) - return Object.fromEntries(attrs) + const defaultFilter = ([k, v]) => { + const attrKey = k.toLowerCase() + if (attrKey === 'style') return false + if (attrKey === 'class') { + return v === 'greentext' || v === 'cyantext' + } + return true + } + return Object.fromEntries(attrs.filter(filter || defaultFilter)) } /** @@ -50,7 +58,7 @@ export const processTextForEmoji = (text, emojis, processor) => { if (char === ':') { const next = text.slice(i + 1) let found = false - for (let emoji of emojis) { + for (const emoji of emojis) { if (next.slice(0, emoji.shortcode.length + 1) === (emoji.shortcode + ':')) { found = emoji break diff --git a/src/services/lists_fetcher/lists_fetcher.service.js b/src/services/lists_fetcher/lists_fetcher.service.js @@ -0,0 +1,22 @@ +import apiService from '../api/api.service.js' +import { promiseInterval } from '../promise_interval/promise_interval.js' + +const fetchAndUpdate = ({ store, credentials }) => { + return apiService.fetchLists({ credentials }) + .then(lists => { + store.commit('setLists', lists) + }, () => {}) + .catch(() => {}) +} + +const startFetching = ({ credentials, store }) => { + const boundFetchAndUpdate = () => fetchAndUpdate({ credentials, store }) + boundFetchAndUpdate() + return promiseInterval(boundFetchAndUpdate, 240000) +} + +const listsFetcher = { + startFetching +} + +export default listsFetcher diff --git a/src/services/locale/locale.service.js b/src/services/locale/locale.service.js @@ -1,12 +1,35 @@ +import languagesObject from '../../i18n/messages' +import ISO6391 from 'iso-639-1' +import _ from 'lodash' + const specialLanguageCodes = { - 'ja_easy': 'ja', - 'zh_Hant': 'zh-HANT' + ja_easy: 'ja', + zh_Hant: 'zh-HANT', + zh: 'zh-Hans' } const internalToBrowserLocale = code => specialLanguageCodes[code] || code +const internalToBackendLocale = code => internalToBrowserLocale(code).replace('_', '-') + +const getLanguageName = (code) => { + const specialLanguageNames = { + ja_easy: 'やさしいにほんご', + zh: '简体中文', + zh_Hant: '繁體中文' + } + const languageName = specialLanguageNames[code] || ISO6391.getNativeName(code) + const browserLocale = internalToBrowserLocale(code) + return languageName.charAt(0).toLocaleUpperCase(browserLocale) + languageName.slice(1) +} + +const languages = _.map(languagesObject.languages, (code) => ({ code, name: getLanguageName(code) })).sort((a, b) => a.name.localeCompare(b.name)) + const localeService = { - internalToBrowserLocale + internalToBrowserLocale, + internalToBackendLocale, + languages, + getLanguageName } export default localeService diff --git a/src/services/new_api/password_reset.js b/src/services/new_api/password_reset.js @@ -1,6 +1,6 @@ import { reduce } from 'lodash' -const MASTODON_PASSWORD_RESET_URL = `/auth/password` +const MASTODON_PASSWORD_RESET_URL = '/auth/password' const resetPassword = ({ instance, email }) => { const params = { email } diff --git a/src/services/notification_utils/notification_utils.js b/src/services/notification_utils/notification_utils.js @@ -14,11 +14,13 @@ export const visibleTypes = store => { rootState.config.notificationVisibility.follows && 'follow', rootState.config.notificationVisibility.followRequest && 'follow_request', rootState.config.notificationVisibility.moves && 'move', - rootState.config.notificationVisibility.emojiReactions && 'pleroma:emoji_reaction' + rootState.config.notificationVisibility.emojiReactions && 'pleroma:emoji_reaction', + rootState.config.notificationVisibility.reports && 'pleroma:report', + rootState.config.notificationVisibility.polls && 'poll' ].filter(_ => _)) } -const statusNotifications = ['like', 'mention', 'repeat', 'pleroma:emoji_reaction'] +const statusNotifications = ['like', 'mention', 'repeat', 'pleroma:emoji_reaction', 'poll'] export const isStatusNotification = (type) => includes(statusNotifications, type) @@ -98,6 +100,12 @@ export const prepareNotificationObject = (notification, i18n) => { case 'follow_request': i18nString = 'follow_request' break + case 'pleroma:report': + i18nString = 'submitted_report' + break + case 'poll': + i18nString = 'poll_ended' + break } if (notification.type === 'pleroma:emoji_reaction') { diff --git a/src/services/notifications_fetcher/notifications_fetcher.service.js b/src/services/notifications_fetcher/notifications_fetcher.service.js @@ -1,6 +1,18 @@ import apiService from '../api/api.service.js' import { promiseInterval } from '../promise_interval/promise_interval.js' +// For using include_types when fetching notifications. +// Note: chat_mention excluded as pleroma-fe polls them separately +const mastoApiNotificationTypes = [ + 'mention', + 'favourite', + 'reblog', + 'follow', + 'move', + 'pleroma:emoji_reaction', + 'pleroma:report' +] + const update = ({ store, notifications, older }) => { store.dispatch('addNewNotifications', { notifications, older }) } @@ -11,24 +23,22 @@ const fetchAndUpdate = ({ store, credentials, older = false, since }) => { const rootState = store.rootState || store.state const timelineData = rootState.statuses.notifications const hideMutedPosts = getters.mergedConfig.hideMutedPosts - const allowFollowingMove = rootState.users.currentUser.allow_following_move - - args['withMuted'] = !hideMutedPosts - args['withMove'] = !allowFollowingMove + args.includeTypes = mastoApiNotificationTypes + args.withMuted = !hideMutedPosts - args['timeline'] = 'notifications' + args.timeline = 'notifications' if (older) { if (timelineData.minId !== Number.POSITIVE_INFINITY) { - args['until'] = timelineData.minId + args.until = timelineData.minId } return fetchNotifications({ store, args, older }) } else { // fetch new notifications if (since === undefined && timelineData.maxId !== Number.POSITIVE_INFINITY) { - args['since'] = timelineData.maxId + args.since = timelineData.maxId } else if (since !== null) { - args['since'] = since + args.since = since } const result = fetchNotifications({ store, args, older }) @@ -41,7 +51,7 @@ const fetchAndUpdate = ({ store, credentials, older = false, since }) => { const readNotifsIds = notifications.filter(n => n.seen).map(n => n.id) const numUnseenNotifs = notifications.length - readNotifsIds.length if (numUnseenNotifs > 0 && readNotifsIds.length > 0) { - args['since'] = Math.max(...readNotifsIds) + args.since = Math.max(...readNotifsIds) fetchNotifications({ store, args, older }) } @@ -66,6 +76,7 @@ const fetchNotifications = ({ store, args, older }) => { messageArgs: [error.message], timeout: 5000 }) + console.error(error) }) } diff --git a/src/services/offset_finder/offset_finder.service.js b/src/services/offset_finder/offset_finder.service.js @@ -9,7 +9,7 @@ export const findOffset = (child, parent, { top = 0, left = 0 } = {}, ignorePadd result.left += ignorePadding ? 0 : leftPadding } - if (child.offsetParent && (parent === window || parent.contains(child.offsetParent) || parent === child.offsetParent)) { + if (child.offsetParent && window.getComputedStyle(child.offsetParent).position !== 'sticky' && (parent === window || parent.contains(child.offsetParent) || parent === child.offsetParent)) { return findOffset(child.offsetParent, parent, result, false) } else { if (parent !== window) { diff --git a/src/services/push/push.js b/src/services/push/push.js @@ -1,4 +1,4 @@ -import runtime from 'serviceworker-webpack-plugin/lib/runtime' +import runtime from 'serviceworker-webpack5-plugin/lib/runtime' function urlBase64ToUint8Array (base64String) { const padding = '='.repeat((4 - base64String.length % 4) % 4) @@ -43,7 +43,7 @@ function deleteSubscriptionFromBackEnd (token) { method: 'DELETE', headers: { 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token}` + Authorization: `Bearer ${token}` } }).then((response) => { if (!response.ok) throw new Error('Bad status code from server.') @@ -56,7 +56,7 @@ function sendSubscriptionToBackEnd (subscription, token, notificationVisibility) method: 'POST', headers: { 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token}` + Authorization: `Bearer ${token}` }, body: JSON.stringify({ subscription, diff --git a/src/services/resettable_async_component.js b/src/services/resettable_async_component.js @@ -1,4 +1,4 @@ -import Vue from 'vue' +import { defineAsyncComponent, shallowReactive, h } from 'vue' /* By default async components don't have any way to recover, if component is * failed, it is failed forever. This helper tries to remedy that by recreating @@ -8,23 +8,21 @@ import Vue from 'vue' * actual target component itself if needs to be. */ function getResettableAsyncComponent (asyncComponent, options) { - const asyncComponentFactory = () => () => ({ - component: asyncComponent(), + const asyncComponentFactory = () => () => defineAsyncComponent({ + loader: asyncComponent, ...options }) - const observe = Vue.observable({ c: asyncComponentFactory() }) + const observe = shallowReactive({ c: asyncComponentFactory() }) return { - functional: true, - render (createElement, { data, children }) { + render () { // emit event resetAsyncComponent to reloading - data.on = {} - data.on.resetAsyncComponent = () => { - observe.c = asyncComponentFactory() - // parent.$forceUpdate() - } - return createElement(observe.c, data, children) + return h(observe.c(), { + onResetAsyncComponent () { + observe.c = asyncComponentFactory() + } + }) } } } diff --git a/src/services/status_poster/status_poster.service.js b/src/services/status_poster/status_poster.service.js @@ -47,6 +47,47 @@ const postStatus = ({ }) } +const editStatus = ({ + store, + statusId, + status, + spoilerText, + sensitive, + poll, + media = [], + contentType = 'text/plain' +}) => { + const mediaIds = map(media, 'id') + + return apiService.editStatus({ + id: statusId, + credentials: store.state.users.currentUser.credentials, + status, + spoilerText, + sensitive, + poll, + mediaIds, + contentType + }) + .then((data) => { + if (!data.error) { + store.dispatch('addNewStatuses', { + statuses: [data], + timeline: 'friends', + showImmediately: true, + noIdUpdate: true // To prevent missing notices on next pull. + }) + } + return data + }) + .catch((err) => { + console.error('Error editing status', err) + return { + error: err.message + } + }) +} + const uploadMedia = ({ store, formData }) => { const credentials = store.state.users.currentUser.credentials return apiService.uploadMedia({ credentials, formData }) @@ -59,6 +100,7 @@ const setMediaDescription = ({ store, id, description }) => { const statusPosterService = { postStatus, + editStatus, uploadMedia, setMediaDescription } diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js @@ -1,6 +1,7 @@ import { convert } from 'chromatism' import { rgb2hex, hex2rgb, rgba2css, getCssColor, relativeLuminance } from '../color_convert/color_convert.js' import { getColors, computeDynamicColor, getOpacitySlot } from '../theme_data/theme_data.service.js' +import { defaultState } from '../../modules/config.js' export const applyTheme = (input) => { const { rules } = generatePreset(input) @@ -13,10 +14,40 @@ export const applyTheme = (input) => { const styleSheet = styleEl.sheet styleSheet.toString() - styleSheet.insertRule(`body { ${rules.radii} }`, 'index-max') - styleSheet.insertRule(`body { ${rules.colors} }`, 'index-max') - styleSheet.insertRule(`body { ${rules.shadows} }`, 'index-max') - styleSheet.insertRule(`body { ${rules.fonts} }`, 'index-max') + styleSheet.insertRule(`:root { ${rules.radii} }`, 'index-max') + styleSheet.insertRule(`:root { ${rules.colors} }`, 'index-max') + styleSheet.insertRule(`:root { ${rules.shadows} }`, 'index-max') + styleSheet.insertRule(`:root { ${rules.fonts} }`, 'index-max') + body.classList.remove('hidden') +} + +const configColumns = ({ sidebarColumnWidth, contentColumnWidth, notifsColumnWidth }) => + ({ sidebarColumnWidth, contentColumnWidth, notifsColumnWidth }) + +const defaultConfigColumns = configColumns(defaultState) + +export const applyConfig = (config) => { + const columns = configColumns(config) + + if (columns === defaultConfigColumns) { + return + } + + const head = document.head + const body = document.body + body.classList.add('hidden') + + const rules = Object + .entries(columns) + .filter(([k, v]) => v) + .map(([k, v]) => `--${k}: ${v}`).join(';') + + const styleEl = document.createElement('style') + head.appendChild(styleEl) + const styleSheet = styleEl.sheet + + styleSheet.toString() + styleSheet.insertRule(`:root { ${rules} }`, 'index-max') body.classList.remove('hidden') } diff --git a/src/services/theme_data/pleromafe.js b/src/services/theme_data/pleromafe.js @@ -709,6 +709,14 @@ export const SLOT_INHERITANCE = { textColor: 'bw' }, + badgeNeutral: '--cGreen', + badgeNeutralText: { + depends: ['text', 'badgeNeutral'], + layer: 'badge', + variant: 'badgeNeutral', + textColor: 'bw' + }, + chatBg: { depends: ['bg'] }, diff --git a/src/services/theme_data/theme_data.service.js b/src/services/theme_data/theme_data.service.js @@ -39,7 +39,7 @@ import { LAYERS, DEFAULT_OPACITY, SLOT_INHERITANCE } from './pleromafe.js' export const CURRENT_VERSION = 3 export const getLayersArray = (layer, data = LAYERS) => { - let array = [layer] + const array = [layer] let parent = data[layer] while (parent) { array.unshift(parent) @@ -138,6 +138,7 @@ export const topoSort = ( if (depsA === depsB || (depsB !== 0 && depsA !== 0)) return ai - bi if (depsA === 0 && depsB !== 0) return -1 if (depsB === 0 && depsA !== 0) return 1 + return 0 // failsafe, shouldn't happen? }).map(({ data }) => data) } diff --git a/src/services/timeline_fetcher/timeline_fetcher.service.js b/src/services/timeline_fetcher/timeline_fetcher.service.js @@ -3,12 +3,13 @@ import { camelCase } from 'lodash' import apiService from '../api/api.service.js' import { promiseInterval } from '../promise_interval/promise_interval.js' -const update = ({ store, statuses, timeline, showImmediately, userId, pagination }) => { +const update = ({ store, statuses, timeline, showImmediately, userId, listId, pagination }) => { const ccTimeline = camelCase(timeline) store.dispatch('addNewStatuses', { timeline: ccTimeline, userId, + listId, statuses, showImmediately, pagination @@ -22,6 +23,7 @@ const fetchAndUpdate = ({ older = false, showImmediately = false, userId = false, + listId = false, tag = false, until, since @@ -34,20 +36,21 @@ const fetchAndUpdate = ({ const loggedIn = !!rootState.users.currentUser if (older) { - args['until'] = until || timelineData.minId + args.until = until || timelineData.minId } else { if (since === undefined) { - args['since'] = timelineData.maxId + args.since = timelineData.maxId } else if (since !== null) { - args['since'] = since + args.since = since } } - args['userId'] = userId - args['tag'] = tag - args['withMuted'] = !hideMutedPosts + args.userId = userId + args.listId = listId + args.tag = tag + args.withMuted = !hideMutedPosts if (loggedIn && ['friends', 'public', 'publicAndExternal'].includes(timeline)) { - args['replyVisibility'] = replyVisibility + args.replyVisibility = replyVisibility } const numStatusesBeforeFetch = timelineData.statuses.length @@ -60,9 +63,9 @@ const fetchAndUpdate = ({ const { data: statuses, pagination } = response if (!older && statuses.length >= 20 && !timelineData.loading && numStatusesBeforeFetch > 0) { - store.dispatch('queueFlush', { timeline: timeline, id: timelineData.maxId }) + store.dispatch('queueFlush', { timeline, id: timelineData.maxId }) } - update({ store, statuses, timeline, showImmediately, userId, pagination }) + update({ store, statuses, timeline, showImmediately, userId, listId, pagination }) return { statuses, pagination } }) .catch((error) => { @@ -75,14 +78,15 @@ const fetchAndUpdate = ({ }) } -const startFetching = ({ timeline = 'friends', credentials, store, userId = false, tag = false }) => { +const startFetching = ({ timeline = 'friends', credentials, store, userId = false, listId = false, tag = false }) => { const rootState = store.rootState || store.state const timelineData = rootState.statuses.timelines[camelCase(timeline)] const showImmediately = timelineData.visibleStatuses.length === 0 timelineData.userId = userId - fetchAndUpdate({ timeline, credentials, store, showImmediately, userId, tag }) + timelineData.listId = listId + fetchAndUpdate({ timeline, credentials, store, showImmediately, userId, listId, tag }) const boundFetchAndUpdate = () => - fetchAndUpdate({ timeline, credentials, store, userId, tag }) + fetchAndUpdate({ timeline, credentials, store, userId, listId, tag }) return promiseInterval(boundFetchAndUpdate, 10000) } const timelineFetcher = { diff --git a/src/services/user_highlighter/user_highlighter.js b/src/services/user_highlighter/user_highlighter.js @@ -36,7 +36,7 @@ const highlightStyle = (prefs) => { 'linear-gradient(to right,', `${solidColor} ,`, `${solidColor} 2px,`, - `transparent 6px` + 'transparent 6px' ].join(' '), backgroundPosition: '0 0', ...customProps diff --git a/src/sw.js b/src/sw.js @@ -3,12 +3,10 @@ import localForage from 'localforage' import { parseNotification } from './services/entity_normalizer/entity_normalizer.service.js' import { prepareNotificationObject } from './services/notification_utils/notification_utils.js' -import Vue from 'vue' -import VueI18n from 'vue-i18n' +import { createI18n } from 'vue-i18n' import messages from './i18n/service_worker_messages.js' -Vue.use(VueI18n) -const i18n = new VueI18n({ +const i18n = createI18n({ // By default, use the browser locale, we will update it if neccessary locale: 'en', fallbackLocale: 'en', @@ -59,8 +57,8 @@ self.addEventListener('notificationclick', (event) => { event.notification.close() event.waitUntil(getWindowClients().then((list) => { - for (var i = 0; i < list.length; i++) { - var client = list[i] + for (let i = 0; i < list.length; i++) { + const client = list[i] if (client.url === '/' && 'focus' in client) { return client.focus() } } diff --git a/static/config.json b/static/config.json @@ -14,6 +14,7 @@ "logoMask": true, "logoLeft": false, "minimalScopesMode": false, + "disableUpdateNotification": false, "nsfwCensorImage": "", "postContentType": "text/plain", "redirectRootLogin": "/main/friends", diff --git a/static/emoji.json b/static/emoji.json @@ -1,1431 +0,0 @@ -{ - "100": "💯", - "1234": "🔢", - "1st_place_medal": "🥇", - "2nd_place_medal": "🥈", - "3rd_place_medal": "🥉", - "8ball": "🎱", - "a_button_blood_type": "🅰", - "ab": "🆎", - "abacus": "🧮", - "abc": "🔤", - "abcd": "🔡", - "accept": "🉑", - "adhesive_bandage": "🩹", - "admission_tickets": "🎟", - "adult": "🧑", - "aerial_tramway": "🚡", - "airplane": "✈", - "airplane_arriving": "🛬", - "airplane_departure": "🛫", - "alarm_clock": "⏰", - "alembic": "⚗️", - "alien": "👽", - "ambulance": "🚑", - "amphora": "🏺", - "anchor": "⚓", - "angel": "👼", - "anger": "💢", - "anger_right": "🗯", - "angry": "😠", - "anguished": "😧", - "ant": "🐜", - "apple": "🍎", - "aquarius": "♒", - "aries": "♈", - "arrow_backward": "◀️", - "arrow_double_down": "⏬", - "arrow_double_up": "⏫", - "arrow_down": "⬇️", - "arrow_down_small": "🔽", - "arrow_forward": "▶️", - "arrow_heading_down": "⤵️", - "arrow_heading_up": "⤴️", - "arrow_left": "⬅️", - "arrow_lower_left": "↙️", - "arrow_lower_right": "↘️", - "arrow_right": "➡", - "arrow_right_hook": "↪️", - "arrow_up": "⬆️", - "arrow_up_down": "↕", - "arrow_up_small": "🔼", - "arrow_upper_left": "↖", - "arrow_upper_right": "↗️", - "arrows_clockwise": "🔃", - "arrows_counterclockwise": "🔄", - "art": "🎨", - "articulated_lorry": "🚛", - "artist_palette": "🎨", - "asterisk": "*⃣", - "astonished": "😲", - "athletic_shoe": "👟", - "atm": "🏧", - "atom": "⚛", - "atom_symbol": "⚛️", - "auto_rickshaw": "🛺", - "automobile": "🚗", - "avocado": "🥑", - "axe": "🪓", - "b_button_blood_type": "🅱", - "baby": "👶", - "baby_bottle": "🍼", - "baby_chick": "🐤", - "baby_symbol": "🚼", - "back": "🔙", - "bacon": "🥓", - "badger": "🦡", - "badminton": "🏸", - "bagel": "🥯", - "baggage_claim": "🛄", - "baguette_bread": "🥖", - "balance_scale": "⚖️", - "bald": "🦲", - "ballet_shoes": "🩰", - "balloon": "🎈", - "ballot_box": "🗳", - "ballot_box_with_check": "☑️", - "bamboo": "🎍", - "banana": "🍌", - "bangbang": "‼️", - "banjo": "🪕", - "bank": "🏦", - "bar_chart": "📊", - "barber": "💈", - "baseball": "⚾", - "basket": "🧺", - "basketball": "🏀", - "basketballer": "⛹", - "bat": "🦇", - "bath": "🛀", - "bathtub": "🛁", - "battery": "🔋", - "beach_umbrella": "⛱", - "beach_with_umbrella": "🏖", - "bear": "🐻", - "beard": "🧔", - "bearded_person": "🧔", - "bed": "🛏", - "bee": "🐝", - "beer": "🍺", - "beers": "🍻", - "beetle": "🐞", - "beginner": "🔰", - "bell": "🔔", - "bellhop_bell": "🛎", - "bento": "🍱", - "beverage_box": "🧃", - "bicyclist": "🚴", - "bike": "🚲", - "bikini": "👙", - "billed_cap": "🧢", - "biohazard": "☣️", - "bird": "🐦", - "birthday": "🎂", - "black_circle": "⚫", - "black_heart": "🖤", - "black_joker": "🃏", - "black_large_square": "⬛", - "black_medium_small_square": "◾", - "black_medium_square": "◼", - "black_nib": "✒️", - "black_small_square": "▪", - "black_square_button": "🔲", - "blond_haired_person": "👱", - "blossom": "🌼", - "blowfish": "🐡", - "blue_book": "📘", - "blue_car": "🚙", - "blue_circle": "🔵", - "blue_heart": "💙", - "blue_square": "🟦", - "blush": "😊", - "boar": "🐗", - "bomb": "💣", - "bone": "🦴", - "book": "📖", - "bookmark": "🔖", - "bookmark_tabs": "📑", - "books": "📚", - "boom": "💥", - "boot": "👢", - "bouquet": "💐", - "bow": "🙇", - "bow_and_arrow": "🏹", - "bowl_with_spoon": "🥣", - "bowling": "🎳", - "boxing_glove": "🥊", - "boy": "👦", - "brain": "🧠", - "bread": "🍞", - "breast_feeding": "🤱", - "breastfeeding": "🤱", - "brick": "🧱", - "bride_with_veil": "👰", - "bridge_at_night": "🌉", - "briefcase": "💼", - "briefs": "🩲", - "broccoli": "🥦", - "broken_heart": "💔", - "broom": "🧹", - "brown_circle": "🟤", - "brown_heart": "🤎", - "bug": "🐛", - "building_construction": "🏗", - "bulb": "💡", - "bullettrain_front": "🚅", - "bullettrain_side": "🚄", - "burrito": "🌯", - "bus": "🚌", - "busstop": "🚏", - "bust_in_silhouette": "👤", - "busts_in_silhouette": "👥", - "butter": "🧈", - "butterfly": "🦋", - "cactus": "🌵", - "cake": "🍰", - "calendar": "📆", - "call_me": "🤙", - "call_me_hand": "🤙", - "calling": "📲", - "camel": "🐫", - "camera": "📷", - "camera_with_flash": "📸", - "camping": "🏕", - "cancer": "♋", - "candle": "🕯", - "candy": "🍬", - "canned_food": "🥫", - "canoe": "🛶", - "capital_abcd": "🔠", - "capricorn": "♑", - "card_file_box": "🗃", - "card_index": "📇", - "card_index_dividers": "🗂", - "carousel_horse": "🎠", - "carrot": "🥕", - "cat": "🐱", - "cat2": "🐈", - "cd": "💿", - "chains": "⛓️", - "chair": "🪑", - "champagne": "🍾", - "champagne_glass": "🥂", - "chart": "💹", - "chart_with_downwards_trend": "📉", - "chart_with_upwards_trend": "📈", - "check_box_with_check": "☑", - "check_mark": "✔", - "checkered_flag": "🏁", - "cheese": "🧀", - "cheese_wedge": "🧀", - "cherries": "🍒", - "cherry_blossom": "🌸", - "chess_pawn": "♟", - "chestnut": "🌰", - "chicken": "🐔", - "child": "🧒", - "children_crossing": "🚸", - "chipmunk": "🐿", - "chocolate_bar": "🍫", - "chopsticks": "🥢", - "christmas_tree": "🎄", - "church": "⛪", - "cinema": "🎦", - "circled_m": "Ⓜ", - "circus_tent": "🎪", - "city_dusk": "🌆", - "city_sunset": "🌇", - "cityscape": "🏙", - "cityscape_at_dusk": "🌆", - "cl": "🆑", - "clap": "👏", - "clapper": "🎬", - "classical_building": "🏛", - "clinking_glasses": "🥂", - "clipboard": "📋", - "clock1": "🕐", - "clock10": "🕙", - "clock1030": "🕥", - "clock11": "🕚", - "clock1130": "🕦", - "clock12": "🕛", - "clock1230": "🕧", - "clock130": "🕜", - "clock2": "🕑", - "clock230": "🕝", - "clock3": "🕒", - "clock330": "🕞", - "clock4": "🕓", - "clock430": "🕟", - "clock5": "🕔", - "clock530": "🕠", - "clock6": "🕕", - "clock630": "🕡", - "clock7": "🕖", - "clock730": "🕢", - "clock8": "🕗", - "clock830": "🕣", - "clock9": "🕘", - "clock930": "🕤", - "closed_book": "📕", - "closed_lock_with_key": "🔐", - "closed_umbrella": "🌂", - "cloud": "☁️", - "cloud_with_lightning": "🌩", - "cloud_with_lightning_and_rain": "⛈️", - "cloud_with_rain": "🌧", - "cloud_with_snow": "🌨", - "clown": "🤡", - "clown_face": "🤡", - "club_suit": "♣️", - "clubs": "♣", - "coat": "🧥", - "cocktail": "🍸", - "coconut": "🥥", - "coffee": "☕", - "coffin": "⚰️", - "cold_face": "🥶", - "cold_sweat": "😰", - "comet": "☄️", - "compass": "🧭", - "compression": "🗜", - "computer": "💻", - "computer_mouse": "🖱", - "confetti_ball": "🎊", - "confounded": "😖", - "confused": "😕", - "congratulations": "㊗", - "construction": "🚧", - "construction_worker": "👷", - "control_knobs": "🎛", - "convenience_store": "🏪", - "cookie": "🍪", - "cooking": "🍳", - "cool": "🆒", - "cop": "👮", - "copyright": "©", - "corn": "🌽", - "couch_and_lamp": "🛋", - "couple": "👫", - "couple_with_heart": "💑", - "couplekiss": "💏", - "cow": "🐮", - "cow2": "🐄", - "cowboy": "🤠", - "cowboy_hat_face": "🤠", - "crab": "🦀", - "crayon": "🖍", - "crazy_face": "🤪", - "credit_card": "💳", - "crescent_moon": "🌙", - "cricket": "🦗", - "cricket_game": "🏏", - "crocodile": "🐊", - "croissant": "🥐", - "cross": "✝️", - "crossed_fingers": "🤞", - "crossed_flags": "🎌", - "crossed_swords": "⚔️", - "crown": "👑", - "cry": "😢", - "crying_cat_face": "😿", - "crystal_ball": "🔮", - "cucumber": "🥒", - "cup_with_straw": "🥤", - "cupcake": "🧁", - "cupid": "💘", - "curling_stone": "🥌", - "curly_hair": "🦱", - "curly_loop": "➰", - "currency_exchange": "💱", - "curry": "🍛", - "custard": "🍮", - "customs": "🛃", - "cut_of_meat": "🥩", - "cyclone": "🌀", - "dagger": "🗡", - "dancer": "💃", - "dancers": "👯", - "dango": "🍡", - "dark_skin_tone": "🏿", - "dark_sunglasses": "🕶", - "dart": "🎯", - "dash": "💨", - "date": "📅", - "deaf_person": "🧏", - "deciduous_tree": "🌳", - "deer": "🦌", - "department_store": "🏬", - "derelict_house": "🏚", - "desert": "🏜", - "desert_island": "🏝", - "desktop_computer": "🖥", - "detective": "🕵", - "diamond_shape_with_a_dot_inside": "💠", - "diamond_suit": "♦️", - "diamonds": "♦", - "disappointed": "😞", - "disappointed_relieved": "😥", - "diving_mask": "🤿", - "diya_lamp": "🪔", - "dizzy": "💫", - "dizzy_face": "😵", - "dna": "🧬", - "do_not_litter": "🚯", - "dog": "🐶", - "dog2": "🐕", - "dollar": "💵", - "dolls": "🎎", - "dolphin": "🐬", - "door": "🚪", - "double_exclamation_mark": "‼", - "doughnut": "🍩", - "dove": "🕊", - "down_arrow": "⬇", - "downleft_arrow": "↙", - "downright_arrow": "↘", - "dragon": "🐉", - "dragon_face": "🐲", - "dress": "👗", - "dromedary_camel": "🐪", - "drooling_face": "🤤", - "drop_of_blood": "🩸", - "droplet": "💧", - "drum": "🥁", - "duck": "🦆", - "dumpling": "🥟", - "dvd": "📀", - "e-mail": "📧", - "eagle": "🦅", - "ear": "👂", - "ear_of_rice": "🌾", - "ear_with_hearing_aid": "🦻", - "earth_africa": "🌍", - "earth_americas": "🌎", - "earth_asia": "🌏", - "egg": "🥚", - "eggplant": "🍆", - "eight": "8⃣", - "eight_pointed_black_star": "✴️", - "eight_spoked_asterisk": "✳️", - "eightpointed_star": "✴", - "eightspoked_asterisk": "✳", - "eject_button": "⏏", - "electric_plug": "🔌", - "elephant": "🐘", - "elf": "🧝", - "end": "🔚", - "envelope": "✉", - "envelope_with_arrow": "📩", - "euro": "💶", - "european_castle": "🏰", - "european_post_office": "🏤", - "evergreen_tree": "🌲", - "exclamation": "❗", - "exclamation_question_mark": "⁉", - "exploding_head": "🤯", - "expressionless": "😑", - "eye": "👁", - "eyeglasses": "👓", - "eyes": "👀", - "face_vomiting": "🤮", - "face_with_hand_over_mouth": "🤭", - "face_with_headbandage": "🤕", - "face_with_monocle": "🧐", - "face_with_raised_eyebrow": "🤨", - "face_with_symbols_on_mouth": "🤬", - "face_with_symbols_over_mouth": "🤬", - "face_with_thermometer": "🤒", - "factory": "🏭", - "fairy": "🧚", - "falafel": "🧆", - "fallen_leaf": "🍂", - "family": "👪", - "fast_forward": "⏩", - "fax": "📠", - "fearful": "😨", - "feet": "🐾", - "female_sign": "♀", - "ferris_wheel": "🎡", - "ferry": "⛴️", - "field_hockey": "🏑", - "file_cabinet": "🗄", - "file_folder": "📁", - "film_frames": "🎞", - "film_projector": "📽", - "fingers_crossed": "🤞", - "fire": "🔥", - "fire_engine": "🚒", - "fire_extinguisher": "🧯", - "firecracker": "🧨", - "fireworks": "🎆", - "first_place": "🥇", - "first_quarter_moon": "🌓", - "first_quarter_moon_with_face": "🌛", - "fish": "🐟", - "fish_cake": "🍥", - "fishing_pole_and_fish": "🎣", - "fist": "✊", - "five": "5⃣", - "flag_black": "🏴", - "flag_white": "🏳", - "flags": "🎏", - "flamingo": "🦩", - "flashlight": "🔦", - "flat_shoe": "🥿", - "fleur-de-lis": "⚜", - "fleurde-lis": "⚜️", - "floppy_disk": "💾", - "flower_playing_cards": "🎴", - "flushed": "😳", - "flying_disc": "🥏", - "flying_saucer": "🛸", - "fog": "🌫", - "foggy": "🌁", - "foot": "🦶", - "football": "🏈", - "footprints": "👣", - "fork_and_knife": "🍴", - "fork_and_knife_with_plate": "🍽", - "fortune_cookie": "🥠", - "fountain": "⛲", - "fountain_pen": "🖋", - "four": "4⃣", - "four_leaf_clover": "🍀", - "fox": "🦊", - "framed_picture": "🖼", - "free": "🆓", - "french_bread": "🥖", - "fried_shrimp": "🍤", - "fries": "🍟", - "frog": "🐸", - "frowning": "😦", - "frowning_face": "☹️", - "fuelpump": "⛽", - "full_moon": "🌕", - "full_moon_with_face": "🌝", - "funeral_urn": "⚱️", - "game_die": "🎲", - "garlic": "🧄", - "gear": "⚙️", - "gem": "💎", - "gemini": "♊", - "genie": "🧞", - "ghost": "👻", - "gift": "🎁", - "gift_heart": "💝", - "giraffe": "🦒", - "girl": "👧", - "glass_of_milk": "🥛", - "globe_with_meridians": "🌐", - "gloves": "🧤", - "goal": "🥅", - "goal_net": "🥅", - "goat": "🐐", - "goggles": "🥽", - "golf": "⛳", - "golfer": "🏌", - "gorilla": "🦍", - "grapes": "🍇", - "green_apple": "🍏", - "green_book": "📗", - "green_circle": "🟢", - "green_heart": "💚", - "green_salad": "🥗", - "green_square": "🟩", - "grey_exclamation": "❕", - "grey_question": "❔", - "grimacing": "😬", - "grin": "😁", - "grinning": "😀", - "guard": "💂", - "guardsman": "💂", - "guide_dog": "🦮", - "guitar": "🎸", - "gun": "🔫", - "haircut": "💇", - "hamburger": "🍔", - "hammer": "🔨", - "hammer_and_pick": "⚒️", - "hammer_and_wrench": "🛠", - "hamster": "🐹", - "hand_with_fingers_splayed": "🖐", - "handbag": "👜", - "handshake": "🤝", - "hash": "#⃣", - "hatched_chick": "🐥", - "hatching_chick": "🐣", - "head_bandage": "🤕", - "headphones": "🎧", - "hear_no_evil": "🙉", - "heart": "❤️", - "heart_decoration": "💟", - "heart_exclamation": "❣", - "heart_eyes": "😍", - "heart_eyes_cat": "😻", - "heart_suit": "♥️", - "heartbeat": "💓", - "heartpulse": "💗", - "hearts": "♥", - "heavy_check_mark": "✔️", - "heavy_division_sign": "➗", - "heavy_dollar_sign": "💲", - "heavy_minus_sign": "➖", - "heavy_multiplication_x": "✖️", - "heavy_plus_sign": "➕", - "hedgehog": "🦔", - "helicopter": "🚁", - "herb": "🌿", - "hibiscus": "🌺", - "high_brightness": "🔆", - "high_heel": "👠", - "hiking_boot": "🥾", - "hindu_temple": "🛕", - "hippopotamus": "🦛", - "hockey": "🏒", - "hole": "🕳", - "honey_pot": "🍯", - "horse": "🐴", - "horse_racing": "🏇", - "hospital": "🏥", - "hot_face": "🥵", - "hot_pepper": "🌶", - "hot_springs": "♨", - "hotdog": "🌭", - "hotel": "🏨", - "hotsprings": "♨️", - "hourglass": "⌛", - "hourglass_flowing_sand": "⏳", - "house": "🏠", - "house_with_garden": "🏡", - "houses": "🏘", - "hugging": "🤗", - "hundred_points": "💯", - "hushed": "😯", - "ice": "🧊", - "ice_cream": "🍨", - "ice_hockey": "🏒", - "ice_skate": "⛸️", - "icecream": "🍦", - "id": "🆔", - "ideograph_advantage": "🉐", - "imp": "👿", - "inbox_tray": "📥", - "incoming_envelope": "📨", - "index_pointing_up": "☝", - "infinity": "♾", - "information": "ℹ️", - "information_desk_person": "💁", - "information_source": "ℹ", - "innocent": "😇", - "input_numbers": "🔢", - "interrobang": "⁉️", - "iphone": "📱", - "izakaya_lantern": "🏮", - "jack_o_lantern": "🎃", - "japan": "🗾", - "japanese_castle": "🏯", - "japanese_congratulations_button": "㊗️", - "japanese_free_of_charge_button": "🈚", - "japanese_goblin": "👺", - "japanese_ogre": "👹", - "japanese_reserved_button": "🈯", - "japanese_secret_button": "㊙️", - "japanese_service_charge_button": "🈂", - "jeans": "👖", - "joy": "😂", - "joy_cat": "😹", - "joystick": "🕹", - "kaaba": "🕋", - "kangaroo": "🦘", - "key": "🔑", - "keyboard": "⌨️", - "keycap_ten": "🔟", - "kick_scooter": "🛴", - "kimono": "👘", - "kiss": "💋", - "kissing": "😗", - "kissing_cat": "😽", - "kissing_closed_eyes": "😚", - "kissing_heart": "😘", - "kissing_smiling_eyes": "😙", - "kitchen_knife": "🔪", - "kite": "🪁", - "kiwi": "🥝", - "kiwi_fruit": "🥝", - "knife": "🔪", - "koala": "🐨", - "koko": "🈁", - "lab_coat": "🥼", - "label": "🏷", - "lacrosse": "🥍", - "large_blue_diamond": "🔷", - "large_orange_diamond": "🔶", - "last_quarter_moon": "🌗", - "last_quarter_moon_with_face": "🌜", - "last_track_button": "⏮️", - "latin_cross": "✝", - "laughing": "😆", - "leafy_green": "🥬", - "leaves": "🍃", - "ledger": "📒", - "left_arrow": "⬅", - "left_arrow_curving_right": "↪", - "left_facing_fist": "🤛", - "left_luggage": "🛅", - "left_right_arrow": "↔", - "leftfacing_fist": "🤛", - "leftright_arrow": "↔️", - "leftwards_arrow_with_hook": "↩️", - "leg": "🦵", - "lemon": "🍋", - "leo": "♌", - "leopard": "🐆", - "level_slider": "🎚", - "libra": "♎", - "light_rail": "🚈", - "light_skin_tone": "🏻", - "link": "🔗", - "linked_paperclips": "🖇", - "lion_face": "🦁", - "lips": "👄", - "lipstick": "💄", - "lizard": "🦎", - "llama": "🦙", - "lobster": "🦞", - "lock": "🔒", - "lock_with_ink_pen": "🔏", - "lollipop": "🍭", - "loop": "➿", - "lotion_bottle": "🧴", - "loud_sound": "🔊", - "loudspeaker": "📢", - "love_hotel": "🏩", - "love_letter": "💌", - "love_you_gesture": "🤟", - "loveyou_gesture": "🤟", - "low_brightness": "🔅", - "luggage": "🧳", - "lying_face": "🤥", - "m": "Ⓜ️", - "mag": "🔍", - "mag_right": "🔎", - "mage": "🧙", - "magnet": "🧲", - "mahjong": "🀄", - "mailbox": "📫", - "mailbox_closed": "📪", - "mailbox_with_mail": "📬", - "mailbox_with_no_mail": "📭", - "male_sign": "♂", - "man": "👨", - "man_dancing": "🕺", - "man_in_suit": "🕴", - "man_in_tuxedo": "🤵", - "man_with_chinese_cap": "👲", - "man_with_gua_pi_mao": "👲", - "man_with_turban": "👳", - "mango": "🥭", - "mans_shoe": "👞", - "mantelpiece_clock": "🕰", - "manual_wheelchair": "🦽", - "maple_leaf": "🍁", - "martial_arts_uniform": "🥋", - "mask": "😷", - "massage": "💆", - "mate": "🧉", - "meat_on_bone": "🍖", - "mechanical_arm": "🦾", - "mechanical_leg": "🦿", - "medal": "🏅", - "medical_symbol": "⚕", - "medium_skin_tone": "🏽", - "mediumdark_skin_tone": "🏾", - "mediumlight_skin_tone": "🏼", - "mega": "📣", - "melon": "🍈", - "memo": "📝", - "menorah": "🕎", - "mens": "🚹", - "merperson": "🧜", - "metal": "🤘", - "metro": "🚇", - "microbe": "🦠", - "microphone": "🎤", - "microscope": "🔬", - "middle_finger": "🖕", - "military_medal": "🎖", - "milk": "🥛", - "milky_way": "🌌", - "minibus": "🚐", - "minidisc": "💽", - "mobile_phone_off": "📴", - "money_mouth": "🤑", - "money_with_wings": "💸", - "moneybag": "💰", - "moneymouth_face": "🤑", - "monkey": "🐒", - "monkey_face": "🐵", - "monorail": "🚝", - "moon_cake": "🥮", - "mortar_board": "🎓", - "mosque": "🕌", - "mosquito": "🦟", - "motor_boat": "🛥", - "motor_scooter": "🛵", - "motorcycle": "🏍", - "motorized_wheelchair": "🦼", - "motorway": "🛣", - "mount_fuji": "🗻", - "mountain": "⛰️", - "mountain_bicyclist": "🚵", - "mountain_cableway": "🚠", - "mountain_railway": "🚞", - "mouse": "🐭", - "mouse2": "🐁", - "movie_camera": "🎥", - "moyai": "🗿", - "mrs_claus": "🤶", - "multiplication_sign": "✖", - "muscle": "💪", - "mushroom": "🍄", - "musical_keyboard": "🎹", - "musical_note": "🎵", - "musical_score": "🎼", - "mute": "🔇", - "nail_care": "💅", - "name_badge": "📛", - "national_park": "🏞", - "nauseated_face": "🤢", - "nazar_amulet": "🧿", - "necktie": "👔", - "negative_squared_cross_mark": "❎", - "nerd": "🤓", - "neutral_face": "😐", - "new": "🆕", - "new_moon": "🌑", - "new_moon_with_face": "🌚", - "newspaper": "📰", - "next_track_button": "⏭️", - "ng": "🆖", - "night_with_stars": "🌃", - "nine": "9⃣", - "no_bell": "🔕", - "no_bicycles": "🚳", - "no_entry": "⛔", - "no_entry_sign": "🚫", - "no_good": "🙅", - "no_mobile_phones": "📵", - "no_mouth": "😶", - "no_pedestrians": "🚷", - "no_smoking": "🚭", - "non-potable_water": "🚱", - "nose": "👃", - "notebook": "📓", - "notebook_with_decorative_cover": "📔", - "notes": "🎶", - "nut_and_bolt": "🔩", - "o": "⭕", - "o_button_blood_type": "🅾", - "ocean": "🌊", - "octagonal_sign": "🛑", - "octopus": "🐙", - "oden": "🍢", - "office": "🏢", - "oil_drum": "🛢", - "ok": "🆗", - "ok_hand": "👌", - "ok_woman": "🙆", - "old_key": "🗝", - "older_adult": "🧓", - "older_man": "👴", - "older_person": "🧓", - "older_woman": "👵", - "om_symbol": "🕉", - "on": "🔛", - "oncoming_automobile": "🚘", - "oncoming_bus": "🚍", - "oncoming_fist": "👊", - "oncoming_police_car": "🚔", - "oncoming_taxi": "🚖", - "one": "1⃣", - "onepiece_swimsuit": "🩱", - "onion": "🧅", - "open_file_folder": "📂", - "open_hands": "👐", - "open_mouth": "😮", - "ophiuchus": "⛎", - "orange_book": "📙", - "orange_circle": "🟠", - "orange_heart": "🧡", - "orange_square": "🟧", - "orangutan": "🦧", - "orthodox_cross": "☦️", - "otter": "🦦", - "outbox_tray": "📤", - "owl": "🦉", - "ox": "🐂", - "oyster": "🦪", - "p_button": "🅿", - "package": "📦", - "page_facing_up": "📄", - "page_with_curl": "📃", - "pager": "📟", - "paintbrush": "🖌", - "palm_tree": "🌴", - "palms_up_together": "🤲", - "pancakes": "🥞", - "panda_face": "🐼", - "paperclip": "📎", - "parachute": "🪂", - "parrot": "🦜", - "part_alternation_mark": "〽", - "partly_sunny": "⛅", - "partying_face": "🥳", - "passenger_ship": "🛳", - "passport_control": "🛂", - "pause_button": "⏸️", - "peace": "☮", - "peace_symbol": "☮️", - "peach": "🍑", - "peacock": "🦚", - "peanuts": "🥜", - "pear": "🍐", - "pen": "🖊", - "pencil": "📝", - "pencil2": "✏", - "penguin": "🐧", - "pensive": "😔", - "people_with_bunny_ears_partying": "👯", - "people_wrestling": "🤼", - "performing_arts": "🎭", - "persevere": "😣", - "person": "🧑", - "person_biking": "🚴", - "person_bouncing_ball": "⛹️", - "person_bowing": "🙇", - "person_cartwheeling": "🤸", - "person_climbing": "🧗", - "person_doing_cartwheel": "🤸", - "person_facepalming": "🤦", - "person_fencing": "🤺", - "person_frowning": "🙍", - "person_gesturing_no": "🙅", - "person_gesturing_ok": "🙆", - "person_getting_haircut": "💇", - "person_getting_massage": "💆", - "person_in_lotus_position": "🧘", - "person_in_steamy_room": "🧖", - "person_juggling": "🤹", - "person_kneeling": "🧎", - "person_mountain_biking": "🚵", - "person_playing_handball": "🤾", - "person_playing_water_polo": "🤽", - "person_pouting": "🙎", - "person_raising_hand": "🙋", - "person_rowing_boat": "🚣", - "person_running": "🏃", - "person_shrugging": "🤷", - "person_standing": "🧍", - "person_surfing": "🏄", - "person_swimming": "🏊", - "person_tipping_hand": "💁", - "person_walking": "🚶", - "person_wearing_turban": "👳", - "person_with_blond_hair": "👱", - "person_with_pouting_face": "🙎", - "petri_dish": "🧫", - "pick": "⛏️", - "pie": "🥧", - "pig": "🐷", - "pig2": "🐖", - "pig_nose": "🐽", - "pill": "💊", - "pinching_hand": "🤏", - "pineapple": "🍍", - "ping_pong": "🏓", - "pisces": "♓", - "pizza": "🍕", - "place_of_worship": "🛐", - "play_button": "▶", - "play_or_pause_button": "⏯️", - "play_pause": "⏯", - "pleading_face": "🥺", - "point_down": "👇", - "point_left": "👈", - "point_right": "👉", - "point_up": "☝️", - "point_up_2": "👆", - "police_car": "🚓", - "police_officer": "👮", - "poodle": "🐩", - "poop": "💩", - "popcorn": "🍿", - "post_office": "🏣", - "postal_horn": "📯", - "postbox": "📮", - "potable_water": "🚰", - "potato": "🥔", - "pouch": "👝", - "poultry_leg": "🍗", - "pound": "💷", - "pouting_cat": "😾", - "pray": "🙏", - "prayer_beads": "📿", - "pregnant_woman": "🤰", - "pretzel": "🥨", - "prince": "🤴", - "princess": "👸", - "printer": "🖨", - "probing_cane": "🦯", - "punch": "👊", - "purple_circle": "🟣", - "purple_heart": "💜", - "purse": "👛", - "pushpin": "📌", - "put_litter_in_its_place": "🚮", - "puzzle_piece": "🧩", - "question": "❓", - "rabbit": "🐰", - "rabbit2": "🐇", - "raccoon": "🦝", - "racehorse": "🐎", - "racing_car": "🏎", - "radio": "📻", - "radio_button": "🔘", - "radioactive": "☢️", - "rage": "😡", - "railway_car": "🚃", - "railway_track": "🛤", - "rainbow": "🌈", - "raised_back_of_hand": "🤚", - "raised_hand": "✋", - "raised_hands": "🙌", - "raising_hand": "🙋", - "ram": "🐏", - "ramen": "🍜", - "rat": "🐀", - "razor": "🪒", - "receipt": "🧾", - "record_button": "⏺️", - "recycle": "♻", - "recycling_symbol": "♻️", - "red_car": "🚗", - "red_circle": "🔴", - "red_envelope": "🧧", - "red_hair": "🦰", - "red_heart": "❤", - "red_square": "🟥", - "regional_indicator_a": "🇦", - "regional_indicator_b": "🇧", - "regional_indicator_c": "🇨", - "regional_indicator_d": "🇩", - "regional_indicator_e": "🇪", - "regional_indicator_f": "🇫", - "regional_indicator_g": "🇬", - "regional_indicator_h": "🇭", - "regional_indicator_i": "🇮", - "regional_indicator_j": "🇯", - "regional_indicator_k": "🇰", - "regional_indicator_l": "🇱", - "regional_indicator_m": "🇲", - "regional_indicator_n": "🇳", - "regional_indicator_o": "🇴", - "regional_indicator_p": "🇵", - "regional_indicator_q": "🇶", - "regional_indicator_r": "🇷", - "regional_indicator_s": "🇸", - "regional_indicator_t": "🇹", - "regional_indicator_u": "🇺", - "regional_indicator_v": "🇻", - "regional_indicator_w": "🇼", - "regional_indicator_x": "🇽", - "regional_indicator_y": "🇾", - "regional_indicator_z": "🇿", - "registered": "®", - "relieved": "😌", - "reminder_ribbon": "🎗", - "repeat": "🔁", - "repeat_one": "🔂", - "rescue_worker’s_helmet": "⛑️", - "restroom": "🚻", - "reverse_button": "◀", - "revolving_hearts": "💞", - "rewind": "⏪", - "rhino": "🦏", - "rhinoceros": "🦏", - "ribbon": "🎀", - "rice": "🍚", - "rice_ball": "🍙", - "rice_cracker": "🍘", - "rice_scene": "🎑", - "right_arrow": "➡️", - "right_arrow_curving_down": "⤵", - "right_arrow_curving_left": "↩", - "right_arrow_curving_up": "⤴", - "right_facing_fist": "🤜", - "rightfacing_fist": "🤜", - "ring": "💍", - "ringed_planet": "🪐", - "robot": "🤖", - "rocket": "🚀", - "rofl": "🤣", - "roll_of_paper": "🧻", - "rolledup_newspaper": "🗞", - "roller_coaster": "🎢", - "rolling_eyes": "🙄", - "rolling_on_the_floor_laughing": "🤣", - "rooster": "🐓", - "rose": "🌹", - "rosette": "🏵", - "rotating_light": "🚨", - "round_pushpin": "📍", - "rowboat": "🚣", - "rugby_football": "🏉", - "runner": "🏃", - "running_shirt_with_sash": "🎽", - "safety_pin": "🧷", - "safety_vest": "🦺", - "sagittarius": "♐", - "sailboat": "⛵", - "sake": "🍶", - "salad": "🥗", - "salt": "🧂", - "sandal": "👡", - "sandwich": "🥪", - "santa": "🎅", - "sari": "🥻", - "satellite": "📡", - "sauropod": "🦕", - "saxophone": "🎷", - "scales": "⚖", - "scarf": "🧣", - "school": "🏫", - "school_satchel": "🎒", - "scissors": "✂", - "scooter": "🛴", - "scorpion": "🦂", - "scorpius": "♏", - "scream": "😱", - "scream_cat": "🙀", - "scroll": "📜", - "seat": "💺", - "second_place": "🥈", - "secret": "㊙", - "see_no_evil": "🙈", - "seedling": "🌱", - "selfie": "🤳", - "seven": "7⃣", - "shallow_pan_of_food": "🥘", - "shamrock": "☘️", - "shark": "🦈", - "shaved_ice": "🍧", - "sheep": "🐑", - "shell": "🐚", - "shield": "🛡", - "shinto_shrine": "⛩️", - "ship": "🚢", - "shirt": "👕", - "shopping_bags": "🛍", - "shopping_cart": "🛒", - "shorts": "🩳", - "shower": "🚿", - "shrimp": "🦐", - "shushing_face": "🤫", - "sign_of_the_horns": "🤘", - "signal_strength": "📶", - "six": "6⃣", - "six_pointed_star": "🔯", - "skateboard": "🛹", - "ski": "🎿", - "skier": "⛷️", - "skull": "💀", - "skull_and_crossbones": "☠️", - "skull_crossbones": "☠", - "skunk": "🦨", - "sled": "🛷", - "sleeping": "😴", - "sleeping_accommodation": "🛌", - "sleepy": "😪", - "slight_frown": "🙁", - "slight_smile": "🙂", - "slightly_frowning_face": "🙁", - "slot_machine": "🎰", - "sloth": "🦥", - "small_airplane": "🛩", - "small_blue_diamond": "🔹", - "small_orange_diamond": "🔸", - "small_red_triangle": "🔺", - "small_red_triangle_down": "🔻", - "smile": "😄", - "smile_cat": "😸", - "smiley": "😃", - "smiley_cat": "😺", - "smiling": "☺️", - "smiling_face": "☺", - "smiling_face_with_hearts": "🥰", - "smiling_imp": "😈", - "smirk": "😏", - "smirk_cat": "😼", - "smoking": "🚬", - "snail": "🐌", - "snake": "🐍", - "sneezing_face": "🤧", - "snowboarder": "🏂", - "snowcapped_mountain": "🏔", - "snowflake": "❄", - "snowman": "⛄", - "soap": "🧼", - "sob": "😭", - "soccer": "⚽", - "socks": "🧦", - "softball": "🥎", - "soon": "🔜", - "sos": "🆘", - "sound": "🔉", - "space_invader": "👾", - "spade_suit": "♠️", - "spades": "♠", - "spaghetti": "🍝", - "sparkle": "❇", - "sparkler": "🎇", - "sparkles": "✨", - "sparkling_heart": "💖", - "speak_no_evil": "🙊", - "speaker": "🔈", - "speaking_head": "🗣", - "speech_balloon": "💬", - "speech_left": "🗨", - "speedboat": "🚤", - "spider": "🕷", - "spider_web": "🕸", - "spiral_calendar": "🗓", - "spiral_notepad": "🗒", - "sponge": "🧽", - "spoon": "🥄", - "squid": "🦑", - "stadium": "🏟", - "star": "⭐", - "star2": "🌟", - "star_and_crescent": "☪️", - "star_of_david": "✡", - "star_struck": "🤩", - "stars": "🌠", - "starstruck": "🤩", - "station": "🚉", - "statue_of_liberty": "🗽", - "steam_locomotive": "🚂", - "stethoscope": "🩺", - "stew": "🍲", - "stop_button": "⏹️", - "stopwatch": "⏱️", - "straight_ruler": "📏", - "strawberry": "🍓", - "stuck_out_tongue": "😛", - "stuck_out_tongue_closed_eyes": "😝", - "stuck_out_tongue_winking_eye": "😜", - "studio_microphone": "🎙", - "stuffed_flatbread": "🥙", - "sun": "☀", - "sun_behind_large_cloud": "🌥", - "sun_behind_rain_cloud": "🌦", - "sun_behind_small_cloud": "🌤", - "sun_with_face": "🌞", - "sunflower": "🌻", - "sunglasses": "😎", - "sunny": "☀️", - "sunrise": "🌅", - "sunrise_over_mountains": "🌄", - "superhero": "🦸", - "supervillain": "🦹", - "surfer": "🏄", - "sushi": "🍣", - "suspension_railway": "🚟", - "swan": "🦢", - "sweat": "😓", - "sweat_drops": "💦", - "sweat_smile": "😅", - "sweet_potato": "🍠", - "swimmer": "🏊", - "symbols": "🔣", - "synagogue": "🕍", - "syringe": "💉", - "t_rex": "🦖", - "taco": "🌮", - "tada": "🎉", - "takeout_box": "🥡", - "tanabata_tree": "🎋", - "tangerine": "🍊", - "taurus": "♉", - "taxi": "🚕", - "tea": "🍵", - "teddy_bear": "🧸", - "telephone": "☎", - "telephone_receiver": "📞", - "telescope": "🔭", - "tennis": "🎾", - "tent": "⛺", - "test_tube": "🧪", - "thermometer": "🌡", - "thermometer_face": "🤒", - "thinking": "🤔", - "third_place": "🥉", - "thought_balloon": "💭", - "thread": "🧵", - "three": "3⃣", - "thumbsdown": "👎", - "thumbsup": "👍", - "ticket": "🎫", - "tiger": "🐯", - "tiger2": "🐅", - "timer_clock": "⏲️", - "tired_face": "😫", - "tm": "™", - "toilet": "🚽", - "tokyo_tower": "🗼", - "tomato": "🍅", - "tone1": "🏻", - "tone2": "🏼", - "tone3": "🏽", - "tone4": "🏾", - "tone5": "🏿", - "tongue": "👅", - "toolbox": "🧰", - "tooth": "🦷", - "top": "🔝", - "tophat": "🎩", - "tornado": "🌪", - "track_next": "⏭", - "track_previous": "⏮", - "trackball": "🖲", - "tractor": "🚜", - "trade_mark": "™️", - "traffic_light": "🚥", - "train": "🚋", - "train2": "🚆", - "tram": "🚊", - "trex": "🦖", - "triangular_flag_on_post": "🚩", - "triangular_ruler": "📐", - "trident": "🔱", - "triumph": "😤", - "trolleybus": "🚎", - "trophy": "🏆", - "tropical_drink": "🍹", - "tropical_fish": "🐠", - "truck": "🚚", - "trumpet": "🎺", - "tulip": "🌷", - "tumbler_glass": "🥃", - "turkey": "🦃", - "turtle": "🐢", - "tv": "📺", - "twisted_rightwards_arrows": "🔀", - "two": "2⃣", - "two_hearts": "💕", - "two_men_holding_hands": "👬", - "two_women_holding_hands": "👭", - "u5272": "🈹", - "u5408": "🈴", - "u55b6": "🈺", - "u6307": "🈯", - "u6708": "🈷", - "u6709": "🈶", - "u6e80": "🈵", - "u7121": "🈚", - "u7533": "🈸", - "u7981": "🈲", - "u7a7a": "🈳", - "umbrella": "☔", - "umbrella_on_ground": "⛱️", - "unamused": "😒", - "underage": "🔞", - "unicorn": "🦄", - "unlock": "🔓", - "up": "🆙", - "up_arrow": "⬆", - "updown_arrow": "↕️", - "upleft_arrow": "↖️", - "upright_arrow": "↗", - "upside_down": "🙃", - "v": "✌️", - "vampire": "🧛", - "vertical_traffic_light": "🚦", - "vhs": "📼", - "vibration_mode": "📳", - "victory_hand": "✌", - "video_camera": "📹", - "video_game": "🎮", - "violin": "🎻", - "virgo": "♍", - "volcano": "🌋", - "volleyball": "🏐", - "vs": "🆚", - "vulcan": "🖖", - "vulcan_salute": "🖖", - "waffle": "🧇", - "walking": "🚶", - "waning_crescent_moon": "🌘", - "waning_gibbous_moon": "🌖", - "warning": "⚠", - "wastebasket": "🗑", - "watch": "⌚", - "water_buffalo": "🐃", - "watermelon": "🍉", - "wave": "👋", - "wavy_dash": "〰️", - "waxing_crescent_moon": "🌒", - "waxing_gibbous_moon": "🌔", - "wc": "🚾", - "weary": "😩", - "wedding": "💒", - "weightlifter": "🏋", - "whale": "🐳", - "whale2": "🐋", - "wheel_of_dharma": "☸️", - "wheelchair": "♿", - "white_check_mark": "✅", - "white_circle": "⚪", - "white_flower": "💮", - "white_hair": "🦳", - "white_heart": "🤍", - "white_large_square": "⬜", - "white_medium_small_square": "◽", - "white_medium_square": "◻️", - "white_small_square": "▫️", - "white_square_button": "🔳", - "wilted_flower": "🥀", - "wilted_rose": "🥀", - "wind_blowing_face": "🌬", - "wind_chime": "🎐", - "wine_glass": "🍷", - "wink": "😉", - "wolf": "🐺", - "woman": "👩", - "woman_with_headscarf": "🧕", - "womans_clothes": "👚", - "womans_hat": "👒", - "womens": "🚺", - "woozy_face": "🥴", - "world_map": "🗺", - "worried": "😟", - "wrench": "🔧", - "writing_hand": "✍️", - "x": "❌", - "yarn": "🧶", - "yawning_face": "🥱", - "yellow_circle": "🟡", - "yellow_heart": "💛", - "yellow_square": "🟨", - "yen": "💴", - "yin_yang": "☯️", - "yoyo": "🪀", - "yum": "😋", - "zany_face": "🤪", - "zap": "⚡", - "zebra": "🦓", - "zero": "0⃣", - "zipper_mouth": "🤐", - "zombie": "🧟", - "zzz": "💤" -} -\ No newline at end of file diff --git a/test/e2e/custom-assertions/elementCount.js b/test/e2e/custom-assertions/elementCount.js @@ -16,7 +16,7 @@ exports.assertion = function (selector, count) { return res.value } this.command = function (cb) { - var self = this + const self = this return this.api.execute(function (selector) { return document.querySelectorAll(selector).length }, [selector], function (res) { diff --git a/test/e2e/nightwatch.conf.js b/test/e2e/nightwatch.conf.js @@ -1,45 +1,45 @@ require('@babel/register') -var config = require('../../config') +const config = require('../../config') // http://nightwatchjs.org/guide#settings-file module.exports = { - 'src_folders': ['test/e2e/specs'], - 'output_folder': 'test/e2e/reports', - 'custom_assertions_path': ['test/e2e/custom-assertions'], + src_folders: ['test/e2e/specs'], + output_folder: 'test/e2e/reports', + custom_assertions_path: ['test/e2e/custom-assertions'], - 'selenium': { - 'start_process': true, - 'server_path': 'node_modules/selenium-server/lib/runner/selenium-server-standalone-2.53.1.jar', - 'host': '127.0.0.1', - 'port': 4444, - 'cli_args': { + selenium: { + start_process: true, + server_path: 'node_modules/selenium-server/lib/runner/selenium-server-standalone-2.53.1.jar', + host: '127.0.0.1', + port: 4444, + cli_args: { 'webdriver.chrome.driver': require('chromedriver').path } }, - 'test_settings': { - 'default': { - 'selenium_port': 4444, - 'selenium_host': 'localhost', - 'silent': true, - 'globals': { - 'devServerURL': 'http://localhost:' + (process.env.PORT || config.dev.port) + test_settings: { + default: { + selenium_port: 4444, + selenium_host: 'localhost', + silent: true, + globals: { + devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port) } }, - 'chrome': { - 'desiredCapabilities': { - 'browserName': 'chrome', - 'javascriptEnabled': true, - 'acceptSslCerts': true + chrome: { + desiredCapabilities: { + browserName: 'chrome', + javascriptEnabled: true, + acceptSslCerts: true } }, - 'firefox': { - 'desiredCapabilities': { - 'browserName': 'firefox', - 'javascriptEnabled': true, - 'acceptSslCerts': true + firefox: { + desiredCapabilities: { + browserName: 'firefox', + javascriptEnabled: true, + acceptSslCerts: true } } } diff --git a/test/e2e/runner.js b/test/e2e/runner.js @@ -1,6 +1,6 @@ // 1. start the dev server using production config process.env.NODE_ENV = 'testing' -var server = require('../../build/dev-server.js') +const server = require('../../build/dev-server.js') // 2. run the nightwatch test suite against it // to run in additional browsers: @@ -9,7 +9,7 @@ var server = require('../../build/dev-server.js') // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox` // For more information on Nightwatch's config file, see // http://nightwatchjs.org/guide#settings-file -var opts = process.argv.slice(2) +let opts = process.argv.slice(2) if (opts.indexOf('--config') === -1) { opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js']) } @@ -17,8 +17,8 @@ if (opts.indexOf('--env') === -1) { opts = opts.concat(['--env', 'chrome']) } -var spawn = require('cross-spawn') -var runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' }) +const spawn = require('cross-spawn') +const runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' }) runner.on('exit', function (code) { server.close() diff --git a/test/unit/karma.conf.js b/test/unit/karma.conf.js @@ -4,24 +4,19 @@ // https://github.com/webpack/karma-webpack // var path = require('path') -var merge = require('webpack-merge') -var HtmlWebpackPlugin = require('html-webpack-plugin') -var baseConfig = require('../../build/webpack.base.conf') -var utils = require('../../build/utils') -var webpack = require('webpack') +const merge = require('webpack-merge') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const baseConfig = require('../../build/webpack.base.conf') +const utils = require('../../build/utils') +const webpack = require('webpack') // var projectRoot = path.resolve(__dirname, '../../') -var webpackConfig = merge(baseConfig, { +const webpackConfig = merge(baseConfig, { // use inline sourcemap for karma-sourcemap-loader module: { rules: utils.styleLoaders() }, - devtool: '#inline-source-map', - // vue: { - // loaders: { - // js: 'isparta' - // } - // }, + devtool: 'inline-source-map', plugins: [ new webpack.DefinePlugin({ 'process.env': require('../../config/test.env') @@ -37,22 +32,6 @@ var webpackConfig = merge(baseConfig, { // no need for app entry during tests delete webpackConfig.entry -// make sure isparta loader is applied before eslint -// webpackConfig.module.preLoaders = webpackConfig.module.preLoaders || [] -// webpackConfig.module.preLoaders.unshift({ -// test: /\.js$/, -// loader: 'isparta', -// include: path.resolve(projectRoot, 'src') -// }) - -// // only apply babel for test files when using isparta -// webpackConfig.module.loaders.some(function (loader, i) { -// if (loader.loader === 'babel') { -// loader.include = path.resolve(projectRoot, 'test/unit') -// return true -// } -// }) - module.exports = function (config) { config.set({ // to run in additional browsers: @@ -63,7 +42,7 @@ module.exports = function (config) { frameworks: ['mocha', 'sinon-chai'], reporters: ['mocha'], customLaunchers: { - 'FirefoxHeadless': { + FirefoxHeadless: { base: 'Firefox', flags: [ '-headless' diff --git a/test/unit/specs/boot/routes.spec.js b/test/unit/specs/boot/routes.spec.js @@ -1,45 +1,67 @@ -import Vuex from 'vuex' import routes from 'src/boot/routes' -import { createLocalVue } from '@vue/test-utils' -import VueRouter from 'vue-router' +import { createRouter, createMemoryHistory } from 'vue-router' +import { createStore } from 'vuex' -const localVue = createLocalVue() -localVue.use(Vuex) -localVue.use(VueRouter) - -const store = new Vuex.Store({ +const store = createStore({ state: { instance: {} } }) describe('routes', () => { - const router = new VueRouter({ - mode: 'abstract', + const router = createRouter({ + history: createMemoryHistory(), routes: routes(store) }) - it('root path', () => { - router.push('/main/all') + it('root path', async () => { + await router.push('/main/all') + + const matchedComponents = router.currentRoute.value.matched + + // eslint-disable-next-line no-prototype-builtins + expect(matchedComponents[0].components.default.components.hasOwnProperty('Timeline')).to.eql(true) + }) + + it('user\'s profile', async () => { + await router.push('/fake-user-name') + + const matchedComponents = router.currentRoute.value.matched + + // eslint-disable-next-line no-prototype-builtins + expect(matchedComponents[0].components.default.components.hasOwnProperty('UserCard')).to.eql(true) + }) + + it('user\'s profile at /users', async () => { + await router.push('/users/fake-user-name') + + const matchedComponents = router.currentRoute.value.matched + + // eslint-disable-next-line no-prototype-builtins + expect(matchedComponents[0].components.default.components.hasOwnProperty('UserCard')).to.eql(true) + }) + + it('list view', async () => { + await router.push('/lists') - const matchedComponents = router.getMatchedComponents() + const matchedComponents = router.currentRoute.value.matched - expect(matchedComponents[0].components.hasOwnProperty('Timeline')).to.eql(true) + expect(Object.prototype.hasOwnProperty.call(matchedComponents[0].components.default.components, 'ListsCard')).to.eql(true) }) - it('user\'s profile', () => { - router.push('/fake-user-name') + it('list timeline', async () => { + await router.push('/lists/1') - const matchedComponents = router.getMatchedComponents() + const matchedComponents = router.currentRoute.value.matched - expect(matchedComponents[0].components.hasOwnProperty('UserCard')).to.eql(true) + expect(Object.prototype.hasOwnProperty.call(matchedComponents[0].components.default.components, 'Timeline')).to.eql(true) }) - it('user\'s profile at /users', () => { - router.push('/users/fake-user-name') + it('list edit', async () => { + await router.push('/lists/1/edit') - const matchedComponents = router.getMatchedComponents() + const matchedComponents = router.currentRoute.value.matched - expect(matchedComponents[0].components.hasOwnProperty('UserCard')).to.eql(true) + expect(Object.prototype.hasOwnProperty.call(matchedComponents[0].components.default.components, 'BasicUserCard')).to.eql(true) }) }) diff --git a/test/unit/specs/components/emoji_input.spec.js b/test/unit/specs/components/emoji_input.spec.js @@ -1,108 +1,116 @@ -import { shallowMount, createLocalVue } from '@vue/test-utils' +import { h } from 'vue' +import { shallowMount } from '@vue/test-utils' import EmojiInput from 'src/components/emoji_input/emoji_input.vue' +import vClickOutside from 'click-outside-vue3' const generateInput = (value, padEmoji = true) => { - const localVue = createLocalVue() - localVue.directive('click-outside', () => {}) const wrapper = shallowMount(EmojiInput, { - propsData: { - suggest: () => [], - enableEmojiPicker: true, - value - }, - mocks: { - $store: { - getters: { - mergedConfig: { - padEmoji + global: { + renderStubDefaultSlot: true, + mocks: { + $store: { + getters: { + mergedConfig: { + padEmoji + } } } + }, + stubs: { + FAIcon: true + }, + directives: { + 'click-outside': vClickOutside } }, - slots: { - default: '<input />' + props: { + suggest: () => [], + enableEmojiPicker: true, + modelValue: value }, - localVue + slots: { + default: () => h('input', '') + } }) - return [wrapper, localVue] + return wrapper } describe('EmojiInput', () => { describe('insertion mechanism', () => { it('inserts string at the end with trailing space', () => { const initialString = 'Testing' - const [wrapper] = generateInput(initialString) + const wrapper = generateInput(initialString) const input = wrapper.find('input') input.setValue(initialString) wrapper.setData({ caret: initialString.length }) wrapper.vm.insert({ insertion: '(test)', keepOpen: false }) - const inputEvents = wrapper.emitted().input + const inputEvents = wrapper.emitted()['update:modelValue'] expect(inputEvents[inputEvents.length - 1][0]).to.eql('Testing (test) ') }) it('inserts string at the end with trailing space (source has a trailing space)', () => { const initialString = 'Testing ' - const [wrapper] = generateInput(initialString) + const wrapper = generateInput(initialString) const input = wrapper.find('input') input.setValue(initialString) wrapper.setData({ caret: initialString.length }) wrapper.vm.insert({ insertion: '(test)', keepOpen: false }) - const inputEvents = wrapper.emitted().input + const inputEvents = wrapper.emitted()['update:modelValue'] expect(inputEvents[inputEvents.length - 1][0]).to.eql('Testing (test) ') }) it('inserts string at the begginning without leading space', () => { const initialString = 'Testing' - const [wrapper] = generateInput(initialString) + const wrapper = generateInput(initialString) const input = wrapper.find('input') input.setValue(initialString) wrapper.setData({ caret: 0 }) wrapper.vm.insert({ insertion: '(test)', keepOpen: false }) - const inputEvents = wrapper.emitted().input + const inputEvents = wrapper.emitted()['update:modelValue'] expect(inputEvents[inputEvents.length - 1][0]).to.eql('(test) Testing') }) it('inserts string between words without creating extra spaces', () => { const initialString = 'Spurdo Sparde' - const [wrapper] = generateInput(initialString) + const wrapper = generateInput(initialString) const input = wrapper.find('input') input.setValue(initialString) wrapper.setData({ caret: 6 }) wrapper.vm.insert({ insertion: ':ebin:', keepOpen: false }) - const inputEvents = wrapper.emitted().input + const inputEvents = wrapper.emitted()['update:modelValue'] expect(inputEvents[inputEvents.length - 1][0]).to.eql('Spurdo :ebin: Sparde') }) it('inserts string between words without creating extra spaces (other caret)', () => { const initialString = 'Spurdo Sparde' - const [wrapper] = generateInput(initialString) + const wrapper = generateInput(initialString) const input = wrapper.find('input') input.setValue(initialString) wrapper.setData({ caret: 7 }) wrapper.vm.insert({ insertion: ':ebin:', keepOpen: false }) - const inputEvents = wrapper.emitted().input + const inputEvents = wrapper.emitted()['update:modelValue'] expect(inputEvents[inputEvents.length - 1][0]).to.eql('Spurdo :ebin: Sparde') }) it('inserts string without any padding if padEmoji setting is set to false', () => { const initialString = 'Eat some spam!' - const [wrapper] = generateInput(initialString, false) + const wrapper = generateInput(initialString, false) const input = wrapper.find('input') input.setValue(initialString) wrapper.setData({ caret: initialString.length, keepOpen: false }) wrapper.vm.insert({ insertion: ':spam:' }) - const inputEvents = wrapper.emitted().input + const inputEvents = wrapper.emitted()['update:modelValue'] expect(inputEvents[inputEvents.length - 1][0]).to.eql('Eat some spam!:spam:') }) it('correctly sets caret after insertion at beginning', (done) => { const initialString = '1234' - const [wrapper, vue] = generateInput(initialString) + const wrapper = generateInput(initialString) const input = wrapper.find('input') input.setValue(initialString) wrapper.setData({ caret: 0 }) wrapper.vm.insert({ insertion: '1234', keepOpen: false }) - vue.nextTick(() => { + wrapper.vm.$nextTick(() => { expect(wrapper.vm.caret).to.eql(5) done() }) @@ -110,12 +118,12 @@ describe('EmojiInput', () => { it('correctly sets caret after insertion at end', (done) => { const initialString = '1234' - const [wrapper, vue] = generateInput(initialString) + const wrapper = generateInput(initialString) const input = wrapper.find('input') input.setValue(initialString) wrapper.setData({ caret: initialString.length }) wrapper.vm.insert({ insertion: '1234', keepOpen: false }) - vue.nextTick(() => { + wrapper.vm.$nextTick(() => { expect(wrapper.vm.caret).to.eql(10) done() }) @@ -123,12 +131,12 @@ describe('EmojiInput', () => { it('correctly sets caret after insertion if padEmoji setting is set to false', (done) => { const initialString = '1234' - const [wrapper, vue] = generateInput(initialString, false) + const wrapper = generateInput(initialString, false) const input = wrapper.find('input') input.setValue(initialString) wrapper.setData({ caret: initialString.length }) wrapper.vm.insert({ insertion: '1234', keepOpen: false }) - vue.nextTick(() => { + wrapper.vm.$nextTick(() => { expect(wrapper.vm.caret).to.eql(8) done() }) diff --git a/test/unit/specs/components/rich_content.spec.js b/test/unit/specs/components/rich_content.spec.js @@ -1,27 +1,44 @@ -import { mount, shallowMount, createLocalVue } from '@vue/test-utils' +import { mount, shallowMount } from '@vue/test-utils' import RichContent from 'src/components/rich_content/rich_content.jsx' -const localVue = createLocalVue() const attentions = [] +const global = { + mocks: { + $store: { + state: {}, + getters: { + mergedConfig: () => ({ + mentionLinkShowTooltip: true + }), + findUserByUrl: () => null + } + } + }, + stubs: { + FAIcon: true + } +} -const makeMention = (who) => { +const makeMention = (who, noClass) => { attentions.push({ statusnet_profile_url: `https://fake.tld/@${who}` }) - return `<span class="h-card"><a class="u-url mention" href="https://fake.tld/@${who}">@<span>${who}</span></a></span>` + return noClass + ? `<span><a href="https://fake.tld/@${who}">@<span>${who}</span></a></span>` + : `<span class="h-card"><a class="u-url mention" href="https://fake.tld/@${who}">@<span>${who}</span></a></span>` } const p = (...data) => `<p>${data.join('')}</p>` const compwrap = (...data) => `<span class="RichContent">${data.join('')}</span>` const mentionsLine = (times) => [ - '<mentionsline-stub mentions="', + '<mentions-line-stub mentions="', new Array(times).fill('[object Object]').join(','), - '"></mentionsline-stub>' + '"></mentions-line-stub>' ].join('') describe('RichContent', () => { it('renders simple post without exploding', () => { const html = p('Hello world!') const wrapper = shallowMount(RichContent, { - localVue, - propsData: { + global, + props: { attentions, handleLinks: true, greentext: true, @@ -30,7 +47,7 @@ describe('RichContent', () => { } }) - expect(wrapper.html()).to.eql(compwrap(html)) + expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(html)) }) it('unescapes everything as needed', () => { @@ -43,8 +60,8 @@ describe('RichContent', () => { 'Testing \'em all' ].join('') const wrapper = shallowMount(RichContent, { - localVue, - propsData: { + global, + props: { attentions, handleLinks: true, greentext: true, @@ -53,7 +70,7 @@ describe('RichContent', () => { } }) - expect(wrapper.html()).to.eql(compwrap(expected)) + expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected)) }) it('replaces mention with mentionsline', () => { @@ -62,8 +79,8 @@ describe('RichContent', () => { ' how are you doing today?' ) const wrapper = shallowMount(RichContent, { - localVue, - propsData: { + global, + props: { attentions, handleLinks: true, greentext: true, @@ -72,7 +89,7 @@ describe('RichContent', () => { } }) - expect(wrapper.html()).to.eql(compwrap(p( + expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(p( mentionsLine(1), ' how are you doing today?' ))) @@ -93,17 +110,17 @@ describe('RichContent', () => { ), // TODO fix this extra line somehow? p( - '<mentionsline-stub mentions="', + '<mentions-line-stub mentions="', '[object Object],', '[object Object],', '[object Object]', - '"></mentionsline-stub>' + '"></mentions-line-stub>' ) ].join('') const wrapper = shallowMount(RichContent, { - localVue, - propsData: { + global, + props: { attentions, handleLinks: true, greentext: true, @@ -112,7 +129,7 @@ describe('RichContent', () => { } }) - expect(wrapper.html()).to.eql(compwrap(expected)) + expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected)) }) it('Does not touch links if link handling is disabled', () => { @@ -124,14 +141,24 @@ describe('RichContent', () => { ].join(''), [ makeMention('John'), - makeMention('Josh'), - makeMention('Jeremy') + makeMention('Josh'), makeMention('Jeremy') + ].join('') + ].join('\n') + const strippedHtml = [ + [ + makeMention('Jack', true), + 'let\'s meet up with ', + makeMention('Janet', true) + ].join(''), + [ + makeMention('John', true), + makeMention('Josh', true), makeMention('Jeremy', true) ].join('') ].join('\n') const wrapper = shallowMount(RichContent, { - localVue, - propsData: { + global, + props: { attentions, handleLinks: false, greentext: true, @@ -140,7 +167,7 @@ describe('RichContent', () => { } }) - expect(wrapper.html()).to.eql(compwrap(html)) + expect(wrapper.html()).to.eql(compwrap(strippedHtml)) }) it('Adds greentext and cyantext to the post', () => { @@ -154,8 +181,8 @@ describe('RichContent', () => { ].join('\n') const wrapper = shallowMount(RichContent, { - localVue, - propsData: { + global, + props: { attentions, handleLinks: false, greentext: true, @@ -174,8 +201,8 @@ describe('RichContent', () => { ].join('\n') const wrapper = shallowMount(RichContent, { - localVue, - propsData: { + global, + props: { attentions, handleLinks: false, greentext: false, @@ -191,12 +218,12 @@ describe('RichContent', () => { const html = p('Ebin :DDDD :spurdo:') const expected = p( 'Ebin :DDDD ', - '<anonymous-stub alt=":spurdo:" src="about:blank" title=":spurdo:" class="emoji img"></anonymous-stub>' + '<anonymous-stub src="about:blank" alt=":spurdo:" class="emoji img" title=":spurdo:"></anonymous-stub>' ) const wrapper = shallowMount(RichContent, { - localVue, - propsData: { + global, + props: { attentions, handleLinks: false, greentext: false, @@ -205,15 +232,15 @@ describe('RichContent', () => { } }) - expect(wrapper.html()).to.eql(compwrap(expected)) + expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected)) }) it('Doesn\'t add nonexistent emoji to post', () => { const html = p('Lol :lol:') const wrapper = shallowMount(RichContent, { - localVue, - propsData: { + global, + props: { attentions, handleLinks: false, greentext: false, @@ -222,7 +249,7 @@ describe('RichContent', () => { } }) - expect(wrapper.html()).to.eql(compwrap(html)) + expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(html)) }) it('Greentext + last mentions', () => { @@ -240,8 +267,8 @@ describe('RichContent', () => { ].join('\n') const wrapper = shallowMount(RichContent, { - localVue, - propsData: { + global, + props: { attentions, handleLinks: true, greentext: true, @@ -272,8 +299,8 @@ describe('RichContent', () => { ].join('<br>') const wrapper = shallowMount(RichContent, { - localVue, - propsData: { + global, + props: { attentions, handleLinks: true, greentext: true, @@ -282,7 +309,7 @@ describe('RichContent', () => { } }) - expect(wrapper.html()).to.eql(compwrap(expected)) + expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected)) }) it('buggy example/hashtags', () => { @@ -300,16 +327,16 @@ describe('RichContent', () => { '<p>', '<a href="http://macrochan.org/images/N/H/NHCMDUXJPPZ6M3Z2CQ6D2EBRSWGE7MZY.jpg" target="_blank">', 'NHCMDUXJPPZ6M3Z2CQ6D2EBRSWGE7MZY.jpg</a>', - ' <hashtaglink-stub url="https://shitposter.club/tag/nou" content="#nou" tag="nou">', - '</hashtaglink-stub>', - ' <hashtaglink-stub url="https://shitposter.club/tag/screencap" content="#screencap" tag="screencap">', - '</hashtaglink-stub>', + ' <hashtag-link-stub url="https://shitposter.club/tag/nou" content="#nou" tag="nou">', + '</hashtag-link-stub>', + ' <hashtag-link-stub url="https://shitposter.club/tag/screencap" content="#screencap" tag="screencap">', + '</hashtag-link-stub>', ' </p>' ].join('') const wrapper = shallowMount(RichContent, { - localVue, - propsData: { + global, + props: { attentions, handleLinks: true, greentext: true, @@ -318,7 +345,7 @@ describe('RichContent', () => { } }) - expect(wrapper.html()).to.eql(compwrap(expected)) + expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected)) }) it('rich contents of a mention are handled properly', () => { @@ -342,7 +369,7 @@ describe('RichContent', () => { p( '<span class="MentionsLine">', '<span class="MentionLink mention-link">', - '<a href="lol" target="_blank" class="original">', + '<a href="lol" class="original" target="_blank">', '<span>', 'https://</span>', '<span>', @@ -350,10 +377,7 @@ describe('RichContent', () => { '<span>', '</span>', '</a>', - ' ', - '<!---->', // v-if placeholder, mentionlink's "new" (i.e. rich) display '</span>', - '<!---->', // v-if placeholder, mentionsline's extra mentions and stuff '</span>' ), p( @@ -362,8 +386,8 @@ describe('RichContent', () => { ].join('') const wrapper = mount(RichContent, { - localVue, - propsData: { + global, + props: { attentions, handleLinks: true, greentext: true, @@ -372,7 +396,75 @@ describe('RichContent', () => { } }) - expect(wrapper.html()).to.eql(compwrap(expected)) + expect(wrapper.html().replace(/\n/g, '').replace(/<!--.*?-->/g, '')).to.eql(compwrap(expected)) + }) + + it('rich contents of nested mentions are handled properly', () => { + attentions.push({ statusnet_profile_url: 'lol' }) + const html = [ + '<span class="poast-style">', + '<a href="lol" class="mention">', + '<span>', + 'https://</span>', + '<span>', + 'lol.tld/</span>', + '<span>', + '</span>', + '</a>', + ' ', + '<a href="lol" class="mention">', + '<span>', + 'https://</span>', + '<span>', + 'lol.tld/</span>', + '<span>', + '</span>', + '</a>', + ' ', + '</span>', + 'Testing' + ].join('') + const expected = [ + '<span>', + '<span class="MentionsLine">', + '<span class="MentionLink mention-link">', + '<a href="lol" class="original" target="_blank">', + '<span>', + 'https://</span>', + '<span>', + 'lol.tld/</span>', + '<span>', + '</span>', + '</a>', + '</span>', + '<span class="MentionLink mention-link">', + '<a href="lol" class="original" target="_blank">', + '<span>', + 'https://</span>', + '<span>', + 'lol.tld/</span>', + '<span>', + '</span>', + '</a>', + '</span>', + '</span>', + ' ', + '</span>', + 'Testing' + ].join('') + + const wrapper = mount(RichContent, { + global, + props: { + attentions, + handleLinks: true, + greentext: true, + emoji: [], + html + } + }) + + expect(wrapper.html().replace(/\n/g, '').replace(/<!--.*?-->/g, '')).to.eql(compwrap(expected)) }) it('rich contents of a link are handled properly', () => { @@ -406,8 +498,8 @@ describe('RichContent', () => { ].join('') const wrapper = shallowMount(RichContent, { - localVue, - propsData: { + global, + props: { attentions, handleLinks: true, greentext: true, @@ -416,7 +508,7 @@ describe('RichContent', () => { } }) - expect(wrapper.html()).to.eql(compwrap(expected)) + expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected)) }) it.skip('[INFORMATIVE] Performance testing, 10 000 simple posts', () => { @@ -453,8 +545,8 @@ describe('RichContent', () => { const t0 = performance.now() const wrapper = mount(TestComponent, { - localVue, - propsData: { + global, + props: { attentions, handleLinks, vhtml diff --git a/test/unit/specs/components/timeline.spec.js b/test/unit/specs/components/timeline.spec.js @@ -1,27 +0,0 @@ -import { getExcludedStatusIdsByPinning } from 'src/components/timeline/timeline.js' - -describe('Timeline', () => { - describe('getExcludedStatusIdsByPinning', () => { - const mockStatuses = (ids) => ids.map(id => ({ id })) - - it('should return only members of both pinnedStatusIds and ids of the given statuses', () => { - const statusIds = [1, 2, 3, 4] - const statuses = mockStatuses(statusIds) - const pinnedStatusIds = [1, 3, 5] - const result = getExcludedStatusIdsByPinning(statuses, pinnedStatusIds) - result.forEach(item => { - expect(item).to.be.oneOf(statusIds) - expect(item).to.be.oneOf(pinnedStatusIds) - }) - }) - - it('should return ids of pinned statuses not posted before any unpinned status', () => { - const pinnedStatusIdSet1 = ['PINNED1', 'PINNED2'] - const pinnedStatusIdSet2 = ['PINNED3', 'PINNED4'] - const pinnedStatusIds = [...pinnedStatusIdSet1, ...pinnedStatusIdSet2] - const statusIds = [...pinnedStatusIdSet1, 'UNPINNED1', ...pinnedStatusIdSet2] - const statuses = mockStatuses(statusIds) - expect(getExcludedStatusIdsByPinning(statuses, pinnedStatusIds)).to.eql(pinnedStatusIdSet1) - }) - }) -}) diff --git a/test/unit/specs/components/user_profile.spec.js b/test/unit/specs/components/user_profile.spec.js @@ -1,12 +1,9 @@ -import { mount, createLocalVue } from '@vue/test-utils' -import Vuex from 'vuex' +import { mount } from '@vue/test-utils' +import { createStore } from 'vuex' import UserProfile from 'src/components/user_profile/user_profile.vue' import backendInteractorService from 'src/services/backend_interactor_service/backend_interactor_service.js' import { getters } from 'src/modules/users.js' -const localVue = createLocalVue() -localVue.use(Vuex) - const mutations = { clearTimeline: () => {} } @@ -18,6 +15,7 @@ const actions = { const testGetters = { findUser: state => getters.findUser(state.users), + findUserByName: state => getters.findUserByName(state.users), relationship: state => getters.relationship(state.users), mergedConfig: state => ({ colors: '', @@ -42,7 +40,7 @@ const extUser = { screen_name_ui: 'testUser@test.instance' } -const externalProfileStore = new Vuex.Store({ +const externalProfileStore = createStore({ mutations, actions, getters: testGetters, @@ -98,13 +96,14 @@ const externalProfileStore = new Vuex.Store({ credentials: '' }, usersObject: { 100: extUser }, + usersByNameObject: {}, users: [extUser], relationships: {} } } }) -const localProfileStore = new Vuex.Store({ +const localProfileStore = createStore({ mutations, actions, getters: testGetters, @@ -166,24 +165,27 @@ const localProfileStore = new Vuex.Store({ currentUser: { credentials: '' }, - usersObject: { 100: localUser, 'testuser': localUser }, + usersObject: { 100: localUser }, + usersByNameObject: { testuser: localUser }, users: [localUser], relationships: {} } } }) -describe('UserProfile', () => { +// https://github.com/vuejs/test-utils/issues/1382 +describe.skip('UserProfile', () => { it('renders external profile', () => { const wrapper = mount(UserProfile, { - localVue, - store: externalProfileStore, - mocks: { - $route: { - params: { id: 100 }, - name: 'external-user-profile' - }, - $t: (msg) => msg + global: { + plugins: [externalProfileStore], + mocks: { + $route: { + params: { id: 100 }, + name: 'external-user-profile' + }, + $t: (msg) => msg + } } }) @@ -192,14 +194,15 @@ describe('UserProfile', () => { it('renders local profile', () => { const wrapper = mount(UserProfile, { - localVue, - store: localProfileStore, - mocks: { - $route: { - params: { name: 'testUser' }, - name: 'user-profile' - }, - $t: (msg) => msg + global: { + plugins: [localProfileStore], + mocks: { + $route: { + params: { name: 'testUser' }, + name: 'user-profile' + }, + $t: (msg) => msg + } } }) diff --git a/test/unit/specs/modules/lists.spec.js b/test/unit/specs/modules/lists.spec.js @@ -0,0 +1,83 @@ +import { cloneDeep } from 'lodash' +import { defaultState, mutations, getters } from '../../../../src/modules/lists.js' + +describe('The lists module', () => { + describe('mutations', () => { + it('updates array of all lists', () => { + const state = cloneDeep(defaultState) + const list = { id: '1', title: 'testList' } + + mutations.setLists(state, [list]) + expect(state.allLists).to.have.length(1) + expect(state.allLists).to.eql([list]) + }) + + it('adds a new list with a title, updating the title for existing lists', () => { + const state = cloneDeep(defaultState) + const list = { id: '1', title: 'testList' } + const modList = { id: '1', title: 'anotherTestTitle' } + + mutations.setList(state, { listId: list.id, title: list.title }) + expect(state.allListsObject[list.id]).to.eql({ title: list.title, accountIds: [] }) + expect(state.allLists).to.have.length(1) + expect(state.allLists[0]).to.eql(list) + + mutations.setList(state, { listId: modList.id, title: modList.title }) + expect(state.allListsObject[modList.id]).to.eql({ title: modList.title, accountIds: [] }) + expect(state.allLists).to.have.length(1) + expect(state.allLists[0]).to.eql(modList) + }) + + it('adds a new list with an array of IDs, updating the IDs for existing lists', () => { + const state = cloneDeep(defaultState) + const list = { id: '1', accountIds: ['1', '2', '3'] } + const modList = { id: '1', accountIds: ['3', '4', '5'] } + + mutations.setListAccounts(state, { listId: list.id, accountIds: list.accountIds }) + expect(state.allListsObject[list.id]).to.eql({ accountIds: list.accountIds }) + + mutations.setListAccounts(state, { listId: modList.id, accountIds: modList.accountIds }) + expect(state.allListsObject[modList.id]).to.eql({ accountIds: modList.accountIds }) + }) + + it('deletes a list', () => { + const state = { + allLists: [{ id: '1', title: 'testList' }], + allListsObject: { + 1: { title: 'testList', accountIds: ['1', '2', '3'] } + } + } + const listId = '1' + + mutations.deleteList(state, { listId }) + expect(state.allLists).to.have.length(0) + expect(state.allListsObject).to.eql({}) + }) + }) + + describe('getters', () => { + it('returns list title', () => { + const state = { + allLists: [{ id: '1', title: 'testList' }], + allListsObject: { + 1: { title: 'testList', accountIds: ['1', '2', '3'] } + } + } + const id = '1' + + expect(getters.findListTitle(state)(id)).to.eql('testList') + }) + + it('returns list accounts', () => { + const state = { + allLists: [{ id: '1', title: 'testList' }], + allListsObject: { + 1: { title: 'testList', accountIds: ['1', '2', '3'] } + } + } + const id = '1' + + expect(getters.findListAccounts(state)(id)).to.eql(['1', '2', '3']) + }) + }) +}) diff --git a/test/unit/specs/modules/serverSideStorage.spec.js b/test/unit/specs/modules/serverSideStorage.spec.js @@ -0,0 +1,338 @@ +import { cloneDeep } from 'lodash' + +import { + VERSION, + COMMAND_TRIM_FLAGS, + COMMAND_TRIM_FLAGS_AND_RESET, + _moveItemInArray, + _getRecentData, + _getAllFlags, + _mergeFlags, + _mergePrefs, + _resetFlags, + mutations, + defaultState, + newUserFlags +} from 'src/modules/serverSideStorage.js' + +describe('The serverSideStorage module', () => { + describe('mutations', () => { + describe('setServerSideStorage', () => { + const { setServerSideStorage } = mutations + const user = { + created_at: new Date('1999-02-09'), + storage: {} + } + + it('should initialize storage if none present', () => { + const state = cloneDeep(defaultState) + setServerSideStorage(state, user) + expect(state.cache._version).to.eql(VERSION) + expect(state.cache._timestamp).to.be.a('number') + expect(state.cache.flagStorage).to.eql(defaultState.flagStorage) + expect(state.cache.prefsStorage).to.eql(defaultState.prefsStorage) + }) + + it('should initialize storage with proper flags for new users if none present', () => { + const state = cloneDeep(defaultState) + setServerSideStorage(state, { ...user, created_at: new Date() }) + expect(state.cache._version).to.eql(VERSION) + expect(state.cache._timestamp).to.be.a('number') + expect(state.cache.flagStorage).to.eql(newUserFlags) + expect(state.cache.prefsStorage).to.eql(defaultState.prefsStorage) + }) + + it('should merge flags even if remote timestamp is older', () => { + const state = { + ...cloneDeep(defaultState), + cache: { + _timestamp: Date.now(), + _version: VERSION, + ...cloneDeep(defaultState) + } + } + setServerSideStorage( + state, + { + ...user, + storage: { + _timestamp: 123, + _version: VERSION, + flagStorage: { + ...defaultState.flagStorage, + updateCounter: 1 + }, + prefsStorage: { + ...defaultState.prefsStorage + } + } + } + ) + expect(state.cache.flagStorage).to.eql({ + ...defaultState.flagStorage, + updateCounter: 1 + }) + }) + + it('should reset local timestamp to remote if contents are the same', () => { + const state = { + ...cloneDeep(defaultState), + cache: null + } + setServerSideStorage( + state, + { + ...user, + storage: { + _timestamp: 123, + _version: VERSION, + flagStorage: { + ...defaultState.flagStorage, + updateCounter: 999 + } + } + } + ) + expect(state.cache._timestamp).to.eql(123) + expect(state.flagStorage.updateCounter).to.eql(999) + expect(state.cache.flagStorage.updateCounter).to.eql(999) + }) + + it('should remote version if local missing', () => { + const state = cloneDeep(defaultState) + setServerSideStorage(state, user) + expect(state.cache._version).to.eql(VERSION) + expect(state.cache._timestamp).to.be.a('number') + expect(state.cache.flagStorage).to.eql(defaultState.flagStorage) + }) + }) + describe('setPreference', () => { + const { setPreference, updateCache, addCollectionPreference, removeCollectionPreference } = mutations + + it('should set preference and update journal log accordingly', () => { + const state = cloneDeep(defaultState) + setPreference(state, { path: 'simple.testing', value: 1 }) + expect(state.prefsStorage.simple.testing).to.eql(1) + expect(state.prefsStorage._journal.length).to.eql(1) + expect(state.prefsStorage._journal[0]).to.eql({ + path: 'simple.testing', + operation: 'set', + args: [1], + // should have A timestamp, we don't really care what it is + timestamp: state.prefsStorage._journal[0].timestamp + }) + }) + + it('should keep journal to a minimum', () => { + const state = cloneDeep(defaultState) + setPreference(state, { path: 'simple.testing', value: 1 }) + setPreference(state, { path: 'simple.testing', value: 2 }) + addCollectionPreference(state, { path: 'collections.testing', value: 2 }) + removeCollectionPreference(state, { path: 'collections.testing', value: 2 }) + updateCache(state, { username: 'test' }) + expect(state.prefsStorage.simple.testing).to.eql(2) + expect(state.prefsStorage.collections.testing).to.eql([]) + expect(state.prefsStorage._journal.length).to.eql(2) + expect(state.prefsStorage._journal[0]).to.eql({ + path: 'simple.testing', + operation: 'set', + args: [2], + // should have A timestamp, we don't really care what it is + timestamp: state.prefsStorage._journal[0].timestamp + }) + expect(state.prefsStorage._journal[1]).to.eql({ + path: 'collections.testing', + operation: 'removeFromCollection', + args: [2], + // should have A timestamp, we don't really care what it is + timestamp: state.prefsStorage._journal[1].timestamp + }) + }) + + it('should remove duplicate entries from journal', () => { + const state = cloneDeep(defaultState) + setPreference(state, { path: 'simple.testing', value: 1 }) + setPreference(state, { path: 'simple.testing', value: 1 }) + addCollectionPreference(state, { path: 'collections.testing', value: 2 }) + addCollectionPreference(state, { path: 'collections.testing', value: 2 }) + updateCache(state, { username: 'test' }) + expect(state.prefsStorage.simple.testing).to.eql(1) + expect(state.prefsStorage.collections.testing).to.eql([2]) + expect(state.prefsStorage._journal.length).to.eql(2) + }) + }) + }) + + describe('helper functions', () => { + describe('_moveItemInArray', () => { + it('should move item according to movement value', () => { + expect(_moveItemInArray([1, 2, 3, 4], 4, -1)).to.eql([1, 2, 4, 3]) + expect(_moveItemInArray([1, 2, 3, 4], 1, 2)).to.eql([2, 3, 1, 4]) + }) + it('should clamp movement to within array', () => { + expect(_moveItemInArray([1, 2, 3, 4], 4, -10)).to.eql([4, 1, 2, 3]) + expect(_moveItemInArray([1, 2, 3, 4], 3, 99)).to.eql([1, 2, 4, 3]) + }) + }) + describe('_getRecentData', () => { + it('should handle nulls correctly', () => { + expect(_getRecentData(null, null)).to.eql({ recent: null, stale: null, needUpload: true }) + }) + + it('doesn\'t choke on invalid data', () => { + expect(_getRecentData({ a: 1 }, { b: 2 })).to.eql({ recent: null, stale: null, needUpload: true }) + }) + + it('should prefer the valid non-null correctly, needUpload works properly', () => { + const nonNull = { _version: VERSION, _timestamp: 1 } + expect(_getRecentData(nonNull, null)).to.eql({ recent: nonNull, stale: null, needUpload: true }) + expect(_getRecentData(null, nonNull)).to.eql({ recent: nonNull, stale: null, needUpload: false }) + }) + + it('should prefer the one with higher timestamp', () => { + const a = { _version: VERSION, _timestamp: 1 } + const b = { _version: VERSION, _timestamp: 2 } + + expect(_getRecentData(a, b)).to.eql({ recent: b, stale: a, needUpload: false }) + expect(_getRecentData(b, a)).to.eql({ recent: b, stale: a, needUpload: false }) + }) + + it('case where both are same', () => { + const a = { _version: VERSION, _timestamp: 3 } + const b = { _version: VERSION, _timestamp: 3 } + + expect(_getRecentData(a, b)).to.eql({ recent: b, stale: a, needUpload: false }) + expect(_getRecentData(b, a)).to.eql({ recent: b, stale: a, needUpload: false }) + }) + }) + + describe('_getAllFlags', () => { + it('should handle nulls properly', () => { + expect(_getAllFlags(null, null)).to.eql([]) + }) + it('should output list of keys if passed single object', () => { + expect(_getAllFlags({ flagStorage: { a: 1, b: 1, c: 1 } }, null)).to.eql(['a', 'b', 'c']) + }) + it('should union keys of both objects', () => { + expect(_getAllFlags({ flagStorage: { a: 1, b: 1, c: 1 } }, { flagStorage: { c: 1, d: 1 } })).to.eql(['a', 'b', 'c', 'd']) + }) + }) + + describe('_mergeFlags', () => { + it('should handle merge two flag sets correctly picking higher numbers', () => { + expect( + _mergeFlags( + { flagStorage: { a: 0, b: 3 } }, + { flagStorage: { b: 1, c: 4, d: 9 } }, + ['a', 'b', 'c', 'd']) + ).to.eql({ a: 0, b: 3, c: 4, d: 9 }) + }) + }) + + describe('_mergePrefs', () => { + it('should prefer recent and apply journal to it', () => { + expect( + _mergePrefs( + // RECENT + { + simple: { a: 1, b: 0, c: true }, + _journal: [ + { path: 'simple.b', operation: 'set', args: [0], timestamp: 2 }, + { path: 'simple.c', operation: 'set', args: [true], timestamp: 4 } + ] + }, + // STALE + { + simple: { a: 1, b: 1, c: false }, + _journal: [ + { path: 'simple.a', operation: 'set', args: [1], timestamp: 1 }, + { path: 'simple.b', operation: 'set', args: [1], timestamp: 3 } + ] + } + ) + ).to.eql({ + simple: { a: 1, b: 1, c: true }, + _journal: [ + { path: 'simple.a', operation: 'set', args: [1], timestamp: 1 }, + { path: 'simple.b', operation: 'set', args: [1], timestamp: 3 }, + { path: 'simple.c', operation: 'set', args: [true], timestamp: 4 } + ] + }) + }) + + it('should allow setting falsy values', () => { + expect( + _mergePrefs( + // RECENT + { + simple: { a: 1, b: 0, c: false }, + _journal: [ + { path: 'simple.b', operation: 'set', args: [0], timestamp: 2 }, + { path: 'simple.c', operation: 'set', args: [false], timestamp: 4 } + ] + }, + // STALE + { + simple: { a: 0, b: 0, c: true }, + _journal: [ + { path: 'simple.a', operation: 'set', args: [0], timestamp: 1 }, + { path: 'simple.b', operation: 'set', args: [0], timestamp: 3 } + ] + } + ) + ).to.eql({ + simple: { a: 0, b: 0, c: false }, + _journal: [ + { path: 'simple.a', operation: 'set', args: [0], timestamp: 1 }, + { path: 'simple.b', operation: 'set', args: [0], timestamp: 3 }, + { path: 'simple.c', operation: 'set', args: [false], timestamp: 4 } + ] + }) + }) + + it('should work with strings', () => { + expect( + _mergePrefs( + // RECENT + { + simple: { a: 'foo' }, + _journal: [ + { path: 'simple.a', operation: 'set', args: ['foo'], timestamp: 2 } + ] + }, + // STALE + { + simple: { a: 'bar' }, + _journal: [ + { path: 'simple.a', operation: 'set', args: ['bar'], timestamp: 4 } + ] + } + ) + ).to.eql({ + simple: { a: 'bar' }, + _journal: [ + { path: 'simple.a', operation: 'set', args: ['bar'], timestamp: 4 } + ] + }) + }) + }) + + describe('_resetFlags', () => { + it('should reset all known flags to 0 when reset flag is set to > 0 and < 9000', () => { + const totalFlags = { a: 0, b: 3, reset: 1 } + + expect(_resetFlags(totalFlags)).to.eql({ a: 0, b: 0, reset: 0 }) + }) + it('should trim all flags to known when reset is set to 1000', () => { + const totalFlags = { a: 0, b: 3, c: 33, reset: COMMAND_TRIM_FLAGS } + + expect(_resetFlags(totalFlags, { a: 0, b: 0, reset: 0 })).to.eql({ a: 0, b: 3, reset: 0 }) + }) + it('should trim all flags to known and reset when reset is set to 1001', () => { + const totalFlags = { a: 0, b: 3, c: 33, reset: COMMAND_TRIM_FLAGS_AND_RESET } + + expect(_resetFlags(totalFlags, { a: 0, b: 0, reset: 0 })).to.eql({ a: 0, b: 0, reset: 0 }) + }) + }) + }) +}) diff --git a/test/unit/specs/modules/statuses.spec.js b/test/unit/specs/modules/statuses.spec.js @@ -245,7 +245,7 @@ describe('Statuses module', () => { it('increments count in existing reaction', () => { const state = defaultState() const status = makeMockStatus({ id: '1' }) - status.emoji_reactions = [ { name: '😂', count: 1, accounts: [] } ] + status.emoji_reactions = [{ name: '😂', count: 1, accounts: [] }] mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) mutations.addOwnReaction(state, { id: '1', emoji: '😂', currentUser: { id: 'me' } }) @@ -269,7 +269,7 @@ describe('Statuses module', () => { it('decreases count in existing reaction', () => { const state = defaultState() const status = makeMockStatus({ id: '1' }) - status.emoji_reactions = [ { name: '😂', count: 2, accounts: [{ id: 'me' }] } ] + status.emoji_reactions = [{ name: '😂', count: 2, accounts: [{ id: 'me' }] }] mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) mutations.removeOwnReaction(state, { id: '1', emoji: '😂', currentUser: { id: 'me' } }) diff --git a/test/unit/specs/modules/users.spec.js b/test/unit/specs/modules/users.spec.js @@ -57,24 +57,27 @@ describe('The users module', () => { }) describe('findUser', () => { - it('returns user with matching screen_name', () => { + it('does not return user with matching screen_name', () => { const user = { screen_name: 'Guy', id: '1' } const state = { usersObject: { - 1: user, + 1: user + }, + usersByNameObject: { guy: user } } const name = 'Guy' - const expected = { screen_name: 'Guy', id: '1' } - expect(getters.findUser(state)(name)).to.eql(expected) + expect(getters.findUser(state)(name)).to.eql(undefined) }) it('returns user with matching id', () => { const user = { screen_name: 'Guy', id: '1' } const state = { usersObject: { - 1: user, + 1: user + }, + usersByNameObject: { guy: user } } @@ -83,4 +86,35 @@ describe('The users module', () => { expect(getters.findUser(state)(id)).to.eql(expected) }) }) + + describe('findUserByName', () => { + it('returns user with matching screen_name', () => { + const user = { screen_name: 'Guy', id: '1' } + const state = { + usersObject: { + 1: user + }, + usersByNameObject: { + guy: user + } + } + const name = 'Guy' + const expected = { screen_name: 'Guy', id: '1' } + expect(getters.findUserByName(state)(name)).to.eql(expected) + }) + + it('does not return user with matching id', () => { + const user = { screen_name: 'Guy', id: '1' } + const state = { + usersObject: { + 1: user + }, + usersByNameObject: { + guy: user + } + } + const id = '1' + expect(getters.findUserByName(state)(id)).to.eql(undefined) + }) + }) }) diff --git a/test/unit/specs/services/chat_service/chat_service.spec.js b/test/unit/specs/services/chat_service/chat_service.spec.js @@ -24,23 +24,23 @@ describe('chatService', () => { describe('.add', () => { it("Doesn't add duplicates", () => { const chat = chatService.empty() - chatService.add(chat, { messages: [ message1 ] }) - chatService.add(chat, { messages: [ message1 ] }) + chatService.add(chat, { messages: [message1] }) + chatService.add(chat, { messages: [message1] }) expect(chat.messages.length).to.eql(1) - chatService.add(chat, { messages: [ message2 ] }) + chatService.add(chat, { messages: [message2] }) expect(chat.messages.length).to.eql(2) }) it('Updates minId and lastMessage and newMessageCount', () => { const chat = chatService.empty() - chatService.add(chat, { messages: [ message1 ] }) + chatService.add(chat, { messages: [message1] }) expect(chat.maxId).to.eql(message1.id) expect(chat.minId).to.eql(message1.id) expect(chat.newMessageCount).to.eql(1) - chatService.add(chat, { messages: [ message2 ] }) + chatService.add(chat, { messages: [message2] }) expect(chat.maxId).to.eql(message2.id) expect(chat.minId).to.eql(message1.id) expect(chat.newMessageCount).to.eql(2) @@ -50,7 +50,7 @@ describe('chatService', () => { expect(chat.lastSeenMessageId).to.eql(message2.id) // Add message with higher id - chatService.add(chat, { messages: [ message3 ] }) + chatService.add(chat, { messages: [message3] }) expect(chat.newMessageCount).to.eql(1) }) }) @@ -59,9 +59,9 @@ describe('chatService', () => { it('Updates minId and lastMessage', () => { const chat = chatService.empty() - chatService.add(chat, { messages: [ message1 ] }) - chatService.add(chat, { messages: [ message2 ] }) - chatService.add(chat, { messages: [ message3 ] }) + chatService.add(chat, { messages: [message1] }) + chatService.add(chat, { messages: [message2] }) + chatService.add(chat, { messages: [message3] }) expect(chat.maxId).to.eql(message3.id) expect(chat.minId).to.eql(message1.id) @@ -80,9 +80,9 @@ describe('chatService', () => { it('Inserts date separators', () => { const chat = chatService.empty() - chatService.add(chat, { messages: [ message1 ] }) - chatService.add(chat, { messages: [ message2 ] }) - chatService.add(chat, { messages: [ message3 ] }) + chatService.add(chat, { messages: [message1] }) + chatService.add(chat, { messages: [message2] }) + chatService.add(chat, { messages: [message3] }) const view = chatService.getView(chat) expect(view.map(i => i.type)).to.eql(['date', 'message', 'message', 'date', 'message']) diff --git a/test/unit/specs/services/date_utils/date_utils.spec.js b/test/unit/specs/services/date_utils/date_utils.spec.js @@ -11,30 +11,30 @@ describe('DateUtils', () => { it('rounds down for past', () => { const time = Date.now() - 1.8 * DateUtils.HOUR - expect(DateUtils.relativeTime(time)).to.eql({ num: 1, key: 'time.hour' }) + expect(DateUtils.relativeTime(time)).to.eql({ num: 1, key: 'time.unit.hours' }) }) it('rounds up for future', () => { const time = Date.now() + 1.8 * DateUtils.HOUR - expect(DateUtils.relativeTime(time)).to.eql({ num: 2, key: 'time.hours' }) + expect(DateUtils.relativeTime(time)).to.eql({ num: 2, key: 'time.unit.hours' }) }) it('uses plural when necessary', () => { const time = Date.now() - 3.8 * DateUtils.WEEK - expect(DateUtils.relativeTime(time)).to.eql({ num: 3, key: 'time.weeks' }) + expect(DateUtils.relativeTime(time)).to.eql({ num: 3, key: 'time.unit.weeks' }) }) it('works with date string', () => { const time = Date.now() - 4 * DateUtils.MONTH const dateString = new Date(time).toISOString() - expect(DateUtils.relativeTime(dateString)).to.eql({ num: 4, key: 'time.months' }) + expect(DateUtils.relativeTime(dateString)).to.eql({ num: 4, key: 'time.unit.months' }) }) }) describe('relativeTimeShort', () => { it('returns the short version of the same relative time', () => { const time = Date.now() + 2 * DateUtils.YEAR - expect(DateUtils.relativeTimeShort(time)).to.eql({ num: 2, key: 'time.years_short' }) + expect(DateUtils.relativeTimeShort(time)).to.eql({ num: 2, key: 'time.unit.years_short' }) }) }) }) diff --git a/test/unit/specs/services/entity_normalizer/entity_normalizer.spec.js b/test/unit/specs/services/entity_normalizer/entity_normalizer.spec.js @@ -195,7 +195,7 @@ describe('API Entities normalizer', () => { expect(parsedPost).to.have.property('type', 'status') expect(parsedRepeat).to.have.property('type', 'retweet') expect(parsedRepeat).to.have.property('retweeted_status') - expect(parsedRepeat).to.have.deep.property('retweeted_status.id', 'deadbeef') + expect(parsedRepeat).to.have.nested.property('retweeted_status.id', 'deadbeef') }) it('sets nsfw for statuses with the #nsfw tag', () => { @@ -229,7 +229,7 @@ describe('API Entities normalizer', () => { expect(parsedPost).to.have.property('type', 'status') expect(parsedRepeat).to.have.property('type', 'retweet') expect(parsedRepeat).to.have.property('retweeted_status') - expect(parsedRepeat).to.have.deep.property('retweeted_status.id', 'deadbeef') + expect(parsedRepeat).to.have.nested.property('retweeted_status.id', 'deadbeef') }) }) }) @@ -269,7 +269,8 @@ describe('API Entities normalizer', () => { it('converts IDN to unicode and marks it as internatonal', () => { const user = makeMockUserMasto({ acct: 'lain@xn--lin-6cd.com' }) - expect(parseUser(user)).to.have.property('screen_name_ui').that.equal('lain@🌏lаin.com') + expect(parseUser(user)).to.have.property('screen_name_ui').that.equal('lain@lаin.com') + expect(parseUser(user)).to.have.property('screen_name_ui_contains_non_ascii').that.equal(true) }) }) @@ -284,9 +285,9 @@ describe('API Entities normalizer', () => { }) expect(parseNotification(notif)).to.have.property('id', 123) expect(parseNotification(notif)).to.have.property('seen', false) - expect(parseNotification(notif)).to.have.deep.property('status.id', '444') - expect(parseNotification(notif)).to.have.deep.property('action.id', '444') - expect(parseNotification(notif)).to.have.deep.property('from_profile.id', 'spurdo') + expect(parseNotification(notif)).to.have.nested.property('status.id', '444') + expect(parseNotification(notif)).to.have.nested.property('action.id', '444') + expect(parseNotification(notif)).to.have.nested.property('from_profile.id', 'spurdo') }) it('correctly normalizes favorite notifications', () => { @@ -303,9 +304,9 @@ describe('API Entities normalizer', () => { expect(parseNotification(notif)).to.have.property('id', 123) expect(parseNotification(notif)).to.have.property('type', 'like') expect(parseNotification(notif)).to.have.property('seen', true) - expect(parseNotification(notif)).to.have.deep.property('status.id', '4412') - expect(parseNotification(notif)).to.have.deep.property('action.id', '444') - expect(parseNotification(notif)).to.have.deep.property('from_profile.id', 'spurdo') + expect(parseNotification(notif)).to.have.nested.property('status.id', '4412') + expect(parseNotification(notif)).to.have.nested.property('action.id', '444') + expect(parseNotification(notif)).to.have.nested.property('from_profile.id', 'spurdo') }) }) @@ -314,8 +315,8 @@ describe('API Entities normalizer', () => { const linkHeader = '<https://example.com/api/v1/notifications?max_id=861676>; rel="next", <https://example.com/api/v1/notifications?min_id=861741>; rel="prev"' const result = parseLinkHeaderPagination(linkHeader) expect(result).to.eql({ - 'maxId': 861676, - 'minId': 861741 + maxId: 861676, + minId: 861741 }) }) @@ -323,8 +324,8 @@ describe('API Entities normalizer', () => { const linkHeader = '<http://example.com/api/v1/timelines/home?max_id=9waQx5IIS48qVue2Ai>; rel="next", <http://example.com/api/v1/timelines/home?min_id=9wi61nIPnfn674xgie>; rel="prev"' const result = parseLinkHeaderPagination(linkHeader, { flakeId: true }) expect(result).to.eql({ - 'maxId': '9waQx5IIS48qVue2Ai', - 'minId': '9wi61nIPnfn674xgie' + maxId: '9waQx5IIS48qVue2Ai', + minId: '9wi61nIPnfn674xgie' }) }) }) diff --git a/test/unit/specs/services/file_size_format/file_size_format.spec.js b/test/unit/specs/services/file_size_format/file_size_format.spec.js @@ -25,8 +25,8 @@ describe('fileSizeFormat', () => { } ] - var res = [] - for (var value in values) { + const res = [] + for (const value in values) { res.push(fileSizeFormatService.fileSizeFormat(values[value])) } expect(res).to.eql(expected) diff --git a/test/unit/specs/services/theme_data/sanity_checks.spec.js b/test/unit/specs/services/theme_data/sanity_checks.spec.js @@ -6,7 +6,7 @@ const checkColors = (output) => { expect(v, key).to.be.an('object') expect(v, key).to.include.all.keys('r', 'g', 'b') 'rgba'.split('').forEach(k => { - if ((k === 'a' && v.hasOwnProperty('a')) || k !== 'a') { + if ((k === 'a' && Object.prototype.hasOwnProperty.call(v, 'a')) || k !== 'a') { expect(v[k], key + '.' + k).to.be.a('number') expect(v[k], key + '.' + k).to.be.least(0) expect(v[k], key + '.' + k).to.be.most(k === 'a' ? 1 : 255) diff --git a/yarn.lock b/yarn.lock @@ -2,353 +2,606 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0": +"@ampproject/remapping@^2.1.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34" + integrity sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg== + dependencies: + "@jridgewell/trace-mapping" "^0.3.0" + +"@babel/code-frame@7.0.0", "@babel/code-frame@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" + integrity sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA== dependencies: "@babel/highlight" "^7.0.0" -"@babel/code-frame@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" - integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== - dependencies: - "@babel/highlight" "^7.10.4" - -"@babel/code-frame@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" - integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw== - dependencies: - "@babel/highlight" "^7.0.0" +"@babel/code-frame@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== + dependencies: + "@babel/highlight" "^7.16.7" + +"@babel/code-frame@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" + integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== + dependencies: + "@babel/highlight" "^7.18.6" + +"@babel/compat-data@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.7.tgz#078d8b833fbbcc95286613be8c716cef2b519fa2" + integrity sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ== + +"@babel/compat-data@^7.18.6", "@babel/compat-data@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d" + integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ== + +"@babel/compat-data@^7.19.4", "@babel/compat-data@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.0.tgz#9b61938c5f688212c7b9ae363a819df7d29d4093" + integrity sha512-Gt9jszFJYq7qzXVK4slhc6NzJXnOVmRECWcVjF/T23rNXD9NtWQ0W3qxdg+p9wWIB+VQw3GYV/U2Ha9bRTfs4w== + +"@babel/core@7.19.6": + version "7.19.6" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.19.6.tgz#7122ae4f5c5a37c0946c066149abd8e75f81540f" + integrity sha512-D2Ue4KHpc6Ys2+AxpIx1BZ8+UegLLLE2p3KJEuJRKmokHOtl49jQ5ny1773KsGLZs8MQvBidAF6yWUJxRqtKtg== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.19.6" + "@babel/helper-compilation-targets" "^7.19.3" + "@babel/helper-module-transforms" "^7.19.6" + "@babel/helpers" "^7.19.4" + "@babel/parser" "^7.19.6" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.19.6" + "@babel/types" "^7.19.4" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.1" + semver "^6.3.0" -"@babel/core@>=7.9.0": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.10.5.tgz#1f15e2cca8ad9a1d78a38ddba612f5e7cdbbd330" - integrity sha512-O34LQooYVDXPl7QWCdW9p4NR+QlzOr7xShPPJz8GsuCU3/8ua/wqTr7gmnxXv+WBESiGU/G5s16i6tUvHkNb+w== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.10.5" - "@babel/helper-module-transforms" "^7.10.5" - "@babel/helpers" "^7.10.4" - "@babel/parser" "^7.10.5" - "@babel/template" "^7.10.4" - "@babel/traverse" "^7.10.5" - "@babel/types" "^7.10.5" +"@babel/core@^7.12.3": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.10.tgz#39ad504991d77f1f3da91be0b8b949a5bc466fb8" + integrity sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.18.10" + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-module-transforms" "^7.18.9" + "@babel/helpers" "^7.18.9" + "@babel/parser" "^7.18.10" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.18.10" + "@babel/types" "^7.18.10" convert-source-map "^1.7.0" debug "^4.1.0" - gensync "^1.0.0-beta.1" - json5 "^2.1.2" - lodash "^4.17.19" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.1" + semver "^6.3.0" -"@babel/core@^7.7.5": - version "7.7.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.7.5.tgz#ae1323cd035b5160293307f50647e83f8ba62f7e" - integrity sha512-M42+ScN4+1S9iB6f+TL7QBpoQETxbclx+KNoKJABghnKYE+fMzSGqst0BZJc8CpI625bwPwYgUyRvxZ+0mZzpw== - dependencies: - "@babel/code-frame" "^7.5.5" - "@babel/generator" "^7.7.4" - "@babel/helpers" "^7.7.4" - "@babel/parser" "^7.7.5" - "@babel/template" "^7.7.4" - "@babel/traverse" "^7.7.4" - "@babel/types" "^7.7.4" +"@babel/core@^7.17.9": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.6.tgz#54a107a3c298aee3fe5e1947a6464b9b6faca03d" + integrity sha512-cQbWBpxcbbs/IUredIPkHiAGULLV8iwgNRMFzvbhEXISp4f3rUUXE5+TIw6KwUWUR3DwyI6gmBRnmAtYaWehwQ== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.18.6" + "@babel/helper-compilation-targets" "^7.18.6" + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helpers" "^7.18.6" + "@babel/parser" "^7.18.6" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.6" + "@babel/types" "^7.18.6" convert-source-map "^1.7.0" debug "^4.1.0" - json5 "^2.1.0" - lodash "^4.17.13" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.1" + semver "^6.3.0" -"@babel/generator@^7.10.5": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.5.tgz#1b903554bc8c583ee8d25f1e8969732e6b829a69" - integrity sha512-3vXxr3FEW7E7lJZiWQ3bM4+v/Vyr9C+hpolQ8BGFr9Y8Ri2tFLWTixmwKBafDujO1WVah4fhZBeU1bieKdghig== +"@babel/eslint-parser@7.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.19.1.tgz#4f68f6b0825489e00a24b41b6a1ae35414ecd2f4" + integrity sha512-AqNf2QWt1rtu2/1rLswy6CDP7H9Oh3mMhk177Y67Rg8d7RD9WfOLLv8CGn6tisFvS2htm86yIe1yLF6I1UDaGQ== dependencies: - "@babel/types" "^7.10.5" - jsesc "^2.5.1" - source-map "^0.5.0" + "@nicolo-ribaudo/eslint-scope-5-internals" "5.1.1-v1" + eslint-visitor-keys "^2.1.0" + semver "^6.3.0" -"@babel/generator@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.7.4.tgz#db651e2840ca9aa66f327dcec1dc5f5fa9611369" - integrity sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg== +"@babel/generator@^7.17.3": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.7.tgz#8da2599beb4a86194a3b24df6c085931d9ee45ad" + integrity sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w== dependencies: - "@babel/types" "^7.7.4" + "@babel/types" "^7.17.0" jsesc "^2.5.1" - lodash "^4.17.13" source-map "^0.5.0" -"@babel/helper-annotate-as-pure@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.7.4.tgz#bb3faf1e74b74bd547e867e48f551fa6b098b6ce" - integrity sha512-2BQmQgECKzYKFPpiycoF9tlb5HA4lrVyAmLLVK177EcQAqjVLciUb2/R+n1boQ9y5ENV3uz2ZqiNw7QMBBw1Og== +"@babel/generator@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.10.tgz#794f328bfabdcbaf0ebf9bf91b5b57b61fa77a2a" + integrity sha512-0+sW7e3HjQbiHbj1NeU/vN8ornohYlacAfZIaXhdoGweQqgcNy69COVciYYqEXJ/v+9OBA7Frxm4CVAuNqKeNA== dependencies: - "@babel/types" "^7.7.4" + "@babel/types" "^7.18.10" + "@jridgewell/gen-mapping" "^0.3.2" + jsesc "^2.5.1" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.7.4.tgz#5f73f2b28580e224b5b9bd03146a4015d6217f5f" - integrity sha512-Biq/d/WtvfftWZ9Uf39hbPBYDUo986m5Bb4zhkeYDGUllF43D+nUe5M6Vuo6/8JDK/0YX/uBdeoQpyaNhNugZQ== +"@babel/generator@^7.18.6", "@babel/generator@^7.18.7": + version "7.18.7" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.7.tgz#2aa78da3c05aadfc82dbac16c99552fc802284bd" + integrity sha512-shck+7VLlY72a2w9c3zYWuE1pwOKEiQHV7GTUbSnhyl5eu3i04t30tBY82ZRWrDfo3gkakCFtevExnxbkf2a3A== dependencies: - "@babel/helper-explode-assignable-expression" "^7.7.4" - "@babel/types" "^7.7.4" + "@babel/types" "^7.18.7" + "@jridgewell/gen-mapping" "^0.3.2" + jsesc "^2.5.1" -"@babel/helper-call-delegate@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.7.4.tgz#621b83e596722b50c0066f9dc37d3232e461b801" - integrity sha512-8JH9/B7J7tCYJ2PpWVpw9JhPuEVHztagNVuQAFBVFYluRMlpG7F1CgKEgGeL6KFqcsIa92ZYVj6DSc0XwmN1ZA== +"@babel/generator@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.9.tgz#68337e9ea8044d6ddc690fb29acae39359cca0a5" + integrity sha512-wt5Naw6lJrL1/SGkipMiFxJjtyczUWTP38deiP1PO60HsBjDeKk08CGC3S8iVuvf0FmTdgKwU1KIXzSKL1G0Ug== dependencies: - "@babel/helper-hoist-variables" "^7.7.4" - "@babel/traverse" "^7.7.4" - "@babel/types" "^7.7.4" + "@babel/types" "^7.18.9" + "@jridgewell/gen-mapping" "^0.3.2" + jsesc "^2.5.1" -"@babel/helper-create-regexp-features-plugin@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.7.4.tgz#6d5762359fd34f4da1500e4cff9955b5299aaf59" - integrity sha512-Mt+jBKaxL0zfOIWrfQpnfYCN7/rS6GKx6CCCfuoqVVd+17R8zNDlzVYmIi9qyb2wOk002NsmSTDymkIygDUH7A== +"@babel/generator@^7.19.6", "@babel/generator@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.0.tgz#0bfc5379e0efb05ca6092091261fcdf7ec36249d" + integrity sha512-GUPcXxWibClgmYJuIwC2Bc2Lg+8b9VjaJ+HlNdACEVt+Wlr1eoU1OPZjZRm7Hzl0gaTsUZNQfeihvZJhG7oc3w== dependencies: - "@babel/helper-regex" "^7.4.4" - regexpu-core "^4.6.0" + "@babel/types" "^7.20.0" + "@jridgewell/gen-mapping" "^0.3.2" + jsesc "^2.5.1" -"@babel/helper-define-map@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.7.4.tgz#2841bf92eb8bd9c906851546fe6b9d45e162f176" - integrity sha512-v5LorqOa0nVQUvAUTUF3KPastvUt/HzByXNamKQ6RdJRTV7j8rLL+WB5C/MzzWAwOomxDhYFb1wLLxHqox86lg== +"@babel/helper-annotate-as-pure@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" + integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw== dependencies: - "@babel/helper-function-name" "^7.7.4" - "@babel/types" "^7.7.4" - lodash "^4.17.13" + "@babel/types" "^7.16.7" -"@babel/helper-explode-assignable-expression@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.7.4.tgz#fa700878e008d85dc51ba43e9fb835cddfe05c84" - integrity sha512-2/SicuFrNSXsZNBxe5UGdLr+HZg+raWBLE9vC98bdYOKX/U6PY0mdGlYUJdtTDPSU0Lw0PNbKKDpwYHJLn2jLg== +"@babel/helper-annotate-as-pure@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" + integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== dependencies: - "@babel/traverse" "^7.7.4" - "@babel/types" "^7.7.4" + "@babel/types" "^7.18.6" -"@babel/helper-function-name@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a" - integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ== +"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.6.tgz#f14d640ed1ee9246fb33b8255f08353acfe70e6a" + integrity sha512-KT10c1oWEpmrIRYnthbzHgoOf6B+Xd6a5yhdbNtdhtG7aO1or5HViuf1TQR36xY/QprXA5nvxO6nAjhJ4y38jw== dependencies: - "@babel/helper-get-function-arity" "^7.10.4" - "@babel/template" "^7.10.4" - "@babel/types" "^7.10.4" + "@babel/helper-explode-assignable-expression" "^7.18.6" + "@babel/types" "^7.18.6" -"@babel/helper-function-name@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz#ab6e041e7135d436d8f0a3eca15de5b67a341a2e" - integrity sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ== +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz#69e64f57b524cde3e5ff6cc5a9f4a387ee5563bf" + integrity sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg== dependencies: - "@babel/helper-get-function-arity" "^7.7.4" - "@babel/template" "^7.7.4" - "@babel/types" "^7.7.4" + "@babel/compat-data" "^7.18.8" + "@babel/helper-validator-option" "^7.18.6" + browserslist "^4.20.2" + semver "^6.3.0" -"@babel/helper-get-function-arity@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2" - integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A== +"@babel/helper-compilation-targets@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.6.tgz#18d35bfb9f83b1293c22c55b3d576c1315b6ed96" + integrity sha512-vFjbfhNCzqdeAtZflUFrG5YIFqGTqsctrtkZ1D/NB0mDW9TwW3GmmUepYY4G9wCET5rY5ugz4OGTcLd614IzQg== dependencies: - "@babel/types" "^7.10.4" + "@babel/compat-data" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + browserslist "^4.20.2" + semver "^6.3.0" -"@babel/helper-get-function-arity@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz#cb46348d2f8808e632f0ab048172130e636005f0" - integrity sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA== +"@babel/helper-compilation-targets@^7.19.0", "@babel/helper-compilation-targets@^7.19.3": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz#6bf5374d424e1b3922822f1d9bdaa43b1a139d0a" + integrity sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ== dependencies: - "@babel/types" "^7.7.4" + "@babel/compat-data" "^7.20.0" + "@babel/helper-validator-option" "^7.18.6" + browserslist "^4.21.3" + semver "^6.3.0" -"@babel/helper-hoist-variables@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.7.4.tgz#612384e3d823fdfaaf9fce31550fe5d4db0f3d12" - integrity sha512-wQC4xyvc1Jo/FnLirL6CEgPgPCa8M74tOdjWpRhQYapz5JC7u3NYU1zCVoVAGCE3EaIP9T1A3iW0WLJ+reZlpQ== - dependencies: - "@babel/types" "^7.7.4" +"@babel/helper-create-class-features-plugin@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.6.tgz#6f15f8459f3b523b39e00a99982e2c040871ed72" + integrity sha512-YfDzdnoxHGV8CzqHGyCbFvXg5QESPFkXlHtvdCkesLjjVMT2Adxe4FGUR5ChIb3DxSaXO12iIOCWoXdsUVwnqw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.6" + "@babel/helper-function-name" "^7.18.6" + "@babel/helper-member-expression-to-functions" "^7.18.6" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + +"@babel/helper-create-regexp-features-plugin@^7.16.7": + version "7.17.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz#1dcc7d40ba0c6b6b25618997c5dbfd310f186fe1" + integrity sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + regexpu-core "^5.0.1" + +"@babel/helper-create-regexp-features-plugin@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz#3e35f4e04acbbf25f1b3534a657610a000543d3c" + integrity sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + regexpu-core "^5.1.0" + +"@babel/helper-create-regexp-features-plugin@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz#7976aca61c0984202baca73d84e2337a5424a41b" + integrity sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + regexpu-core "^5.1.0" + +"@babel/helper-define-polyfill-provider@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz#8612e55be5d51f0cd1f36b4a5a83924e89884b7a" + integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww== + dependencies: + "@babel/helper-compilation-targets" "^7.17.7" + "@babel/helper-plugin-utils" "^7.16.7" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" -"@babel/helper-member-expression-to-functions@^7.10.4": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.5.tgz#172f56e7a63e78112f3a04055f24365af702e7ee" - integrity sha512-HiqJpYD5+WopCXIAbQDG0zye5XYVvcO9w/DHp5GsaGkRUaamLj2bEtu6i8rnGGprAhHM3qidCMgp71HF4endhA== +"@babel/helper-environment-visitor@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" + integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== dependencies: - "@babel/types" "^7.10.5" + "@babel/types" "^7.16.7" -"@babel/helper-member-expression-to-functions@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.7.4.tgz#356438e2569df7321a8326644d4b790d2122cb74" - integrity sha512-9KcA1X2E3OjXl/ykfMMInBK+uVdfIVakVe7W7Lg3wfXUNyS3Q1HWLFRwZIjhqiCGbslummPDnmb7vIekS0C1vw== - dependencies: - "@babel/types" "^7.7.4" +"@babel/helper-environment-visitor@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.6.tgz#b7eee2b5b9d70602e59d1a6cad7dd24de7ca6cd7" + integrity sha512-8n6gSfn2baOY+qlp+VSzsosjCVGFqWKmDF0cCWOybh52Dw3SEyoWR1KrhMJASjLwIEkkAufZ0xvr+SxLHSpy2Q== -"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.7.4.tgz#e5a92529f8888bf319a6376abfbd1cebc491ad91" - integrity sha512-dGcrX6K9l8258WFjyDLJwuVKxR4XZfU0/vTUgOQYWEnRD8mgr+p4d6fCUMq/ys0h4CCt/S5JhbvtyErjWouAUQ== - dependencies: - "@babel/types" "^7.7.4" +"@babel/helper-environment-visitor@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" + integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== -"@babel/helper-module-imports@^7.0.0-beta.49": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz#96081b7111e486da4d2cd971ad1a4fe216cc2e3d" +"@babel/helper-explode-assignable-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" + integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== dependencies: - "@babel/types" "^7.0.0" - -"@babel/helper-module-imports@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620" - integrity sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw== - dependencies: - "@babel/types" "^7.10.4" - -"@babel/helper-module-transforms@^7.10.5": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.10.5.tgz#120c271c0b3353673fcdfd8c053db3c544a260d6" - integrity sha512-4P+CWMJ6/j1W915ITJaUkadLObmCRRSC234uctJfn/vHrsLNxsR8dwlcXv9ZhJWzl77awf+mWXSZEKt5t0OnlA== - dependencies: - "@babel/helper-module-imports" "^7.10.4" - "@babel/helper-replace-supers" "^7.10.4" - "@babel/helper-simple-access" "^7.10.4" - "@babel/helper-split-export-declaration" "^7.10.4" - "@babel/template" "^7.10.4" - "@babel/types" "^7.10.5" - lodash "^4.17.19" - -"@babel/helper-module-transforms@^7.7.4", "@babel/helper-module-transforms@^7.7.5": - version "7.7.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.7.5.tgz#d044da7ffd91ec967db25cd6748f704b6b244835" - integrity sha512-A7pSxyJf1gN5qXVcidwLWydjftUN878VkalhXX5iQDuGyiGK3sOrrKKHF4/A4fwHtnsotv/NipwAeLzY4KQPvw== - dependencies: - "@babel/helper-module-imports" "^7.7.4" - "@babel/helper-simple-access" "^7.7.4" - "@babel/helper-split-export-declaration" "^7.7.4" - "@babel/template" "^7.7.4" - "@babel/types" "^7.7.4" - lodash "^4.17.13" + "@babel/types" "^7.18.6" -"@babel/helper-optimise-call-expression@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673" - integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg== +"@babel/helper-function-name@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz#f1ec51551fb1c8956bc8dd95f38523b6cf375f8f" + integrity sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA== dependencies: - "@babel/types" "^7.10.4" + "@babel/helper-get-function-arity" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/types" "^7.16.7" -"@babel/helper-optimise-call-expression@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.7.4.tgz#034af31370d2995242aa4df402c3b7794b2dcdf2" - integrity sha512-VB7gWZ2fDkSuqW6b1AKXkJWO5NyNI3bFL/kK79/30moK57blr6NbH8xcl2XcKCwOmJosftWunZqfO84IGq3ZZg== +"@babel/helper-function-name@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.6.tgz#8334fecb0afba66e6d87a7e8c6bb7fed79926b83" + integrity sha512-0mWMxV1aC97dhjCah5U5Ua7668r5ZmSC2DLfH2EZnf9c3/dHZKiFa5pRLMH5tjSl471tY6496ZWk/kjNONBxhw== dependencies: - "@babel/types" "^7.7.4" - -"@babel/helper-plugin-utils@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250" - integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA== + "@babel/template" "^7.18.6" + "@babel/types" "^7.18.6" -"@babel/helper-regex@^7.0.0", "@babel/helper-regex@^7.4.4": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.5.5.tgz#0aa6824f7100a2e0e89c1527c23936c152cab351" - integrity sha512-CkCYQLkfkiugbRDO8eZn6lRuR8kzZoGXCg3149iTk5se7g6qykSpy3+hELSwquhu+TgHn8nkLiBwHvNX8Hofcw== +"@babel/helper-function-name@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0" + integrity sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A== dependencies: - lodash "^4.17.13" + "@babel/template" "^7.18.6" + "@babel/types" "^7.18.9" -"@babel/helper-remap-async-to-generator@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.7.4.tgz#c68c2407350d9af0e061ed6726afb4fff16d0234" - integrity sha512-Sk4xmtVdM9sA/jCI80f+KS+Md+ZHIpjuqmYPk1M7F/upHou5e4ReYmExAiu6PVe65BhJPZA2CY9x9k4BqE5klw== +"@babel/helper-function-name@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" + integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== dependencies: - "@babel/helper-annotate-as-pure" "^7.7.4" - "@babel/helper-wrap-function" "^7.7.4" - "@babel/template" "^7.7.4" - "@babel/traverse" "^7.7.4" - "@babel/types" "^7.7.4" + "@babel/template" "^7.18.10" + "@babel/types" "^7.19.0" -"@babel/helper-replace-supers@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf" - integrity sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A== +"@babel/helper-get-function-arity@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419" + integrity sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw== dependencies: - "@babel/helper-member-expression-to-functions" "^7.10.4" - "@babel/helper-optimise-call-expression" "^7.10.4" - "@babel/traverse" "^7.10.4" - "@babel/types" "^7.10.4" + "@babel/types" "^7.16.7" -"@babel/helper-replace-supers@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.7.4.tgz#3c881a6a6a7571275a72d82e6107126ec9e2cdd2" - integrity sha512-pP0tfgg9hsZWo5ZboYGuBn/bbYT/hdLPVSS4NMmiRJdwWhP0IznPwN9AE1JwyGsjSPLC364I0Qh5p+EPkGPNpg== +"@babel/helper-hoist-variables@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" + integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== dependencies: - "@babel/helper-member-expression-to-functions" "^7.7.4" - "@babel/helper-optimise-call-expression" "^7.7.4" - "@babel/traverse" "^7.7.4" - "@babel/types" "^7.7.4" + "@babel/types" "^7.16.7" -"@babel/helper-simple-access@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461" - integrity sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw== +"@babel/helper-hoist-variables@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" + integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== dependencies: - "@babel/template" "^7.10.4" - "@babel/types" "^7.10.4" + "@babel/types" "^7.18.6" -"@babel/helper-simple-access@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.7.4.tgz#a169a0adb1b5f418cfc19f22586b2ebf58a9a294" - integrity sha512-zK7THeEXfan7UlWsG2A6CI/L9jVnI5+xxKZOdej39Y0YtDYKx9raHk5F2EtK9K8DHRTihYwg20ADt9S36GR78A== +"@babel/helper-member-expression-to-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.6.tgz#44802d7d602c285e1692db0bad9396d007be2afc" + integrity sha512-CeHxqwwipekotzPDUuJOfIMtcIHBuc7WAzLmTYWctVigqS5RktNMQ5bEwQSuGewzYnCtTWa3BARXeiLxDTv+Ng== dependencies: - "@babel/template" "^7.7.4" - "@babel/types" "^7.7.4" + "@babel/types" "^7.18.6" -"@babel/helper-split-export-declaration@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz#2c70576eaa3b5609b24cb99db2888cc3fc4251d1" - integrity sha512-pySBTeoUff56fL5CBU2hWm9TesA4r/rOkI9DyJLvvgz09MB9YtfIYe3iBriVaYNaPe+Alua0vBIOVOLs2buWhg== +"@babel/helper-member-expression-to-functions@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815" + integrity sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg== dependencies: - "@babel/types" "^7.10.4" + "@babel/types" "^7.18.9" -"@babel/helper-split-export-declaration@^7.7.4": +"@babel/helper-module-imports@^7.0.0": version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz#57292af60443c4a3622cf74040ddc28e68336fd8" - integrity sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug== + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.7.4.tgz#e5a92529f8888bf319a6376abfbd1cebc491ad91" + integrity sha512-dGcrX6K9l8258WFjyDLJwuVKxR4XZfU0/vTUgOQYWEnRD8mgr+p4d6fCUMq/ys0h4CCt/S5JhbvtyErjWouAUQ== dependencies: "@babel/types" "^7.7.4" -"@babel/helper-validator-identifier@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" - integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== - -"@babel/helper-wrap-function@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.7.4.tgz#37ab7fed5150e22d9d7266e830072c0cdd8baace" - integrity sha512-VsfzZt6wmsocOaVU0OokwrIytHND55yvyT4BPB9AIIgwr8+x7617hetdJTsuGwygN5RC6mxA9EJztTjuwm2ofg== +"@babel/helper-module-imports@^7.0.0-beta.49": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz#96081b7111e486da4d2cd971ad1a4fe216cc2e3d" dependencies: - "@babel/helper-function-name" "^7.7.4" - "@babel/template" "^7.7.4" - "@babel/traverse" "^7.7.4" - "@babel/types" "^7.7.4" + "@babel/types" "^7.0.0" -"@babel/helpers@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.4.tgz#2abeb0d721aff7c0a97376b9e1f6f65d7a475044" - integrity sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA== - dependencies: - "@babel/template" "^7.10.4" - "@babel/traverse" "^7.10.4" - "@babel/types" "^7.10.4" +"@babel/helper-module-imports@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" + integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-module-transforms@^7.18.6": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.8.tgz#4f8408afead0188cfa48672f9d0e5787b61778c8" + integrity sha512-che3jvZwIcZxrwh63VfnFTUzcAM9v/lznYkkRxIBGMPt1SudOKHAEec0SIRCfiuIzTcF7VGj/CaTT6gY4eWxvA== + dependencies: + "@babel/helper-environment-visitor" "^7.18.6" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.18.6" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.8" + "@babel/types" "^7.18.8" + +"@babel/helper-module-transforms@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz#5a1079c005135ed627442df31a42887e80fcb712" + integrity sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.18.6" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.9" + "@babel/types" "^7.18.9" + +"@babel/helper-module-transforms@^7.19.6": + version "7.19.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.19.6.tgz#6c52cc3ac63b70952d33ee987cbee1c9368b533f" + integrity sha512-fCmcfQo/KYr/VXXDIyd3CBGZ6AFhPFy1TfSEJ+PilGVlQT6jcbqtHAM4C1EciRqMza7/TpOUZliuSH+U6HAhJw== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.19.4" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.19.6" + "@babel/types" "^7.19.4" + +"@babel/helper-optimise-call-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" + integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== + dependencies: + "@babel/types" "^7.18.6" -"@babel/helpers@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.7.4.tgz#62c215b9e6c712dadc15a9a0dcab76c92a940302" - integrity sha512-ak5NGZGJ6LV85Q1Zc9gn2n+ayXOizryhjSUBTdu5ih1tlVCJeuQENzc4ItyCVhINVXvIT/ZQ4mheGIsfBkpskg== - dependencies: - "@babel/template" "^7.7.4" - "@babel/traverse" "^7.7.4" - "@babel/types" "^7.7.4" +"@babel/helper-plugin-utils@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250" + integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA== + +"@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" + integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== + +"@babel/helper-plugin-utils@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.6.tgz#9448974dd4fb1d80fefe72e8a0af37809cd30d6d" + integrity sha512-gvZnm1YAAxh13eJdkb9EWHBnF3eAub3XTLCZEehHT2kWxiKVRL64+ae5Y6Ivne0mVHmMYKT+xWgZO+gQhuLUBg== + +"@babel/helper-plugin-utils@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f" + integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w== + +"@babel/helper-plugin-utils@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz#4796bb14961521f0f8715990bee2fb6e51ce21bf" + integrity sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw== + +"@babel/helper-remap-async-to-generator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.6.tgz#fa1f81acd19daee9d73de297c0308783cd3cfc23" + integrity sha512-z5wbmV55TveUPZlCLZvxWHtrjuJd+8inFhk7DG0WW87/oJuGDcjDiu7HIvGcpf5464L6xKCg3vNkmlVVz9hwyQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.6" + "@babel/helper-wrap-function" "^7.18.6" + "@babel/types" "^7.18.6" + +"@babel/helper-remap-async-to-generator@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" + integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-wrap-function" "^7.18.9" + "@babel/types" "^7.18.9" + +"@babel/helper-replace-supers@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.6.tgz#efedf51cfccea7b7b8c0f00002ab317e7abfe420" + integrity sha512-fTf7zoXnUGl9gF25fXCWE26t7Tvtyn6H4hkLSYhATwJvw2uYxd3aoXplMSe0g9XbwK7bmxNes7+FGO0rB/xC0g== + dependencies: + "@babel/helper-environment-visitor" "^7.18.6" + "@babel/helper-member-expression-to-functions" "^7.18.6" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/traverse" "^7.18.6" + "@babel/types" "^7.18.6" + +"@babel/helper-replace-supers@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz#1092e002feca980fbbb0bd4d51b74a65c6a500e6" + integrity sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/traverse" "^7.18.9" + "@babel/types" "^7.18.9" + +"@babel/helper-simple-access@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea" + integrity sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-simple-access@^7.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.19.4.tgz#be553f4951ac6352df2567f7daa19a0ee15668e7" + integrity sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg== + dependencies: + "@babel/types" "^7.19.4" + +"@babel/helper-skip-transparent-expression-wrappers@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz#778d87b3a758d90b471e7b9918f34a9a02eb5818" + integrity sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw== + dependencies: + "@babel/types" "^7.18.9" + +"@babel/helper-split-export-declaration@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" + integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-split-export-declaration@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" + integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-string-parser@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56" + integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== + +"@babel/helper-string-parser@^7.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" + integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== + +"@babel/helper-validator-identifier@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== + +"@babel/helper-validator-identifier@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" + integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== + +"@babel/helper-validator-identifier@^7.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== + +"@babel/helper-validator-option@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" + integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== + +"@babel/helper-wrap-function@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.18.6.tgz#ec44ea4ad9d8988b90c3e465ba2382f4de81a073" + integrity sha512-I5/LZfozwMNbwr/b1vhhuYD+J/mU+gfGAj5td7l5Rv9WYmH6i3Om69WGKNmlIpsVW/mF6O5bvTKbvDQZVgjqOw== + dependencies: + "@babel/helper-function-name" "^7.18.6" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.6" + "@babel/types" "^7.18.6" + +"@babel/helper-wrap-function@^7.18.9": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.18.10.tgz#a7fcd3ab9b1be4c9b52cf7d7fdc1e88c2ce93396" + integrity sha512-95NLBP59VWdfK2lyLKe6eTMq9xg+yWKzxzxbJ1wcYNi1Auz200+83fMDADjRxBvc2QQor5zja2yTQzXGhk2GtQ== + dependencies: + "@babel/helper-function-name" "^7.18.9" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.18.10" + "@babel/types" "^7.18.10" + +"@babel/helpers@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.6.tgz#4c966140eaa1fcaa3d5a8c09d7db61077d4debfd" + integrity sha512-vzSiiqbQOghPngUYt/zWGvK3LAsPhz55vc9XNN0xAl2gV4ieShI2OQli5duxWHD+72PZPTKAcfcZDE1Cwc5zsQ== + dependencies: + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.6" + "@babel/types" "^7.18.6" + +"@babel/helpers@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.9.tgz#4bef3b893f253a1eced04516824ede94dcfe7ff9" + integrity sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ== + dependencies: + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.9" + "@babel/types" "^7.18.9" + +"@babel/helpers@^7.19.4": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.0.tgz#27c8ffa8cc32a2ed3762fba48886e7654dbcf77f" + integrity sha512-aGMjYraN0zosCEthoGLdqot1oRsmxVTQRHadsUPz5QM44Zej2PYRz7XiDE7GqnkZnNtLbOuxqoZw42vkU7+XEQ== + dependencies: + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.0" + "@babel/types" "^7.20.0" "@babel/highlight@^7.0.0": version "7.0.0" @@ -358,499 +611,819 @@ esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/highlight@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" - integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== +"@babel/highlight@^7.16.7": + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" + integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== dependencies: - "@babel/helper-validator-identifier" "^7.10.4" + "@babel/helper-validator-identifier" "^7.16.7" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.10.4", "@babel/parser@^7.10.5": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.5.tgz#e7c6bf5a7deff957cec9f04b551e2762909d826b" - integrity sha512-wfryxy4bE1UivvQKSQDU4/X6dr+i8bctjUjj8Zyt3DQy7NtPizJXT8M52nqpNKL+nq2PW8lxk4ZqLj0fD4B4hQ== +"@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" + chalk "^2.0.0" + js-tokens "^4.0.0" -"@babel/parser@^7.7.4", "@babel/parser@^7.7.5": - version "7.7.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.7.5.tgz#cbf45321619ac12d83363fcf9c94bb67fa646d71" - integrity sha512-KNlOe9+/nk4i29g0VXgl8PEXIRms5xKLJeuZ6UptN0fHv+jDiriG+y94X6qAgWTR0h3KaoM1wK5G5h7MHFRSig== +"@babel/parser@^7.14.7": + version "7.18.11" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.11.tgz#68bb07ab3d380affa9a3f96728df07969645d2d9" + integrity sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ== + +"@babel/parser@^7.16.4": + version "7.17.8" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.8.tgz#2817fb9d885dd8132ea0f8eb615a6388cca1c240" + integrity sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ== + +"@babel/parser@^7.16.7", "@babel/parser@^7.17.3": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.7.tgz#fc19b645a5456c8d6fdb6cecd3c66c0173902800" + integrity sha512-bm3AQf45vR4gKggRfvJdYJ0gFLoCbsPxiFLSH6hTVYABptNHY6l9NrhnucVjQ/X+SPtLANT9lc0fFhikj+VBRA== + +"@babel/parser@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.10.tgz#94b5f8522356e69e8277276adf67ed280c90ecc1" + integrity sha512-TYk3OA0HKL6qNryUayb5UUEhM/rkOQozIBEA5ITXh5DWrSp0TlUQXMyZmnWxG/DizSWBeeQ0Zbc5z8UGaaqoeg== + +"@babel/parser@^7.18.6", "@babel/parser@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.8.tgz#822146080ac9c62dac0823bb3489622e0bc1cbdf" + integrity sha512-RSKRfYX20dyH+elbJK2uqAkVyucL+xXzhqlMD5/ZXx+dAAwpyB7HsvnHe/ZUGOF+xLr5Wx9/JoXVTj6BQE2/oA== + +"@babel/parser@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.9.tgz#f2dde0c682ccc264a9a8595efd030a5cc8fd2539" + integrity sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg== + +"@babel/parser@^7.19.6", "@babel/parser@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.0.tgz#b26133c888da4d79b0d3edcf42677bcadc783046" + integrity sha512-G9VgAhEaICnz8iiJeGJQyVl6J2nTjbW0xeisva0PK6XcKsga7BIaqm4ZF8Rg1Wbaqmy6znspNqhPaPkyukujzg== + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" + integrity sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz#a11af19aa373d68d561f08e0a57242350ed0ec50" + integrity sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + "@babel/plugin-proposal-optional-chaining" "^7.18.9" + +"@babel/plugin-proposal-async-generator-functions@^7.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.19.1.tgz#34f6f5174b688529342288cd264f80c9ea9fb4a7" + integrity sha512-0yu8vNATgLy4ivqMNBIwb1HebCelqN7YX8SL3FDXORv/RqT0zEEWUCH4GH44JsSrvCu6GqnAdR5EBFAPeNBB4Q== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/plugin-syntax-async-generators" "^7.8.4" + +"@babel/plugin-proposal-class-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" + integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-proposal-class-static-block@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz#8aa81d403ab72d3962fc06c26e222dacfc9b9020" + integrity sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + +"@babel/plugin-proposal-dynamic-import@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94" + integrity sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + +"@babel/plugin-proposal-export-namespace-from@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203" + integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-proposal-json-strings@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz#7e8788c1811c393aff762817e7dbf1ebd0c05f0b" + integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-json-strings" "^7.8.3" + +"@babel/plugin-proposal-logical-assignment-operators@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz#8148cbb350483bf6220af06fa6db3690e14b2e23" + integrity sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" + integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-proposal-numeric-separator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" + integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-proposal-object-rest-spread@^7.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.19.4.tgz#a8fc86e8180ff57290c91a75d83fe658189b642d" + integrity sha512-wHmj6LDxVDnL+3WhXteUBaoM1aVILZODAUjg11kHqG4cOlfgMQGxw6aCgvrXrmaJR3Bn14oZhImyCPZzRpC93Q== + dependencies: + "@babel/compat-data" "^7.19.4" + "@babel/helper-compilation-targets" "^7.19.3" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.18.8" -"@babel/plugin-proposal-async-generator-functions@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.7.4.tgz#0351c5ac0a9e927845fffd5b82af476947b7ce6d" - integrity sha512-1ypyZvGRXriY/QP668+s8sFr2mqinhkRDMPSQLNghCQE+GAkFtp+wkHVvg2+Hdki8gwP+NFzJBJ/N1BfzCCDEw== +"@babel/plugin-proposal-optional-catch-binding@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" + integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-remap-async-to-generator" "^7.7.4" - "@babel/plugin-syntax-async-generators" "^7.7.4" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-proposal-dynamic-import@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.7.4.tgz#dde64a7f127691758cbfed6cf70de0fa5879d52d" - integrity sha512-StH+nGAdO6qDB1l8sZ5UBV8AC3F2VW2I8Vfld73TMKyptMU9DY5YsJAS8U81+vEtxcH3Y/La0wG0btDrhpnhjQ== +"@babel/plugin-proposal-optional-chaining@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz#e8e8fe0723f2563960e4bf5e9690933691915993" + integrity sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-dynamic-import" "^7.7.4" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" -"@babel/plugin-proposal-json-strings@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.7.4.tgz#7700a6bfda771d8dc81973249eac416c6b4c697d" - integrity sha512-wQvt3akcBTfLU/wYoqm/ws7YOAQKu8EVJEvHip/mzkNtjaclQoCCIqKXFP5/eyfnfbQCDV3OLRIK3mIVyXuZlw== +"@babel/plugin-proposal-private-methods@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea" + integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-json-strings" "^7.7.4" + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-proposal-object-rest-spread@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.7.4.tgz#cc57849894a5c774214178c8ab64f6334ec8af71" - integrity sha512-rnpnZR3/iWKmiQyJ3LKJpSwLDcX/nSXhdLk4Aq/tXOApIvyu7qoabrige0ylsAJffaUC51WiBu209Q0U+86OWQ== +"@babel/plugin-proposal-private-property-in-object@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz#a64137b232f0aca3733a67eb1a144c192389c503" + integrity sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-object-rest-spread" "^7.7.4" + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" -"@babel/plugin-proposal-optional-catch-binding@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.7.4.tgz#ec21e8aeb09ec6711bc0a39ca49520abee1de379" - integrity sha512-DyM7U2bnsQerCQ+sejcTNZh8KQEUuC3ufzdnVnSiUv/qoGJp2Z3hanKL18KDhsBT5Wj6a7CMT5mdyCNJsEaA9w== +"@babel/plugin-proposal-unicode-property-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" + integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.7.4" + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-proposal-unicode-property-regex@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.7.4.tgz#7c239ccaf09470dbe1d453d50057460e84517ebb" - integrity sha512-cHgqHgYvffluZk85dJ02vloErm3Y6xtH+2noOBOJ2kXOJH3aVCDnj5eR/lVNlTnYu4hndAPJD3rTFjW3qee0PA== +"@babel/plugin-proposal-unicode-property-regex@^7.4.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz#635d18eb10c6214210ffc5ff4932552de08188a2" + integrity sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-create-regexp-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-syntax-async-generators@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.7.4.tgz#331aaf310a10c80c44a66b238b6e49132bd3c889" - integrity sha512-Li4+EjSpBgxcsmeEF8IFcfV/+yJGxHXDirDkEoyFjumuwbmfCVHUt0HuowD/iGM7OhIRyXJH9YXxqiH6N815+g== +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-dynamic-import@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.7.4.tgz#29ca3b4415abfe4a5ec381e903862ad1a54c3aec" - integrity sha512-jHQW0vbRGvwQNgyVxwDh4yuXu4bH1f5/EICJLAhl1SblLs2CDhrsmCk+v5XLdE9wxtAFRyxx+P//Iw+a5L/tTg== +"@babel/plugin-syntax-class-properties@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-syntax-json-strings@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.7.4.tgz#86e63f7d2e22f9e27129ac4e83ea989a382e86cc" - integrity sha512-QpGupahTQW1mHRXddMG5srgpHWqRLwJnJZKXTigB9RPFCCGbDGCgBeM/iC82ICXp414WeYx/tD54w7M2qRqTMg== +"@babel/plugin-syntax-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-jsx@^7.2.0": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.7.4.tgz#dab2b56a36fb6c3c222a1fbc71f7bf97f327a9ec" - integrity sha512-wuy6fiMe9y7HeZBWXYCGt2RGxZOj0BImZ9EyXJVnVGBKO/Br592rbR3rtIQn0eQhAk9vqaKP5n8tVqEFBQMfLg== +"@babel/plugin-syntax-dynamic-import@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-object-rest-spread@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.7.4.tgz#47cf220d19d6d0d7b154304701f468fc1cc6ff46" - integrity sha512-mObR+r+KZq0XhRVS2BrBKBpr5jqrqzlPvS9C9vuOf5ilSwzloAl7RPWLrgKdWS6IreaVrjHxTjtyqFiOisaCwg== +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-syntax-optional-catch-binding@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.7.4.tgz#a3e38f59f4b6233867b4a92dcb0ee05b2c334aa6" - integrity sha512-4ZSuzWgFxqHRE31Glu+fEr/MirNZOMYmD/0BhBWyLyOOQz/gTAl7QmWm2hX1QxEIXsr2vkdlwxIzTyiYRC4xcQ== +"@babel/plugin-syntax-import-assertions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz#cd6190500a4fa2fe31990a963ffab4b63e4505e4" + integrity sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-syntax-top-level-await@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.7.4.tgz#bd7d8fa7b9fee793a36e4027fd6dd1aa32f946da" - integrity sha512-wdsOw0MvkL1UIgiQ/IFr3ETcfv1xb8RMM0H9wbiDyLaJFyiDg5oZvDLCXosIXmFeIlweML5iOBXAkqddkYNizg== +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-transform-arrow-functions@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.7.4.tgz#76309bd578addd8aee3b379d809c802305a98a12" - integrity sha512-zUXy3e8jBNPiffmqkHRNDdZM2r8DWhCB7HhcoyZjiK1TxYEluLHAvQuYnTT+ARqRpabWqy/NHkO6e3MsYB5YfA== +"@babel/plugin-syntax-jsx@^7.0.0": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz#50b6571d13f764266a113d77c82b4a6508bbe665" + integrity sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-async-to-generator@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.7.4.tgz#694cbeae6d613a34ef0292713fa42fb45c4470ba" - integrity sha512-zpUTZphp5nHokuy8yLlyafxCJ0rSlFoSHypTUWgpdwoDXWQcseaect7cJ8Ppk6nunOM6+5rPMkod4OYKPR5MUg== +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== dependencies: - "@babel/helper-module-imports" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-remap-async-to-generator" "^7.7.4" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-block-scoped-functions@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.7.4.tgz#d0d9d5c269c78eaea76227ace214b8d01e4d837b" - integrity sha512-kqtQzwtKcpPclHYjLK//3lH8OFsCDuDJBaFhVwf8kqdnF6MN4l618UDlcA7TfRs3FayrHj+svYnSX8MC9zmUyQ== +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-transform-block-scoping@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.7.4.tgz#200aad0dcd6bb80372f94d9e628ea062c58bf224" - integrity sha512-2VBe9u0G+fDt9B5OV5DQH4KBf5DoiNkwFKOz0TCvBWvdAN2rOykCTkrL+jTLxfCAm76l9Qo5OqL7HBOx2dWggg== +"@babel/plugin-syntax-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - lodash "^4.17.13" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-classes@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.7.4.tgz#c92c14be0a1399e15df72667067a8f510c9400ec" - integrity sha512-sK1mjWat7K+buWRuImEzjNf68qrKcrddtpQo3swi9j7dUcG6y6R6+Di039QN2bD1dykeswlagupEmpOatFHHUg== +"@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== dependencies: - "@babel/helper-annotate-as-pure" "^7.7.4" - "@babel/helper-define-map" "^7.7.4" - "@babel/helper-function-name" "^7.7.4" - "@babel/helper-optimise-call-expression" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.7.4" - "@babel/helper-split-export-declaration" "^7.7.4" - globals "^11.1.0" + "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-transform-computed-properties@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.7.4.tgz#e856c1628d3238ffe12d668eb42559f79a81910d" - integrity sha512-bSNsOsZnlpLLyQew35rl4Fma3yKWqK3ImWMSC/Nc+6nGjC9s5NFWAer1YQ899/6s9HxO2zQC1WoFNfkOqRkqRQ== +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-transform-destructuring@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.7.4.tgz#2b713729e5054a1135097b6a67da1b6fe8789267" - integrity sha512-4jFMXI1Cu2aXbcXXl8Lr6YubCn6Oc7k9lLsu8v61TZh+1jny2BWmdtvY9zSUlLdGUvcy9DMAWyZEOqjsbeg/wA== +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-transform-dotall-regex@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.7.4.tgz#f7ccda61118c5b7a2599a72d5e3210884a021e96" - integrity sha512-mk0cH1zyMa/XHeb6LOTXTbG7uIJ8Rrjlzu91pUx/KS3JpcgaTDwMS8kM+ar8SLOvlL2Lofi4CGBAjCo3a2x+lw== +"@babel/plugin-syntax-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-duplicate-keys@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.7.4.tgz#3d21731a42e3f598a73835299dd0169c3b90ac91" - integrity sha512-g1y4/G6xGWMD85Tlft5XedGaZBCIVN+/P0bs6eabmcPP9egFleMAo65OOjlhcz1njpwagyY3t0nsQC9oTFegJA== +"@babel/plugin-syntax-top-level-await@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-exponentiation-operator@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.7.4.tgz#dd30c0191e3a1ba19bcc7e389bdfddc0729d5db9" - integrity sha512-MCqiLfCKm6KEA1dglf6Uqq1ElDIZwFuzz1WH5mTf8k2uQSxEJMbOIEh7IZv7uichr7PMfi5YVSrr1vz+ipp7AQ== +"@babel/plugin-transform-arrow-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz#19063fcf8771ec7b31d742339dac62433d0611fe" + integrity sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-for-of@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.7.4.tgz#248800e3a5e507b1f103d8b4ca998e77c63932bc" - integrity sha512-zZ1fD1B8keYtEcKF+M1TROfeHTKnijcVQm0yO/Yu1f7qoDoxEIc/+GX6Go430Bg84eM/xwPFp0+h4EbZg7epAA== +"@babel/plugin-transform-async-to-generator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz#ccda3d1ab9d5ced5265fdb13f1882d5476c71615" + integrity sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-remap-async-to-generator" "^7.18.6" -"@babel/plugin-transform-function-name@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.7.4.tgz#75a6d3303d50db638ff8b5385d12451c865025b1" - integrity sha512-E/x09TvjHNhsULs2IusN+aJNRV5zKwxu1cpirZyRPw+FyyIKEHPXTsadj48bVpc1R5Qq1B5ZkzumuFLytnbT6g== +"@babel/plugin-transform-block-scoped-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" + integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== dependencies: - "@babel/helper-function-name" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-literals@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.7.4.tgz#27fe87d2b5017a2a5a34d1c41a6b9f6a6262643e" - integrity sha512-X2MSV7LfJFm4aZfxd0yLVFrEXAgPqYoDG53Br/tCKiKYfX0MjVjQeWPIhPHHsCqzwQANq+FLN786fF5rgLS+gw== +"@babel/plugin-transform-block-scoping@^7.19.4": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.0.tgz#91fe5e6ffc9ba13cb6c95ed7f0b1204f68c988c5" + integrity sha512-sXOohbpHZSk7GjxK9b3dKB7CfqUD5DwOH+DggKzOQ7TXYP+RCSbRykfjQmn/zq+rBjycVRtLf9pYhAaEJA786w== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.19.0" -"@babel/plugin-transform-member-expression-literals@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.7.4.tgz#aee127f2f3339fc34ce5e3055d7ffbf7aa26f19a" - integrity sha512-9VMwMO7i69LHTesL0RdGy93JU6a+qOPuvB4F4d0kR0zyVjJRVJRaoaGjhtki6SzQUu8yen/vxPKN6CWnCUw6bA== +"@babel/plugin-transform-classes@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz#0e61ec257fba409c41372175e7c1e606dc79bb20" + integrity sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-compilation-targets" "^7.19.0" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-replace-supers" "^7.18.9" + "@babel/helper-split-export-declaration" "^7.18.6" + globals "^11.1.0" -"@babel/plugin-transform-modules-amd@^7.7.5": - version "7.7.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.7.5.tgz#39e0fb717224b59475b306402bb8eedab01e729c" - integrity sha512-CT57FG4A2ZUNU1v+HdvDSDrjNWBrtCmSH6YbbgN3Lrf0Di/q/lWRxZrE72p3+HCCz9UjfZOEBdphgC0nzOS6DQ== +"@babel/plugin-transform-computed-properties@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz#2357a8224d402dad623caf6259b611e56aec746e" + integrity sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw== dependencies: - "@babel/helper-module-transforms" "^7.7.5" - "@babel/helper-plugin-utils" "^7.0.0" - babel-plugin-dynamic-import-node "^2.3.0" + "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-modules-commonjs@^7.7.5": - version "7.7.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.7.5.tgz#1d27f5eb0bcf7543e774950e5b2fa782e637b345" - integrity sha512-9Cq4zTFExwFhQI6MT1aFxgqhIsMWQWDVwOgLzl7PTWJHsNaqFvklAU+Oz6AQLAS0dJKTwZSOCo20INwktxpi3Q== +"@babel/plugin-transform-destructuring@^7.19.4": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.0.tgz#712829ef4825d9cc04bb379de316f981e9a6f648" + integrity sha512-1dIhvZfkDVx/zn2S1aFwlruspTt4189j7fEkH0Y0VyuDM6bQt7bD6kLcz3l4IlLG+e5OReaBz9ROAbttRtUHqA== dependencies: - "@babel/helper-module-transforms" "^7.7.5" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-simple-access" "^7.7.4" - babel-plugin-dynamic-import-node "^2.3.0" + "@babel/helper-plugin-utils" "^7.19.0" -"@babel/plugin-transform-modules-systemjs@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.7.4.tgz#cd98152339d3e763dfe838b7d4273edaf520bb30" - integrity sha512-y2c96hmcsUi6LrMqvmNDPBBiGCiQu0aYqpHatVVu6kD4mFEXKjyNxd/drc18XXAf9dv7UXjrZwBVmTTGaGP8iw== +"@babel/plugin-transform-dotall-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8" + integrity sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg== dependencies: - "@babel/helper-hoist-variables" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - babel-plugin-dynamic-import-node "^2.3.0" + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-modules-umd@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.7.4.tgz#1027c355a118de0aae9fee00ad7813c584d9061f" - integrity sha512-u2B8TIi0qZI4j8q4C51ktfO7E3cQ0qnaXFI1/OXITordD40tt17g/sXqgNNCcMTcBFKrUPcGDx+TBJuZxLx7tw== +"@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz#6b2d67686fab15fb6a7fd4bd895d5982cfc81241" + integrity sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ== dependencies: - "@babel/helper-module-transforms" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-create-regexp-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-named-capturing-groups-regex@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.7.4.tgz#fb3bcc4ee4198e7385805007373d6b6f42c98220" - integrity sha512-jBUkiqLKvUWpv9GLSuHUFYdmHg0ujC1JEYoZUfeOOfNydZXp1sXObgyPatpcwjWgsdBGsagWW0cdJpX/DO2jMw== +"@babel/plugin-transform-duplicate-keys@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz#687f15ee3cdad6d85191eb2a372c4528eaa0ae0e" + integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.7.4" + "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-new-target@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.7.4.tgz#4a0753d2d60639437be07b592a9e58ee00720167" - integrity sha512-CnPRiNtOG1vRodnsyGX37bHQleHE14B9dnnlgSeEs3ek3fHN1A1SScglTCg1sfbe7sRQ2BUcpgpTpWSfMKz3gg== +"@babel/plugin-transform-exponentiation-operator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" + integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-object-super@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.7.4.tgz#48488937a2d586c0148451bf51af9d7dda567262" - integrity sha512-ho+dAEhC2aRnff2JCA0SAK7V2R62zJd/7dmtoe7MHcso4C2mS+vZjn1Pb1pCVZvJs1mgsvv5+7sT+m3Bysb6eg== +"@babel/plugin-transform-for-of@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz#6ef8a50b244eb6a0bdbad0c7c61877e4e30097c1" + integrity sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.7.4" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-parameters@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.7.4.tgz#da4555c97f39b51ac089d31c7380f03bca4075ce" - integrity sha512-VJwhVePWPa0DqE9vcfptaJSzNDKrWU/4FbYCjZERtmqEs05g3UMXnYMZoXja7JAJ7Y7sPZipwm/pGApZt7wHlw== +"@babel/plugin-transform-function-name@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" + integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== dependencies: - "@babel/helper-call-delegate" "^7.7.4" - "@babel/helper-get-function-arity" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-property-literals@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.7.4.tgz#2388d6505ef89b266103f450f9167e6bd73f98c2" - integrity sha512-MatJhlC4iHsIskWYyawl53KuHrt+kALSADLQQ/HkhTjX954fkxIEh4q5slL4oRAnsm/eDoZ4q0CIZpcqBuxhJQ== +"@babel/plugin-transform-literals@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" + integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-regenerator@^7.7.5": - version "7.7.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.7.5.tgz#3a8757ee1a2780f390e89f246065ecf59c26fce9" - integrity sha512-/8I8tPvX2FkuEyWbjRCt4qTAgZK0DVy8QRguhA524UH48RfGJy94On2ri+dCuwOpcerPRl9O4ebQkRcVzIaGBw== +"@babel/plugin-transform-member-expression-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" + integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== dependencies: - regenerator-transform "^0.14.0" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-reserved-words@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.7.4.tgz#6a7cf123ad175bb5c69aec8f6f0770387ed3f1eb" - integrity sha512-OrPiUB5s5XvkCO1lS7D8ZtHcswIC57j62acAnJZKqGGnHP+TIc/ljQSrgdX/QyOTdEK5COAhuc820Hi1q2UgLQ== +"@babel/plugin-transform-modules-amd@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz#8c91f8c5115d2202f277549848874027d7172d21" + integrity sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-runtime@^7.7.6": - version "7.7.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.7.6.tgz#4f2b548c88922fb98ec1c242afd4733ee3e12f61" - integrity sha512-tajQY+YmXR7JjTwRvwL4HePqoL3DYxpYXIHKVvrOIvJmeHe2y1w4tz5qz9ObUDC9m76rCzIMPyn4eERuwA4a4A== +"@babel/plugin-transform-modules-commonjs@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz#afd243afba166cca69892e24a8fd8c9f2ca87883" + integrity sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q== dependencies: - "@babel/helper-module-imports" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - resolve "^1.8.1" - semver "^5.5.1" + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-simple-access" "^7.18.6" + babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-shorthand-properties@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.7.4.tgz#74a0a9b2f6d67a684c6fbfd5f0458eb7ba99891e" - integrity sha512-q+suddWRfIcnyG5YiDP58sT65AJDZSUhXQDZE3r04AuqD6d/XLaQPPXSBzP2zGerkgBivqtQm9XKGLuHqBID6Q== +"@babel/plugin-transform-modules-systemjs@^7.19.0": + version "7.19.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.6.tgz#59e2a84064b5736a4471b1aa7b13d4431d327e0d" + integrity sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-module-transforms" "^7.19.6" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-validator-identifier" "^7.19.1" -"@babel/plugin-transform-spread@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.7.4.tgz#aa673b356fe6b7e70d69b6e33a17fef641008578" - integrity sha512-8OSs0FLe5/80cndziPlg4R0K6HcWSM0zyNhHhLsmw/Nc5MaA49cAsnoJ/t/YZf8qkG7fD+UjTRaApVDB526d7Q== +"@babel/plugin-transform-modules-umd@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz#81d3832d6034b75b54e62821ba58f28ed0aab4b9" + integrity sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-sticky-regex@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.7.4.tgz#ffb68c05090c30732076b1285dc1401b404a123c" - integrity sha512-Ls2NASyL6qtVe1H1hXts9yuEeONV2TJZmplLONkMPUG158CtmnrzW5Q5teibM5UVOFjG0D3IC5mzXR6pPpUY7A== +"@babel/plugin-transform-named-capturing-groups-regex@^7.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.1.tgz#ec7455bab6cd8fb05c525a94876f435a48128888" + integrity sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-regex" "^7.0.0" + "@babel/helper-create-regexp-features-plugin" "^7.19.0" + "@babel/helper-plugin-utils" "^7.19.0" -"@babel/plugin-transform-template-literals@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.7.4.tgz#1eb6411736dd3fe87dbd20cc6668e5121c17d604" - integrity sha512-sA+KxLwF3QwGj5abMHkHgshp9+rRz+oY9uoRil4CyLtgEuE/88dpkeWgNk5qKVsJE9iSfly3nvHapdRiIS2wnQ== +"@babel/plugin-transform-new-target@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz#d128f376ae200477f37c4ddfcc722a8a1b3246a8" + integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw== dependencies: - "@babel/helper-annotate-as-pure" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-typeof-symbol@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.7.4.tgz#3174626214f2d6de322882e498a38e8371b2140e" - integrity sha512-KQPUQ/7mqe2m0B8VecdyaW5XcQYaePyl9R7IsKd+irzj6jvbhoGnRE+M0aNkyAzI07VfUQ9266L5xMARitV3wg== +"@babel/plugin-transform-object-super@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" + integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.6" -"@babel/plugin-transform-unicode-regex@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.7.4.tgz#a3c0f65b117c4c81c5b6484f2a5e7b95346b83ae" - integrity sha512-N77UUIV+WCvE+5yHw+oks3m18/umd7y392Zv7mYTpFqHtkpcc+QUz+gLJNTWVlWROIWeLqY0f3OjZxV5TcXnRw== +"@babel/plugin-transform-parameters@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz#ee9f1a0ce6d78af58d0956a9378ea3427cccb48a" + integrity sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-property-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" + integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-regenerator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz#585c66cb84d4b4bf72519a34cfce761b8676ca73" + integrity sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + regenerator-transform "^0.15.0" -"@babel/preset-env@^7.7.6": - version "7.7.6" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.7.6.tgz#39ac600427bbb94eec6b27953f1dfa1d64d457b2" - integrity sha512-k5hO17iF/Q7tR9Jv8PdNBZWYW6RofxhnxKjBMc0nG4JTaWvOTiPoO/RLFwAKcA4FpmuBFm6jkoqaRJLGi0zdaQ== +"@babel/plugin-transform-reserved-words@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz#b1abd8ebf8edaa5f7fe6bbb8d2133d23b6a6f76a" + integrity sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-runtime@7.19.6": + version "7.19.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz#9d2a9dbf4e12644d6f46e5e75bfbf02b5d6e9194" + integrity sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw== + dependencies: + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.19.0" + babel-plugin-polyfill-corejs2 "^0.3.3" + babel-plugin-polyfill-corejs3 "^0.6.0" + babel-plugin-polyfill-regenerator "^0.4.1" + semver "^6.3.0" + +"@babel/plugin-transform-shorthand-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" + integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-spread@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz#dd60b4620c2fec806d60cfaae364ec2188d593b6" + integrity sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w== + dependencies: + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + +"@babel/plugin-transform-sticky-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" + integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-template-literals@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" + integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-typeof-symbol@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz#c8cea68263e45addcd6afc9091429f80925762c0" + integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-unicode-escapes@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz#1ecfb0eda83d09bbcb77c09970c2dd55832aa246" + integrity sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-unicode-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" + integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/preset-env@7.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.19.4.tgz#4c91ce2e1f994f717efb4237891c3ad2d808c94b" + integrity sha512-5QVOTXUdqTCjQuh2GGtdd7YEhoRXBMVGROAtsBeLGIbIz3obCBIfRMT1I3ZKkMgNzwkyCkftDXSSkHxnfVf4qg== + dependencies: + "@babel/compat-data" "^7.19.4" + "@babel/helper-compilation-targets" "^7.19.3" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9" + "@babel/plugin-proposal-async-generator-functions" "^7.19.1" + "@babel/plugin-proposal-class-properties" "^7.18.6" + "@babel/plugin-proposal-class-static-block" "^7.18.6" + "@babel/plugin-proposal-dynamic-import" "^7.18.6" + "@babel/plugin-proposal-export-namespace-from" "^7.18.9" + "@babel/plugin-proposal-json-strings" "^7.18.6" + "@babel/plugin-proposal-logical-assignment-operators" "^7.18.9" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" + "@babel/plugin-proposal-numeric-separator" "^7.18.6" + "@babel/plugin-proposal-object-rest-spread" "^7.19.4" + "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" + "@babel/plugin-proposal-optional-chaining" "^7.18.9" + "@babel/plugin-proposal-private-methods" "^7.18.6" + "@babel/plugin-proposal-private-property-in-object" "^7.18.6" + "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.18.6" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-transform-arrow-functions" "^7.18.6" + "@babel/plugin-transform-async-to-generator" "^7.18.6" + "@babel/plugin-transform-block-scoped-functions" "^7.18.6" + "@babel/plugin-transform-block-scoping" "^7.19.4" + "@babel/plugin-transform-classes" "^7.19.0" + "@babel/plugin-transform-computed-properties" "^7.18.9" + "@babel/plugin-transform-destructuring" "^7.19.4" + "@babel/plugin-transform-dotall-regex" "^7.18.6" + "@babel/plugin-transform-duplicate-keys" "^7.18.9" + "@babel/plugin-transform-exponentiation-operator" "^7.18.6" + "@babel/plugin-transform-for-of" "^7.18.8" + "@babel/plugin-transform-function-name" "^7.18.9" + "@babel/plugin-transform-literals" "^7.18.9" + "@babel/plugin-transform-member-expression-literals" "^7.18.6" + "@babel/plugin-transform-modules-amd" "^7.18.6" + "@babel/plugin-transform-modules-commonjs" "^7.18.6" + "@babel/plugin-transform-modules-systemjs" "^7.19.0" + "@babel/plugin-transform-modules-umd" "^7.18.6" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.19.1" + "@babel/plugin-transform-new-target" "^7.18.6" + "@babel/plugin-transform-object-super" "^7.18.6" + "@babel/plugin-transform-parameters" "^7.18.8" + "@babel/plugin-transform-property-literals" "^7.18.6" + "@babel/plugin-transform-regenerator" "^7.18.6" + "@babel/plugin-transform-reserved-words" "^7.18.6" + "@babel/plugin-transform-shorthand-properties" "^7.18.6" + "@babel/plugin-transform-spread" "^7.19.0" + "@babel/plugin-transform-sticky-regex" "^7.18.6" + "@babel/plugin-transform-template-literals" "^7.18.9" + "@babel/plugin-transform-typeof-symbol" "^7.18.9" + "@babel/plugin-transform-unicode-escapes" "^7.18.10" + "@babel/plugin-transform-unicode-regex" "^7.18.6" + "@babel/preset-modules" "^0.1.5" + "@babel/types" "^7.19.4" + babel-plugin-polyfill-corejs2 "^0.3.3" + babel-plugin-polyfill-corejs3 "^0.6.0" + babel-plugin-polyfill-regenerator "^0.4.1" + core-js-compat "^3.25.1" + semver "^6.3.0" + +"@babel/preset-modules@^0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" + integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== dependencies: - "@babel/helper-module-imports" "^7.7.4" "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-async-generator-functions" "^7.7.4" - "@babel/plugin-proposal-dynamic-import" "^7.7.4" - "@babel/plugin-proposal-json-strings" "^7.7.4" - "@babel/plugin-proposal-object-rest-spread" "^7.7.4" - "@babel/plugin-proposal-optional-catch-binding" "^7.7.4" - "@babel/plugin-proposal-unicode-property-regex" "^7.7.4" - "@babel/plugin-syntax-async-generators" "^7.7.4" - "@babel/plugin-syntax-dynamic-import" "^7.7.4" - "@babel/plugin-syntax-json-strings" "^7.7.4" - "@babel/plugin-syntax-object-rest-spread" "^7.7.4" - "@babel/plugin-syntax-optional-catch-binding" "^7.7.4" - "@babel/plugin-syntax-top-level-await" "^7.7.4" - "@babel/plugin-transform-arrow-functions" "^7.7.4" - "@babel/plugin-transform-async-to-generator" "^7.7.4" - "@babel/plugin-transform-block-scoped-functions" "^7.7.4" - "@babel/plugin-transform-block-scoping" "^7.7.4" - "@babel/plugin-transform-classes" "^7.7.4" - "@babel/plugin-transform-computed-properties" "^7.7.4" - "@babel/plugin-transform-destructuring" "^7.7.4" - "@babel/plugin-transform-dotall-regex" "^7.7.4" - "@babel/plugin-transform-duplicate-keys" "^7.7.4" - "@babel/plugin-transform-exponentiation-operator" "^7.7.4" - "@babel/plugin-transform-for-of" "^7.7.4" - "@babel/plugin-transform-function-name" "^7.7.4" - "@babel/plugin-transform-literals" "^7.7.4" - "@babel/plugin-transform-member-expression-literals" "^7.7.4" - "@babel/plugin-transform-modules-amd" "^7.7.5" - "@babel/plugin-transform-modules-commonjs" "^7.7.5" - "@babel/plugin-transform-modules-systemjs" "^7.7.4" - "@babel/plugin-transform-modules-umd" "^7.7.4" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.7.4" - "@babel/plugin-transform-new-target" "^7.7.4" - "@babel/plugin-transform-object-super" "^7.7.4" - "@babel/plugin-transform-parameters" "^7.7.4" - "@babel/plugin-transform-property-literals" "^7.7.4" - "@babel/plugin-transform-regenerator" "^7.7.5" - "@babel/plugin-transform-reserved-words" "^7.7.4" - "@babel/plugin-transform-shorthand-properties" "^7.7.4" - "@babel/plugin-transform-spread" "^7.7.4" - "@babel/plugin-transform-sticky-regex" "^7.7.4" - "@babel/plugin-transform-template-literals" "^7.7.4" - "@babel/plugin-transform-typeof-symbol" "^7.7.4" - "@babel/plugin-transform-unicode-regex" "^7.7.4" - "@babel/types" "^7.7.4" - browserslist "^4.6.0" - core-js-compat "^3.4.7" - invariant "^2.2.2" - js-levenshtein "^1.1.3" - semver "^5.5.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/types" "^7.4.4" + esutils "^2.0.2" -"@babel/register@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.7.4.tgz#45a4956471a9df3b012b747f5781cc084ee8f128" - integrity sha512-/fmONZqL6ZMl9KJUYajetCrID6m0xmL4odX7v+Xvoxcv0DdbP/oO0TWIeLUCHqczQ6L6njDMqmqHFy2cp3FFsA== +"@babel/register@7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.18.9.tgz#1888b24bc28d5cc41c412feb015e9ff6b96e439c" + integrity sha512-ZlbnXDcNYHMR25ITwwNKT88JiaukkdVj/nG7r3wnuXkOTHc60Uy05PwMCPre0hSkY68E6zK3xz+vUJSP2jWmcw== dependencies: + clone-deep "^4.0.1" find-cache-dir "^2.0.0" - lodash "^4.17.13" make-dir "^2.1.0" - pirates "^4.0.0" + pirates "^4.0.5" source-map-support "^0.5.16" -"@babel/runtime@^7.7.6": - version "7.7.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.6.tgz#d18c511121aff1b4f2cd1d452f1bac9601dd830f" - integrity sha512-BWAJxpNVa0QlE5gZdWjSxXtemZyZ9RmrmVozxt3NUXeZhVIJ5ANyqmMc0JDrivBZyxUuQvFxlvH4OWWOogGfUw== - dependencies: - regenerator-runtime "^0.13.2" +"@babel/runtime@7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.0.tgz#824a9ef325ffde6f78056059db3168c08785e24a" + integrity sha512-NDYdls71fTXoU8TZHfbBWg7DiZfNzClcKui/+kyi6ppD2L1qnWW3VV6CjtaBXSUGGhiTWJ6ereOIkUvenif66Q== + dependencies: + regenerator-runtime "^0.13.10" + +"@babel/runtime@^7.8.4": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.7.tgz#a5f3328dc41ff39d803f311cfe17703418cf9825" + integrity sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/template@^7.0.0", "@babel/template@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" + integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/parser" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/template@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" + integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.18.10" + "@babel/types" "^7.18.10" + +"@babel/template@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.6.tgz#1283f4993e00b929d6e2d3c72fdc9168a2977a31" + integrity sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.18.6" + "@babel/types" "^7.18.6" + +"@babel/traverse@^7.0.0": + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.3.tgz#0ae0f15b27d9a92ba1f2263358ea7c4e7db47b57" + integrity sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.17.3" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/parser" "^7.17.3" + "@babel/types" "^7.17.0" + debug "^4.1.0" + globals "^11.1.0" -"@babel/template@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" - integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/parser" "^7.10.4" - "@babel/types" "^7.10.4" +"@babel/traverse@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.10.tgz#37ad97d1cb00efa869b91dd5d1950f8a6cf0cb08" + integrity sha512-J7ycxg0/K9XCtLyHf0cz2DqDihonJeIo+z+HEdRe9YuT8TY4A66i+Ab2/xZCEW7Ro60bPCBBfqqboHSamoV3+g== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.18.10" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.18.10" + "@babel/types" "^7.18.10" + debug "^4.1.0" + globals "^11.1.0" -"@babel/template@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.7.4.tgz#428a7d9eecffe27deac0a98e23bf8e3675d2a77b" - integrity sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw== - dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/parser" "^7.7.4" - "@babel/types" "^7.7.4" +"@babel/traverse@^7.18.6", "@babel/traverse@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.8.tgz#f095e62ab46abf1da35e5a2011f43aee72d8d5b0" + integrity sha512-UNg/AcSySJYR/+mIcJQDCv00T+AqRO7j/ZEJLzpaYtgM48rMg5MnkJgyNqkzo88+p4tfRvZJCEiwwfG6h4jkRg== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.18.7" + "@babel/helper-environment-visitor" "^7.18.6" + "@babel/helper-function-name" "^7.18.6" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.18.8" + "@babel/types" "^7.18.8" + debug "^4.1.0" + globals "^11.1.0" -"@babel/traverse@^7.10.4", "@babel/traverse@^7.10.5": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.10.5.tgz#77ce464f5b258be265af618d8fddf0536f20b564" - integrity sha512-yc/fyv2gUjPqzTz0WHeRJH2pv7jA9kA7mBX2tXl/x5iOE81uaVPuGPtaYk7wmkx4b67mQ7NqI8rmT2pF47KYKQ== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.10.5" - "@babel/helper-function-name" "^7.10.4" - "@babel/helper-split-export-declaration" "^7.10.4" - "@babel/parser" "^7.10.5" - "@babel/types" "^7.10.5" +"@babel/traverse@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.9.tgz#deeff3e8f1bad9786874cb2feda7a2d77a904f98" + integrity sha512-LcPAnujXGwBgv3/WHv01pHtb2tihcyW1XuL9wd7jqh1Z8AQkTd+QVjMrMijrln0T7ED3UXLIy36P9Ao7W75rYg== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.18.9" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.18.9" + "@babel/types" "^7.18.9" debug "^4.1.0" globals "^11.1.0" - lodash "^4.17.19" -"@babel/traverse@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.7.4.tgz#9c1e7c60fb679fe4fcfaa42500833333c2058558" - integrity sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw== - dependencies: - "@babel/code-frame" "^7.5.5" - "@babel/generator" "^7.7.4" - "@babel/helper-function-name" "^7.7.4" - "@babel/helper-split-export-declaration" "^7.7.4" - "@babel/parser" "^7.7.4" - "@babel/types" "^7.7.4" +"@babel/traverse@^7.19.6", "@babel/traverse@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.0.tgz#538c4c6ce6255f5666eba02252a7b59fc2d5ed98" + integrity sha512-5+cAXQNARgjRUK0JWu2UBwja4JLSO/rBMPJzpsKb+oBF5xlUuCfljQepS4XypBQoiigL0VQjTZy6WiONtUdScQ== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.20.0" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.20.0" + "@babel/types" "^7.20.0" debug "^4.1.0" globals "^11.1.0" - lodash "^4.17.13" "@babel/types@^7.0.0", "@babel/types@^7.0.0-beta.49": version "7.2.2" @@ -860,13 +1433,46 @@ lodash "^4.17.10" to-fast-properties "^2.0.0" -"@babel/types@^7.10.4", "@babel/types@^7.10.5": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.5.tgz#d88ae7e2fde86bfbfe851d4d81afa70a997b5d15" - integrity sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q== +"@babel/types@^7.16.7", "@babel/types@^7.17.0", "@babel/types@^7.4.4": + version "7.17.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" + integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + to-fast-properties "^2.0.0" + +"@babel/types@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.10.tgz#4908e81b6b339ca7c6b7a555a5fc29446f26dde6" + integrity sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ== + dependencies: + "@babel/helper-string-parser" "^7.18.10" + "@babel/helper-validator-identifier" "^7.18.6" + to-fast-properties "^2.0.0" + +"@babel/types@^7.18.6", "@babel/types@^7.18.7", "@babel/types@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.8.tgz#c5af199951bf41ba4a6a9a6d0d8ad722b30cd42f" + integrity sha512-qwpdsmraq0aJ3osLJRApsc2ouSJCdnMeZwB0DhbtHAtRpZNZCdlbRnHIgcRKzdE1g0iOGg644fzjOBcdOz9cPw== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" + to-fast-properties "^2.0.0" + +"@babel/types@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.9.tgz#7148d64ba133d8d73a41b3172ac4b83a1452205f" + integrity sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" + to-fast-properties "^2.0.0" + +"@babel/types@^7.19.0", "@babel/types@^7.19.4", "@babel/types@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.0.tgz#52c94cf8a7e24e89d2a194c25c35b17a64871479" + integrity sha512-Jlgt3H0TajCW164wkTOTzHkZb075tMQMULzrLUoUeKmO7eFL96GgDxf7/Axhc5CAuKE3KFyVW1p6ysKsi2oXAg== dependencies: - "@babel/helper-validator-identifier" "^7.10.4" - lodash "^4.17.19" + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" "@babel/types@^7.7.4": @@ -878,42 +1484,264 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@chenfengyuan/vue-qrcode@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@chenfengyuan/vue-qrcode/-/vue-qrcode-1.0.0.tgz#07103fe2048ce0160cddde836fb5378cc7951d6f" +"@chenfengyuan/vue-qrcode@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@chenfengyuan/vue-qrcode/-/vue-qrcode-2.0.0.tgz#8cd01f6fc528d471680ebe812ec47c830aea7e63" + integrity sha512-33Cfr0zjbc3Dd8d5b1IgzXRAgXH0c2Gv19VI4snS25V/x9Z41eg769tC+Us1x+vqgQQhgD5YUjLnkpkrQfeMSw== + +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + +"@eslint/eslintrc@^1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.3.tgz#2b044ab39fdfa75b4688184f9e573ce3c5b0ff95" + integrity sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.4.0" + globals "^13.15.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@fortawesome/fontawesome-common-types@6.2.0": + version "6.2.0" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.2.0.tgz#76467a94aa888aeb22aafa43eb6ff889df3a5a7f" + integrity sha512-rBevIsj2nclStJ7AxTdfsa3ovHb1H+qApwrxcTVo+NNdeJiB9V75hsKfrkG5AwNcRUNxrPPiScGYCNmLMoh8pg== + +"@fortawesome/fontawesome-svg-core@6.2.0": + version "6.2.0" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.2.0.tgz#11856eaf4dd1d865c442ddea1eed8ee855186ba2" + integrity sha512-Cf2mAAeMWFMzpLC7Y9H1I4o3wEU+XovVJhTiNG8ZNgSQj53yl7OCJaS80K4YjrABWZzbAHVaoHE1dVJ27AAYXw== + dependencies: + "@fortawesome/fontawesome-common-types" "6.2.0" + +"@fortawesome/free-regular-svg-icons@6.2.0": + version "6.2.0" + resolved "https://registry.yarnpkg.com/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.2.0.tgz#947e1f03be17da3a60bfeb2666b5348b19448ce2" + integrity sha512-M1dG+PAmkYMTL9BSUHFXY5oaHwBYfHCPhbJ8qj8JELsc9XCrUJ6eEHWip4q0tE+h9C0DVyFkwIM9t7QYyCpprQ== + dependencies: + "@fortawesome/fontawesome-common-types" "6.2.0" + +"@fortawesome/free-solid-svg-icons@6.2.0": + version "6.2.0" + resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.2.0.tgz#8dcde48109354fd7a5ece8ea48d678bb91d4b5f0" + integrity sha512-UjCILHIQ4I8cN46EiQn0CZL/h8AwCGgR//1c4R96Q5viSRwuKVo0NdQEc4bm+69ZwC0dUvjbDqAHF1RR5FA3XA== + dependencies: + "@fortawesome/fontawesome-common-types" "6.2.0" + +"@fortawesome/vue-fontawesome@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.0.1.tgz#ced35cefc52b364f7db973f2fe9f50c3dd160715" + integrity sha512-CdXZJoCS+aEPec26ZP7hWWU3SaJlQPZSCGdgpQ2qGl2HUmtUUNrI3zC4XWdn1JUmh3t5OuDeRG1qB4eGRNSD4A== + +"@humanwhocodes/config-array@^0.11.6": + version "0.11.7" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.7.tgz#38aec044c6c828f6ed51d5d7ae3d9b9faf6dbb0f" + integrity sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw== dependencies: - qrcode "^1.3.0" + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.5" -"@fortawesome/fontawesome-common-types@^0.2.32": - version "0.2.32" - resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.32.tgz#3436795d5684f22742989bfa08f46f50f516f259" - integrity sha512-ux2EDjKMpcdHBVLi/eWZynnPxs0BtFVXJkgHIxXRl+9ZFaHPvYamAfCzeeQFqHRjuJtX90wVnMRaMQAAlctz3w== +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@fortawesome/fontawesome-svg-core@^1.2.32": - version "1.2.32" - resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.32.tgz#da092bfc7266aa274be8604de610d7115f9ba6cf" - integrity sha512-XjqyeLCsR/c/usUpdWcOdVtWFVjPbDFBTQkn2fQRrWhhUoxriQohO2RWDxLyUM8XpD+Zzg5xwJ8gqTYGDLeGaQ== +"@humanwhocodes/object-schema@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== + +"@intlify/bundle-utils@next": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@intlify/bundle-utils/-/bundle-utils-3.0.0.tgz#d7667b3e6c5889988d9fd27acc2c7c068a2bfbc3" + integrity sha512-y43Z5Q3ZJvxqtD8xUH6U3yrlZeay7ZTqkzv1GQ4b0mGQtk5uptOT9Ra4qvGuUv8QyPQsortrA/OHWUD5ax5ZNQ== dependencies: - "@fortawesome/fontawesome-common-types" "^0.2.32" + "@intlify/message-compiler" next + "@intlify/shared" next + jsonc-eslint-parser "^1.0.1" + source-map "^0.6.1" + yaml-eslint-parser "^0.3.2" -"@fortawesome/free-regular-svg-icons@^5.15.1": - version "5.15.1" - resolved "https://registry.yarnpkg.com/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.1.tgz#a8897d0ce325352dbba0e943101323e0175ee2b2" - integrity sha512-eD9NWFy89e7SVVtrLedJUxIpCBGhd4x7s7dhesokjyo1Tw62daqN5UcuAGu1NrepLLq1IeAYUVfWwnOjZ/j3HA== +"@intlify/core-base@9.2.2": + version "9.2.2" + resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-9.2.2.tgz#5353369b05cc9fe35cab95fe20afeb8a4481f939" + integrity sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA== dependencies: - "@fortawesome/fontawesome-common-types" "^0.2.32" + "@intlify/devtools-if" "9.2.2" + "@intlify/message-compiler" "9.2.2" + "@intlify/shared" "9.2.2" + "@intlify/vue-devtools" "9.2.2" -"@fortawesome/free-solid-svg-icons@^5.15.1": - version "5.15.1" - resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.1.tgz#e1432676ddd43108b41197fee9f86d910ad458ef" - integrity sha512-EFMuKtzRMNbvjab/SvJBaOOpaqJfdSap/Nl6hst7CgrJxwfORR1drdTV6q1Ib/JVzq4xObdTDcT6sqTaXMqfdg== +"@intlify/devtools-if@9.2.2": + version "9.2.2" + resolved "https://registry.yarnpkg.com/@intlify/devtools-if/-/devtools-if-9.2.2.tgz#b13d9ac4b4e2fe6d2e7daa556517a8061fe8bd39" + integrity sha512-4ttr/FNO29w+kBbU7HZ/U0Lzuh2cRDhP8UlWOtV9ERcjHzuyXVZmjyleESK6eVP60tGC9QtQW9yZE+JeRhDHkg== dependencies: - "@fortawesome/fontawesome-common-types" "^0.2.32" + "@intlify/shared" "9.2.2" -"@fortawesome/vue-fontawesome@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@fortawesome/vue-fontawesome/-/vue-fontawesome-2.0.0.tgz#63da3e459147cebb0a8d58eed81d6071db9f5973" - integrity sha512-N3VKw7KzRfOm8hShUVldpinlm13HpvLBQgT63QS+aCrIRLwjoEUXY5Rcmttbfb6HkzZaeqjLqd/aZCQ53UjQpg== +"@intlify/message-compiler@9.2.2": + version "9.2.2" + resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-9.2.2.tgz#e42ab6939b8ae5b3d21faf6a44045667a18bba1c" + integrity sha512-IUrQW7byAKN2fMBe8z6sK6riG1pue95e5jfokn8hA5Q3Bqy4MBJ5lJAofUsawQJYHeoPJ7svMDyBaVJ4d0GTtA== + dependencies: + "@intlify/shared" "9.2.2" + source-map "0.6.1" + +"@intlify/message-compiler@next": + version "9.2.0-beta.34" + resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-9.2.0-beta.34.tgz#a1a85c43f578e8ab00d30774963291fe2fdbc0b9" + integrity sha512-l7JjkXJBW2l6cFZqPvI6oWU6QZn/i70agU6QB02W1O+6quFCFNpsiPswgYyQrmfHt4F4qYhLJUMZltIW/cqcbw== + dependencies: + "@intlify/shared" "9.2.0-beta.34" + source-map "0.6.1" + +"@intlify/shared@9.2.0-beta.34", "@intlify/shared@next": + version "9.2.0-beta.34" + resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.2.0-beta.34.tgz#e8e9a93455eadcc9785fe2e2437fe037fc267f7d" + integrity sha512-hbUKcVbTOkLVpnlSeZE1OPgEI7FpvhuZF/gb84xECTjXEImIa3u0fIcJKUUffv3dlAx8fMOE5xKgDzngidm0tw== + +"@intlify/shared@9.2.2": + version "9.2.2" + resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.2.2.tgz#5011be9ca2b4ab86f8660739286e2707f9abb4a5" + integrity sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q== + +"@intlify/vue-devtools@9.2.2": + version "9.2.2" + resolved "https://registry.yarnpkg.com/@intlify/vue-devtools/-/vue-devtools-9.2.2.tgz#b95701556daf7ebb3a2d45aa3ae9e6415aed8317" + integrity sha512-+dUyqyCHWHb/UcvY1MlIpO87munedm3Gn6E9WWYdWrMuYLcoIoOEVDWSS8xSwtlPU+kA+MEQTP6Q1iI/ocusJg== + dependencies: + "@intlify/core-base" "9.2.2" + "@intlify/shared" "9.2.2" + +"@intlify/vue-i18n-loader@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@intlify/vue-i18n-loader/-/vue-i18n-loader-5.0.0.tgz#26f7b9d55b3feb5d50cdbbd537c7ed4b2396b3fb" + integrity sha512-rlqWLHrXdchvI9jsI5XA7/3UqE+4pgBD40d+9DWdyRkKeXfMMO9lmkp21jOKC8afWcK0NW5qzYTjp+JEJ6ymZA== + dependencies: + "@intlify/bundle-utils" next + "@intlify/shared" next + js-yaml "^4.1.0" + json5 "^2.2.0" + loader-utils "^2.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jest/schemas@^29.0.0": + version "29.0.0" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.0.0.tgz#5f47f5994dd4ef067fb7b4188ceac45f77fe952a" + integrity sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA== + dependencies: + "@sinclair/typebox" "^0.24.1" + +"@jest/types@^29.2.1": + version "29.2.1" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.2.1.tgz#ec9c683094d4eb754e41e2119d8bdaef01cf6da0" + integrity sha512-O/QNDQODLnINEPAI0cl9U6zUIDXEWXt6IC1o2N2QENuos7hlGUIthlKyV4p6ki3TvXFX071blj8HUhgLGquPjw== + dependencies: + "@jest/schemas" "^29.0.0" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@^3.0.3": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c" + integrity sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/source-map@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" + integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.11" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec" + integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg== + +"@jridgewell/trace-mapping@^0.3.0": + version "0.3.4" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz#f6a0832dffd5b8a6aaa633b7d9f8e8e94c83a0c3" + integrity sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/trace-mapping@^0.3.14": + version "0.3.15" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774" + integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/trace-mapping@^0.3.9": + version "0.3.14" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" + integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@kazvmoe-infra/pinch-zoom-element@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@kazvmoe-infra/pinch-zoom-element/-/pinch-zoom-element-1.2.0.tgz#eb3ca34c53b4410c689d60aca02f4a497ce84aba" + integrity sha512-HBrhH5O/Fsp2bB7EGTXzCsBAVcMjknSagKC5pBdGpKsF8meHISR0kjDIdw4YoE0S+0oNMwJ6ZUZyIBrdywxPPw== + dependencies: + pointer-tracker "^2.0.3" + +"@kazvmoe-infra/unicode-emoji-json@0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@kazvmoe-infra/unicode-emoji-json/-/unicode-emoji-json-0.4.0.tgz#555bab2f8d11db74820ef0a2fbe2805b17c22587" + integrity sha512-22OffREdHzD0U6A/W4RaFPV8NR73za6euibtAxNxO/fu5A6TwxRO2lAdbDWKJH9COv/vYs8zqfEiSalXH2nXJA== + +"@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1": + version "5.1.1-v1" + resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129" + integrity sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg== + dependencies: + eslint-scope "5.1.1" + +"@nightwatch/chai@5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@nightwatch/chai/-/chai-5.0.2.tgz#86b20908fc090dffd5c9567c0392bc6a494cc2e6" + integrity sha512-yzILJFCcE75OPoRfBlJ80Y3Ky06ljsdrK4Ld92yhmM477vxO2GEguwnd+ldl7pdSYTcg1gSJ1bPPQrA+/Hrn+A== + dependencies: + assertion-error "1.1.0" + check-error "1.0.2" + deep-eql "4.0.1" + loupe "2.3.4" + pathval "1.1.1" + type-detect "4.0.8" "@nodelib/fs.scandir@2.1.3": version "2.1.3" @@ -923,11 +1751,24 @@ "@nodelib/fs.stat" "2.0.3" run-parallel "^1.1.9" +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + "@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2": version "2.0.3" resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3" integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA== +"@nodelib/fs.stat@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + "@nodelib/fs.walk@^1.2.3": version "1.2.4" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976" @@ -936,43 +1777,194 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" -"@npmcli/move-file@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" - integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== +"@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== dependencies: - mkdirp "^1.0.4" - rimraf "^3.0.2" + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" -"@stylelint/postcss-css-in-js@^0.37.1": - version "0.37.2" - resolved "https://registry.yarnpkg.com/@stylelint/postcss-css-in-js/-/postcss-css-in-js-0.37.2.tgz#7e5a84ad181f4234a2480803422a47b8749af3d2" - integrity sha512-nEhsFoJurt8oUmieT8qy4nk81WRHmJynmVwn/Vts08PL9fhgIsMhk1GId5yAN643OzqEEb5S/6At2TZW7pqPDA== +"@ruffle-rs/ruffle@0.1.0-nightly.2022.7.12": + version "0.1.0-nightly.2022.7.12" + resolved "https://registry.yarnpkg.com/@ruffle-rs/ruffle/-/ruffle-0.1.0-nightly.2022.7.12.tgz#c2d77fce7a0e98d51a6535371550e0bff019d0ea" + integrity sha512-DFsiT4kdUuSHsYXzHV97e9Ui3FkcsHEg1GyHJipt/lCpCoZ2uRtP41uEz9eNc9ug8jWd7UyXxJmdkkRvs9UHgQ== + +"@sinclair/typebox@^0.24.1": + version "0.24.51" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f" + integrity sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA== + +"@sinonjs/commons@^1.7.0": + version "1.8.3" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" + integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== + dependencies: + type-detect "4.0.8" + +"@sinonjs/commons@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3" + integrity sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg== dependencies: - "@babel/core" ">=7.9.0" + type-detect "4.0.8" -"@stylelint/postcss-markdown@^0.36.1": - version "0.36.1" - resolved "https://registry.yarnpkg.com/@stylelint/postcss-markdown/-/postcss-markdown-0.36.1.tgz#829b87e6c0f108014533d9d7b987dc9efb6632e8" - integrity sha512-iDxMBWk9nB2BPi1VFQ+Dc5+XpvODBHw2n3tYpaBZuEAFQlbtF9If0Qh5LTTwSi/XwdbJ2jt+0dis3i8omyggpw== +"@sinonjs/fake-timers@^7.0.4": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz#2524eae70c4910edccf99b2f4e6efc5894aff7b5" + integrity sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg== dependencies: - remark "^12.0.0" - unist-util-find-all-after "^3.0.1" + "@sinonjs/commons" "^1.7.0" -"@testim/chrome-version@^1.0.7": - version "1.0.7" - resolved "https://registry.yarnpkg.com/@testim/chrome-version/-/chrome-version-1.0.7.tgz#0cd915785ec4190f08a3a6acc9b61fc38fb5f1a9" - integrity sha512-8UT/J+xqCYfn3fKtOznAibsHpiuDshCb0fwgWxRazTT19Igp9ovoXMPhXyLD6m3CKQGTMHgqoxaFfMWaL40Rnw== +"@sinonjs/fake-timers@^9.1.2": + version "9.1.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c" + integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@sinonjs/samsam@^7.0.1": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-7.0.1.tgz#5b5fa31c554636f78308439d220986b9523fc51f" + integrity sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw== + dependencies: + "@sinonjs/commons" "^2.0.0" + lodash.get "^4.4.2" + type-detect "^4.0.8" + +"@sinonjs/text-encoding@^0.7.1": + version "0.7.2" + resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz#5981a8db18b56ba38ef0efb7d995b12aa7b51918" + integrity sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ== + +"@socket.io/base64-arraybuffer@~1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#568d9beae00b0d835f4f8c53fd55714986492e61" + integrity sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ== + +"@stylelint/postcss-css-in-js@^0.37.2": + version "0.37.3" + resolved "https://registry.yarnpkg.com/@stylelint/postcss-css-in-js/-/postcss-css-in-js-0.37.3.tgz#d149a385e07ae365b0107314c084cb6c11adbf49" + integrity sha512-scLk3cSH1H9KggSniseb2KNAU5D9FWc3H7BxCSAIdtU9OWIyw0zkEZ9qEKHryRM+SExYXRKNb7tOOVNAsQ3iwg== + dependencies: + "@babel/core" "^7.17.9" + +"@stylelint/postcss-markdown@^0.36.2": + version "0.36.2" + resolved "https://registry.yarnpkg.com/@stylelint/postcss-markdown/-/postcss-markdown-0.36.2.tgz#0a540c4692f8dcdfc13c8e352c17e7bfee2bb391" + integrity sha512-2kGbqUVJUGE8dM+bMzXG/PYUWKkjLIkRLWNh39OaADkiabDRdw8ATFCgbMz5xdIcvwspPAluSL7uY+ZiTWdWmQ== + dependencies: + remark "^13.0.0" + unist-util-find-all-after "^3.0.2" + +"@testim/chrome-version@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@testim/chrome-version/-/chrome-version-1.1.2.tgz#092005c5b77bd3bb6576a4677110a11485e11864" + integrity sha512-1c4ZOETSRpI0iBfIFUqU4KqwBAB2lHUAlBjZz/YqOHqwM9dTTzjV6Km0ZkiEiSCx/tLr1BtESIKyWWMww+RUqw== + +"@trysound/sax@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" + integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== -"@types/json-schema@^7.0.6": - version "7.0.7" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" - integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== +"@types/component-emitter@^1.2.10": + version "1.2.11" + resolved "https://registry.yarnpkg.com/@types/component-emitter/-/component-emitter-1.2.11.tgz#50d47d42b347253817a39709fef03ce66a108506" + integrity sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ== + +"@types/cookie@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" + integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== + +"@types/cors@^2.8.12": + version "2.8.12" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" + integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== + +"@types/eslint-scope@^3.7.3": + version "3.7.4" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" + integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*", "@types/eslint@^7.29.0 || ^8.4.1": + version "8.4.5" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.5.tgz#acdfb7dd36b91cc5d812d7c093811a8f3d9b31e4" + integrity sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" + integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== + +"@types/estree@^0.0.51": + version "0.0.51" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" + integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== + +"@types/html-minifier-terser@^6.0.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" + integrity sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg== + +"@types/http-proxy@^1.17.8": + version "1.17.9" + resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.9.tgz#7f0e7931343761efde1e2bf48c40f02f3f75705a" + integrity sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw== + dependencies: + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" + integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" + integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.11" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" + integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== + +"@types/json-schema@^7.0.5": + version "7.0.10" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.10.tgz#9b05b7896166cd00e9cbd59864853abf65d9ac23" + integrity sha512-BLO9bBq59vW3fxCpD4o0N4U+DXsvwvIcl+jofw0frQo/GrBFC+/jRZj1E7kgp6dvTyNmA4y6JCV5Id/r3mNP5A== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= + +"@types/mdast@^3.0.0": + version "3.0.10" + resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af" + integrity sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA== + dependencies: + "@types/unist" "*" "@types/minimist@^1.2.0": version "1.2.0" @@ -984,6 +1976,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.19.tgz#5135176a8330b88ece4e9ab1fdcfc0a545b4bab4" integrity sha512-4nhBPStMK04rruRVtVc6cDqhu7S9GZai0fpXgPXrFpcPX6Xul8xnrjSdGB4KPBVYG/R5+fXWdCM8qBoiULWGPQ== +"@types/node@>=10.0.0": + version "17.0.22" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.22.tgz#38b6c4b9b2f3ed9f2e376cce42a298fb2375251e" + integrity sha512-8FwbVoG4fy+ykY86XCAclKZDORttqE5/s7dyWZKLXTdv3vRy5HozBEinG5IqhvPXXzIZEcTVbuHlQEI6iuwcmw== + "@types/normalize-package-data@^2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" @@ -994,11 +1991,28 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/unist@*": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" + integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== + "@types/unist@^2.0.0", "@types/unist@^2.0.2": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e" integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ== +"@types/yargs-parser@*": + version "21.0.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" + integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== + +"@types/yargs@^17.0.8": + version "17.0.13" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.13.tgz#34cced675ca1b1d51fcf4d34c3c6f0fa142a5c76" + integrity sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg== + dependencies: + "@types/yargs-parser" "*" + "@types/yauzl@^2.9.1": version "2.9.1" resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.1.tgz#d10f69f9f522eef3cf98e30afb684a1e1ec923af" @@ -1006,241 +2020,284 @@ dependencies: "@types/node" "*" -"@ungap/event-target@^0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@ungap/event-target/-/event-target-0.1.0.tgz#88d527d40de86c4b0c99a060ca241d755999915b" - integrity sha512-W2oyj0Fe1w/XhPZjkI3oUcDUAmu5P4qsdT2/2S8aMhtAWM/CE/jYWtji0pKNPDfxLI75fa5gWSEmnynKMNP/oA== - -"@vue/babel-helper-vue-jsx-merge-props@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.2.1.tgz#31624a7a505fb14da1d58023725a4c5f270e6a81" - integrity sha512-QOi5OW45e2R20VygMSNhyQHvpdUwQZqGPc748JLGCYEy+yp8fNFNdbNIGAgZmi9e+2JHPd6i6idRuqivyicIkA== +"@ungap/event-target@0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@ungap/event-target/-/event-target-0.2.3.tgz#be682c681126dca2371c4e1a1721f8e8bb400905" + integrity sha512-7Bz0qdvxNGV9n0f+xcMKU7wsEfK6PNzo8IdAcOiBgMNyCuU0Mk9dv0Hbd/Kgr+MFFfn4xLHFbuOt820egT5qEA== -"@vue/babel-plugin-transform-vue-jsx@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@vue/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-1.2.1.tgz#646046c652c2f0242727f34519d917b064041ed7" - integrity sha512-HJuqwACYehQwh1fNT8f4kyzqlNMpBuUK4rSiSES5D4QsYncv5fxFsLyrxFPG2ksO7t5WP+Vgix6tt6yKClwPzA== - dependencies: - "@babel/helper-module-imports" "^7.0.0" - "@babel/plugin-syntax-jsx" "^7.2.0" - "@vue/babel-helper-vue-jsx-merge-props" "^1.2.1" - html-tags "^2.0.0" - lodash.kebabcase "^4.1.1" - svg-tags "^1.0.0" +"@ungap/promise-all-settled@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" + integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== -"@vue/babel-preset-jsx@^1.2.4": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@vue/babel-preset-jsx/-/babel-preset-jsx-1.2.4.tgz#92fea79db6f13b01e80d3a0099e2924bdcbe4e87" - integrity sha512-oRVnmN2a77bYDJzeGSt92AuHXbkIxbf/XXSE3klINnh9AXBmVS1DGa1f0d+dDYpLfsAKElMnqKTQfKn7obcL4w== - dependencies: - "@vue/babel-helper-vue-jsx-merge-props" "^1.2.1" - "@vue/babel-plugin-transform-vue-jsx" "^1.2.1" - "@vue/babel-sugar-composition-api-inject-h" "^1.2.1" - "@vue/babel-sugar-composition-api-render-instance" "^1.2.4" - "@vue/babel-sugar-functional-vue" "^1.2.2" - "@vue/babel-sugar-inject-h" "^1.2.2" - "@vue/babel-sugar-v-model" "^1.2.3" - "@vue/babel-sugar-v-on" "^1.2.3" - -"@vue/babel-sugar-composition-api-inject-h@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@vue/babel-sugar-composition-api-inject-h/-/babel-sugar-composition-api-inject-h-1.2.1.tgz#05d6e0c432710e37582b2be9a6049b689b6f03eb" - integrity sha512-4B3L5Z2G+7s+9Bwbf+zPIifkFNcKth7fQwekVbnOA3cr3Pq71q71goWr97sk4/yyzH8phfe5ODVzEjX7HU7ItQ== - dependencies: - "@babel/plugin-syntax-jsx" "^7.2.0" +"@vue/babel-helper-vue-jsx-merge-props@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.4.0.tgz#8d53a1e21347db8edbe54d339902583176de09f2" + integrity sha512-JkqXfCkUDp4PIlFdDQ0TdXoIejMtTHP67/pvxlgeY+u5k3LEdKuWZ3LK6xkxo52uDoABIVyRwqVkfLQJhk7VBA== -"@vue/babel-sugar-composition-api-render-instance@^1.2.4": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@vue/babel-sugar-composition-api-render-instance/-/babel-sugar-composition-api-render-instance-1.2.4.tgz#e4cbc6997c344fac271785ad7a29325c51d68d19" - integrity sha512-joha4PZznQMsxQYXtR3MnTgCASC9u3zt9KfBxIeuI5g2gscpTsSKRDzWQt4aqNIpx6cv8On7/m6zmmovlNsG7Q== - dependencies: - "@babel/plugin-syntax-jsx" "^7.2.0" +"@vue/babel-helper-vue-transform-on@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.0.2.tgz#9b9c691cd06fc855221a2475c3cc831d774bc7dc" + integrity sha512-hz4R8tS5jMn8lDq6iD+yWL6XNB699pGIVLk7WSJnn1dbpjaazsjZQkieJoRX6gW5zpYSCFqQ7jUquPNY65tQYA== -"@vue/babel-sugar-functional-vue@^1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@vue/babel-sugar-functional-vue/-/babel-sugar-functional-vue-1.2.2.tgz#267a9ac8d787c96edbf03ce3f392c49da9bd2658" - integrity sha512-JvbgGn1bjCLByIAU1VOoepHQ1vFsroSA/QkzdiSs657V79q6OwEWLCQtQnEXD/rLTA8rRit4rMOhFpbjRFm82w== +"@vue/babel-plugin-jsx@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.1.1.tgz#0c5bac27880d23f89894cd036a37b55ef61ddfc1" + integrity sha512-j2uVfZjnB5+zkcbc/zsOc0fSNGCMMjaEXP52wdwdIfn0qjFfEYpYZBFKFg+HHnQeJCVrjOeO0YxgaL7DMrym9w== dependencies: - "@babel/plugin-syntax-jsx" "^7.2.0" + "@babel/helper-module-imports" "^7.0.0" + "@babel/plugin-syntax-jsx" "^7.0.0" + "@babel/template" "^7.0.0" + "@babel/traverse" "^7.0.0" + "@babel/types" "^7.0.0" + "@vue/babel-helper-vue-transform-on" "^1.0.2" + camelcase "^6.0.0" + html-tags "^3.1.0" + svg-tags "^1.0.0" -"@vue/babel-sugar-inject-h@^1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@vue/babel-sugar-inject-h/-/babel-sugar-inject-h-1.2.2.tgz#d738d3c893367ec8491dcbb669b000919293e3aa" - integrity sha512-y8vTo00oRkzQTgufeotjCLPAvlhnpSkcHFEp60+LJUwygGcd5Chrpn5480AQp/thrxVm8m2ifAk0LyFel9oCnw== +"@vue/compiler-core@3.2.41": + version "3.2.41" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.41.tgz#fb5b25f23817400f44377d878a0cdead808453ef" + integrity sha512-oA4mH6SA78DT+96/nsi4p9DX97PHcNROxs51lYk7gb9Z4BPKQ3Mh+BLn6CQZBw857Iuhu28BfMSRHAlPvD4vlw== dependencies: - "@babel/plugin-syntax-jsx" "^7.2.0" + "@babel/parser" "^7.16.4" + "@vue/shared" "3.2.41" + estree-walker "^2.0.2" + source-map "^0.6.1" -"@vue/babel-sugar-v-model@^1.2.3": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@vue/babel-sugar-v-model/-/babel-sugar-v-model-1.2.3.tgz#fa1f29ba51ebf0aa1a6c35fa66d539bc459a18f2" - integrity sha512-A2jxx87mySr/ulAsSSyYE8un6SIH0NWHiLaCWpodPCVOlQVODCaSpiR4+IMsmBr73haG+oeCuSvMOM+ttWUqRQ== - dependencies: - "@babel/plugin-syntax-jsx" "^7.2.0" - "@vue/babel-helper-vue-jsx-merge-props" "^1.2.1" - "@vue/babel-plugin-transform-vue-jsx" "^1.2.1" - camelcase "^5.0.0" - html-tags "^2.0.0" - svg-tags "^1.0.0" +"@vue/compiler-dom@3.2.41": + version "3.2.41" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.41.tgz#dc63dcd3ce8ca8a8721f14009d498a7a54380299" + integrity sha512-xe5TbbIsonjENxJsYRbDJvthzqxLNk+tb3d/c47zgREDa/PCp6/Y4gC/skM4H6PIuX5DAxm7fFJdbjjUH2QTMw== + dependencies: + "@vue/compiler-core" "3.2.41" + "@vue/shared" "3.2.41" + +"@vue/compiler-sfc@3.2.41": + version "3.2.41" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.41.tgz#238fb8c48318408c856748f4116aff8cc1dc2a73" + integrity sha512-+1P2m5kxOeaxVmJNXnBskAn3BenbTmbxBxWOtBq3mQTCokIreuMULFantBUclP0+KnzNCMOvcnKinqQZmiOF8w== + dependencies: + "@babel/parser" "^7.16.4" + "@vue/compiler-core" "3.2.41" + "@vue/compiler-dom" "3.2.41" + "@vue/compiler-ssr" "3.2.41" + "@vue/reactivity-transform" "3.2.41" + "@vue/shared" "3.2.41" + estree-walker "^2.0.2" + magic-string "^0.25.7" + postcss "^8.1.10" + source-map "^0.6.1" -"@vue/babel-sugar-v-on@^1.2.3": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@vue/babel-sugar-v-on/-/babel-sugar-v-on-1.2.3.tgz#342367178586a69f392f04bfba32021d02913ada" - integrity sha512-kt12VJdz/37D3N3eglBywV8GStKNUhNrsxChXIV+o0MwVXORYuhDTHJRKPgLJRb/EY3vM2aRFQdxJBp9CLikjw== +"@vue/compiler-ssr@3.2.41": + version "3.2.41" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.41.tgz#344f564d68584b33367731c04ffc949784611fcb" + integrity sha512-Y5wPiNIiaMz/sps8+DmhaKfDm1xgj6GrH99z4gq2LQenfVQcYXmHIOBcs5qPwl7jaW3SUQWjkAPKMfQemEQZwQ== + dependencies: + "@vue/compiler-dom" "3.2.41" + "@vue/shared" "3.2.41" + +"@vue/devtools-api@^6.0.0-beta.11": + version "6.1.3" + resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.1.3.tgz#a44c52e8fa6d22f84db3abdcdd0be5135b7dd7cf" + integrity sha512-79InfO2xHv+WHIrH1bHXQUiQD/wMls9qBk6WVwGCbdwP7/3zINtvqPNMtmSHXsIKjvUAHc8L0ouOj6ZQQRmcXg== + +"@vue/devtools-api@^6.2.1": + version "6.2.1" + resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.2.1.tgz#6f2948ff002ec46df01420dfeff91de16c5b4092" + integrity sha512-OEgAMeQXvCoJ+1x8WyQuVZzFo0wcyCmUR3baRVLmKBo1LmYZWMlRiXlux5jd0fqVJu6PfDbOrZItVqUEzLobeQ== + +"@vue/devtools-api@^6.4.5": + version "6.4.5" + resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.4.5.tgz#d54e844c1adbb1e677c81c665ecef1a2b4bb8380" + integrity sha512-JD5fcdIuFxU4fQyXUu3w2KpAJHzTVdN+p4iOX2lMWSHMOoQdMAcpFLZzm9Z/2nmsoZ1a96QEhZ26e50xLBsgOQ== + +"@vue/reactivity-transform@3.2.41": + version "3.2.41" + resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.41.tgz#9ff938877600c97f646e09ac1959b5150fb11a0c" + integrity sha512-mK5+BNMsL4hHi+IR3Ft/ho6Za+L3FA5j8WvreJ7XzHrqkPq8jtF/SMo7tuc9gHjLDwKZX1nP1JQOKo9IEAn54A== + dependencies: + "@babel/parser" "^7.16.4" + "@vue/compiler-core" "3.2.41" + "@vue/shared" "3.2.41" + estree-walker "^2.0.2" + magic-string "^0.25.7" + +"@vue/reactivity@3.2.41": + version "3.2.41" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.41.tgz#0ad3bdf76d76822da1502dc9f394dafd02642963" + integrity sha512-9JvCnlj8uc5xRiQGZ28MKGjuCoPhhTwcoAdv3o31+cfGgonwdPNuvqAXLhlzu4zwqavFEG5tvaoINQEfxz+l6g== + dependencies: + "@vue/shared" "3.2.41" + +"@vue/runtime-core@3.2.41": + version "3.2.41" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.41.tgz#775bfc00b3fadbaddab77138f23322aee3517a76" + integrity sha512-0LBBRwqnI0p4FgIkO9q2aJBBTKDSjzhnxrxHYengkAF6dMOjeAIZFDADAlcf2h3GDALWnblbeprYYpItiulSVQ== + dependencies: + "@vue/reactivity" "3.2.41" + "@vue/shared" "3.2.41" + +"@vue/runtime-dom@3.2.41": + version "3.2.41" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.41.tgz#cdf86be7410f7b15c29632a96ce879e5b4c9ab92" + integrity sha512-U7zYuR1NVIP8BL6jmOqmapRAHovEFp7CSw4pR2FacqewXNGqZaRfHoNLQsqQvVQ8yuZNZtxSZy0FFyC70YXPpA== + dependencies: + "@vue/runtime-core" "3.2.41" + "@vue/shared" "3.2.41" + csstype "^2.6.8" + +"@vue/server-renderer@3.2.41": + version "3.2.41" + resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.41.tgz#ca64552c05878f94e8d191ac439141c06c0fb2ad" + integrity sha512-7YHLkfJdTlsZTV0ae5sPwl9Gn/EGr2hrlbcS/8naXm2CDpnKUwC68i1wGlrYAfIgYWL7vUZwk2GkYLQH5CvFig== + dependencies: + "@vue/compiler-ssr" "3.2.41" + "@vue/shared" "3.2.41" + +"@vue/shared@3.2.41": + version "3.2.41" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.41.tgz#fbc95422df654ea64e8428eced96ba6ad555d2bb" + integrity sha512-W9mfWLHmJhkfAmV+7gDjcHeAWALQtgGT3JErxULl0oz6R6+3ug91I7IErs93eCFhPCZPHBs4QJS7YWEV7A3sxw== + +"@vue/test-utils@2.2.6": + version "2.2.6" + resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-2.2.6.tgz#23d85b81d05be36f12aa802459a7876457dec795" + integrity sha512-64zHtJZdG7V/U2L0j/z3Pt5bSygccI3xs+Kl7LB73AZK4MQ8WONJhqDQPK8leUFFA9CmmoJygeky7zcl2hX10A== + +"@vuelidate/core@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@vuelidate/core/-/core-2.0.0.tgz#dfe73ba3f997646e07bd4da4f327fe53c29782cb" + integrity sha512-xIFgdQlScO0aaSZ0wTGPJh8YcTMNAj5veI8yPgiAyxOT+GV7vNQFiU1vpYWCL4cklkkhYvRRSC2OEX7YOZNmPQ== dependencies: - "@babel/plugin-syntax-jsx" "^7.2.0" - "@vue/babel-plugin-transform-vue-jsx" "^1.2.1" - camelcase "^5.0.0" + vue-demi "^0.13.11" -"@vue/test-utils@^1.0.0-beta.26": - version "1.0.0-beta.28" - resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-1.0.0-beta.28.tgz#767c43413df8cde86128735e58923803e444b9a5" +"@vuelidate/validators@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@vuelidate/validators/-/validators-2.0.0.tgz#1ddd86c6c81b2cfbb5720961e951cc53ec0a80be" + integrity sha512-fQQcmDWfz7pyH5/JPi0Ng2GEgNK1pUHn/Z/j5rG/Q+HwhgIXvJblTPcZwKOj1ABL7V4UVuGKECvZCDHNGOwdrg== dependencies: - dom-event-types "^1.0.0" - lodash "^4.17.4" + vue-demi "^0.13.11" -"@webassemblyjs/ast@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" - integrity sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA== +"@webassemblyjs/ast@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" + integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== dependencies: - "@webassemblyjs/helper-module-context" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/wast-parser" "1.9.0" - -"@webassemblyjs/floating-point-hex-parser@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz#3c3d3b271bddfc84deb00f71344438311d52ffb4" - integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== - -"@webassemblyjs/helper-api-error@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz#203f676e333b96c9da2eeab3ccef33c45928b6a2" - integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== + "@webassemblyjs/helper-numbers" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" -"@webassemblyjs/helper-buffer@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz#a1442d269c5feb23fcbc9ef759dac3547f29de00" - integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA== +"@webassemblyjs/floating-point-hex-parser@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f" + integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ== -"@webassemblyjs/helper-code-frame@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz#647f8892cd2043a82ac0c8c5e75c36f1d9159f27" - integrity sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA== - dependencies: - "@webassemblyjs/wast-printer" "1.9.0" +"@webassemblyjs/helper-api-error@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16" + integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg== -"@webassemblyjs/helper-fsm@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz#c05256b71244214671f4b08ec108ad63b70eddb8" - integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw== +"@webassemblyjs/helper-buffer@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" + integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== -"@webassemblyjs/helper-module-context@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz#25d8884b76839871a08a6c6f806c3979ef712f07" - integrity sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g== +"@webassemblyjs/helper-numbers@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae" + integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ== dependencies: - "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/floating-point-hex-parser" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@xtuc/long" "4.2.2" -"@webassemblyjs/helper-wasm-bytecode@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz#4fed8beac9b8c14f8c58b70d124d549dd1fe5790" - integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== +"@webassemblyjs/helper-wasm-bytecode@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1" + integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q== -"@webassemblyjs/helper-wasm-section@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz#5a4138d5a6292ba18b04c5ae49717e4167965346" - integrity sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw== +"@webassemblyjs/helper-wasm-section@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a" + integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg== dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" -"@webassemblyjs/ieee754@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz#15c7a0fbaae83fb26143bbacf6d6df1702ad39e4" - integrity sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg== +"@webassemblyjs/ieee754@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614" + integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ== dependencies: "@xtuc/ieee754" "^1.2.0" -"@webassemblyjs/leb128@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.0.tgz#f19ca0b76a6dc55623a09cffa769e838fa1e1c95" - integrity sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw== +"@webassemblyjs/leb128@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5" + integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw== dependencies: "@xtuc/long" "4.2.2" -"@webassemblyjs/utf8@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.0.tgz#04d33b636f78e6a6813227e82402f7637b6229ab" - integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w== - -"@webassemblyjs/wasm-edit@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz#3fe6d79d3f0f922183aa86002c42dd256cfee9cf" - integrity sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/helper-wasm-section" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - "@webassemblyjs/wasm-opt" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - "@webassemblyjs/wast-printer" "1.9.0" - -"@webassemblyjs/wasm-gen@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz#50bc70ec68ded8e2763b01a1418bf43491a7a49c" - integrity sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/ieee754" "1.9.0" - "@webassemblyjs/leb128" "1.9.0" - "@webassemblyjs/utf8" "1.9.0" - -"@webassemblyjs/wasm-opt@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz#2211181e5b31326443cc8112eb9f0b9028721a61" - integrity sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - -"@webassemblyjs/wasm-parser@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz#9d48e44826df4a6598294aa6c87469d642fff65e" - integrity sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-api-error" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/ieee754" "1.9.0" - "@webassemblyjs/leb128" "1.9.0" - "@webassemblyjs/utf8" "1.9.0" - -"@webassemblyjs/wast-parser@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz#3031115d79ac5bd261556cecc3fa90a3ef451914" - integrity sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/floating-point-hex-parser" "1.9.0" - "@webassemblyjs/helper-api-error" "1.9.0" - "@webassemblyjs/helper-code-frame" "1.9.0" - "@webassemblyjs/helper-fsm" "1.9.0" - "@xtuc/long" "4.2.2" - -"@webassemblyjs/wast-printer@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz#4935d54c85fef637b00ce9f52377451d00d47899" - integrity sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/wast-parser" "1.9.0" +"@webassemblyjs/utf8@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff" + integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ== + +"@webassemblyjs/wasm-edit@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6" + integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/helper-wasm-section" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-opt" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + "@webassemblyjs/wast-printer" "1.11.1" + +"@webassemblyjs/wasm-gen@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76" + integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wasm-opt@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2" + integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + +"@webassemblyjs/wasm-parser@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199" + integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wast-printer@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0" + integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg== + dependencies: + "@webassemblyjs/ast" "1.11.1" "@xtuc/long" "4.2.2" "@xtuc/ieee754@^1.2.0": @@ -1251,10 +2308,6 @@ version "4.2.2" resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" -abbrev@1, abbrev@1.0.x: - version "1.0.9" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" - accepts@~1.3.4: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" @@ -1262,36 +2315,33 @@ accepts@~1.3.4: mime-types "~2.1.24" negotiator "0.6.2" -accepts@~1.3.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" +accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== dependencies: - mime-types "~2.1.18" - negotiator "0.6.1" - -acorn-jsx@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e" + mime-types "~2.1.34" + negotiator "0.6.3" -acorn@^6.0.2, acorn@^6.0.7: - version "6.1.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.1.tgz#7d25ae05bb8ad1f9b699108e1094ecd7884adc1f" +acorn-import-assertions@^1.7.6: + version "1.8.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" + integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw== -acorn@^6.4.1: - version "6.4.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" - integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== +acorn-jsx@^5.2.0, acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -after@0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" +acorn@^7.1.1, acorn@^7.4.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -agent-base@2: - version "2.1.1" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-2.1.1.tgz#d6de10d5af6132d5bd692427d46fc538539094c7" - dependencies: - extend "~3.0.0" - semver "~5.0.1" +acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0: + version "8.8.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" + integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== agent-base@6: version "6.0.2" @@ -1308,67 +2358,65 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv-errors@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" - -ajv-keywords@^3.1.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.0.tgz#4b831e7b531415a7cc518cd404e73f6193c6349d" +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" -ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: +ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@^6.1.0, ajv@^6.9.1: - version "6.10.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1" +ajv-keywords@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== dependencies: - fast-deep-equal "^2.0.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" + fast-deep-equal "^3.1.3" -ajv@^6.10.2: - version "6.12.3" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.3.tgz#18c5af38a111ddeb4f2697bd78d68abc1cabd706" - integrity sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA== +ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^6.12.5: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== +ajv@^8.0.0, ajv@^8.0.1, ajv@^8.8.0: + version "8.11.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" + integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg== dependencies: fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" uri-js "^4.2.2" -alphanum-sort@^1.0.1, alphanum-sort@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" +ansi-align@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" + integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== + dependencies: + string-width "^4.1.0" -amdefine@>=0.0.4: - version "1.0.1" - resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" +ansi-colors@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== ansi-colors@^3.0.0: version "3.2.4" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" -ansi-escapes@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" - -ansi-html@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" +ansi-html-community@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" + integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== ansi-regex@^2.0.0: version "2.1.1" @@ -1387,16 +2435,28 @@ ansi-regex@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" -ansi-styles@^3.2.0, ansi-styles@^3.2.1: +ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" dependencies: color-convert "^1.9.0" +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + ansi-styles@^4.1.0: version "4.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" @@ -1405,18 +2465,14 @@ ansi-styles@^4.1.0: "@types/color-name" "^1.1.1" color-convert "^2.0.1" -ansi-styles@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.0.0.tgz#cb102df1c56f5123eab8b67cd7b98027a0279178" - -anymatch@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" +ansi-to-html@^0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/ansi-to-html/-/ansi-to-html-0.7.2.tgz#a92c149e4184b571eb29a0135ca001a8e2d710cb" + integrity sha512-v6MqmEpNlxF+POuyhKkidusCHWWkaLcGRURzivcU3I9tv7k4JVhFcnukrM5Rlk2rUywdZuzYAZ+kbZqWCnfN3g== dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" + entities "^2.2.0" -anymatch@~3.1.1: +anymatch@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== @@ -1424,265 +2480,116 @@ anymatch@~3.1.1: normalize-path "^3.0.0" picomatch "^2.0.4" -aproba@^1.0.3, aproba@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - -are-we-there-yet@~1.1.2: - version "1.1.5" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - dependencies: - sprintf-js "~1.0.2" - -arr-diff@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" - dependencies: - arr-flatten "^1.0.1" - -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" - -arr-flatten@^1.0.1, arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - -array-find-index@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" -array-includes@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d" +array-includes@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9" + integrity sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw== dependencies: - define-properties "^1.1.2" - es-abstract "^1.7.0" - -array-slice@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-0.2.3.tgz#dd3cfb80ed7973a75117cdac69b0b99ec86186f5" + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + get-intrinsic "^1.1.1" + is-string "^1.0.7" array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array-unique@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" - -array-unique@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" - -arraybuffer.slice@~0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" +array.prototype.flat@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz#07e0975d84bbc7c48cd1879d609e682598d33e13" + integrity sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= -arrify@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" - integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== - -asn1.js@^4.0.0: - version "4.10.1" - resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" - dependencies: - bn.js "^4.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - -assert@^1.1.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" - dependencies: - util "0.10.3" - -assertion-error@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.0.0.tgz#c7f85438fdd466bc7ca16ab90c81513797a5d23b" - -assertion-error@^1.0.1: +assertion-error@1.1.0, assertion-error@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - -ast-types@0.x.x: - version "0.11.7" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.11.7.tgz#f318bf44e339db6a320be0009ded64ec1471f46c" - -astral-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" - -async-each@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" - -async-limiter@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" - -async@1.x: - version "1.5.2" - resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" - -async@^2.0.0: - version "2.6.2" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" - dependencies: - lodash "^4.17.11" +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== -async@^2.5.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" - dependencies: - lodash "^4.17.10" +async@^3.2.3: + version "3.2.4" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" + integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== -atob@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -autoprefixer@^6.3.1, autoprefixer@^6.4.0: - version "6.7.7" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014" +autoprefixer@10.4.12: + version "10.4.12" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.12.tgz#183f30bf0b0722af54ee5ef257f7d4320bb33129" + integrity sha512-WrCGV9/b97Pa+jtwf5UGaRjgQIg7OK3D06GnoYoZNcG1Xb8Gt3EfuKjlhh9i/VtT16g6PYjZ69jdJ2g8FxSC4Q== dependencies: - browserslist "^1.7.6" - caniuse-db "^1.0.30000634" + browserslist "^4.21.4" + caniuse-lite "^1.0.30001407" + fraction.js "^4.2.0" normalize-range "^0.1.2" - num2fraction "^1.2.2" - postcss "^5.2.16" - postcss-value-parser "^3.2.3" + picocolors "^1.0.0" + postcss-value-parser "^4.2.0" -autoprefixer@^9.8.0: - version "9.8.5" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.5.tgz#2c225de229ddafe1d1424c02791d0c3e10ccccaa" - integrity sha512-C2p5KkumJlsTHoNv9w31NrBRgXhf6eCMteJuHZi2xhkgC+5Vm40MEtCKPhc0qdgAOhox0YPy1SQHTAky05UoKg== +autoprefixer@^9.8.6: + version "9.8.8" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.8.tgz#fd4bd4595385fa6f06599de749a4d5f7a474957a" + integrity sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA== dependencies: browserslist "^4.12.0" - caniuse-lite "^1.0.30001097" - colorette "^1.2.0" + caniuse-lite "^1.0.30001109" normalize-range "^0.1.2" num2fraction "^1.2.2" + picocolors "^0.2.1" postcss "^7.0.32" postcss-value-parser "^4.1.0" -axios@^0.21.0: - version "0.21.1" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8" - integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA== - dependencies: - follow-redirects "^1.10.0" - -babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" - dependencies: - chalk "^1.1.3" - esutils "^2.0.2" - js-tokens "^3.0.2" - -babel-core@^6.1.4, babel-core@^6.26.0: - version "6.26.3" - resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207" - dependencies: - babel-code-frame "^6.26.0" - babel-generator "^6.26.0" - babel-helpers "^6.24.1" - babel-messages "^6.23.0" - babel-register "^6.26.0" - babel-runtime "^6.26.0" - babel-template "^6.26.0" - babel-traverse "^6.26.0" - babel-types "^6.26.0" - babylon "^6.18.0" - convert-source-map "^1.5.1" - debug "^2.6.9" - json5 "^0.5.1" - lodash "^4.17.4" - minimatch "^3.0.4" - path-is-absolute "^1.0.1" - private "^0.1.8" - slash "^1.0.0" - source-map "^0.5.7" - -babel-eslint@^7.0.0: - version "7.2.3" - resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-7.2.3.tgz#b2fe2d80126470f5c19442dc757253a897710827" - dependencies: - babel-code-frame "^6.22.0" - babel-traverse "^6.23.1" - babel-types "^6.23.0" - babylon "^6.17.0" - -babel-generator@^6.26.0: - version "6.26.1" - resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90" - dependencies: - babel-messages "^6.23.0" - babel-runtime "^6.26.0" - babel-types "^6.26.0" - detect-indent "^4.0.0" - jsesc "^1.3.0" - lodash "^4.17.4" - source-map "^0.5.7" - trim-right "^1.0.1" - -babel-helpers@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" - dependencies: - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-loader@^8.0.6: - version "8.0.6" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.6.tgz#e33bdb6f362b03f4bb141a0c21ab87c501b70dfb" - integrity sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw== +axios@^0.27.2: + version "0.27.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" + integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== dependencies: - find-cache-dir "^2.0.0" - loader-utils "^1.0.2" - mkdirp "^0.5.1" - pify "^4.0.1" + follow-redirects "^1.14.9" + form-data "^4.0.0" -babel-messages@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" +babel-loader@8.2.5: + version "8.2.5" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.5.tgz#d45f585e654d5a5d90f5350a779d7647c5ed512e" + integrity sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ== dependencies: - babel-runtime "^6.22.0" + find-cache-dir "^3.3.1" + loader-utils "^2.0.0" + make-dir "^3.1.0" + schema-utils "^2.6.5" -babel-plugin-dynamic-import-node@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f" - integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ== +babel-plugin-dynamic-import-node@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" + integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== dependencies: object.assign "^4.1.0" -babel-plugin-lodash@^3.3.4: +babel-plugin-lodash@3.3.4: version "3.3.4" resolved "https://registry.yarnpkg.com/babel-plugin-lodash/-/babel-plugin-lodash-3.3.4.tgz#4f6844358a1340baed182adbeffa8df9967bc196" integrity sha512-yDZLjK7TCkWl1gpBeBGmuaDIFhZKmkoL+Cu2MUUjv5VxUZx/z7tBGBCBcQs5RI1Bkz5LLmNdjx7paOyQtMovyg== @@ -1693,277 +2600,165 @@ babel-plugin-lodash@^3.3.4: lodash "^4.17.10" require-package-name "^2.0.1" -babel-register@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" +babel-plugin-polyfill-corejs2@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122" + integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q== dependencies: - babel-core "^6.26.0" - babel-runtime "^6.26.0" - core-js "^2.5.0" - home-or-tmp "^2.0.0" - lodash "^4.17.4" - mkdirp "^0.5.1" - source-map-support "^0.4.15" - -babel-runtime@^6.22.0, babel-runtime@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" - dependencies: - core-js "^2.4.0" - regenerator-runtime "^0.11.0" - -babel-template@^6.24.1, babel-template@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" - dependencies: - babel-runtime "^6.26.0" - babel-traverse "^6.26.0" - babel-types "^6.26.0" - babylon "^6.18.0" - lodash "^4.17.4" - -babel-traverse@^6.23.1, babel-traverse@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" - dependencies: - babel-code-frame "^6.26.0" - babel-messages "^6.23.0" - babel-runtime "^6.26.0" - babel-types "^6.26.0" - babylon "^6.18.0" - debug "^2.6.8" - globals "^9.18.0" - invariant "^2.2.2" - lodash "^4.17.4" - -babel-types@^6.23.0, babel-types@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" - dependencies: - babel-runtime "^6.26.0" - esutils "^2.0.2" - lodash "^4.17.4" - to-fast-properties "^1.0.3" + "@babel/compat-data" "^7.17.7" + "@babel/helper-define-polyfill-provider" "^0.3.3" + semver "^6.1.1" -babylon@^6.17.0, babylon@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" +babel-plugin-polyfill-corejs3@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz#56ad88237137eade485a71b52f72dbed57c6230a" + integrity sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.3" + core-js-compat "^3.25.1" -backo2@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" +babel-plugin-polyfill-regenerator@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747" + integrity sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.3" bail@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== -balanced-match@^0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" - balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" -base64-arraybuffer@0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" - -base64-js@^1.0.2: - version "1.3.0" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" - -base64id@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/base64id/-/base64id-1.0.0.tgz#47688cb99bb6804f0e06d3e763b1c32e57d8e6b6" - -base@^0.11.1: - version "0.11.2" - resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" - dependencies: - cache-base "^1.0.1" - class-utils "^0.3.5" - component-emitter "^1.2.1" - define-property "^1.0.0" - isobject "^3.0.1" - mixin-deep "^1.2.0" - pascalcase "^0.1.1" +balanced-match@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9" + integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA== -better-assert@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522" - dependencies: - callsite "1.0.0" +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -big.js@^3.1.3: - version "3.2.0" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" +base64id@2.0.0, base64id@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" + integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" -binary-extensions@^1.0.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.12.0.tgz#c2d780f53d45bba8317a8902d4ceeaf3a6385b14" - binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -blob@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" - -bluebird@^3.1.1, bluebird@^3.3.0: - version "3.5.3" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7" - -bluebird@^3.5.5: - version "3.7.2" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" - integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== +bl@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: - version "4.11.8" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" +body-parser@1.20.1: + version "1.20.1" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" + integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== + dependencies: + bytes "3.1.2" + content-type "~1.0.4" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.1" + type-is "~1.6.18" + unpipe "1.0.0" -body-parser@1.18.3, body-parser@^1.16.1: - version "1.18.3" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4" +body-parser@^1.19.0: + version "1.19.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.2.tgz#4714ccd9c157d44797b8b5607d72c0b89952f26e" + integrity sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw== dependencies: - bytes "3.0.0" + bytes "3.1.2" content-type "~1.0.4" debug "2.6.9" depd "~1.1.2" - http-errors "~1.6.3" - iconv-lite "0.4.23" + http-errors "1.8.1" + iconv-lite "0.4.24" on-finished "~2.3.0" - qs "6.5.2" - raw-body "2.3.3" - type-is "~1.6.16" + qs "6.9.7" + raw-body "2.4.3" + type-is "~1.6.18" -body-scroll-lock@^2.6.4: - version "2.6.4" - resolved "https://registry.yarnpkg.com/body-scroll-lock/-/body-scroll-lock-2.6.4.tgz#567abc60ef4d656a79156781771398ef40462e94" - integrity sha512-NP08WsovlmxEoZP9pdlqrE+AhNaivlTrz9a0FF37BQsnOrpN48eNqivKkE7SYpM9N+YIPjsdVzfLAUQDBm6OQw== +body-scroll-lock@3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/body-scroll-lock/-/body-scroll-lock-3.1.5.tgz#c1392d9217ed2c3e237fee1e910f6cdd80b7aaec" + integrity sha512-Yi1Xaml0EvNA0OYWxXiYNqY24AfWkbA6w5vxE7GWxtKfzIbZM+Qw+aSmkgsbWzbHiy/RCSkUZBplVxTA+E4jJg== -boolbase@~1.0.0: +boolbase@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== -brace-expansion@^1.0.0, brace-expansion@^1.1.7: +boxen@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" + integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^6.2.0" + chalk "^4.1.0" + cli-boxes "^2.2.1" + string-width "^4.2.2" + type-fest "^0.20.2" + widest-line "^3.1.0" + wrap-ansi "^7.0.0" + +brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" dependencies: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^0.1.2: - version "0.1.5" - resolved "https://registry.yarnpkg.com/braces/-/braces-0.1.5.tgz#c085711085291d8b75fdd74eab0f8597280711e6" - dependencies: - expand-range "^0.1.0" - -braces@^1.8.2: - version "1.8.5" - resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" - dependencies: - expand-range "^1.8.1" - preserve "^0.2.0" - repeat-element "^1.1.2" - -braces@^2.3.1, braces@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== dependencies: - arr-flatten "^1.1.0" - array-unique "^0.3.2" - extend-shallow "^2.0.1" - fill-range "^4.0.0" - isobject "^3.0.1" - repeat-element "^1.1.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" + balanced-match "^1.0.0" -braces@^3.0.1, braces@~3.0.2: +braces@^3.0.1, braces@^3.0.2, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" -brorand@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" - -browser-stdout@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" - -browserify-aes@^1.0.0, browserify-aes@^1.0.4: - version "1.2.0" - resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" - dependencies: - buffer-xor "^1.0.3" - cipher-base "^1.0.0" - create-hash "^1.1.0" - evp_bytestokey "^1.0.3" - inherits "^2.0.1" - safe-buffer "^5.0.1" - -browserify-cipher@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" - dependencies: - browserify-aes "^1.0.4" - browserify-des "^1.0.0" - evp_bytestokey "^1.0.0" - -browserify-des@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" - dependencies: - cipher-base "^1.0.1" - des.js "^1.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -browserify-rsa@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" - dependencies: - bn.js "^4.1.0" - randombytes "^2.0.1" - -browserify-sign@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" - dependencies: - bn.js "^4.1.1" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.2" - elliptic "^6.0.0" - inherits "^2.0.1" - parse-asn1 "^5.0.0" - -browserify-zlib@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" - dependencies: - pako "~1.0.5" +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== -browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: - version "1.7.7" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9" +browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4.20.3: + version "4.21.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.3.tgz#5df277694eb3c48bc5c4b05af3e8b7e09c5a6d1a" + integrity sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ== dependencies: - caniuse-db "^1.0.30000639" - electron-to-chromium "^1.2.7" + caniuse-lite "^1.0.30001370" + electron-to-chromium "^1.4.202" + node-releases "^2.0.6" + update-browserslist-db "^1.0.5" browserslist@^4.12.0: version "4.13.0" @@ -1975,161 +2770,74 @@ browserslist@^4.12.0: escalade "^3.0.1" node-releases "^1.1.58" -browserslist@^4.6.0, browserslist@^4.8.2: - version "4.8.2" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.8.2.tgz#b45720ad5fbc8713b7253c20766f701c9a694289" - integrity sha512-+M4oeaTplPm/f1pXDw84YohEv7B1i/2Aisei8s4s6k3QsoSHa7i5sz8u/cGQkkatCPxMASKxPualR4wwYgVboA== +browserslist@^4.20.2: + version "4.21.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.1.tgz#c9b9b0a54c7607e8dc3e01a0d311727188011a00" + integrity sha512-Nq8MFCSrnJXSc88yliwlzQe3qNe3VntIjhsArW9IJOEPSHNx23FalwApUVbzAWABLhYJJ7y8AynWI/XM8OdfjQ== dependencies: - caniuse-lite "^1.0.30001015" - electron-to-chromium "^1.3.322" - node-releases "^1.1.42" - -buffer-alloc-unsafe@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" + caniuse-lite "^1.0.30001359" + electron-to-chromium "^1.4.172" + node-releases "^2.0.5" + update-browserslist-db "^1.0.4" -buffer-alloc@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" +browserslist@^4.21.3, browserslist@^4.21.4: + version "4.21.4" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" + integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== dependencies: - buffer-alloc-unsafe "^1.1.0" - buffer-fill "^1.0.0" + caniuse-lite "^1.0.30001400" + electron-to-chromium "^1.4.251" + node-releases "^2.0.6" + update-browserslist-db "^1.0.9" buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= -buffer-fill@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" - buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" -buffer-xor@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" - -buffer@^4.3.0: - version "4.9.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - isarray "^1.0.0" - -builtin-modules@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" - -builtin-status-codes@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" - -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - -cacache@^12.0.2: - version "12.0.4" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" - integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== - dependencies: - bluebird "^3.5.5" - chownr "^1.1.1" - figgy-pudding "^3.5.1" - glob "^7.1.4" - graceful-fs "^4.1.15" - infer-owner "^1.0.3" - lru-cache "^5.1.1" - mississippi "^3.0.0" - mkdirp "^0.5.1" - move-concurrently "^1.0.1" - promise-inflight "^1.0.1" - rimraf "^2.6.3" - ssri "^6.0.1" - unique-filename "^1.1.1" - y18n "^4.0.0" - -cacache@^15.0.5: - version "15.0.6" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.6.tgz#65a8c580fda15b59150fb76bf3f3a8e45d583099" - integrity sha512-g1WYDMct/jzW+JdWEyjaX2zoBkZ6ZT9VpOyp2I/VMtDsNLffNat3kqPFfi1eDRSK9/SuKGyORDHcQMcPF8sQ/w== +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== dependencies: - "@npmcli/move-file" "^1.0.1" - chownr "^2.0.0" - fs-minipass "^2.0.0" - glob "^7.1.4" - infer-owner "^1.0.4" - lru-cache "^6.0.0" - minipass "^3.1.1" - minipass-collect "^1.0.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.2" - mkdirp "^1.0.3" - p-map "^4.0.0" - promise-inflight "^1.0.1" - rimraf "^3.0.2" - ssri "^8.0.1" - tar "^6.0.2" - unique-filename "^1.1.1" + base64-js "^1.3.1" + ieee754 "^1.1.13" -cache-base@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" +builtins@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/builtins/-/builtins-5.0.1.tgz#87f6db9ab0458be728564fa81d876d8d74552fa9" + integrity sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ== dependencies: - collection-visit "^1.0.0" - component-emitter "^1.2.1" - get-value "^2.0.6" - has-value "^1.0.0" - isobject "^3.0.1" - set-value "^2.0.0" - to-object-path "^0.3.0" - union-value "^1.0.0" - unset-value "^1.0.0" + semver "^7.0.0" -caller-callsite@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" - integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= - dependencies: - callsites "^2.0.0" +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== -caller-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" - integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== dependencies: - caller-callsite "^2.0.0" - -callsite@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" - -callsites@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" - integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= + function-bind "^1.1.1" + get-intrinsic "^1.0.2" callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" -camel-case@3.0.x: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" - dependencies: - no-case "^2.2.0" - upper-case "^1.1.1" - -camelcase-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" +camel-case@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" + integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== dependencies: - camelcase "^2.0.0" - map-obj "^1.0.0" + pascal-case "^3.1.2" + tslib "^2.0.3" camelcase-keys@^6.2.2: version "6.2.2" @@ -2140,10 +2848,6 @@ camelcase-keys@^6.2.2: map-obj "^4.0.0" quick-lru "^4.0.1" -camelcase@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" - camelcase@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" @@ -2158,56 +2862,62 @@ camelcase@^6.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.0.0.tgz#5259f7c30e35e278f1bdc2a4d91230b37cad981e" integrity sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w== -can-promise@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/can-promise/-/can-promise-0.0.1.tgz#7a7597ad801fb14c8b22341dfec314b6bd6ad8d3" - dependencies: - window-or-global "^1.0.1" +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-api@^1.5.2: - version "1.6.1" - resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c" +caniuse-api@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" + integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== dependencies: - browserslist "^1.3.6" - caniuse-db "^1.0.30000529" + browserslist "^4.0.0" + caniuse-lite "^1.0.0" lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: - version "1.0.30000928" - resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000928.tgz#2e83d2b14276442da239511615eb7c62fed0cfa7" - -caniuse-lite@^1.0.30001015: - version "1.0.30001015" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001015.tgz#15a7ddf66aba786a71d99626bc8f2b91c6f0f5f0" - integrity sha512-/xL2AbW/XWHNu1gnIrO8UitBGoFthcsDgU9VLK1/dpsoxbaD5LscHozKze05R6WLsBvLhqv78dAPozMFQBYLbQ== +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001370: + version "1.0.30001376" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001376.tgz#af2450833e5a06873fbb030a9556ca9461a2736d" + integrity sha512-I27WhtOQ3X3v3it9gNs/oTpoE5KpwmqKR5oKPA8M0G7uMXh9Ty81Q904HpKUrM30ei7zfcL5jE7AXefgbOfMig== -caniuse-lite@^1.0.30001093, caniuse-lite@^1.0.30001097: +caniuse-lite@^1.0.30001093: version "1.0.30001107" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001107.tgz#809360df7a5b3458f627aa46b0f6ed6d5239da9a" - integrity sha512-86rCH+G8onCmdN4VZzJet5uPELII59cUzDphko3thQFgAQG1RNa+sVLDoALIhRYmflo5iSIzWY3vu1XTWtNMQQ== - -ccount@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.0.5.tgz#ac82a944905a65ce204eb03023157edf29425c17" - integrity sha512-MOli1W+nfbPLlKEhInaxhRdp7KVLFxLN5ykwzHgLsLI3H3gs5jjFAK4Eoj3OzzcxCtumDaI8onoVDeQyWaNTkw== - -chai-nightwatch@~0.1.x: - version "0.1.1" - resolved "https://registry.yarnpkg.com/chai-nightwatch/-/chai-nightwatch-0.1.1.tgz#1ca56de768d3c0868fe7fc2f4d32c2fe894e6be9" - dependencies: - assertion-error "1.0.0" - deep-eql "0.1.3" - -chai@^3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/chai/-/chai-3.5.0.tgz#4d02637b067fe958bdbfdd3a40ec56fef7373247" - dependencies: - assertion-error "^1.0.1" - deep-eql "^0.1.3" - type-detect "^1.0.0" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001107.tgz#809360df7a5b3458f627aa46b0f6ed6d5239da9a" + integrity sha512-86rCH+G8onCmdN4VZzJet5uPELII59cUzDphko3thQFgAQG1RNa+sVLDoALIhRYmflo5iSIzWY3vu1XTWtNMQQ== -chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: +caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001359: + version "1.0.30001366" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001366.tgz#c73352c83830a9eaf2dea0ff71fb4b9a4bbaa89c" + integrity sha512-yy7XLWCubDobokgzudpkKux8e0UOOnLHE6mlNJBzT3lZJz6s5atSEzjoL+fsCPkI0G8MP5uVdDx1ur/fXEWkZA== + +caniuse-lite@^1.0.30001400, caniuse-lite@^1.0.30001407: + version "1.0.30001418" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz#5f459215192a024c99e3e3a53aac310fc7cf24e6" + integrity sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg== + +chai-nightwatch@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/chai-nightwatch/-/chai-nightwatch-0.5.3.tgz#980ecf63dde5a04e7f3524370682c7ff01178ffb" + integrity sha512-38ixH/mqpY6IwnZkz6xPqx8aB5/KVR+j6VPugcir3EGOsphnWXrPH/mUt8Jp+ninL6ghY0AaJDQ10hSfCPGy/g== + dependencies: + assertion-error "1.1.0" + +chai@4.3.7: + version "4.3.7" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51" + integrity sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^4.1.2" + get-func-name "^2.0.0" + loupe "^2.3.1" + pathval "^1.1.1" + type-detect "^4.0.5" + +chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" dependencies: @@ -2217,7 +2927,7 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: +chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" dependencies: @@ -2225,7 +2935,15 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.1.0: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== @@ -2233,19 +2951,6 @@ chalk@^4.0.0, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.4.0.tgz#5199a3ddcd0c1efe23bc08c1b027b06176e0c64f" - dependencies: - ansi-styles "~1.0.0" - has-color "~0.1.0" - strip-ansi "~0.1.0" - -character-entities-html4@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.4.tgz#0e64b0a3753ddbf1fdc044c5fd01d0199a02e125" - integrity sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g== - character-entities-legacy@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" @@ -2261,123 +2966,63 @@ character-reference-invalid@^1.0.0: resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== -chardet@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" - -chokidar@^2.0.0, chokidar@^2.0.3: - version "2.1.6" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.6.tgz#b6cad653a929e244ce8a834244164d241fa954c5" - dependencies: - anymatch "^2.0.0" - async-each "^1.0.1" - braces "^2.3.2" - glob-parent "^3.1.0" - inherits "^2.0.3" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^3.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.2.1" - upath "^1.1.1" - optionalDependencies: - fsevents "^1.2.7" - -chokidar@^2.1.8: - version "2.1.8" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" - integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== - dependencies: - anymatch "^2.0.0" - async-each "^1.0.1" - braces "^2.3.2" - glob-parent "^3.1.0" - inherits "^2.0.3" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^3.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.2.1" - upath "^1.1.1" - optionalDependencies: - fsevents "^1.2.7" +check-error@1.0.2, check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== -chokidar@^3.4.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" - integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== +chokidar@3.5.3, "chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.1: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== dependencies: - anymatch "~3.1.1" + anymatch "~3.1.2" braces "~3.0.2" - glob-parent "~5.1.0" + glob-parent "~5.1.2" is-binary-path "~2.1.0" is-glob "~4.0.1" normalize-path "~3.0.0" - readdirp "~3.5.0" + readdirp "~3.6.0" optionalDependencies: - fsevents "~2.3.1" - -chownr@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494" - -chownr@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" - integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + fsevents "~2.3.2" -chromatism@^3.0.0: +chromatism@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/chromatism/-/chromatism-3.0.0.tgz#a7249d353c1e4f3577e444ac41171c4e2e624b12" + integrity sha1-pySdNTweTzV35ESsQRccTi5iSxI= chrome-trace-event@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== -chromedriver@^87.0.1: - version "87.0.4" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-87.0.4.tgz#749f69e9427880abff19c1838258c35238397e50" - integrity sha512-kD4N/L8c0nAzh1eEAiAbEIq6Pn5TvGvckODvP5dPqF90q5tPiAJZCoWWSOUV/mrPxiodjHPfmNeOfGERHugzug== +chromedriver@104.0.0: + version "104.0.0" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-104.0.0.tgz#2f730f52a567280872567bf3497e1c673b6f4275" + integrity sha512-zbHZutN2ATo19xA6nXwwLn+KueD/5w8ap5m4b6bCb8MIaRFnyDwMbFoy7oFAjlSMpCFL3KSaZRiWUwjj//N3yQ== dependencies: - "@testim/chrome-version" "^1.0.7" - axios "^0.21.0" + "@testim/chrome-version" "^1.1.2" + axios "^0.27.2" del "^6.0.0" extract-zip "^2.0.1" https-proxy-agent "^5.0.0" - mkdirp "^1.0.4" proxy-from-env "^1.1.0" tcp-port-used "^1.0.1" -cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -circular-json@^0.5.5: - version "0.5.9" - resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.9.tgz#932763ae88f4f7dead7a0d09c8a51a4743a53b1d" - -clap@^1.0.9: - version "1.2.3" - resolved "https://registry.yarnpkg.com/clap/-/clap-1.2.3.tgz#4f36745b32008492557f46412d66d50cb99bce51" - dependencies: - chalk "^1.1.3" +ci-info@3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" + integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw== -class-utils@^0.3.5: - version "0.3.6" - resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" - dependencies: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" +ci-info@^3.2.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.5.0.tgz#bfac2a29263de4c829d806b1ab478e35091e171f" + integrity sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw== -clean-css@4.2.x: - version "4.2.1" - resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17" +clean-css@^5.2.2: + version "5.3.1" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.1.tgz#d0610b0b90d125196a2894d35366f734e5d7aa32" + integrity sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg== dependencies: source-map "~0.6.0" @@ -2386,11 +3031,10 @@ clean-stack@^2.0.0: resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -cli-cursor@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" - dependencies: - restore-cursor "^1.0.1" +cli-boxes@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== cli-cursor@^2.1.0: version "2.1.0" @@ -2398,21 +3042,45 @@ cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" -cli-spinners@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.2.0.tgz#85078737913b880f6ec9ffe7b65e83ec7776284f" +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" -cli-width@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" +cli-spinners@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a" + integrity sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg== -cliui@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" +cli-spinners@^2.5.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a" + integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== + +click-outside-vue3@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/click-outside-vue3/-/click-outside-vue3-4.0.1.tgz#81a6ac01696b301764b42db6fdbdf28e7cd8ef95" + integrity sha512-sbplNecrup5oGqA3o4bo8XmvHRT6q9fvw21Z67aDbTqB9M6LF7CuYLTlLvNtOgKU6W3zst5H5zJuEh4auqA34g== + +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== dependencies: - string-width "^2.1.1" - strip-ansi "^4.0.0" - wrap-ansi "^2.0.0" + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" clone-deep@^4.0.1: version "4.0.1" @@ -2432,34 +3100,9 @@ clone-regexp@^2.1.0: clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== -co@~3.0.6: - version "3.0.6" - resolved "https://registry.yarnpkg.com/co/-/co-3.0.6.tgz#1445f226c5eb956138e68c9ac30167ea7d2e6bda" - -coa@~1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.4.tgz#a9ef153660d6a86a8bdec0289a5c684d217432fd" - dependencies: - q "^1.1.2" - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - -collapse-white-space@^1.0.2: - version "1.0.6" - resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" - integrity sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ== - -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" - -color-convert@^1.3.0, color-convert@^1.9.0: +color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" dependencies: @@ -2472,7 +3115,7 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@1.1.3, color-name@^1.0.0: +color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" @@ -2481,146 +3124,77 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-string@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991" - dependencies: - color-name "^1.0.0" - -color@^0.11.0: - version "0.11.4" - resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764" - dependencies: - clone "^1.0.2" - color-convert "^1.3.0" - color-string "^0.3.0" - -colorette@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b" - integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== - -colormin@^1.0.5: - version "1.1.2" - resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133" - dependencies: - color "^0.11.0" - css-color-names "0.0.4" - has "^1.0.1" - -colors@^1.1.0: - version "1.3.3" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.3.tgz#39e005d546afe01e01f9c4ca8fa50f686a01205d" - -colors@~0.6.0: - version "0.6.2" - resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc" - -colors@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" - -combine-lists@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/combine-lists/-/combine-lists-1.0.1.tgz#458c07e09e0d900fc28b70a3fec2dacd1d2cb7f6" - dependencies: - lodash "^4.5.0" +colord@^2.9.1: + version "2.9.3" + resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" + integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== -commander@2.17.x, commander@~2.17.1: - version "2.17.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" +colors@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== -commander@2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: - graceful-readlink ">= 1.0.0" + delayed-stream "~1.0.0" commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +commander@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" -component-bind@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" - -component-emitter@1.2.1, component-emitter@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" - -component-inherit@0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" +component-emitter@~1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -concat-stream@^1.5.0: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -connect-history-api-fallback@^1.1.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" +connect-history-api-fallback@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" + integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== -connect@^3.6.0: - version "3.6.6" - resolved "https://registry.yarnpkg.com/connect/-/connect-3.6.6.tgz#09eff6c55af7236e137135a72574858b6786f524" +connect@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" + integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== dependencies: debug "2.6.9" - finalhandler "1.1.0" - parseurl "~1.3.2" + finalhandler "1.1.2" + parseurl "~1.3.3" utils-merge "1.0.1" -console-browserify@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" - dependencies: - date-now "^0.1.4" - -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - -consolidate@^0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.14.5.tgz#5a25047bc76f73072667c8cb52c989888f494c63" +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== dependencies: - bluebird "^3.1.1" - -constants-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" - -contains-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" - -content-disposition@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + safe-buffer "5.2.1" content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" -convert-source-map@^1.5.1: - version "1.6.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20" - dependencies: - safe-buffer "~5.1.1" - convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" @@ -2632,260 +3206,194 @@ cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" -cookie@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" - -copy-concurrently@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" - dependencies: - aproba "^1.1.1" - fs-write-stream-atomic "^1.0.8" - iferr "^0.1.5" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.0" +cookie@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" +cookie@~0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== -copy-webpack-plugin@^6.4.1: - version "6.4.1" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-6.4.1.tgz#138cd9b436dbca0a6d071720d5414848992ec47e" - integrity sha512-MXyPCjdPVx5iiWyl40Va3JGh27bKzOTNY3NjUTrosD2q7dR/cLD0013uqJ3BpFbUjyONINjb6qI7nDIJujrMbA== +copy-webpack-plugin@11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz#96d4dbdb5f73d02dd72d0528d1958721ab72e04a" + integrity sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ== dependencies: - cacache "^15.0.5" - fast-glob "^3.2.4" - find-cache-dir "^3.3.1" - glob-parent "^5.1.1" - globby "^11.0.1" - loader-utils "^2.0.0" + fast-glob "^3.2.11" + glob-parent "^6.0.1" + globby "^13.1.1" normalize-path "^3.0.0" - p-limit "^3.0.2" - schema-utils "^3.0.0" - serialize-javascript "^5.0.1" - webpack-sources "^1.4.3" + schema-utils "^4.0.0" + serialize-javascript "^6.0.0" -core-js-compat@^3.4.7: - version "3.4.8" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.4.8.tgz#f72e6a4ed76437ea710928f44615f926a81607d5" - integrity sha512-l3WTmnXHV2Sfu5VuD7EHE2w7y+K68+kULKt5RJg8ZJk3YhHF1qLD4O8v8AmNq+8vbOwnPFFDvds25/AoEvMqlQ== +core-js-compat@^3.25.1: + version "3.26.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.26.0.tgz#94e2cf8ba3e63800c4956ea298a6473bc9d62b44" + integrity sha512-piOX9Go+Z4f9ZiBFLnZ5VrOpBl0h7IGCkiFUN11QTe6LjAvOT3ifL/5TdoizMh99hcGy5SoLyWbapIY/PIb/3A== dependencies: - browserslist "^4.8.2" - semver "^6.3.0" - -core-js@^2.2.0, core-js@^2.4.0, core-js@^2.5.0: - version "2.6.2" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.2.tgz#267988d7268323b349e20b4588211655f0e83944" + browserslist "^4.21.4" core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" -cosmiconfig@^2.1.0, cosmiconfig@^2.1.1: - version "2.2.2" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-2.2.2.tgz#6173cebd56fac042c1f4390edf7af6c07c7cb892" - dependencies: - is-directory "^0.3.1" - js-yaml "^3.4.3" - minimist "^1.2.0" - object-assign "^4.1.0" - os-homedir "^1.0.1" - parse-json "^2.2.0" - require-from-string "^1.1.0" - -cosmiconfig@^5.0.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" - integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== +cors@~2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== dependencies: - import-fresh "^2.0.0" - is-directory "^0.3.1" - js-yaml "^3.13.1" - parse-json "^4.0.0" + object-assign "^4" + vary "^1" -cosmiconfig@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" - integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== +cosmiconfig@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" + integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== dependencies: "@types/parse-json" "^4.0.0" - import-fresh "^3.1.0" + import-fresh "^3.2.1" parse-json "^5.0.0" path-type "^4.0.0" - yaml "^1.7.2" - -create-ecdh@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" - dependencies: - bn.js "^4.1.0" - elliptic "^6.0.0" + yaml "^1.10.0" -create-hash@^1.1.0, create-hash@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" - dependencies: - cipher-base "^1.0.1" - inherits "^2.0.1" - md5.js "^1.3.4" - ripemd160 "^2.0.1" - sha.js "^2.4.0" +cropperjs@1.5.12: + version "1.5.12" + resolved "https://registry.yarnpkg.com/cropperjs/-/cropperjs-1.5.12.tgz#d9c0db2bfb8c0d769d51739e8f916bbc44e10f50" + integrity sha512-re7UdjE5UnwdrovyhNzZ6gathI4Rs3KGCBSc8HCIjUo5hO42CtzyblmWLj6QWVw7huHyDMfpKxhiO2II77nhDw== -create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: - version "1.1.7" - resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" +cross-spawn@7.0.3, cross-spawn@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== dependencies: - cipher-base "^1.0.3" - create-hash "^1.1.0" - inherits "^2.0.1" - ripemd160 "^2.0.0" - safe-buffer "^5.0.1" - sha.js "^2.4.8" + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" -cropperjs@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/cropperjs/-/cropperjs-1.4.3.tgz#dc44d6c9e73269e7f96894c726ab91e8913f9e90" +css-declaration-sorter@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.3.0.tgz#72ebd995c8f4532ff0036631f7365cce9759df14" + integrity sha512-OGT677UGHJTAVMRhPO+HJ4oKln3wkBTwtDFH0ojbqm+MJm6xuDMHp2nkhh/ThaBqq20IbraBQSWKfSLNHQO9Og== + +css-loader@6.7.1: + version "6.7.1" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.1.tgz#e98106f154f6e1baf3fc3bc455cb9981c1d5fd2e" + integrity sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw== + dependencies: + icss-utils "^5.1.0" + postcss "^8.4.7" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.0" + postcss-modules-scope "^3.0.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.2.0" + semver "^7.3.5" + +css-minimizer-webpack-plugin@4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.2.2.tgz#79f6199eb5adf1ff7ba57f105e3752d15211eb35" + integrity sha512-s3Of/4jKfw1Hj9CxEO1E5oXhQAxlayuHO2y/ML+C6I9sQ7FdzfEV6QgMLN3vI+qFsjJGIAFLKtQK7t8BOXAIyA== + dependencies: + cssnano "^5.1.8" + jest-worker "^29.1.2" + postcss "^8.4.17" + schema-utils "^4.0.0" + serialize-javascript "^6.0.0" + source-map "^0.6.1" -cross-spawn@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" - dependencies: - lru-cache "^4.0.1" - which "^1.2.9" - -cross-spawn@^6.0.0, cross-spawn@^6.0.5: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - -crypto-browserify@^3.11.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" - dependencies: - browserify-cipher "^1.0.0" - browserify-sign "^4.0.0" - create-ecdh "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.0" - diffie-hellman "^5.0.0" - inherits "^2.0.1" - pbkdf2 "^3.0.3" - public-encrypt "^4.0.0" - randombytes "^2.0.0" - randomfill "^1.0.3" - -css-color-names@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" - -css-loader@^0.28.0: - version "0.28.11" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.11.tgz#c3f9864a700be2711bb5a2462b2389b1a392dab7" - dependencies: - babel-code-frame "^6.26.0" - css-selector-tokenizer "^0.7.0" - cssnano "^3.10.0" - icss-utils "^2.1.0" - loader-utils "^1.0.2" - lodash.camelcase "^4.3.0" - object-assign "^4.1.1" - postcss "^5.0.6" - postcss-modules-extract-imports "^1.2.0" - postcss-modules-local-by-default "^1.2.0" - postcss-modules-scope "^1.1.0" - postcss-modules-values "^1.3.0" - postcss-value-parser "^3.3.0" - source-list-map "^2.0.0" - -css-select@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" +css-select@^4.1.3: + version "4.3.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" + integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== dependencies: - boolbase "~1.0.0" - css-what "2.1" - domutils "1.5.1" - nth-check "~1.0.1" + boolbase "^1.0.0" + css-what "^6.0.1" + domhandler "^4.3.1" + domutils "^2.8.0" + nth-check "^2.0.1" -css-selector-tokenizer@^0.7.0: - version "0.7.1" - resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz#a177271a8bca5019172f4f891fc6eed9cbf68d5d" +css-tree@^1.1.2, css-tree@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== dependencies: - cssesc "^0.1.0" - fastparse "^1.1.1" - regexpu-core "^1.0.0" - -css-what@2.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.2.tgz#c0876d9d0480927d7d4920dcd72af3595649554d" + mdn-data "2.0.14" + source-map "^0.6.1" -cssesc@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4" +css-what@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" + integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== -cssnano@^3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38" - dependencies: - autoprefixer "^6.3.1" - decamelize "^1.1.2" - defined "^1.0.0" - has "^1.0.1" - object-assign "^4.0.1" - postcss "^5.0.14" - postcss-calc "^5.2.0" - postcss-colormin "^2.1.8" - postcss-convert-values "^2.3.4" - postcss-discard-comments "^2.0.4" - postcss-discard-duplicates "^2.0.1" - postcss-discard-empty "^2.0.1" - postcss-discard-overridden "^0.1.1" - postcss-discard-unused "^2.2.1" - postcss-filter-plugins "^2.0.0" - postcss-merge-idents "^2.1.5" - postcss-merge-longhand "^2.0.1" - postcss-merge-rules "^2.0.3" - postcss-minify-font-values "^1.0.2" - postcss-minify-gradients "^1.0.1" - postcss-minify-params "^1.0.4" - postcss-minify-selectors "^2.0.4" - postcss-normalize-charset "^1.1.0" - postcss-normalize-url "^3.0.7" - postcss-ordered-values "^2.1.0" - postcss-reduce-idents "^2.2.2" - postcss-reduce-initial "^1.0.0" - postcss-reduce-transforms "^1.0.3" - postcss-svgo "^2.1.1" - postcss-unique-selectors "^2.0.2" - postcss-value-parser "^3.2.3" - postcss-zindex "^2.0.1" - -csso@~2.3.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/csso/-/csso-2.3.2.tgz#ddd52c587033f49e94b71fc55569f252e8ff5f85" +cssnano-preset-default@^5.2.12: + version "5.2.12" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.2.12.tgz#ebe6596ec7030e62c3eb2b3c09f533c0644a9a97" + integrity sha512-OyCBTZi+PXgylz9HAA5kHyoYhfGcYdwFmyaJzWnzxuGRtnMw/kR6ilW9XzlzlRAtB6PLT/r+prYgkef7hngFew== + dependencies: + css-declaration-sorter "^6.3.0" + cssnano-utils "^3.1.0" + postcss-calc "^8.2.3" + postcss-colormin "^5.3.0" + postcss-convert-values "^5.1.2" + postcss-discard-comments "^5.1.2" + postcss-discard-duplicates "^5.1.0" + postcss-discard-empty "^5.1.1" + postcss-discard-overridden "^5.1.0" + postcss-merge-longhand "^5.1.6" + postcss-merge-rules "^5.1.2" + postcss-minify-font-values "^5.1.0" + postcss-minify-gradients "^5.1.1" + postcss-minify-params "^5.1.3" + postcss-minify-selectors "^5.2.1" + postcss-normalize-charset "^5.1.0" + postcss-normalize-display-values "^5.1.0" + postcss-normalize-positions "^5.1.1" + postcss-normalize-repeat-style "^5.1.1" + postcss-normalize-string "^5.1.0" + postcss-normalize-timing-functions "^5.1.0" + postcss-normalize-unicode "^5.1.0" + postcss-normalize-url "^5.1.0" + postcss-normalize-whitespace "^5.1.1" + postcss-ordered-values "^5.1.3" + postcss-reduce-initial "^5.1.0" + postcss-reduce-transforms "^5.1.0" + postcss-svgo "^5.1.0" + postcss-unique-selectors "^5.1.1" + +cssnano-utils@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-3.1.0.tgz#95684d08c91511edfc70d2636338ca37ef3a6861" + integrity sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA== + +cssnano@^5.1.8: + version "5.1.13" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.1.13.tgz#83d0926e72955332dc4802a7070296e6258efc0a" + integrity sha512-S2SL2ekdEz6w6a2epXn4CmMKU4K3KpcyXLKfAYc9UQQqJRkD/2eLUG0vJ3Db/9OvO5GuAdgXw3pFbR6abqghDQ== dependencies: - clap "^1.0.9" - source-map "^0.5.3" + cssnano-preset-default "^5.2.12" + lilconfig "^2.0.3" + yaml "^1.10.2" -currently-unhandled@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" +csso@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" + integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== dependencies: - array-find-index "^1.0.1" + css-tree "^1.1.2" + +csstype@^2.6.8: + version "2.6.20" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.20.tgz#9229c65ea0b260cf4d3d997cb06288e36a8d6dda" + integrity sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA== -custom-event-polyfill@^1.0.7: +custom-event-polyfill@1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/custom-event-polyfill/-/custom-event-polyfill-1.0.7.tgz#9bc993ddda937c1a30ccd335614c6c58c4f87aee" integrity sha512-TDDkd5DkaZxZFM8p+1I3yAlvM3rSr1wbrOliG4yJiwinMZN8z/iGL7BTlDkrJcYTmgUSb4ywVCc3ZaUtOtC76w== @@ -2894,52 +3402,22 @@ custom-event@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" -cyclist@~0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" - -data-uri-to-buffer@1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz#77163ea9c20d8641b4707e8f18abdf9a78f34835" - -date-format@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/date-format/-/date-format-1.2.0.tgz#615e828e233dd1ab9bb9ae0950e0ceccfa6ecad8" - -date-now@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" - -dateformat@^1.0.6: - version "1.0.12" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" - dependencies: - get-stdin "^4.0.1" - meow "^3.3.0" +date-format@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/date-format/-/date-format-4.0.6.tgz#f6138b8f17968df9815b3d101fc06b0523f066c5" + integrity sha512-B9vvg5rHuQ8cbUXE/RMWMyX2YA5TecT3jKF5fLtGNlzPlU7zblSPmAm2OImDbWL+LDOQ6pUm+4LOFz+ywS41Zw== de-indent@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" integrity sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0= -debug@2, debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: +debug@2.6.9, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: ms "2.0.0" -debug@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" - dependencies: - ms "0.7.1" - -debug@2.6.8: - version "2.6.8" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" - dependencies: - ms "2.0.0" - debug@4: version "4.3.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" @@ -2953,19 +3431,34 @@ debug@4.1.0: dependencies: ms "^2.1.1" -debug@=3.1.0, debug@~3.1.0: +debug@4.3.3: + version "4.3.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" + integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== + dependencies: + ms "2.1.2" + +debug@4.3.4, debug@^4.0.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +debug@=3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" dependencies: ms "2.0.0" -debug@^3.1.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" -debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: +debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" dependencies: @@ -2979,64 +3472,50 @@ decamelize-keys@^1.1.0: decamelize "^1.1.0" map-obj "^1.0.0" -decamelize@^1.1.0, decamelize@^1.1.2, decamelize@^1.2.0: +decamelize@^1.1.0, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" -decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== -deep-eql@0.1.3, deep-eql@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-0.1.3.tgz#ef558acab8de25206cd713906d74e56930eb69f2" +deep-eql@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.0.1.tgz#2b65bc89491d193780c452edee2144a91bb0a445" + integrity sha512-D/Oxqobjr+kxaHsgiQBZq9b6iAWdEj5W/JdJm8deNduAPc9CwXQ3BJJCuEqlrPXcy45iOMkGPZ0T81Dnz7UDCA== dependencies: - type-detect "0.1.1" + type-detect "^4.0.0" -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" +deep-eql@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.2.tgz#270ceb902f87724077e6f6449aed81463f42fc1c" + integrity sha512-gT18+YW4CcW/DBNTwAmqTtkJh7f9qqScu2qFVlx7kCoeY9tlBu9cUcr7+I+Z/noG8INehS3xQgLpTtd/QUTn4w== + dependencies: + type-detect "^4.0.0" -deep-is@^0.1.3, deep-is@~0.1.3: +deep-is@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" -define-properties@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - dependencies: - object-keys "^1.0.12" - -define-property@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" - dependencies: - is-descriptor "^0.1.0" - -define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" - dependencies: - is-descriptor "^1.0.0" - -define-property@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" +defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + integrity sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA== dependencies: - is-descriptor "^1.0.2" - isobject "^3.0.1" + clone "^1.0.2" -defined@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== -degenerator@~1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-1.0.4.tgz#fcf490a37ece266464d9cc431ab98c5819ced095" +define-properties@^1.1.2, define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" dependencies: - ast-types "0.x.x" - escodegen "1.x.x" - esprima "3.x.x" + object-keys "^1.0.12" del@^6.0.0: version "6.0.0" @@ -3052,62 +3531,48 @@ del@^6.0.0: rimraf "^3.0.2" slash "^3.0.0" -delegates@^1.0.0: +delayed-stream@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" -des.js@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" - dependencies: - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - -detect-indent@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" - dependencies: - repeating "^2.0.0" - -detect-libc@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== di@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" -diff@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf" - -diff@3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" +didyoumean@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" + integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== -diff@^3.0.1, diff@^3.1.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" +diff@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" + integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== -diffie-hellman@^5.0.0: - version "5.0.3" - resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" - dependencies: - bn.js "^4.1.0" - miller-rabin "^4.0.0" - randombytes "^2.0.0" +diff@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40" + integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw== dijkstrajs@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/dijkstrajs/-/dijkstrajs-1.0.1.tgz#d3cd81221e3ea40742cfcde556d4e99e98ddc71b" + version "1.0.2" + resolved "https://registry.yarnpkg.com/dijkstrajs/-/dijkstrajs-1.0.2.tgz#2e48c0d3b825462afe75ab4ad5e829c8ece36257" + integrity sha512-QV6PMaHTCNmKSeP6QoXhVTw9snc9VD8MulTT0Bd99Pacp4SS1cjcrYPgBPmibqKVtMJJfqC6XvOXgPMEEPH/fg== dir-glob@^3.0.1: version "3.0.1" @@ -3116,12 +3581,12 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -doctrine@1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== dependencies: esutils "^2.0.2" - isarray "^1.0.0" doctrine@^3.0.0: version "3.0.0" @@ -3129,19 +3594,17 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dom-converter@~0.2: +dom-converter@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" + integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== dependencies: utila "~0.4" -dom-event-types@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/dom-event-types/-/dom-event-types-1.0.0.tgz#5830a0a29e1bf837fe50a70cd80a597232813cae" - -dom-serialize@^2.2.0: +dom-serialize@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" + integrity sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs= dependencies: custom-event "~1.0.0" ent "~2.2.0" @@ -3155,24 +3618,28 @@ dom-serializer@0: domelementtype "~1.1.1" entities "~1.1.1" -domain-browser@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" +dom-serializer@^1.0.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" + integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" domelementtype@1, domelementtype@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" +domelementtype@^2.0.1, domelementtype@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + domelementtype@~1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" -domhandler@2.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.1.0.tgz#d2646f5e57f6c3bab11cf6cb05d3c0acf7412594" - dependencies: - domelementtype "1" - domhandler@^2.3.0: version "2.4.2" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" @@ -3180,18 +3647,12 @@ domhandler@^2.3.0: dependencies: domelementtype "1" -domutils@1.1: - version "1.1.6" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.1.6.tgz#bddc3de099b9a2efacc51c623f28f416ecc57485" - dependencies: - domelementtype "1" - -domutils@1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" +domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" + integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== dependencies: - dom-serializer "0" - domelementtype "1" + domelementtype "^2.2.0" domutils@^1.5.1: version "1.7.0" @@ -3201,52 +3662,58 @@ domutils@^1.5.1: dom-serializer "0" domelementtype "1" -duplexify@^3.4.2, duplexify@^3.6.0: - version "3.7.1" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" +domutils@^2.5.2, domutils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== dependencies: - end-of-stream "^1.0.0" - inherits "^2.0.1" - readable-stream "^2.0.0" - stream-shift "^1.0.0" + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +dotenv@10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" + integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" -ejs@2.5.7: - version "2.5.7" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.7.tgz#cc872c168880ae3c7189762fd5ffc00896c9518a" - -electron-to-chromium@^1.2.7: - version "1.3.100" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.100.tgz#899fb088def210aee6b838a47655bbb299190e13" - -electron-to-chromium@^1.3.322: - version "1.3.322" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.322.tgz#a6f7e1c79025c2b05838e8e344f6e89eb83213a8" - integrity sha512-Tc8JQEfGQ1MzfSzI/bTlSr7btJv/FFO7Yh6tanqVmIWOuNCu6/D1MilIEgLtmWqIrsv+o4IjpLAhgMBr/ncNAA== +ejs@^3.1.8: + version "3.1.8" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.8.tgz#758d32910c78047585c7ef1f92f9ee041c1c190b" + integrity sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ== + dependencies: + jake "^10.8.5" electron-to-chromium@^1.3.488: version "1.3.509" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.509.tgz#830fcb89cd66dc2984d18d794973b99e3f00584c" integrity sha512-cN4lkjNRuTG8rtAqTOVgwpecEC2kbKA04PG6YijcKGHK/kD0xLjiqExcAOmLUwtXZRF8cBeam2I0VZcih919Ug== -elliptic@^6.0.0: - version "6.4.1" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.1.tgz#c2d0b7776911b86722c632c3c06c60f2f819939a" - dependencies: - bn.js "^4.4.0" - brorand "^1.0.1" - hash.js "^1.0.0" - hmac-drbg "^1.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.0" +electron-to-chromium@^1.4.172: + version "1.4.187" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.187.tgz#b884493df00816dc2ce928958c4f6a51a93fe1a8" + integrity sha512-t3iFLHVIMhB8jGZ+8ui951nz6Bna5qKfhxezG3wzXdBJ79qFKPsE2chjjVFNqC1ewhfrPQrw9pmVeo4FFpZeQA== -emoji-regex@^7.0.1: - version "7.0.3" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" +electron-to-chromium@^1.4.202: + version "1.4.219" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.219.tgz#a7a672304b6aa4f376918d3f63a47f2c3906009a" + integrity sha512-zoQJsXOUw0ZA0YxbjkmzBumAJRtr6je5JySuL/bAoFs0DuLiLJ+5FzRF7/ZayihxR2QcewlRZVm5QZdUhwjOgA== + +electron-to-chromium@^1.4.251: + version "1.4.276" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.276.tgz#17837b19dafcc43aba885c4689358b298c19b520" + integrity sha512-EpuHPqu8YhonqLBXHoU6hDJCD98FCe6KDoet3/gY1qsQ6usjJoHqBH2YIVs8FXaAtHwVL8Uqa/fsYao/vq9VWQ== emoji-regex@^8.0.0: version "8.0.0" @@ -3262,61 +3729,51 @@ emojis-list@^3.0.0: resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== -encodeurl@~1.0.1, encodeurl@~1.0.2: +encode-utf8@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/encode-utf8/-/encode-utf8-1.0.3.tgz#f30fdd31da07fb596f281beb2f6b027851994cda" + integrity sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw== + +encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" -end-of-stream@^1.0.0, end-of-stream@^1.1.0: +end-of-stream@^1.1.0: version "1.4.1" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" dependencies: once "^1.4.0" -engine.io-client@~3.2.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.2.1.tgz#6f54c0475de487158a1a7c77d10178708b6add36" - dependencies: - component-emitter "1.2.1" - component-inherit "0.0.3" - debug "~3.1.0" - engine.io-parser "~2.1.1" - has-cors "1.1.0" - indexof "0.0.1" - parseqs "0.0.5" - parseuri "0.0.5" - ws "~3.3.1" - xmlhttprequest-ssl "~1.5.4" - yeast "0.1.2" - -engine.io-parser@~2.1.0, engine.io-parser@~2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.1.3.tgz#757ab970fbf2dfb32c7b74b033216d5739ef79a6" +engine.io-parser@~5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.3.tgz#ca1f0d7b11e290b4bfda251803baea765ed89c09" + integrity sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg== dependencies: - after "0.8.2" - arraybuffer.slice "~0.0.7" - base64-arraybuffer "0.1.5" - blob "0.0.5" - has-binary2 "~1.0.2" + "@socket.io/base64-arraybuffer" "~1.0.2" -engine.io@~3.2.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.2.1.tgz#b60281c35484a70ee0351ea0ebff83ec8c9522a2" +engine.io@~6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.2.0.tgz#003bec48f6815926f2b1b17873e576acd54f41d0" + integrity sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg== dependencies: + "@types/cookie" "^0.4.1" + "@types/cors" "^2.8.12" + "@types/node" ">=10.0.0" accepts "~1.3.4" - base64id "1.0.0" - cookie "0.3.1" - debug "~3.1.0" - engine.io-parser "~2.1.0" - ws "~3.3.1" - -enhanced-resolve@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec" - integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg== + base64id "2.0.0" + cookie "~0.4.1" + cors "~2.8.5" + debug "~4.3.1" + engine.io-parser "~5.0.3" + ws "~8.2.3" + +enhanced-resolve@^5.10.0: + version "5.10.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz#0dc579c3bb2a1032e357ac45b8f3a6f3ad4fb1e6" + integrity sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ== dependencies: - graceful-fs "^4.1.2" - memory-fs "^0.5.0" - tapable "^1.0.0" + graceful-fs "^4.2.4" + tapable "^2.2.0" ent@~2.2.0: version "2.2.0" @@ -3326,32 +3783,63 @@ entities@^1.1.1, entities@~1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" -errno@^0.1.3, errno@~0.1.7: +entities@^2.0.0, entities@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +envinfo@7.8.1: + version "7.8.1" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" + integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== + +errno@^0.1.3: version "0.1.7" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" dependencies: prr "~1.0.1" -error-ex@^1.2.0, error-ex@^1.3.1: +error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" dependencies: is-arrayish "^0.2.1" -es-abstract@^1.5.1, es-abstract@^1.7.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" +es-abstract@^1.19.0, es-abstract@^1.19.1: + version "1.19.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" + integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== dependencies: - es-to-primitive "^1.2.0" + call-bind "^1.0.2" + es-to-primitive "^1.2.1" function-bind "^1.1.1" + get-intrinsic "^1.1.1" + get-symbol-description "^1.0.0" has "^1.0.3" - is-callable "^1.1.4" - is-regex "^1.0.4" - object-keys "^1.0.12" - -es-to-primitive@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" + has-symbols "^1.0.2" + internal-slot "^1.0.3" + is-callable "^1.2.4" + is-negative-zero "^2.0.1" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.1" + is-string "^1.0.7" + is-weakref "^1.0.1" + object-inspect "^1.11.0" + object-keys "^1.1.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.1" + +es-module-lexer@^0.9.0: + version "0.9.3" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" + integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== dependencies: is-callable "^1.1.4" is-date-object "^1.0.1" @@ -3362,226 +3850,272 @@ escalade@^3.0.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.0.2.tgz#6a580d70edb87880f22b4c91d0d56078df6962c4" integrity sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ== -escape-html@^1.0.3, escape-html@~1.0.3: +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-html@1.0.3, escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= -escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" -escodegen@1.8.x: - version "1.8.1" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" - dependencies: - esprima "^2.7.1" - estraverse "^1.9.1" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.2.0" - -escodegen@1.x.x, escodegen@^1.6.1: - version "1.11.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.0.tgz#b27a9389481d5bfd5bec76f7bb1eb3f8f4556589" - dependencies: - esprima "^3.1.3" - estraverse "^4.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - -eslint-config-standard@^12.0.0: - version "12.0.0" - resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-12.0.0.tgz#638b4c65db0bd5a41319f96bba1f15ddad2107d9" - -eslint-friendly-formatter@^2.0.5: - version "2.0.7" - resolved "https://registry.yarnpkg.com/eslint-friendly-formatter/-/eslint-friendly-formatter-2.0.7.tgz#657f95a19af4989636afebb1cc9de6cebbd088ee" - dependencies: - chalk "^1.0.0" - extend "^3.0.0" - minimist "^1.2.0" - text-table "^0.2.0" +eslint-config-standard@17.0.0: + version "17.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-17.0.0.tgz#fd5b6cf1dcf6ba8d29f200c461de2e19069888cf" + integrity sha512-/2ks1GKyqSOkH7JFvXJicu0iMpoojkwB+f5Du/1SC0PtBL+s8v30k9njRZ21pm2drKYm2342jFnGWzttxPmZVg== -eslint-import-resolver-node@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a" +eslint-formatter-friendly@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/eslint-formatter-friendly/-/eslint-formatter-friendly-7.0.0.tgz#32a4998ababa0a39994aed629b831fda7dabc864" + integrity sha512-WXg2D5kMHcRxIZA3ulxdevi8/BGTXu72pfOO5vXHqcAfClfIWDSlOljROjCSOCcKvilgmHz1jDWbvFCZHjMQ5w== dependencies: - debug "^2.6.9" - resolve "^1.5.0" + "@babel/code-frame" "7.0.0" + chalk "2.4.2" + extend "3.0.2" + strip-ansi "5.2.0" + text-table "0.2.0" -eslint-loader@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/eslint-loader/-/eslint-loader-2.1.2.tgz#453542a1230d6ffac90e4e7cb9cadba9d851be68" +eslint-import-resolver-node@^0.3.6: + version "0.3.6" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" + integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== dependencies: - loader-fs-cache "^1.0.0" - loader-utils "^1.0.2" - object-assign "^4.0.1" - object-hash "^1.1.4" - rimraf "^2.6.1" + debug "^3.2.7" + resolve "^1.20.0" -eslint-module-utils@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.4.0.tgz#8b93499e9b00eab80ccb6614e69f03678e84e09a" +eslint-module-utils@^2.7.3: + version "2.7.3" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" + integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== dependencies: - debug "^2.6.8" - pkg-dir "^2.0.0" + debug "^3.2.7" + find-up "^2.1.0" -eslint-plugin-es@^1.3.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-1.4.0.tgz#475f65bb20c993fc10e8c8fe77d1d60068072da6" +eslint-plugin-es@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz#f0822f0c18a535a97c3e714e89f88586a7641ec9" + integrity sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ== dependencies: - eslint-utils "^1.3.0" - regexpp "^2.0.1" + eslint-utils "^2.0.0" + regexpp "^3.0.0" -eslint-plugin-import@^2.13.0: - version "2.17.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.17.2.tgz#d227d5c6dc67eca71eb590d2bb62fb38d86e9fcb" +eslint-plugin-import@2.26.0: + version "2.26.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b" + integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA== dependencies: - array-includes "^3.0.3" - contains-path "^0.1.0" + array-includes "^3.1.4" + array.prototype.flat "^1.2.5" debug "^2.6.9" - doctrine "1.5.0" - eslint-import-resolver-node "^0.3.2" - eslint-module-utils "^2.4.0" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.6" + eslint-module-utils "^2.7.3" has "^1.0.3" - lodash "^4.17.11" - minimatch "^3.0.4" - read-pkg-up "^2.0.0" - resolve "^1.10.0" + is-core-module "^2.8.1" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.values "^1.1.5" + resolve "^1.22.0" + tsconfig-paths "^3.14.1" + +eslint-plugin-n@15.6.0: + version "15.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-15.6.0.tgz#cfb1d2e2e427d620eb9008f8b3b5a40de0c84120" + integrity sha512-Hd/F7wz4Mj44Jp0H6Jtty13NcE69GNTY0rVlgTIj1XBnGGVI6UTdDrpE6vqu3AHo07bygq/N+7OH/lgz1emUJw== + dependencies: + builtins "^5.0.1" + eslint-plugin-es "^4.1.0" + eslint-utils "^3.0.0" + ignore "^5.1.1" + is-core-module "^2.11.0" + minimatch "^3.1.2" + resolve "^1.22.1" + semver "^7.3.8" + +eslint-plugin-promise@6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz#269a3e2772f62875661220631bd4dafcb4083816" + integrity sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig== -eslint-plugin-node@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-7.0.1.tgz#a6e054e50199b2edd85518b89b4e7b323c9f36db" +eslint-plugin-vue@9.7.0: + version "9.7.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.7.0.tgz#d391b9864f128ea2d1ee4dabeafb5f7c0cea981f" + integrity sha512-DrOO3WZCZEwcLsnd3ohFwqCoipGRSTKTBTnLwdhqAbYZtzWl0o7D+D8ZhlmiZvABKTEl8AFsqH1GHGdybyoQmw== dependencies: - eslint-plugin-es "^1.3.1" - eslint-utils "^1.3.1" - ignore "^4.0.2" - minimatch "^3.0.4" - resolve "^1.8.1" - semver "^5.5.0" + eslint-utils "^3.0.0" + natural-compare "^1.4.0" + nth-check "^2.0.1" + postcss-selector-parser "^6.0.9" + semver "^7.3.5" + vue-eslint-parser "^9.0.1" + xml-name-validator "^4.0.0" -eslint-plugin-promise@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.1.1.tgz#1e08cb68b5b2cd8839f8d5864c796f56d82746db" +eslint-scope@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" -eslint-plugin-standard@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.0.0.tgz#f845b45109c99cd90e77796940a344546c8f6b5c" +eslint-scope@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" -eslint-plugin-vue@^5.2.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-5.2.2.tgz#86601823b7721b70bc92d54f1728cfc03b36283c" +eslint-utils@^2.0.0, eslint-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== dependencies: - vue-eslint-parser "^5.0.0" + eslint-visitor-keys "^1.1.0" -eslint-scope@^4.0.0, eslint-scope@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" + eslint-visitor-keys "^2.0.0" -eslint-utils@^1.3.0, eslint-utils@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.3.1.tgz#9a851ba89ee7c460346f97cf8939c7298827e512" +eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== -eslint-visitor-keys@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" +eslint-visitor-keys@^2.0.0, eslint-visitor-keys@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint@^5.16.0: - version "5.16.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" +eslint-visitor-keys@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + +eslint-webpack-plugin@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz#1978cdb9edc461e4b0195a20da950cf57988347c" + integrity sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w== dependencies: - "@babel/code-frame" "^7.0.0" - ajv "^6.9.1" - chalk "^2.1.0" - cross-spawn "^6.0.5" - debug "^4.0.1" + "@types/eslint" "^7.29.0 || ^8.4.1" + jest-worker "^28.0.2" + micromatch "^4.0.5" + normalize-path "^3.0.0" + schema-utils "^4.0.0" + +eslint@8.29.0: + version "8.29.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.29.0.tgz#d74a88a20fb44d59c51851625bc4ee8d0ec43f87" + integrity sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg== + dependencies: + "@eslint/eslintrc" "^1.3.3" + "@humanwhocodes/config-array" "^0.11.6" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" doctrine "^3.0.0" - eslint-scope "^4.0.3" - eslint-utils "^1.3.1" - eslint-visitor-keys "^1.0.0" - espree "^5.0.1" - esquery "^1.0.1" + escape-string-regexp "^4.0.0" + eslint-scope "^7.1.1" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.3.0" + espree "^9.4.0" + esquery "^1.4.0" esutils "^2.0.2" - file-entry-cache "^5.0.1" - functional-red-black-tree "^1.0.1" - glob "^7.1.2" - globals "^11.7.0" - ignore "^4.0.6" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.15.0" + grapheme-splitter "^1.0.4" + ignore "^5.2.0" import-fresh "^3.0.0" imurmurhash "^0.1.4" - inquirer "^6.2.2" - js-yaml "^3.13.0" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-sdsl "^4.1.4" + js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.11" - minimatch "^3.0.4" - mkdirp "^0.5.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" natural-compare "^1.4.0" - optionator "^0.8.2" - path-is-inside "^1.0.2" - progress "^2.0.0" - regexpp "^2.0.1" - semver "^5.5.1" - strip-ansi "^4.0.0" - strip-json-comments "^2.0.1" - table "^5.2.3" + optionator "^0.9.1" + regexpp "^3.2.0" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" text-table "^0.2.0" -espree@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-4.1.0.tgz#728d5451e0fd156c04384a7ad89ed51ff54eb25f" +espree@^6.0.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" + integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== dependencies: - acorn "^6.0.2" - acorn-jsx "^5.0.0" - eslint-visitor-keys "^1.0.0" + acorn "^7.1.1" + acorn-jsx "^5.2.0" + eslint-visitor-keys "^1.1.0" -espree@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" +espree@^9.3.1: + version "9.3.2" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.2.tgz#f58f77bd334731182801ced3380a8cc859091596" + integrity sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA== dependencies: - acorn "^6.0.7" - acorn-jsx "^5.0.0" - eslint-visitor-keys "^1.0.0" - -esprima@2.7.x, esprima@^2.6.0, esprima@^2.7.1: - version "2.7.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" - -esprima@3.x.x, esprima@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" - -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + acorn "^8.7.1" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.3.0" -esquery@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" +espree@^9.4.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.0.tgz#cd4bc3d6e9336c433265fc0aa016fc1aaf182f8a" + integrity sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw== dependencies: - estraverse "^4.0.0" + acorn "^8.8.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.3.0" -esrecurse@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" +esquery@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== dependencies: - estraverse "^4.1.0" + estraverse "^5.1.0" -estraverse@^1.9.1: - version "1.9.3" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.1.1: version "4.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +estree-walker@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" @@ -3590,36 +4124,20 @@ etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" -eventemitter3@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163" +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== -events@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88" +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== -eventsource-polyfill@^0.9.6: +eventsource-polyfill@0.9.6: version "0.9.6" resolved "https://registry.yarnpkg.com/eventsource-polyfill/-/eventsource-polyfill-0.9.6.tgz#10e0d187f111b167f28fdab918843ce7d818f13c" - -evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" - dependencies: - md5.js "^1.3.4" - safe-buffer "^5.1.1" - -execa@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" - dependencies: - cross-spawn "^6.0.0" - get-stream "^4.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" + integrity sha1-EODRh/ERsWfyj9q5GIQ859gY8Tw= execall@^2.0.0: version "2.0.0" @@ -3628,127 +4146,47 @@ execall@^2.0.0: dependencies: clone-regexp "^2.1.0" -exit-hook@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" - -expand-braces@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/expand-braces/-/expand-braces-0.1.2.tgz#488b1d1d2451cb3d3a6b192cfc030f44c5855fea" - dependencies: - array-slice "^0.2.3" - array-unique "^0.2.1" - braces "^0.1.2" - -expand-brackets@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" - dependencies: - is-posix-bracket "^0.1.0" - -expand-brackets@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" - dependencies: - debug "^2.3.3" - define-property "^0.2.5" - extend-shallow "^2.0.1" - posix-character-classes "^0.1.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -expand-range@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-0.1.1.tgz#4cb8eda0993ca56fa4f41fc42f3cbb4ccadff044" - dependencies: - is-number "^0.1.1" - repeat-string "^0.2.2" - -expand-range@^1.8.1: - version "1.8.2" - resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" - dependencies: - fill-range "^2.1.0" - -express@^4.13.3: - version "4.16.4" - resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e" +express@4.18.2: + version "4.18.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" + integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== dependencies: - accepts "~1.3.5" + accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.18.3" - content-disposition "0.5.2" + body-parser "1.20.1" + content-disposition "0.5.4" content-type "~1.0.4" - cookie "0.3.1" + cookie "0.5.0" cookie-signature "1.0.6" debug "2.6.9" - depd "~1.1.2" + depd "2.0.0" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "1.1.1" + finalhandler "1.2.0" fresh "0.5.2" + http-errors "2.0.0" merge-descriptors "1.0.1" methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.2" + on-finished "2.4.1" + parseurl "~1.3.3" path-to-regexp "0.1.7" - proxy-addr "~2.0.4" - qs "6.5.2" - range-parser "~1.2.0" - safe-buffer "5.1.2" - send "0.16.2" - serve-static "1.13.2" - setprototypeof "1.1.0" - statuses "~1.4.0" - type-is "~1.6.16" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" utils-merge "1.0.1" vary "~1.1.2" -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - dependencies: - is-extendable "^0.1.0" - -extend-shallow@^3.0.0, extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" - -extend@3, extend@^3.0.0, extend@~3.0.0: +extend@3.0.2, extend@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - -external-editor@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.3.tgz#5866db29a97826dbe4bf3afd24070ead9ea43a27" - dependencies: - chardet "^0.7.0" - iconv-lite "^0.4.24" - tmp "^0.0.33" - -extglob@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" - dependencies: - is-extglob "^1.0.0" - -extglob@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" - dependencies: - array-unique "^0.3.2" - define-property "^1.0.0" - expand-brackets "^2.1.4" - extend-shallow "^2.0.1" - fragment-cache "^0.2.1" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== extract-zip@^2.0.1: version "2.0.1" @@ -3761,11 +4199,7 @@ extract-zip@^2.0.1: optionalDependencies: "@types/yauzl" "^2.9.1" -fast-deep-equal@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" - -fast-deep-equal@^3.1.1: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== @@ -3782,29 +4216,30 @@ fast-glob@^3.1.1: micromatch "^4.0.2" picomatch "^2.2.1" -fast-glob@^3.2.4: - version "3.2.5" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" - integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg== +fast-glob@^3.2.11, fast-glob@^3.2.5, fast-glob@^3.2.9: + version "3.2.11" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" + integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.0" + glob-parent "^5.1.2" merge2 "^1.3.0" - micromatch "^4.0.2" - picomatch "^2.2.1" + micromatch "^4.0.4" fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" -fast-levenshtein@~2.0.4: +fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== -fastparse@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9" +fastest-levenshtein@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" + integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow== fastq@^1.6.0: version "1.8.0" @@ -3820,55 +4255,19 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" -figgy-pudding@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" - -figures@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" - dependencies: - escape-string-regexp "^1.0.5" - -file-entry-cache@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" - dependencies: - flat-cache "^2.0.1" - -file-loader@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-3.0.1.tgz#f8e0ba0b599918b51adfe45d66d1e771ad560faa" - dependencies: - loader-utils "^1.0.2" - schema-utils "^1.0.0" - -file-uri-to-path@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" - -filename-regex@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" - -fill-range@^2.1.0: - version "2.2.4" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== dependencies: - is-number "^2.1.0" - isobject "^2.0.0" - randomatic "^3.0.0" - repeat-element "^1.1.2" - repeat-string "^1.5.2" + flat-cache "^3.0.4" -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" +filelist@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" + integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" + minimatch "^5.0.1" fill-range@^7.0.1: version "7.0.1" @@ -3877,39 +4276,33 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -finalhandler@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.0.tgz#ce0b6855b45853e791b2fcc680046d88253dd7f5" +finalhandler@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== dependencies: debug "2.6.9" - encodeurl "~1.0.1" + encodeurl "~1.0.2" escape-html "~1.0.3" on-finished "~2.3.0" - parseurl "~1.3.2" - statuses "~1.3.1" + parseurl "~1.3.3" + statuses "~1.5.0" unpipe "~1.0.0" -finalhandler@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105" +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== dependencies: debug "2.6.9" encodeurl "~1.0.2" escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.2" - statuses "~1.4.0" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" unpipe "~1.0.0" -find-cache-dir@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9" - dependencies: - commondir "^1.0.1" - mkdirp "^0.5.1" - pkg-dir "^1.0.0" - -find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: +find-cache-dir@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" dependencies: @@ -3926,14 +4319,15 @@ find-cache-dir@^3.3.1: make-dir "^3.0.2" pkg-dir "^4.1.0" -find-up@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" +find-up@5.0.0, find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== dependencies: - path-exists "^2.0.0" - pinkie-promise "^2.0.0" + locate-path "^6.0.0" + path-exists "^4.0.0" -find-up@^2.0.0, find-up@^2.1.0: +find-up@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" dependencies: @@ -3953,167 +4347,127 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -flat-cache@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== dependencies: - flatted "^2.0.0" - rimraf "2.6.3" - write "1.0.3" + flatted "^3.1.0" + rimraf "^3.0.2" -flatted@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.0.tgz#55122b6536ea496b4b44893ee2608141d10d9916" +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +flatted@^3.1.0: + version "3.2.6" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.6.tgz#022e9218c637f9f3fc9c35ab9c9193f05add60b2" + integrity sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ== + +flatted@^3.2.5: + version "3.2.5" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" + integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== flatten@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" -flush-write-stream@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" - dependencies: - inherits "^2.0.3" - readable-stream "^2.3.6" - follow-redirects@^1.0.0: version "1.6.1" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.6.1.tgz#514973c44b5757368bad8bddfe52f81f015c94cb" dependencies: debug "=3.1.0" -follow-redirects@^1.10.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7" - integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg== - -for-in@^1.0.1, for-in@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - -for-own@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" - dependencies: - for-in "^1.0.1" +follow-redirects@^1.14.9: + version "1.15.1" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" + integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== -formatio@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.2.0.tgz#f3b2167d9068c4698a8d51f4f760a39a54d818eb" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== dependencies: - samsam "1.x" + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" -forwarded@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" - dependencies: - map-cache "^0.2.2" +fraction.js@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" + integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" -from2@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" - dependencies: - inherits "^2.0.1" - readable-stream "^2.0.0" - -fs-minipass@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" - dependencies: - minipass "^2.2.1" - -fs-minipass@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" - integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== +fs-extra@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.1.tgz#27de43b4320e833f6867cc044bfce29fdf0ef3b8" + integrity sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag== dependencies: - minipass "^3.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" -fs-write-stream-atomic@^1.0.8: - version "1.0.10" - resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" +fs-extra@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== dependencies: - graceful-fs "^4.1.2" - iferr "^0.1.5" - imurmurhash "^0.1.4" - readable-stream "1 || 2" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" fs.realpath@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - -fsevents@^1.2.7: - version "1.2.9" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f" - dependencies: - nan "^2.12.1" - node-pre-gyp "^0.12.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" -fsevents@~2.3.1: +fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== -ftp@~0.3.10: - version "0.3.10" - resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" - dependencies: - readable-stream "1.1.x" - xregexp "2.0.0" - -function-bind@^1.0.2, function-bind@^1.1.1: +function-bind@1.1.1, function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - -gensync@^1.0.0-beta.1: - version "1.0.0-beta.1" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" - integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== - -get-caller-file@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -get-stdin@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" +get-caller-file@^2.0.1, get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" get-stdin@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== -get-stream@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - dependencies: - pump "^3.0.0" - get-stream@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" @@ -4121,40 +4475,13 @@ get-stream@^5.1.0: dependencies: pump "^3.0.0" -get-uri@2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-2.0.2.tgz#5c795e71326f6ca1286f2fc82575cd2bab2af578" - dependencies: - data-uri-to-buffer "1" - debug "2" - extend "3" - file-uri-to-path "1" - ftp "~0.3.10" - readable-stream "2" - -get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" - -glob-base@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" - dependencies: - glob-parent "^2.0.0" - is-glob "^2.0.0" - -glob-parent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" - dependencies: - is-glob "^2.0.0" - -glob-parent@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== dependencies: - is-glob "^3.1.0" - path-dirname "^1.0.0" + call-bind "^1.0.2" + get-intrinsic "^1.1.1" glob-parent@^5.1.0: version "5.1.1" @@ -4163,42 +4490,34 @@ glob-parent@^5.1.0: dependencies: is-glob "^4.0.1" -glob-parent@^5.1.1, glob-parent@~5.1.0: +glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -glob@7.0.5: - version "7.0.5" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.5.tgz#b4202a69099bbb4d292a7c1b95b6682b67ebdc95" +glob-parent@^6.0.1, glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.2" - once "^1.3.0" - path-is-absolute "^1.0.0" + is-glob "^4.0.3" -glob@7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.2" - once "^1.3.0" - path-is-absolute "^1.0.0" +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@^5.0.15: - version "5.0.15" - resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" +glob@7.2.0, glob@^7.1.7: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== dependencies: + fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "2 || 3" + minimatch "^3.0.4" once "^1.3.0" path-is-absolute "^1.0.0" @@ -4213,26 +4532,15 @@ glob@^7.0.0, glob@^7.1.1, glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.1.2: - version "7.1.4" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.1.4: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== +glob@^7.2.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.0.4" + minimatch "^3.1.1" once "^1.3.0" path-is-absolute "^1.0.0" @@ -4252,13 +4560,16 @@ global-prefix@^3.0.0: kind-of "^6.0.2" which "^1.3.1" -globals@^11.1.0, globals@^11.7.0: +globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" -globals@^9.18.0: - version "9.18.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" +globals@^13.15.0: + version "13.17.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.17.0.tgz#902eb1e680a41da93945adbdcb5a9f361ba69bd4" + integrity sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw== + dependencies: + type-fest "^0.20.2" globby@^11.0.1: version "11.0.1" @@ -4272,6 +4583,29 @@ globby@^11.0.1: merge2 "^1.3.0" slash "^3.0.0" +globby@^11.0.3: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +globby@^13.1.1: + version "13.1.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.2.tgz#29047105582427ab6eca4f905200667b056da515" + integrity sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ== + dependencies: + dir-glob "^3.0.1" + fast-glob "^3.2.11" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^4.0.0" + globjoin@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43" @@ -4284,32 +4618,34 @@ gonzales-pe@^4.3.0: dependencies: minimist "^1.2.5" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2: +graceful-fs@^4.1.2: version "4.1.15" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" +graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.6: + version "4.2.9" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" + integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== + graceful-fs@^4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== -"graceful-readlink@>= 1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" +graceful-fs@^4.2.9: + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== -growl@1.9.2: - version "1.9.2" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" +grapheme-splitter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" + integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== -handlebars@^4.0.1: - version "4.0.12" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.12.tgz#2c15c8a96d46da5e266700518ba8cb8d919d5bc5" - dependencies: - async "^2.5.0" - optimist "^0.6.1" - source-map "^0.6.1" - optionalDependencies: - uglify-js "^3.1.4" +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== hard-rejection@^2.1.0: version "2.1.0" @@ -4322,23 +4658,10 @@ has-ansi@^2.0.0: dependencies: ansi-regex "^2.0.0" -has-binary2@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d" - dependencies: - isarray "2.0.1" - -has-color@~0.1.0: - version "0.1.7" - resolved "https://registry.yarnpkg.com/has-color/-/has-color-0.1.7.tgz#67144a5260c34fc3cca677d041daf52fe7b78b2f" - -has-cors@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" - -has-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" +has-bigints@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== has-flag@^3.0.0: version "3.0.0" @@ -4353,129 +4676,87 @@ has-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - -has-value@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" - dependencies: - get-value "^2.0.3" - has-values "^0.1.4" - isobject "^2.0.0" - -has-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" - dependencies: - get-value "^2.0.6" - has-values "^1.0.0" - isobject "^3.0.0" - -has-values@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" +has-symbols@^1.0.1, has-symbols@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== -has-values@^1.0.0: +has-tostringtag@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" + has-symbols "^1.0.2" -has@^1.0.1, has@^1.0.3: +has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" dependencies: function-bind "^1.1.1" -hash-base@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - hash-sum@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04" -hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - -he@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" +hash-sum@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-2.0.0.tgz#81d01bb5de8ea4a214ad5d6ead1b523460b0b45a" + integrity sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg== -he@1.2.x, he@^1.1.0: +he@1.2.0, he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - -hmac-drbg@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - -home-or-tmp@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.1" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== hosted-git-info@^2.1.4: version "2.7.1" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" -html-comment-regex@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" +hosted-git-info@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224" + integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA== + dependencies: + lru-cache "^6.0.0" -html-entities@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" +html-entities@^2.1.0: + version "2.3.3" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.3.tgz#117d7626bece327fc8baace8868fa6f5ef856e46" + integrity sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA== -html-minifier@^3.2.3: - version "3.5.21" - resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz#d0040e054730e354db008463593194015212d20c" - dependencies: - camel-case "3.0.x" - clean-css "4.2.x" - commander "2.17.x" - he "1.2.x" - param-case "2.1.x" - relateurl "0.2.x" - uglify-js "3.4.x" +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== -html-tags@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-2.0.0.tgz#10b30a386085f43cede353cc8fa7cb0deeea668b" - integrity sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos= +html-minifier-terser@^6.0.2: + version "6.1.0" + resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#bfc818934cc07918f6b3669f5774ecdfd48f32ab" + integrity sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw== + dependencies: + camel-case "^4.1.2" + clean-css "^5.2.2" + commander "^8.3.0" + he "^1.2.0" + param-case "^3.0.4" + relateurl "^0.2.7" + terser "^5.10.0" html-tags@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140" integrity sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg== -html-webpack-plugin@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz#b01abbd723acaaa7b37b6af4492ebda03d9dd37b" +html-webpack-plugin@5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz#c3911936f57681c1f9f4d8b68c158cd9dfe52f50" + integrity sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw== dependencies: - html-minifier "^3.2.3" - loader-utils "^0.2.16" - lodash "^4.17.3" - pretty-error "^2.0.2" - tapable "^1.0.0" - toposort "^1.0.0" - util.promisify "1.0.0" + "@types/html-minifier-terser" "^6.0.0" + html-minifier-terser "^6.0.2" + lodash "^4.17.21" + pretty-error "^4.0.0" + tapable "^2.0.0" htmlparser2@^3.10.0: version "3.10.1" @@ -4489,61 +4770,58 @@ htmlparser2@^3.10.0: inherits "^2.0.1" readable-stream "^3.1.1" -htmlparser2@~3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.3.0.tgz#cc70d05a59f6542e43f0e685c982e14c924a9efe" +htmlparser2@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== dependencies: - domelementtype "1" - domhandler "2.1" - domutils "1.1" - readable-stream "1.0" + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" -http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" +http-errors@1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" + integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== dependencies: depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.1" -http-proxy-agent@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-1.0.0.tgz#cc1ce38e453bf984a0f7702d2dd59c73d081284a" +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== dependencies: - agent-base "2" - debug "2" - extend "3" + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" -http-proxy-middleware@^0.17.2: - version "0.17.4" - resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz#642e8848851d66f09d4f124912846dbaeb41b833" +http-proxy-middleware@2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" + integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== dependencies: - http-proxy "^1.16.2" - is-glob "^3.1.0" - lodash "^4.17.2" - micromatch "^2.3.11" + "@types/http-proxy" "^1.17.8" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" -http-proxy@^1.13.0, http-proxy@^1.16.2: - version "1.17.0" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.17.0.tgz#7ad38494658f84605e2f6db4436df410f4e5be9a" +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== dependencies: - eventemitter3 "^3.0.0" + eventemitter3 "^4.0.0" follow-redirects "^1.0.0" requires-port "^1.0.0" -https-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" - -https-proxy-agent@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz#35f7da6c48ce4ddbfa264891ac593ee5ff8671e6" - dependencies: - agent-base "2" - debug "2" - extend "3" - https-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" @@ -4552,45 +4830,26 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" -iconv-lite@0.4.23, iconv-lite@^0.4.4: - version "0.4.23" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" - dependencies: - safer-buffer ">= 2.1.2 < 3" - -iconv-lite@^0.4.24: +iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" dependencies: safer-buffer ">= 2.1.2 < 3" -icss-replace-symbols@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" - -icss-utils@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-2.1.0.tgz#83f0a0ec378bf3246178b6c2ad9136f135b1c962" - dependencies: - postcss "^6.0.1" - -ieee754@^1.1.4: - version "1.1.12" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b" - -iferr@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" +icss-utils@^5.0.0, icss-utils@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" + integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== -ignore-walk@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" - dependencies: - minimatch "^3.0.4" +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore@^4.0.2, ignore@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" +ignore@^5.1.1, ignore@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" + integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== ignore@^5.1.4, ignore@^5.1.8: version "5.1.8" @@ -4601,20 +4860,10 @@ immediate@~3.0.5: version "3.0.6" resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" -import-cwd@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" - integrity sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk= - dependencies: - import-from "^2.1.0" - -import-fresh@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" - integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= - dependencies: - caller-path "^2.0.0" - resolve-from "^3.0.0" +immutable@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef" + integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ== import-fresh@^3.0.0: version "3.0.0" @@ -4623,21 +4872,14 @@ import-fresh@^3.0.0: parent-module "^1.0.0" resolve-from "^4.0.0" -import-fresh@^3.1.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" - integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: parent-module "^1.0.0" resolve-from "^4.0.0" -import-from@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-from/-/import-from-2.1.0.tgz#335db7f2a7affd53aaa471d4b8021dee36b7f3b1" - integrity sha1-M1238qev/VOqpHHUuAId7ja387E= - dependencies: - resolve-from "^3.0.0" - import-lazy@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" @@ -4647,12 +4889,6 @@ imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" -indent-string@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" - dependencies: - repeating "^2.0.0" - indent-string@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" @@ -4662,15 +4898,6 @@ indexes-of@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" -indexof@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" - -infer-owner@^1.0.3, infer-owner@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" - integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== - inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -4678,103 +4905,46 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" -inherits@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" - -inherits@^2.0.0: +inherits@2.0.4, inherits@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@^1.3.5, ini@~1.3.0: +ini@^1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" -inject-loader@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/inject-loader/-/inject-loader-2.0.1.tgz#1a7b45d60a81610459ac76079c3ce2a654d0dfc7" - dependencies: - loader-utils "^0.2.3" - -inquirer@^6.2.2: - version "6.3.1" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.3.1.tgz#7a413b5e7950811013a3db491c61d1f3b776e8e7" +internal-slot@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" + integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== dependencies: - ansi-escapes "^3.2.0" - chalk "^2.4.2" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^3.0.3" - figures "^2.0.0" - lodash "^4.17.11" - mute-stream "0.0.7" - run-async "^2.2.0" - rxjs "^6.4.0" - string-width "^2.1.0" - strip-ansi "^5.1.0" - through "^2.3.6" + get-intrinsic "^1.1.0" + has "^1.0.3" + side-channel "^1.0.4" interpret@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" -invariant@^2.2.2: - version "2.2.4" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" - dependencies: - loose-envify "^1.0.0" - -invert-kv@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" - ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" -ip@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.0.1.tgz#c7e356cdea225ae71b36d70f2e71a92ba4e42590" - -ip@^1.1.4: - version "1.1.5" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" - -ipaddr.js@1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e" - -is-absolute-url@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" - -is-accessor-descriptor@^0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" - dependencies: - kind-of "^3.0.2" - -is-accessor-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" - dependencies: - kind-of "^6.0.0" +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== is-alphabetical@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== -is-alphanumeric@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz#4a9cef71daf4c001c1d81d63d140cf53fd6889f4" - integrity sha1-Spzvcdr0wAHB2B1j0UDPU/1oifQ= - is-alphanumerical@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" @@ -4787,11 +4957,12 @@ is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" -is-binary-path@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== dependencies: - binary-extensions "^1.0.0" + has-bigints "^1.0.1" is-binary-path@~2.1.0: version "2.1.0" @@ -4800,157 +4971,106 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" is-buffer@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A== -is-builtin-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" - dependencies: - builtin-modules "^1.0.0" - is-callable@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" -is-data-descriptor@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" +is-callable@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" + integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== + +is-core-module@^2.11.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" + integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== + dependencies: + has "^1.0.3" + +is-core-module@^2.5.0, is-core-module@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" + integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== dependencies: - kind-of "^3.0.2" + has "^1.0.3" -is-data-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" +is-core-module@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== dependencies: - kind-of "^6.0.0" + has "^1.0.3" is-date-object@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" -is-decimal@^1.0.0, is-decimal@^1.0.2: +is-decimal@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== -is-descriptor@^0.1.0: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" - dependencies: - is-accessor-descriptor "^0.1.6" - is-data-descriptor "^0.1.4" - kind-of "^5.0.0" - -is-descriptor@^1.0.0, is-descriptor@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" - dependencies: - is-accessor-descriptor "^1.0.0" - is-data-descriptor "^1.0.0" - kind-of "^6.0.2" - -is-directory@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" - -is-dotfile@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" - -is-equal-shallow@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" - dependencies: - is-primitive "^2.0.0" - -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" - dependencies: - is-plain-object "^2.0.4" - -is-extglob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== -is-extglob@^2.1.0, is-extglob@^2.1.1: +is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" -is-finite@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== -is-glob@^2.0.0, is-glob@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" - dependencies: - is-extglob "^1.0.0" - -is-glob@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" - dependencies: - is-extglob "^2.1.0" - is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" dependencies: is-extglob "^2.1.1" +is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + is-hexadecimal@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== -is-number@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-0.1.1.tgz#69a7af116963d47206ec9bd9b48a14216f1e3806" +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== -is-number@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" - dependencies: - kind-of "^3.0.2" +is-negative-zero@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" +is-number-object@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" + integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g== dependencies: - kind-of "^3.0.2" - -is-number@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + has-tostringtag "^1.0.0" is-number@^7.0.0: version "7.0.0" @@ -4967,53 +5087,55 @@ is-path-inside@^3.0.2: resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017" integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg== -is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" -is-plain-obj@^2.0.0: +is-plain-obj@^2.0.0, is-plain-obj@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== -is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" + integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== + +is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" dependencies: isobject "^3.0.1" -is-posix-bracket@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" - -is-primitive@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" - -is-promise@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" - -is-regex@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== dependencies: - has "^1.0.1" + call-bind "^1.0.2" + has-tostringtag "^1.0.0" is-regexp@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-2.1.0.tgz#cd734a56864e23b956bf4e7c66c396a4c0b22c2d" integrity sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA== -is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" +is-shared-array-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" + integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== -is-svg@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz#cf61090da0d9efbcab8722deba6f032208dbb0e9" +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== dependencies: - html-comment-regex "^1.1.0" + has-tostringtag "^1.0.0" is-symbol@^1.0.2: version "1.0.2" @@ -5021,35 +5143,44 @@ is-symbol@^1.0.2: dependencies: has-symbols "^1.0.0" +is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + is-url@^1.2.2: version "1.2.4" resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" -is-utf8@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - -is-whitespace-character@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" - integrity sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w== - -is-windows@^1.0.2: +is-weakref@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" - -is-word-character@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.4.tgz#ce0e73216f98599060592f62ff31354ddbeb0230" - integrity sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA== + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" is-wsl@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw== + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" is2@2.0.1: version "2.0.1" @@ -5063,122 +5194,140 @@ isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: +isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" -isarray@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" - -isarray@^2.0.1: - version "2.0.4" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.4.tgz#38e7bcbb0f3ba1b7933c86ba1894ddfc3781bbb7" - -isbinaryfile@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.3.tgz#5d6def3edebf6e8ca8cae9c30183a804b5f8be80" - dependencies: - buffer-alloc "^1.2.0" +isbinaryfile@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.8.tgz#5d34b94865bd4946633ecc78a026fc76c5b11fcf" + integrity sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w== isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" -iso-639-1@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/iso-639-1/-/iso-639-1-2.0.3.tgz#72dd3448ac5629c271628c5ac566369428d6ccd0" - -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - dependencies: - isarray "1.0.0" +iso-639-1@2.1.15: + version "2.1.15" + resolved "https://registry.yarnpkg.com/iso-639-1/-/iso-639-1-2.1.15.tgz#20cf78a4f691aeb802c16f17a6bad7d99271e85d" + integrity sha512-7c7mBznZu2ktfvyT582E2msM+Udc1EjOyhVRE/0ZsjD9LBtWSm23h3PtiRh2a35XoUsTQQjJXaJzuLjXsOdFDg== -isobject@^3.0.0, isobject@^3.0.1: +isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" -isparta-loader@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isparta-loader/-/isparta-loader-2.0.0.tgz#4425f496c93f765bbceb4dd938576da307566ed1" +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" + integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== + +istanbul-lib-instrument@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz#31d18bdd127f825dd02ea7bfdfd906f8ab840e9f" + integrity sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A== dependencies: - isparta "4.x.x" + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^6.3.0" -isparta@4.x.x: - version "4.1.1" - resolved "https://registry.yarnpkg.com/isparta/-/isparta-4.1.1.tgz#c92e49672946914ec5407c801160f3374e0b7cb4" - dependencies: - babel-core "^6.1.4" - escodegen "^1.6.1" - esprima "^4.0.0" - istanbul "0.4.5" - mkdirp "^0.5.0" - nomnomnomnom "^2.0.0" - object-assign "^4.0.1" - source-map "^0.5.0" - which "^1.0.9" - -istanbul@0.4.5, istanbul@^0.4.0: - version "0.4.5" - resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz#65c7d73d4c4da84d4f3ac310b918fb0b8033733b" - dependencies: - abbrev "1.0.x" - async "1.x" - escodegen "1.8.x" - esprima "2.7.x" - glob "^5.0.15" - handlebars "^4.0.1" - js-yaml "3.x" - mkdirp "0.5.x" - nopt "3.x" - once "1.x" - resolve "1.1.x" - supports-color "^3.1.0" - which "^1.1.1" - wordwrap "^1.0.0" - -js-base64@^2.1.9: - version "2.5.0" - resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.0.tgz#42255ba183ab67ce59a0dee640afdc00ab5ae93e" +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" -js-levenshtein@^1.1.3: - version "1.1.6" - resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d" - integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g== +istanbul-lib-source-maps@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" +istanbul-reports@^3.0.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.5.tgz#cc9a6ab25cb25659810e4785ed9d9fb742578bae" + integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" +jake@^10.8.5: + version "10.8.5" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" + integrity sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw== + dependencies: + async "^3.2.3" + chalk "^4.0.2" + filelist "^1.0.1" + minimatch "^3.0.4" + +jest-util@^29.2.1: + version "29.2.1" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.2.1.tgz#f26872ba0dc8cbefaba32c34f98935f6cf5fc747" + integrity sha512-P5VWDj25r7kj7kl4pN2rG/RN2c1TLfYYYZYULnS/35nFDjBai+hBeo3MDrYZS7p6IoY3YHZnt2vq4L6mKnLk0g== + dependencies: + "@jest/types" "^29.2.1" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" -js-yaml@3.x, js-yaml@^3.4.3: - version "3.12.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.1.tgz#295c8632a18a23e054cf5c9d3cecafe678167600" +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== dependencies: - argparse "^1.0.7" - esprima "^4.0.0" + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" -js-yaml@^3.13.0, js-yaml@^3.13.1: - version "3.13.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" +jest-worker@^28.0.2: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-28.1.3.tgz#7e3c4ce3fa23d1bb6accb169e7f396f98ed4bb98" + integrity sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g== dependencies: - argparse "^1.0.7" - esprima "^4.0.0" + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" -js-yaml@~3.7.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80" +jest-worker@^29.1.2: + version "29.2.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.2.1.tgz#8ba68255438252e1674f990f0180c54dfa26a3b1" + integrity sha512-ROHTZ+oj7sBrgtv46zZ84uWky71AoYi0vEV9CdEtc1FQunsoAGe5HbQmW76nI5QWdvECVPrSi1MCVUmizSavMg== dependencies: - argparse "^1.0.7" - esprima "^2.6.0" + "@types/node" "*" + jest-util "^29.2.1" + merge-stream "^2.0.0" + supports-color "^8.0.0" -jsesc@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" +js-cookie@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.1.tgz#9e39b4c6c2f56563708d7d31f6f5f21873a92414" + integrity sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw== + +js-sdsl@^4.1.4: + version "4.1.5" + resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.1.5.tgz#1ff1645e6b4d1b028cd3f862db88c9d887f26e2a" + integrity sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q== + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + +js-yaml@4.1.0, js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" jsesc@^2.5.1: version "2.5.2" @@ -5189,43 +5338,39 @@ jsesc@~0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" -json-loader@^0.5.4: +json-loader@0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d" + integrity sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w== -json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: +json-parse-better-errors@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" +json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" -json3@3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" - -json5@^0.5.0, json5@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" - json5@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" dependencies: minimist "^1.2.0" -json5@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.1.tgz#81b6cb04e9ba496f1c7005d07b4368a2638f90b6" - integrity sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ== - dependencies: - minimist "^1.2.0" - json5@^2.1.2: version "2.1.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" @@ -5233,109 +5378,141 @@ json5@^2.1.2: dependencies: minimist "^1.2.5" -karma-coverage@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/karma-coverage/-/karma-coverage-1.1.2.tgz#cc09dceb589a83101aca5fe70c287645ef387689" +json5@^2.2.0, json5@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" + integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== + +jsonc-eslint-parser@^1.0.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsonc-eslint-parser/-/jsonc-eslint-parser-1.4.1.tgz#8cbe99f6f5199acbc5a823c4c0b6135411027fa6" + integrity sha512-hXBrvsR1rdjmB2kQmUjf1rEIa+TqHBGMge8pwi++C+Si1ad7EjZrJcpgwym+QGK/pqTx+K7keFAtLlVNdLRJOg== dependencies: - dateformat "^1.0.6" - istanbul "^0.4.0" - lodash "^4.17.0" - minimatch "^3.0.0" - source-map "^0.5.1" + acorn "^7.4.1" + eslint-utils "^2.1.0" + eslint-visitor-keys "^1.3.0" + espree "^6.0.0" + semver "^6.3.0" -karma-firefox-launcher@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/karma-firefox-launcher/-/karma-firefox-launcher-1.1.0.tgz#2c47030452f04531eb7d13d4fc7669630bb93339" +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jszip@^3.10.0: + version "3.10.1" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2" + integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== + dependencies: + lie "~3.3.0" + pako "~1.0.2" + readable-stream "~2.3.6" + setimmediate "^1.0.5" + +just-extend@^4.0.2: + version "4.2.1" + resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744" + integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg== + +karma-coverage@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/karma-coverage/-/karma-coverage-2.2.0.tgz#64f838b66b71327802e7f6f6c39d569b7024e40c" + integrity sha512-gPVdoZBNDZ08UCzdMHHhEImKrw1+PAOQOIiffv1YsvxFhBjqvo/SVXNk4tqn1SYqX0BJZT6S/59zgxiBe+9OuA== + dependencies: + istanbul-lib-coverage "^3.2.0" + istanbul-lib-instrument "^5.1.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.1" + istanbul-reports "^3.0.5" + minimatch "^3.0.4" + +karma-firefox-launcher@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/karma-firefox-launcher/-/karma-firefox-launcher-2.1.2.tgz#9a38cc783c579a50f3ed2a82b7386186385cfc2d" + integrity sha512-VV9xDQU1QIboTrjtGVD4NCfzIH7n01ZXqy/qpBhnOeGVOkG5JYPEm8kuSd7psHE6WouZaQ9Ool92g8LFweSNMA== + dependencies: + is-wsl "^2.2.0" + which "^2.0.1" -karma-mocha-reporter@^2.2.1: +karma-mocha-reporter@2.2.5: version "2.2.5" resolved "https://registry.yarnpkg.com/karma-mocha-reporter/-/karma-mocha-reporter-2.2.5.tgz#15120095e8ed819186e47a0b012f3cd741895560" + integrity sha1-FRIAlejtgZGG5HoLAS8810GJVWA= dependencies: chalk "^2.1.0" log-symbols "^2.1.0" strip-ansi "^4.0.0" -karma-mocha@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/karma-mocha/-/karma-mocha-1.3.0.tgz#eeaac7ffc0e201eb63c467440d2b69c7cf3778bf" +karma-mocha@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/karma-mocha/-/karma-mocha-2.0.1.tgz#4b0254a18dfee71bdbe6188d9a6861bf86b0cd7d" + integrity sha512-Tzd5HBjm8his2OA4bouAsATYEpZrp9vC7z5E5j4C5Of5Rrs1jY67RAwXNcVmd/Bnk1wgvQRou0zGVLey44G4tQ== dependencies: - minimist "1.2.0" + minimist "^1.2.3" -karma-sinon-chai@^2.0.2: +karma-sinon-chai@2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/karma-sinon-chai/-/karma-sinon-chai-2.0.2.tgz#e28c109b989973abafc28a7c9f09ef24a05e07c2" + integrity sha512-SDgh6V0CUd+7ruL1d3yG6lFzmJNGRNQuEuCYXLaorruNP9nwQfA7hpsp4clx4CbOo5Gsajh3qUOT7CrVStUKMw== -karma-sourcemap-loader@^0.3.7: - version "0.3.7" - resolved "https://registry.yarnpkg.com/karma-sourcemap-loader/-/karma-sourcemap-loader-0.3.7.tgz#91322c77f8f13d46fed062b042e1009d4c4505d8" +karma-sourcemap-loader@0.3.8: + version "0.3.8" + resolved "https://registry.yarnpkg.com/karma-sourcemap-loader/-/karma-sourcemap-loader-0.3.8.tgz#d4bae72fb7a8397328a62b75013d2df937bdcf9c" + integrity sha512-zorxyAakYZuBcHRJE+vbrK2o2JXLFWK8VVjiT/6P+ltLBUGUvqTEkUiQ119MGdOrK7mrmxXHZF1/pfT6GgIZ6g== dependencies: graceful-fs "^4.1.2" -karma-spec-reporter@0.0.26: - version "0.0.26" - resolved "https://registry.yarnpkg.com/karma-spec-reporter/-/karma-spec-reporter-0.0.26.tgz#bf5561377dce1b63cf2c975c1af3e35f199e2265" +karma-spec-reporter@0.0.34: + version "0.0.34" + resolved "https://registry.yarnpkg.com/karma-spec-reporter/-/karma-spec-reporter-0.0.34.tgz#7dc79cdc76b0e37f17006921439600ae3c648669" + integrity sha512-l5H/Nh9q4g2Ysx2CDU2m+NIPyLQpCVbk9c4V02BTZHw3NM6RO1dq3eRpKXCSSdPt4RGfhHk8jDt3XYkGp+5PWg== dependencies: - colors "~0.6.0" + colors "1.4.0" -karma-webpack@^4.0.0-rc.3: - version "4.0.0-rc.6" - resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-4.0.0-rc.6.tgz#02ac6a47c7fc166c8b208446069a424698082405" +karma-webpack@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-5.0.0.tgz#2a2c7b80163fe7ffd1010f83f5507f95ef39f840" + integrity sha512-+54i/cd3/piZuP3dr54+NcFeKOPnys5QeM1IY+0SPASwrtHsliXUiCL50iW+K9WWA7RvamC4macvvQ86l3KtaA== dependencies: - async "^2.0.0" - loader-utils "^1.1.0" - source-map "^0.5.6" - webpack-dev-middleware "^3.2.0" + glob "^7.1.3" + minimatch "^3.0.4" + webpack-merge "^4.1.5" -karma@^3.0.0: - version "3.1.4" - resolved "https://registry.yarnpkg.com/karma/-/karma-3.1.4.tgz#3890ca9722b10d1d14b726e1335931455788499e" - dependencies: - bluebird "^3.3.0" - body-parser "^1.16.1" - chokidar "^2.0.3" - colors "^1.1.0" - combine-lists "^1.0.0" - connect "^3.6.0" - core-js "^2.2.0" +karma@6.4.1: + version "6.4.1" + resolved "https://registry.yarnpkg.com/karma/-/karma-6.4.1.tgz#f2253716dd3a41aaa813fa9f54b6ee047e1127d9" + integrity sha512-Cj57NKOskK7wtFWSlMvZf459iX+kpYIPXmkNUzP2WAFcA7nhr/ALn5R7sw3w+1udFDcpMx/tuB8d5amgm3ijaA== + dependencies: + "@colors/colors" "1.5.0" + body-parser "^1.19.0" + braces "^3.0.2" + chokidar "^3.5.1" + connect "^3.7.0" di "^0.0.1" - dom-serialize "^2.2.0" - expand-braces "^0.1.1" - flatted "^2.0.0" - glob "^7.1.1" - graceful-fs "^4.1.2" - http-proxy "^1.13.0" - isbinaryfile "^3.0.0" - lodash "^4.17.5" - log4js "^3.0.0" - mime "^2.3.1" - minimatch "^3.0.2" - optimist "^0.6.1" - qjobs "^1.1.4" - range-parser "^1.2.0" - rimraf "^2.6.0" - safe-buffer "^5.0.1" - socket.io "2.1.1" + dom-serialize "^2.2.1" + glob "^7.1.7" + graceful-fs "^4.2.6" + http-proxy "^1.18.1" + isbinaryfile "^4.0.8" + lodash "^4.17.21" + log4js "^6.4.1" + mime "^2.5.2" + minimatch "^3.0.4" + mkdirp "^0.5.5" + qjobs "^1.2.0" + range-parser "^1.2.1" + rimraf "^3.0.2" + socket.io "^4.4.1" source-map "^0.6.1" - tmp "0.0.33" - useragent "2.3.0" - -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - dependencies: - is-buffer "^1.1.5" + tmp "^0.2.1" + ua-parser-js "^0.7.30" + yargs "^16.1.1" -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - dependencies: - is-buffer "^1.1.5" - -kind-of@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" - -kind-of@^6.0.0, kind-of@^6.0.2: +kind-of@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" @@ -5344,28 +5521,23 @@ kind-of@^6.0.3: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -known-css-properties@^0.19.0: - version "0.19.0" - resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.19.0.tgz#5d92b7fa16c72d971bda9b7fe295bdf61836ee5b" - integrity sha512-eYboRV94Vco725nKMlpkn3nV2+96p9c3gKXRsYqAJSswSENvBhN7n5L+uDhY58xQa0UukWsDMTGELzmD8Q+wTA== - -lcid@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" - dependencies: - invert-kv "^2.0.0" +klona@^2.0.4, klona@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc" + integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ== -leven@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" - integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== +known-css-properties@^0.21.0: + version "0.21.0" + resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.21.0.tgz#15fbd0bbb83447f3ce09d8af247ed47c68ede80d" + integrity sha512-sZLUnTqimCkvkgRS+kbPlYW5o8q5w1cu+uIisKpEWkj31I8mx8kNG162DwRav8Zirkva6N5uoFsm9kzK4mUXjw== -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" + prelude-ls "^1.2.1" + type-check "~0.4.0" lie@3.1.1: version "3.1.1" @@ -5373,52 +5545,29 @@ lie@3.1.1: dependencies: immediate "~3.0.5" +lie@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" + integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== + dependencies: + immediate "~3.0.5" + +lilconfig@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.6.tgz#32a384558bd58af3d4c6e077dd1ad1d397bc69d4" + integrity sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg== + lines-and-columns@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= -load-json-file@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" - -load-json-file@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - strip-bom "^3.0.0" - -loader-fs-cache@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/loader-fs-cache/-/loader-fs-cache-1.0.1.tgz#56e0bf08bd9708b26a765b68509840c8dec9fdbc" - dependencies: - find-cache-dir "^0.1.1" - mkdirp "0.5.1" - -loader-runner@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" - integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== - -loader-utils@^0.2.16, loader-utils@^0.2.3: - version "0.2.17" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" - dependencies: - big.js "^3.1.3" - emojis-list "^2.0.0" - json5 "^0.5.0" - object-assign "^4.0.1" +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== -loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0: +loader-utils@^1.0.2: version "1.2.3" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" dependencies: @@ -5426,15 +5575,6 @@ loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0: emojis-list "^2.0.0" json5 "^1.0.1" -loader-utils@^1.2.3: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" - integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^1.0.1" - loader-utils@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" @@ -5444,9 +5584,10 @@ loader-utils@^2.0.0: emojis-list "^3.0.0" json5 "^2.1.2" -localforage@^1.5.0: - version "1.7.3" - resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.7.3.tgz#0082b3ca9734679e1bd534995bdd3b24cf10f204" +localforage@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4" + integrity sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg== dependencies: lie "3.1.1" @@ -5471,6 +5612,13 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + lodash._arraycopy@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz#76e7b7c1f1fb92547374878a562ed06a3e50f6e1" @@ -5506,18 +5654,10 @@ lodash._baseclone@^3.0.0: lodash.isarray "^3.0.0" lodash.keys "^3.0.0" -lodash._baseclone@^4.0.0: - version "4.5.7" - resolved "https://registry.yarnpkg.com/lodash._baseclone/-/lodash._baseclone-4.5.7.tgz#ce42ade08384ef5d62fa77c30f61a46e686f8434" - lodash._basecopy@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" -lodash._basecreate@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821" - lodash._baseeach@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/lodash._baseeach/-/lodash._baseeach-3.0.4.tgz#cf8706572ca144e8d9d75227c990da982f932af3" @@ -5564,14 +5704,6 @@ lodash._isiterateecall@^3.0.0: version "3.0.9" resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" -lodash._stack@^4.0.0: - version "4.1.3" - resolved "https://registry.yarnpkg.com/lodash._stack/-/lodash._stack-4.1.3.tgz#751aa76c1b964b047e76d14fc72a093fcb5e2dd0" - -lodash.camelcase@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" - lodash.clone@3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-3.0.3.tgz#84688c73d32b5a90ca25616963f189252a997043" @@ -5580,24 +5712,20 @@ lodash.clone@3.0.3: lodash._bindcallback "^3.0.0" lodash._isiterateecall "^3.0.0" -lodash.create@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7" - dependencies: - lodash._baseassign "^3.0.0" - lodash._basecreate "^3.0.0" - lodash._isiterateecall "^3.0.0" +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= -lodash.defaultsdeep@4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/lodash.defaultsdeep/-/lodash.defaultsdeep-4.3.2.tgz#6c1a586e6c5647b0e64e2d798141b8836158be8a" - dependencies: - lodash._baseclone "^4.0.0" - lodash._stack "^4.0.0" - lodash.isplainobject "^4.0.0" - lodash.keysin "^4.0.0" - lodash.mergewith "^4.0.0" - lodash.rest "^4.0.0" +lodash.defaultsdeep@4.6.1: + version "4.6.1" + resolved "https://registry.yarnpkg.com/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz#512e9bd721d272d94e3d3a63653fa17516741ca6" + integrity sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA== + +lodash.escape@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" + integrity sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw== lodash.find@^3.2.1: version "3.2.1" @@ -5610,6 +5738,11 @@ lodash.find@^3.2.1: lodash.isarray "^3.0.0" lodash.keys "^3.0.0" +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== + lodash.isarguments@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" @@ -5622,6 +5755,11 @@ lodash.isequal@^4.2.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" +lodash.isfunction@^3.0.8: + version "3.0.9" + resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051" + integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw== + lodash.isplainobject@^3.0.0, lodash.isplainobject@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-3.2.0.tgz#9a8238ae16b200432960cd7346512d0123fbf4c5" @@ -5630,19 +5768,10 @@ lodash.isplainobject@^3.0.0, lodash.isplainobject@^3.2.0: lodash.isarguments "^3.0.0" lodash.keysin "^3.0.0" -lodash.isplainobject@^4.0.0: - version "4.0.6" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" - lodash.istypedarray@^3.0.0: version "3.0.6" resolved "https://registry.yarnpkg.com/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz#c9a477498607501d8e8494d283b87c39281cef62" -lodash.kebabcase@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" - integrity sha1-hImxyw0p/4gZXM7KRI/21swpXDY= - lodash.keys@^3.0.0: version "3.1.2" resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" @@ -5658,13 +5787,15 @@ lodash.keysin@^3.0.0: lodash.isarguments "^3.0.0" lodash.isarray "^3.0.0" -lodash.keysin@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.keysin/-/lodash.keysin-4.2.0.tgz#8cc3fb35c2d94acc443a1863e02fa40799ea6f28" - lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== + +lodash.merge@4.6.2, lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== lodash.merge@^3.3.2: version "3.3.2" @@ -5682,28 +5813,16 @@ lodash.merge@^3.3.2: lodash.keysin "^3.0.0" lodash.toplainobject "^3.0.0" -lodash.mergewith@^4.0.0: - version "4.6.1" - resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927" - lodash.pairs@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash.pairs/-/lodash.pairs-3.0.1.tgz#bbe08d5786eeeaa09a15c91ebf0dcb7d2be326a9" dependencies: lodash.keys "^3.0.0" -lodash.rest@^4.0.0: - version "4.0.5" - resolved "https://registry.yarnpkg.com/lodash.rest/-/lodash.rest-4.0.5.tgz#954ef75049262038c96d1fc98b28fdaf9f0772aa" - lodash.restparam@^3.0.0: version "3.6.1" resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" -lodash.tail@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.tail/-/lodash.tail-4.1.1.tgz#d2333a36d9e7717c8ad2f7cacafec7c32b444664" - lodash.toplainobject@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash.toplainobject/-/lodash.toplainobject-3.0.0.tgz#28790ad942d293d78aa663a07ecf7f52ca04198d" @@ -5711,11 +5830,22 @@ lodash.toplainobject@^3.0.0: lodash._basecopy "^3.0.0" lodash.keysin "^3.0.0" +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== + lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== -lodash@^4.16.4, lodash@^4.17.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.5.0: +lodash@4.17.21, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +lodash@^4.17.10: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" @@ -5724,10 +5854,13 @@ lodash@^4.17.13: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19: - version "4.17.19" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" - integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== +log-symbols@4.1.0, log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" log-symbols@^1.0.2: version "1.0.2" @@ -5735,67 +5868,46 @@ log-symbols@^1.0.2: dependencies: chalk "^1.0.0" -log-symbols@^2.1.0, log-symbols@^2.2.0: +log-symbols@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" dependencies: chalk "^2.0.1" -log-symbols@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" - integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== - dependencies: - chalk "^4.0.0" - -log4js@^3.0.0: - version "3.0.6" - resolved "https://registry.yarnpkg.com/log4js/-/log4js-3.0.6.tgz#e6caced94967eeeb9ce399f9f8682a4b2b28c8ff" +log4js@^6.4.1: + version "6.4.4" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.4.4.tgz#c9bc75569f3f40bba22fe1bd0677afa7a6a13bac" + integrity sha512-ncaWPsuw9Vl1CKA406hVnJLGQKy1OHx6buk8J4rE2lVW+NW5Y82G5/DIloO7NkqLOUtNPEANaWC1kZYVjXssPw== dependencies: - circular-json "^0.5.5" - date-format "^1.2.0" - debug "^3.1.0" - rfdc "^1.1.2" - streamroller "0.7.0" - -lolex@^1.4.0, lolex@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.6.0.tgz#3a9a0283452a47d7439e72731b9e07d7386e49f6" + date-format "^4.0.6" + debug "^4.3.4" + flatted "^3.2.5" + rfdc "^1.3.0" + streamroller "^3.0.6" -longest-streak@^2.0.1: +longest-streak@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.4.tgz#b8599957da5b5dab64dee3fe316fa774597d90e4" integrity sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg== -loose-envify@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - -loud-rejection@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" +loupe@2.3.4, loupe@^2.3.1: + version "2.3.4" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.4.tgz#7e0b9bffc76f148f9be769cb1321d3dcf3cb25f3" + integrity sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ== dependencies: - currently-unhandled "^0.4.1" - signal-exit "^3.0.0" - -lower-case@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" + get-func-name "^2.0.0" -lru-cache@4.1.x, lru-cache@^4.0.1, lru-cache@^4.1.1: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" + tslib "^2.0.3" -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" - dependencies: - yallist "^3.0.2" +lozad@1.16.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/lozad/-/lozad-1.16.0.tgz#86ce732c64c69926ccdebb81c8c90bb3735948b4" + integrity sha512-JBr9WjvEFeKoyim3svo/gsQPTkgG/mOHJmDctZ/+U9H3ymUuvEkqpn8bdQMFsvTMcyRJrdJkLv0bXqGm0sP72w== lru-cache@^6.0.0: version "6.0.0" @@ -5804,9 +5916,12 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lru-cache@~2.6.5: - version "2.6.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.6.5.tgz#e56d6354148ede8d7707b58d143220fd08df0fd5" +magic-string@^0.25.7: + version "0.25.9" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" + integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== + dependencies: + sourcemap-codec "^1.4.8" make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" @@ -5815,24 +5930,14 @@ make-dir@^2.0.0, make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" -make-dir@^3.0.2: +make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== dependencies: semver "^6.0.0" -map-age-cleaner@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" - dependencies: - p-defer "^1.0.0" - -map-cache@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - -map-obj@^1.0.0, map-obj@^1.0.1: +map-obj@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" @@ -5841,64 +5946,48 @@ map-obj@^4.0.0: resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.1.0.tgz#b91221b542734b9f14256c0132c897c5d7256fd5" integrity sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g== -map-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" - dependencies: - object-visit "^1.0.0" - -markdown-escapes@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" - integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== - -markdown-table@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-2.0.0.tgz#194a90ced26d31fe753d8b9434430214c011865b" - integrity sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A== - dependencies: - repeat-string "^1.0.0" - -math-expression-evaluator@^1.2.14: - version "1.2.17" - resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac" - -math-random@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.1.tgz#8b3aac588b8a66e4975e3cdea67f7bb329601fac" - mathml-tag-names@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== -md5.js@^1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" +mdast-util-from-markdown@^0.8.0: + version "0.8.5" + resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz#d1ef2ca42bc377ecb0463a987910dae89bd9a28c" + integrity sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ== dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" + "@types/mdast" "^3.0.0" + mdast-util-to-string "^2.0.0" + micromark "~2.11.0" + parse-entities "^2.0.0" + unist-util-stringify-position "^2.0.0" -mdast-util-compact@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/mdast-util-compact/-/mdast-util-compact-2.0.1.tgz#cabc69a2f43103628326f35b1acf735d55c99490" - integrity sha512-7GlnT24gEwDrdAwEHrU4Vv5lLWrEer4KOkAiKT9nYstsTad7Oc1TwqT2zIMKRdZF7cTuaf+GA1E4Kv7jJh8mPA== +mdast-util-to-markdown@^0.6.0: + version "0.6.5" + resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-0.6.5.tgz#b33f67ca820d69e6cc527a93d4039249b504bebe" + integrity sha512-XeV9sDE7ZlOQvs45C9UKMtfTcctcaj/pGwH8YLbMHoMOXNNCn2LsqVQOqrF1+/NU8lKDAqozme9SCXWyo9oAcQ== dependencies: - unist-util-visit "^2.0.0" + "@types/unist" "^2.0.0" + longest-streak "^2.0.0" + mdast-util-to-string "^2.0.0" + parse-entities "^2.0.0" + repeat-string "^1.0.0" + zwitch "^1.0.0" + +mdast-util-to-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz#b8cfe6a713e1091cb5b728fc48885a4767f8b97b" + integrity sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w== + +mdn-data@2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" + integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" -mem@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-4.1.0.tgz#aeb9be2d21f47e78af29e4ac5978e8afa2ca5b8a" - dependencies: - map-age-cleaner "^0.1.1" - mimic-fn "^1.0.0" - p-is-promise "^2.0.0" - memory-fs@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" @@ -5906,53 +5995,34 @@ memory-fs@^0.4.1: errno "^0.1.3" readable-stream "^2.0.1" -memory-fs@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c" - integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA== - dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" - -meow@^3.3.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" - dependencies: - camelcase-keys "^2.0.0" - decamelize "^1.1.2" - loud-rejection "^1.0.0" - map-obj "^1.0.1" - minimist "^1.1.3" - normalize-package-data "^2.3.4" - object-assign "^4.0.1" - read-pkg-up "^1.0.1" - redent "^1.0.0" - trim-newlines "^1.0.0" - -meow@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/meow/-/meow-7.0.1.tgz#1ed4a0a50b3844b451369c48362eb0515f04c1dc" - integrity sha512-tBKIQqVrAHqwit0vfuFPY3LlzJYkEOFyKa3bPgxzNl6q/RtN8KQ+ALYEASYuFayzSAsjlhXj/JZ10rH85Q6TUw== +meow@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-9.0.0.tgz#cd9510bc5cac9dee7d03c73ee1f9ad959f4ea364" + integrity sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ== dependencies: "@types/minimist" "^1.2.0" - arrify "^2.0.1" - camelcase "^6.0.0" camelcase-keys "^6.2.2" + decamelize "^1.2.0" decamelize-keys "^1.1.0" hard-rejection "^2.1.0" - minimist-options "^4.0.2" - normalize-package-data "^2.5.0" + minimist-options "4.1.0" + normalize-package-data "^3.0.0" read-pkg-up "^7.0.1" redent "^3.0.0" trim-newlines "^3.0.0" - type-fest "^0.13.1" - yargs-parser "^18.1.3" + type-fest "^0.18.0" + yargs-parser "^20.2.3" merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" -merge2@^1.3.0: +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== @@ -5961,41 +6031,13 @@ methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" -micromatch@^2.3.11: - version "2.3.11" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" - dependencies: - arr-diff "^2.0.0" - array-unique "^0.2.1" - braces "^1.8.2" - expand-brackets "^0.1.4" - extglob "^0.3.1" - filename-regex "^2.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.1" - kind-of "^3.0.2" - normalize-path "^2.0.1" - object.omit "^2.0.0" - parse-glob "^3.0.4" - regex-cache "^0.4.2" - -micromatch@^3.1.10, micromatch@^3.1.4: - version "3.1.10" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.2" +micromark@~2.11.0: + version "2.11.4" + resolved "https://registry.yarnpkg.com/micromark/-/micromark-2.11.4.tgz#d13436138eea826383e822449c9a5c50ee44665a" + integrity sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA== + dependencies: + debug "^4.0.0" + parse-entities "^2.0.0" micromatch@^4.0.2: version "4.0.2" @@ -6005,26 +6047,29 @@ micromatch@^4.0.2: braces "^3.0.1" picomatch "^2.0.5" -miller-rabin@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" +micromatch@^4.0.4, micromatch@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== dependencies: - bn.js "^4.0.0" - brorand "^1.0.1" + braces "^3.0.2" + picomatch "^2.3.1" mime-db@1.40.0: version "1.40.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" -mime-db@~1.37.0: - version "1.37.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.37.0.tgz#0b6a0ce6fdbe9576e25f1f2d2fde8830dc0ad0d8" +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@~2.1.18: - version "2.1.21" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.21.tgz#28995aa1ecb770742fe6ae7e58f9181c744b3f96" +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: - mime-db "~1.37.0" + mime-db "1.52.0" mime-types@~2.1.24: version "2.1.24" @@ -6032,52 +6077,72 @@ mime-types@~2.1.24: dependencies: mime-db "1.40.0" -mime@1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.0.3, mime@^2.3.1, mime@^2.4.2: - version "2.4.3" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.3.tgz#229687331e86f68924e6cb59e1cdd937f18275fe" +mime@^2.4.4, mime@^2.5.2: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + min-indent@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -mini-css-extract-plugin@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz#ac0059b02b9692515a637115b0cc9fed3a35c7b0" +mini-css-extract-plugin@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz#9a1251d15f2035c342d99a468ab9da7a0451b71e" + integrity sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg== dependencies: - loader-utils "^1.1.0" - schema-utils "^1.0.0" - webpack-sources "^1.1.0" - -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - -minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + schema-utils "^4.0.0" -"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4: +minimatch@3.0.4, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: brace-expansion "^1.1.7" -minimatch@3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" +minimatch@4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4" + integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g== + dependencies: + brace-expansion "^1.1.7" + +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: - brace-expansion "^1.0.0" + brace-expansion "^1.1.7" + +minimatch@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7" + integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg== + dependencies: + brace-expansion "^2.0.1" -minimist-options@^4.0.2: +minimist-options@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== @@ -6086,11 +6151,16 @@ minimist-options@^4.0.2: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@0.0.8, minimist@~0.0.1: +minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" -minimist@1.2.0, minimist@^1.1.3, minimist@^1.2.0: +minimist@1.2.6, minimist@^1.2.3, minimist@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + +minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" @@ -6099,146 +6169,80 @@ minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== -minipass-collect@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" - integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== - dependencies: - minipass "^3.0.0" - -minipass-flush@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" - integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== - dependencies: - minipass "^3.0.0" - -minipass-pipeline@^1.2.2: - version "1.2.4" - resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" - integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== - dependencies: - minipass "^3.0.0" - -minipass@^2.2.1, minipass@^2.3.4: - version "2.3.5" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" - dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" - -minipass@^3.0.0, minipass@^3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" - integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== - dependencies: - yallist "^4.0.0" - -minizlib@^1.1.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" - dependencies: - minipass "^2.2.1" - -minizlib@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" - integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== - dependencies: - minipass "^3.0.0" - yallist "^4.0.0" - -mississippi@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" - dependencies: - concat-stream "^1.5.0" - duplexify "^3.4.2" - end-of-stream "^1.1.0" - flush-write-stream "^1.0.0" - from2 "^2.1.0" - parallel-transform "^1.1.0" - pump "^3.0.0" - pumpify "^1.3.3" - stream-each "^1.1.0" - through2 "^2.0.0" - -mixin-deep@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" - dependencies: - for-in "^1.0.2" - is-extendable "^1.0.1" - -mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: +mkdirp@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: minimist "0.0.8" -mkdirp@^0.5.3: +mkdirp@^0.5.5: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: minimist "^1.2.5" -mkdirp@^1.0.3, mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - mkpath@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/mkpath/-/mkpath-1.0.0.tgz#ebb3a977e7af1c683ae6fda12b545a6ba6c5853d" -mocha-nightwatch@3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/mocha-nightwatch/-/mocha-nightwatch-3.2.2.tgz#91bcb9b3bde057dd7677c78125e491e58d66647c" - dependencies: - browser-stdout "1.3.0" - commander "2.9.0" - debug "2.2.0" - diff "1.4.0" - escape-string-regexp "1.0.5" - glob "7.0.5" - growl "1.9.2" - json3 "3.3.2" - lodash.create "3.1.1" - mkdirp "0.5.1" - supports-color "3.1.2" - -mocha@^3.1.0: - version "3.5.3" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.5.3.tgz#1e0480fe36d2da5858d1eb6acc38418b26eaa20d" - dependencies: - browser-stdout "1.3.0" - commander "2.9.0" - debug "2.6.8" - diff "3.2.0" - escape-string-regexp "1.0.5" - glob "7.1.1" - growl "1.9.2" - he "1.1.1" - json3 "3.3.2" - lodash.create "3.1.1" - mkdirp "0.5.1" - supports-color "3.1.2" - -move-concurrently@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" - dependencies: - aproba "^1.1.1" - copy-concurrently "^1.0.0" - fs-write-stream-atomic "^1.0.8" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.3" - -ms@0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" +mocha@10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.0.0.tgz#205447d8993ec755335c4b13deba3d3a13c4def9" + integrity sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA== + dependencies: + "@ungap/promise-all-settled" "1.1.2" + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.5.3" + debug "4.3.4" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.2.0" + he "1.2.0" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "5.0.1" + ms "2.1.3" + nanoid "3.3.3" + serialize-javascript "6.0.0" + strip-json-comments "3.1.1" + supports-color "8.1.1" + workerpool "6.2.1" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" + +mocha@9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.2.2.tgz#d70db46bdb93ca57402c809333e5a84977a88fb9" + integrity sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g== + dependencies: + "@ungap/promise-all-settled" "1.1.2" + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.5.3" + debug "4.3.3" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.2.0" + growl "1.10.5" + he "1.2.0" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "4.2.1" + ms "2.1.3" + nanoid "3.3.1" + serialize-javascript "6.0.0" + strip-json-comments "3.1.1" + supports-color "8.1.1" + which "2.0.2" + workerpool "6.2.0" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" ms@2.0.0: version "2.0.0" @@ -6249,185 +6253,108 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +ms@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + ms@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" -mute-stream@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" - -nan@^2.12.1: - version "2.14.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" +nanoid@3.3.1, nanoid@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" + integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== -nanomatch@^1.2.9: - version "1.2.13" - resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - define-property "^2.0.2" - extend-shallow "^3.0.2" - fragment-cache "^0.2.1" - is-windows "^1.0.2" - kind-of "^6.0.2" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" +nanoid@3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" + integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== -native-promise-only@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/native-promise-only/-/native-promise-only-0.8.1.tgz#20a318c30cb45f71fe7adfbf7b21c99c1472ef11" +nanoid@^3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" + integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" -needle@^2.2.1: - version "2.2.4" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.4.tgz#51931bff82533b1928b7d1d69e01f1b00ffd2a4e" - dependencies: - debug "^2.1.2" - iconv-lite "^0.4.4" - sax "^1.2.4" - -negotiator@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" - negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" -neo-async@^2.5.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -neo-async@^2.6.1: +neo-async@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -netmask@~1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35" - -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - -nightwatch@^0.9.8: - version "0.9.21" - resolved "https://registry.yarnpkg.com/nightwatch/-/nightwatch-0.9.21.tgz#9e794a7514b4fd5f46602d368e50515232ab9e90" - dependencies: - chai-nightwatch "~0.1.x" - ejs "2.5.7" +nightwatch@2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/nightwatch/-/nightwatch-2.3.3.tgz#b5ab2a78512229b730aeb6d6e7ec1494c56770cf" + integrity sha512-NuTxWtG2bac7e12d7mN7bCnnlRDplPDxdLshWfY70VShAVyCE7qFtz6Lto2FycyOz4oa6w2Knpry08qxjgwf0g== + dependencies: + "@nightwatch/chai" "5.0.2" + ansi-to-html "^0.7.2" + assertion-error "1.1.0" + boxen "5.1.2" + chai-nightwatch "0.5.3" + ci-info "3.3.0" + didyoumean "1.2.2" + dotenv "10.0.0" + ejs "^3.1.8" + envinfo "7.8.1" + fs-extra "^10.1.0" + glob "^7.2.3" lodash.clone "3.0.3" - lodash.defaultsdeep "4.3.2" - minimatch "3.0.3" + lodash.defaultsdeep "4.6.1" + lodash.escape "^4.0.1" + lodash.merge "4.6.2" + minimatch "3.0.4" + minimist "1.2.6" mkpath "1.0.0" - mocha-nightwatch "3.2.2" - optimist "0.6.1" - proxy-agent "2.0.0" - q "1.4.1" - -no-case@^2.2.0: - version "2.3.2" - resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" + mocha "9.2.2" + open "^8.4.0" + ora "5.4.1" + selenium-webdriver "^4.3.1" + semver "7.3.5" + stacktrace-parser "^0.1.10" + strip-ansi "6.0.1" + +nise@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.2.tgz#a7b8909c216b3491fd4fc0b124efb69f3939b449" + integrity sha512-+gQjFi8v+tkfCuSCxfURHLhRhniE/+IaYbIphxAN2JRR9SHKhY8hgXpaXiYfHdw+gcGe4buxgbprBQFab9FkhA== dependencies: - lower-case "^1.1.1" - -node-libs-browser@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" - integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== - dependencies: - assert "^1.1.1" - browserify-zlib "^0.2.0" - buffer "^4.3.0" - console-browserify "^1.1.0" - constants-browserify "^1.0.0" - crypto-browserify "^3.11.0" - domain-browser "^1.1.1" - events "^3.0.0" - https-browserify "^1.0.0" - os-browserify "^0.3.0" - path-browserify "0.0.1" - process "^0.11.10" - punycode "^1.2.4" - querystring-es3 "^0.2.0" - readable-stream "^2.3.3" - stream-browserify "^2.0.1" - stream-http "^2.7.2" - string_decoder "^1.0.0" - timers-browserify "^2.0.4" - tty-browserify "0.0.0" - url "^0.11.0" - util "^0.11.0" - vm-browserify "^1.0.1" - -node-modules-regexp@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" - integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= + "@sinonjs/commons" "^2.0.0" + "@sinonjs/fake-timers" "^7.0.4" + "@sinonjs/text-encoding" "^0.7.1" + just-extend "^4.0.2" + path-to-regexp "^1.7.0" -node-pre-gyp@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149" - dependencies: - detect-libc "^1.0.2" - mkdirp "^0.5.1" - needle "^2.2.1" - nopt "^4.0.1" - npm-packlist "^1.1.6" - npmlog "^4.0.2" - rc "^1.2.7" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^4" - -node-releases@^1.1.42: - version "1.1.42" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.42.tgz#a999f6a62f8746981f6da90627a8d2fc090bbad7" - integrity sha512-OQ/ESmUqGawI2PRX+XIRao44qWYBBfN54ImQYdWVTQqUckuejOg76ysSqDBK8NG3zwySRVnX36JwDQ6x+9GxzA== +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== dependencies: - semver "^6.3.0" + lower-case "^2.0.2" + tslib "^2.0.3" node-releases@^1.1.58: version "1.1.60" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.60.tgz#6948bdfce8286f0b5d0e5a88e8384e954dfe7084" integrity sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA== -nomnomnomnom@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/nomnomnomnom/-/nomnomnomnom-2.0.1.tgz#b2239f031c8d04da67e32836e1e3199e12f7a8e2" - dependencies: - chalk "~0.4.0" - underscore "~1.6.0" - -nopt@3.x: - version "3.0.6" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" - dependencies: - abbrev "1" - -nopt@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" - dependencies: - abbrev "1" - osenv "^0.1.4" - -normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: - version "2.4.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" - dependencies: - hosted-git-info "^2.1.4" - is-builtin-module "^1.0.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" +node-releases@^2.0.5, node-releases@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" + integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== normalize-package-data@^2.5.0: version "2.5.0" @@ -6439,11 +6366,15 @@ normalize-package-data@^2.5.0: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" -normalize-path@^2.0.1, normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" +normalize-package-data@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e" + integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== dependencies: - remove-trailing-separator "^1.0.1" + hosted-git-info "^4.0.1" + is-core-module "^2.5.0" + semver "^7.3.4" + validate-npm-package-license "^3.0.1" normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" @@ -6458,85 +6389,35 @@ normalize-selector@^0.2.0: resolved "https://registry.yarnpkg.com/normalize-selector/-/normalize-selector-0.2.0.tgz#d0b145eb691189c63a78d201dc4fdb1293ef0c03" integrity sha1-0LFF62kRicY6eNIB3E/bEpPvDAM= -normalize-url@^1.4.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" - dependencies: - object-assign "^4.0.1" - prepend-http "^1.0.0" - query-string "^4.1.0" - sort-keys "^1.0.0" - -npm-bundled@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.5.tgz#3c1732b7ba936b3a10325aef616467c0ccbcc979" - -npm-packlist@^1.1.6: - version "1.2.0" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.2.0.tgz#55a60e793e272f00862c7089274439a4cc31fc7f" - dependencies: - ignore-walk "^3.0.1" - npm-bundled "^1.0.1" - -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - dependencies: - path-key "^2.0.0" - -npmlog@^4.0.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== -nth-check@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" +nth-check@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== dependencies: - boolbase "~1.0.0" + boolbase "^1.0.0" num2fraction@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - -object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@^4: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" -object-component@0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" - -object-copy@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" - -object-hash@^1.1.4: - version "1.3.1" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" +object-inspect@^1.11.0, object-inspect@^1.9.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" + integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== -object-keys@^1.0.11, object-keys@^1.0.12: +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" -object-visit@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" - dependencies: - isobject "^3.0.0" - object.assign@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" @@ -6547,25 +6428,31 @@ object.assign@^4.1.0: has-symbols "^1.0.0" object-keys "^1.0.11" -object.getownpropertydescriptors@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" +object.assign@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== dependencies: - define-properties "^1.1.2" - es-abstract "^1.5.1" + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" -object.omit@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" +object.values@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" + integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== dependencies: - for-own "^0.1.4" - is-extendable "^0.1.1" + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" -object.pick@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== dependencies: - isobject "^3.0.1" + ee-first "1.1.1" on-finished@~2.3.0: version "2.3.0" @@ -6573,94 +6460,77 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" -once@1.x, once@^1.3.0, once@^1.3.1, once@^1.4.0: +once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" dependencies: wrappy "1" -onetime@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" - onetime@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" dependencies: mimic-fn "^1.0.0" -opn@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/opn/-/opn-4.0.2.tgz#7abc22e644dff63b0a96d5ab7f2790c0f01abc95" +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: - object-assign "^4.0.1" - pinkie-promise "^2.0.0" + mimic-fn "^2.1.0" -optimist@0.6.1, optimist@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" +open@^8.4.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8" + integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q== dependencies: - minimist "~0.0.1" - wordwrap "~0.0.2" + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" -optionator@^0.8.1, optionator@^0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" +opn@5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" + integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.4" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - wordwrap "~1.0.0" + is-wsl "^1.1.0" -ora@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/ora/-/ora-0.3.0.tgz#367a078ad25cfb096da501115eb5b401e07d7495" +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== dependencies: - chalk "^1.1.1" - cli-cursor "^1.0.2" - cli-spinners "^0.2.0" - log-symbols "^1.0.2" - -os-browserify@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" - -os-homedir@^1.0.0, os-homedir@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" -os-locale@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" +ora@0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-0.4.1.tgz#e268187c8890295f7859ac535fd8ca2c0ff64de7" + integrity sha1-4mgYfIiQKV94WaxTX9jKLA/2Tec= dependencies: - execa "^1.0.0" - lcid "^2.0.0" - mem "^4.0.0" - -os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + chalk "^1.1.1" + cli-cursor "^2.1.0" + cli-spinners "^1.0.0" + log-symbols "^1.0.2" -osenv@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" +ora@5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - -p-defer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - -p-is-promise@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.0.0.tgz#7554e3d572109a87e1f3f53f6a7d85d1b194f4c5" + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" p-limit@^1.1.0: version "1.3.0" @@ -6707,6 +6577,13 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-map@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" @@ -6722,47 +6599,18 @@ p-try@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1" -pac-proxy-agent@1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-1.1.0.tgz#34a385dfdf61d2f0ecace08858c745d3e791fd4d" - dependencies: - agent-base "2" - debug "2" - extend "3" - get-uri "2" - http-proxy-agent "1" - https-proxy-agent "1" - pac-resolver "~2.0.0" - raw-body "2" - socks-proxy-agent "2" - -pac-resolver@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-2.0.0.tgz#99b88d2f193fbdeefc1c9a529c1f3260ab5277cd" - dependencies: - co "~3.0.6" - degenerator "~1.0.2" - ip "1.0.1" - netmask "~1.0.4" - thunkify "~2.1.1" - -pako@~1.0.5: - version "1.0.10" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" - -parallel-transform@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06" - dependencies: - cyclist "~0.2.2" - inherits "^2.0.3" - readable-stream "^2.1.5" +pako@~1.0.2: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== -param-case@2.1.x: - version "2.1.1" - resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" +param-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" + integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== dependencies: - no-case "^2.2.0" + dot-case "^3.0.4" + tslib "^2.0.3" parent-module@^1.0.0: version "1.0.1" @@ -6770,17 +6618,6 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-asn1@^5.0.0: - version "5.1.4" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.4.tgz#37f6628f823fbdeb2273b4d540434a22f3ef1fcc" - dependencies: - asn1.js "^4.0.0" - browserify-aes "^1.0.0" - create-hash "^1.1.0" - evp_bytestokey "^1.0.0" - pbkdf2 "^3.0.3" - safe-buffer "^5.1.1" - parse-entities@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8" @@ -6793,29 +6630,6 @@ parse-entities@^2.0.0: is-decimal "^1.0.0" is-hexadecimal "^1.0.0" -parse-glob@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" - dependencies: - glob-base "^0.3.0" - is-dotfile "^1.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.0" - -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - dependencies: - error-ex "^1.2.0" - -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" - integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= - dependencies: - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - parse-json@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.0.1.tgz#7cfe35c1ccd641bce3981467e6c2ece61b3b3878" @@ -6826,47 +6640,25 @@ parse-json@^5.0.0: json-parse-better-errors "^1.0.1" lines-and-columns "^1.1.6" -parse-link-header@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parse-link-header/-/parse-link-header-1.0.1.tgz#bedfe0d2118aeb84be75e7b025419ec8a61140a7" - integrity sha1-vt/g0hGK64S+deewJUGeyKYRQKc= +parse-link-header@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/parse-link-header/-/parse-link-header-2.0.0.tgz#949353e284f8aa01f2ac857a98f692b57733f6b7" + integrity sha512-xjU87V0VyHZybn2RrCX5TIFGxTVZE6zqqZWMPlIKiSKuWh/X5WZdt+w1Ki1nXB+8L/KtL+nZ4iq+sfI6MrhhMw== dependencies: xtend "~4.0.1" -parseqs@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" - dependencies: - better-assert "~1.0.0" - -parseuri@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a" - dependencies: - better-assert "~1.0.0" - -parseurl@~1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" - -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - -path-browserify@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" - integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== - -path-dirname@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== -path-exists@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" +pascal-case@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" + integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== dependencies: - pinkie-promise "^2.0.0" + no-case "^3.0.4" + tslib "^2.0.3" path-exists@^3.0.0: version "3.0.0" @@ -6877,22 +6669,24 @@ path-exists@^4.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== -path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: +path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" -path-is-inside@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - -path-key@^2.0.0, path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -6903,42 +6697,34 @@ path-to-regexp@^1.7.0: dependencies: isarray "0.0.1" -path-type@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" - dependencies: - graceful-fs "^4.1.2" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -path-type@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" - dependencies: - pify "^2.0.0" - path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -pbkdf2@^3.0.3: - version "3.0.17" - resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" - dependencies: - create-hash "^1.1.2" - create-hmac "^1.1.4" - ripemd160 "^2.0.1" - safe-buffer "^5.0.1" - sha.js "^2.4.8" +pathval@1.1.1, pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" -phoenix@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/phoenix/-/phoenix-1.4.0.tgz#9cec8dbd8cbc59ecd2147bc09ca8ceb56b860d75" +phoenix@1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/phoenix/-/phoenix-1.6.2.tgz#8d1d9f06e51cb893d08059e80488cd0de328e01a" + integrity sha512-VjR27NETvrLSj8rI6DlpVAfo7pCYth/9+1OCoTof4LKEbq0141ze/tdxFHHZzVQSok3gqJUo2h/tqbxR3r8eyw== + +picocolors@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" + integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== picomatch@^2.0.4: version "2.2.3" @@ -6950,42 +6736,19 @@ picomatch@^2.0.5, picomatch@^2.2.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== -pify@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" +picomatch@^2.2.3, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== pify@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - -pirates@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" - integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== - dependencies: - node-modules-regexp "^1.0.0" - -pkg-dir@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" - dependencies: - find-up "^1.0.0" - -pkg-dir@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" - dependencies: - find-up "^2.1.0" +pirates@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" + integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== pkg-dir@^3.0.0: version "3.0.0" @@ -7000,77 +6763,61 @@ pkg-dir@^4.1.0: dependencies: find-up "^4.0.0" -pngjs@^3.3.0: - version "3.3.3" - resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.3.3.tgz#85173703bde3edac8998757b96e5821d0966a21b" - -portal-vue@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/portal-vue/-/portal-vue-2.1.4.tgz#1fc679d77e294dc8d026f1eb84aa467de11b392e" - -posix-character-classes@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" - -postcss-calc@^5.2.0: - version "5.3.1" - resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e" - dependencies: - postcss "^5.0.2" - postcss-message-helpers "^2.0.0" - reduce-css-calc "^1.2.6" +pngjs@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-5.0.0.tgz#e79dd2b215767fd9c04561c01236df960bce7fbb" + integrity sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw== -postcss-colormin@^2.1.8: - version "2.2.2" - resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.2.tgz#6631417d5f0e909a3d7ec26b24c8a8d1e4f96e4b" - dependencies: - colormin "^1.0.5" - postcss "^5.0.13" - postcss-value-parser "^3.2.3" +pointer-tracker@^2.0.3: + version "2.4.0" + resolved "https://registry.yarnpkg.com/pointer-tracker/-/pointer-tracker-2.4.0.tgz#78721c2d2201486db11ec1094377f03023b621b3" + integrity sha512-pWI2tpaM/XNtc9mUTv42Rmjf6mkHvE8LT5DDEq0G7baPNhxNM9E3CepubPplSoSLk9E5bwQrAMyDcPVmJyTW4g== -postcss-convert-values@^2.3.4: - version "2.6.1" - resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz#bbd8593c5c1fd2e3d1c322bb925dcae8dae4d62d" +postcss-calc@^8.2.3: + version "8.2.4" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-8.2.4.tgz#77b9c29bfcbe8a07ff6693dc87050828889739a5" + integrity sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q== dependencies: - postcss "^5.0.11" - postcss-value-parser "^3.1.2" + postcss-selector-parser "^6.0.9" + postcss-value-parser "^4.2.0" -postcss-discard-comments@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d" +postcss-colormin@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.3.0.tgz#3cee9e5ca62b2c27e84fce63affc0cfb5901956a" + integrity sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg== dependencies: - postcss "^5.0.14" + browserslist "^4.16.6" + caniuse-api "^3.0.0" + colord "^2.9.1" + postcss-value-parser "^4.2.0" -postcss-discard-duplicates@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz#b9abf27b88ac188158a5eb12abcae20263b91932" +postcss-convert-values@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.1.2.tgz#31586df4e184c2e8890e8b34a0b9355313f503ab" + integrity sha512-c6Hzc4GAv95B7suy4udszX9Zy4ETyMCgFPUDtWjdFTKH1SE9eFY/jEpHSwTH1QPuwxHpWslhckUQWbNRM4ho5g== dependencies: - postcss "^5.0.4" + browserslist "^4.20.3" + postcss-value-parser "^4.2.0" -postcss-discard-empty@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz#d2b4bd9d5ced5ebd8dcade7640c7d7cd7f4f92b5" - dependencies: - postcss "^5.0.14" +postcss-discard-comments@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz#8df5e81d2925af2780075840c1526f0660e53696" + integrity sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ== -postcss-discard-overridden@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz#8b1eaf554f686fb288cd874c55667b0aa3668d58" - dependencies: - postcss "^5.0.16" +postcss-discard-duplicates@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz#9eb4fe8456706a4eebd6d3b7b777d07bad03e848" + integrity sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw== -postcss-discard-unused@^2.2.1: - version "2.2.3" - resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz#bce30b2cc591ffc634322b5fb3464b6d934f4433" - dependencies: - postcss "^5.0.14" - uniqs "^2.0.0" +postcss-discard-empty@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz#e57762343ff7f503fe53fca553d18d7f0c369c6c" + integrity sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A== -postcss-filter-plugins@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz#82245fdf82337041645e477114d8e593aa18b8ec" - dependencies: - postcss "^5.0.4" +postcss-discard-overridden@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz#7e8c5b53325747e9d90131bb88635282fb4a276e" + integrity sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw== postcss-html@^0.36.0: version "0.36.0" @@ -7086,192 +6833,183 @@ postcss-less@^3.1.4: dependencies: postcss "^7.0.14" -postcss-load-config@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-1.2.0.tgz#539e9afc9ddc8620121ebf9d8c3673e0ce50d28a" +postcss-loader@7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-7.0.1.tgz#4c883cc0a1b2bfe2074377b7a74c1cd805684395" + integrity sha512-VRviFEyYlLjctSM93gAZtcJJ/iSkPZ79zWbN/1fSH+NisBByEiVLqpdVDrPLVSi8DX0oJo12kL/GppTBdKVXiQ== dependencies: - cosmiconfig "^2.1.0" - object-assign "^4.1.0" - postcss-load-options "^1.2.0" - postcss-load-plugins "^2.3.0" + cosmiconfig "^7.0.0" + klona "^2.0.5" + semver "^7.3.7" -postcss-load-config@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.0.tgz#c84d692b7bb7b41ddced94ee62e8ab31b417b003" - integrity sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q== - dependencies: - cosmiconfig "^5.0.0" - import-cwd "^2.0.0" +postcss-media-query-parser@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz#27b39c6f4d94f81b1a73b8f76351c609e5cef244" + integrity sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ= -postcss-load-options@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/postcss-load-options/-/postcss-load-options-1.2.0.tgz#b098b1559ddac2df04bc0bb375f99a5cfe2b6d8c" +postcss-merge-longhand@^5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.1.6.tgz#f378a8a7e55766b7b644f48e5d8c789ed7ed51ce" + integrity sha512-6C/UGF/3T5OE2CEbOuX7iNO63dnvqhGZeUnKkDeifebY0XqkkvrctYSZurpNE902LDf2yKwwPFgotnfSoPhQiw== dependencies: - cosmiconfig "^2.1.0" - object-assign "^4.1.0" + postcss-value-parser "^4.2.0" + stylehacks "^5.1.0" -postcss-load-plugins@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz#745768116599aca2f009fad426b00175049d8d92" +postcss-merge-rules@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.1.2.tgz#7049a14d4211045412116d79b751def4484473a5" + integrity sha512-zKMUlnw+zYCWoPN6yhPjtcEdlJaMUZ0WyVcxTAmw3lkkN/NDMRkOkiuctQEoWAOvH7twaxUUdvBWl0d4+hifRQ== dependencies: - cosmiconfig "^2.1.1" - object-assign "^4.1.0" + browserslist "^4.16.6" + caniuse-api "^3.0.0" + cssnano-utils "^3.1.0" + postcss-selector-parser "^6.0.5" -postcss-loader@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-3.0.0.tgz#6b97943e47c72d845fa9e03f273773d4e8dd6c2d" - integrity sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA== +postcss-minify-font-values@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz#f1df0014a726083d260d3bd85d7385fb89d1f01b" + integrity sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA== dependencies: - loader-utils "^1.1.0" - postcss "^7.0.0" - postcss-load-config "^2.0.0" - schema-utils "^1.0.0" + postcss-value-parser "^4.2.0" -postcss-media-query-parser@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz#27b39c6f4d94f81b1a73b8f76351c609e5cef244" - integrity sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ= - -postcss-merge-idents@^2.1.5: - version "2.1.7" - resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz#4c5530313c08e1d5b3bbf3d2bbc747e278eea270" +postcss-minify-gradients@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz#f1fe1b4f498134a5068240c2f25d46fcd236ba2c" + integrity sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw== dependencies: - has "^1.0.1" - postcss "^5.0.10" - postcss-value-parser "^3.1.1" + colord "^2.9.1" + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" -postcss-merge-longhand@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz#23d90cd127b0a77994915332739034a1a4f3d658" +postcss-minify-params@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-5.1.3.tgz#ac41a6465be2db735099bbd1798d85079a6dc1f9" + integrity sha512-bkzpWcjykkqIujNL+EVEPOlLYi/eZ050oImVtHU7b4lFS82jPnsCb44gvC6pxaNt38Els3jWYDHTjHKf0koTgg== dependencies: - postcss "^5.0.4" + browserslist "^4.16.6" + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" -postcss-merge-rules@^2.0.3: - version "2.1.2" - resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz#d1df5dfaa7b1acc3be553f0e9e10e87c61b5f721" +postcss-minify-selectors@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz#d4e7e6b46147b8117ea9325a915a801d5fe656c6" + integrity sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg== dependencies: - browserslist "^1.5.2" - caniuse-api "^1.5.2" - postcss "^5.0.4" - postcss-selector-parser "^2.2.2" - vendors "^1.0.0" + postcss-selector-parser "^6.0.5" -postcss-message-helpers@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz#a4f2f4fab6e4fe002f0aed000478cdf52f9ba60e" +postcss-modules-extract-imports@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" + integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== -postcss-minify-font-values@^1.0.2: - version "1.0.5" - resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz#4b58edb56641eba7c8474ab3526cafd7bbdecb69" +postcss-modules-local-by-default@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c" + integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ== dependencies: - object-assign "^4.0.1" - postcss "^5.0.4" - postcss-value-parser "^3.0.2" + icss-utils "^5.0.0" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" -postcss-minify-gradients@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz#5dbda11373703f83cfb4a3ea3881d8d75ff5e6e1" +postcss-modules-scope@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" + integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== dependencies: - postcss "^5.0.12" - postcss-value-parser "^3.3.0" + postcss-selector-parser "^6.0.4" -postcss-minify-params@^1.0.4: - version "1.2.2" - resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz#ad2ce071373b943b3d930a3fa59a358c28d6f1f3" +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== dependencies: - alphanum-sort "^1.0.1" - postcss "^5.0.2" - postcss-value-parser "^3.0.2" - uniqs "^2.0.0" + icss-utils "^5.0.0" -postcss-minify-selectors@^2.0.4: - version "2.1.1" - resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz#b2c6a98c0072cf91b932d1a496508114311735bf" - dependencies: - alphanum-sort "^1.0.2" - has "^1.0.1" - postcss "^5.0.14" - postcss-selector-parser "^2.0.0" +postcss-normalize-charset@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz#9302de0b29094b52c259e9b2cf8dc0879879f0ed" + integrity sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg== -postcss-modules-extract-imports@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz#dc87e34148ec7eab5f791f7cd5849833375b741a" +postcss-normalize-display-values@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz#72abbae58081960e9edd7200fcf21ab8325c3da8" + integrity sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA== dependencies: - postcss "^6.0.1" + postcss-value-parser "^4.2.0" -postcss-modules-local-by-default@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069" +postcss-normalize-positions@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz#ef97279d894087b59325b45c47f1e863daefbb92" + integrity sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg== dependencies: - css-selector-tokenizer "^0.7.0" - postcss "^6.0.1" + postcss-value-parser "^4.2.0" -postcss-modules-scope@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90" +postcss-normalize-repeat-style@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz#e9eb96805204f4766df66fd09ed2e13545420fb2" + integrity sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g== dependencies: - css-selector-tokenizer "^0.7.0" - postcss "^6.0.1" + postcss-value-parser "^4.2.0" -postcss-modules-values@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20" +postcss-normalize-string@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz#411961169e07308c82c1f8c55f3e8a337757e228" + integrity sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w== dependencies: - icss-replace-symbols "^1.1.0" - postcss "^6.0.1" + postcss-value-parser "^4.2.0" -postcss-normalize-charset@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz#ef9ee71212d7fe759c78ed162f61ed62b5cb93f1" +postcss-normalize-timing-functions@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz#d5614410f8f0b2388e9f240aa6011ba6f52dafbb" + integrity sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg== dependencies: - postcss "^5.0.5" + postcss-value-parser "^4.2.0" -postcss-normalize-url@^3.0.7: - version "3.0.8" - resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz#108f74b3f2fcdaf891a2ffa3ea4592279fc78222" +postcss-normalize-unicode@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz#3d23aede35e160089a285e27bf715de11dc9db75" + integrity sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ== dependencies: - is-absolute-url "^2.0.0" - normalize-url "^1.4.0" - postcss "^5.0.14" - postcss-value-parser "^3.2.3" + browserslist "^4.16.6" + postcss-value-parser "^4.2.0" -postcss-ordered-values@^2.1.0: - version "2.2.3" - resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz#eec6c2a67b6c412a8db2042e77fe8da43f95c11d" +postcss-normalize-url@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz#ed9d88ca82e21abef99f743457d3729a042adcdc" + integrity sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew== dependencies: - postcss "^5.0.4" - postcss-value-parser "^3.0.1" + normalize-url "^6.0.1" + postcss-value-parser "^4.2.0" -postcss-reduce-idents@^2.2.2: - version "2.4.0" - resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz#c2c6d20cc958284f6abfbe63f7609bf409059ad3" +postcss-normalize-whitespace@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz#08a1a0d1ffa17a7cc6efe1e6c9da969cc4493cfa" + integrity sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA== dependencies: - postcss "^5.0.4" - postcss-value-parser "^3.0.2" + postcss-value-parser "^4.2.0" -postcss-reduce-initial@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz#68f80695f045d08263a879ad240df8dd64f644ea" +postcss-ordered-values@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz#b6fd2bd10f937b23d86bc829c69e7732ce76ea38" + integrity sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ== dependencies: - postcss "^5.0.4" + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" -postcss-reduce-transforms@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz#ff76f4d8212437b31c298a42d2e1444025771ae1" +postcss-reduce-initial@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz#fc31659ea6e85c492fb2a7b545370c215822c5d6" + integrity sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw== dependencies: - has "^1.0.1" - postcss "^5.0.8" - postcss-value-parser "^3.0.1" + browserslist "^4.16.6" + caniuse-api "^3.0.0" -postcss-reporter@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/postcss-reporter/-/postcss-reporter-6.0.1.tgz#7c055120060a97c8837b4e48215661aafb74245f" - integrity sha512-LpmQjfRWyabc+fRygxZjpRxfhRf9u/fdlKf4VHG4TSPbV2XNsuISzYW1KL+1aQzx53CAppa1bKG4APIB/DOXXw== +postcss-reduce-transforms@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz#333b70e7758b802f3dd0ddfe98bb1ccfef96b6e9" + integrity sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ== dependencies: - chalk "^2.4.1" - lodash "^4.17.11" - log-symbols "^2.2.0" - postcss "^7.0.7" + postcss-value-parser "^4.2.0" postcss-resolve-nested-selector@0.1.1, postcss-resolve-nested-selector@^0.1.1: version "0.1.1" @@ -7309,89 +7047,54 @@ postcss-selector-parser@2.2.1: indexes-of "^1.0.1" uniq "^1.0.1" -postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.2: - version "2.2.3" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz#f9437788606c3c9acee16ffe8d8b16297f27bb90" - dependencies: - flatten "^1.0.2" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-selector-parser@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c" - integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg== +postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.9: + version "6.0.10" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" + integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== dependencies: cssesc "^3.0.0" - indexes-of "^1.0.1" - uniq "^1.0.1" + util-deprecate "^1.0.2" -postcss-svgo@^2.1.1: - version "2.1.6" - resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz#b6df18aa613b666e133f08adb5219c2684ac108d" +postcss-svgo@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.1.0.tgz#0a317400ced789f233a28826e77523f15857d80d" + integrity sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA== dependencies: - is-svg "^2.0.0" - postcss "^5.0.14" - postcss-value-parser "^3.2.3" - svgo "^0.7.0" + postcss-value-parser "^4.2.0" + svgo "^2.7.0" postcss-syntax@^0.36.2: version "0.36.2" resolved "https://registry.yarnpkg.com/postcss-syntax/-/postcss-syntax-0.36.2.tgz#f08578c7d95834574e5593a82dfbfa8afae3b51c" integrity sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w== -postcss-unique-selectors@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz#981d57d29ddcb33e7b1dfe1fd43b8649f933ca1d" +postcss-unique-selectors@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz#a9f273d1eacd09e9aa6088f4b0507b18b1b541b6" + integrity sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA== dependencies: - alphanum-sort "^1.0.1" - postcss "^5.0.4" - uniqs "^2.0.0" - -postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0: - version "3.3.1" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" + postcss-selector-parser "^6.0.5" postcss-value-parser@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== -postcss-zindex@^2.0.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.2.0.tgz#d2109ddc055b91af67fc4cb3b025946639d2af22" - dependencies: - has "^1.0.1" - postcss "^5.0.4" - uniqs "^2.0.0" - -postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.6, postcss@^5.0.8, postcss@^5.2.16: - version "5.2.18" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz#badfa1497d46244f6390f58b319830d9107853c5" - dependencies: - chalk "^1.1.3" - js-base64 "^2.1.9" - source-map "^0.5.6" - supports-color "^3.2.3" - -postcss@^6.0.1, postcss@^6.0.8: - version "6.0.23" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324" - dependencies: - chalk "^2.4.1" - source-map "^0.6.1" - supports-color "^5.4.0" +postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^7.0.0: - version "7.0.17" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.17.tgz#4da1bdff5322d4a0acaab4d87f3e782436bad31f" - integrity sha512-546ZowA+KZ3OasvQZHsbuEpysvwTZNGJv9EfyCQdsIDltPSWHAeTQ5fQy/Npi2ZDtLI3zs7Ps/p6wThErhm9fQ== +postcss@8.4.16, postcss@^8.4.7: + version "8.4.16" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.16.tgz#33a1d675fac39941f5f445db0de4db2b6e01d43c" + integrity sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ== dependencies: - chalk "^2.4.2" - source-map "^0.6.1" - supports-color "^6.1.0" + nanoid "^3.3.4" + picocolors "^1.0.0" + source-map-js "^1.0.2" -postcss@^7.0.14, postcss@^7.0.2, postcss@^7.0.21, postcss@^7.0.26, postcss@^7.0.32, postcss@^7.0.6, postcss@^7.0.7: +postcss@^7.0.14, postcss@^7.0.2, postcss@^7.0.21, postcss@^7.0.26, postcss@^7.0.32, postcss@^7.0.6: version "7.0.32" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw== @@ -7400,68 +7103,56 @@ postcss@^7.0.14, postcss@^7.0.2, postcss@^7.0.21, postcss@^7.0.26, postcss@^7.0. source-map "^0.6.1" supports-color "^6.1.0" -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" +postcss@^7.0.35: + version "7.0.39" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309" + integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== + dependencies: + picocolors "^0.2.1" + source-map "^0.6.1" -prepend-http@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" +postcss@^8.1.10: + version "8.4.12" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.12.tgz#1e7de78733b28970fa4743f7da6f3763648b1905" + integrity sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg== + dependencies: + nanoid "^3.3.1" + picocolors "^1.0.0" + source-map-js "^1.0.2" -preserve@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" +postcss@^8.4.17: + version "8.4.18" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.18.tgz#6d50046ea7d3d66a85e0e782074e7203bc7fbca2" + integrity sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA== + dependencies: + nanoid "^3.3.4" + picocolors "^1.0.0" + source-map-js "^1.0.2" -prettier@^1.16.0: - version "1.17.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.17.1.tgz#ed64b4e93e370cb8a25b9ef7fef3e4fd1c0995db" +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -pretty-error@^2.0.2: - version "2.1.1" - resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3" +pretty-error@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6" + integrity sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw== dependencies: - renderkid "^2.0.1" - utila "~0.4" - -private@^0.1.6, private@^0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + lodash "^4.17.20" + renderkid "^3.0.0" process-nextick-args@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" -process@^0.11.10: - version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - -progress@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - -promise-inflight@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" - -proxy-addr@~2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93" - dependencies: - forwarded "~0.1.2" - ipaddr.js "1.8.0" - -proxy-agent@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-2.0.0.tgz#57eb5347aa805d74ec681cb25649dba39c933499" +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== dependencies: - agent-base "2" - debug "2" - extend "3" - http-proxy-agent "1" - https-proxy-agent "1" - lru-cache "~2.6.5" - pac-proxy-agent "1" - socks-proxy-agent "2" + forwarded "0.2.0" + ipaddr.js "1.9.1" proxy-from-env@^1.1.0: version "1.1.0" @@ -7472,28 +7163,6 @@ prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - -public-encrypt@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" - dependencies: - bn.js "^4.1.0" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - parse-asn1 "^5.0.0" - randombytes "^2.0.1" - safe-buffer "^5.1.2" - -pump@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -7501,15 +7170,7 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -pumpify@^1.3.3: - version "1.5.1" - resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" - dependencies: - duplexify "^3.6.0" - inherits "^2.0.3" - pump "^2.0.0" - -punycode.js@^2.1.0: +punycode.js@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/punycode.js/-/punycode.js-2.1.0.tgz#f3937f7a914152c2dc17e9c280a2cf86a26b7cda" integrity sha512-LvGUJ9QHiESLM4yn8JuJWicstRcJKRmP46psQw1HvCZ9puLFwYMKJWvkAkP3OHBVzNzZGx/D53EYJrIaKd9gZQ== @@ -7517,122 +7178,85 @@ punycode.js@^2.1.0: punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" - -punycode@^1.2.4: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== punycode@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" -q@1.4.1, q@^1.1.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e" - -qjobs@^1.1.4: +qjobs@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071" + integrity sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg== -qrcode@^1.3.0: - version "1.3.3" - resolved "https://registry.yarnpkg.com/qrcode/-/qrcode-1.3.3.tgz#5ef50c0c890cffa1897f452070f0f094936993de" +qrcode@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/qrcode/-/qrcode-1.5.0.tgz#95abb8a91fdafd86f8190f2836abbfc500c72d1b" + integrity sha512-9MgRpgVc+/+47dFvQeD6U2s0Z92EsKzcHogtum4QB+UNd025WOJSHvn/hjk9xmzj7Stj95CyUAs31mrjxliEsQ== dependencies: - can-promise "0.0.1" dijkstrajs "^1.0.1" - isarray "^2.0.1" - pngjs "^3.3.0" - yargs "^12.0.5" - -qs@6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + encode-utf8 "^1.0.3" + pngjs "^5.0.0" + yargs "^15.3.1" -query-string@^4.1.0: - version "4.3.4" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" +qs@6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== dependencies: - object-assign "^4.1.0" - strict-uri-encode "^1.0.0" + side-channel "^1.0.4" + +qs@6.9.7: + version "6.9.7" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe" + integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw== -querystring-es3@^0.2.0: +querystring-es3@0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + integrity sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA== -querystring@0.2.0, querystring@^0.2.0: +querystring@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g== quick-lru@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== -randomatic@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed" - dependencies: - is-number "^4.0.0" - kind-of "^6.0.0" - math-random "^1.0.1" - -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: +randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: safe-buffer "^5.1.0" -randomfill@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" - dependencies: - randombytes "^2.0.5" - safe-buffer "^5.1.0" - -range-parser@^1.2.0, range-parser@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" - -range-parser@^1.2.1: +range-parser@^1.2.1, range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2, raw-body@2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3" +raw-body@2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.3.tgz#8f80305d11c2a0a545c2d9d89d7a0286fcead43c" + integrity sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g== dependencies: - bytes "3.0.0" - http-errors "1.6.3" - iconv-lite "0.4.23" + bytes "3.1.2" + http-errors "1.8.1" + iconv-lite "0.4.24" unpipe "1.0.0" -raw-loader@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa" - -rc@^1.2.7: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - -read-pkg-up@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" - dependencies: - find-up "^1.0.0" - read-pkg "^1.0.0" - -read-pkg-up@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" +raw-body@2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== dependencies: - find-up "^2.0.0" - read-pkg "^2.0.0" + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" read-pkg-up@^7.0.1: version "7.0.1" @@ -7643,22 +7267,6 @@ read-pkg-up@^7.0.1: read-pkg "^5.2.0" type-fest "^0.8.1" -read-pkg@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" - dependencies: - load-json-file "^1.0.0" - normalize-package-data "^2.3.2" - path-type "^1.0.0" - -read-pkg@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" - dependencies: - load-json-file "^2.0.0" - normalize-package-data "^2.3.2" - path-type "^2.0.0" - read-pkg@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" @@ -7669,7 +7277,7 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -"readable-stream@1 || 2", readable-stream@2, readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: +readable-stream@^2.0.1: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" dependencies: @@ -7681,25 +7289,7 @@ read-pkg@^5.2.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@1.0: - version "1.0.34" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -readable-stream@1.1.x: - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -readable-stream@^3.1.1: +readable-stream@^3.1.1, readable-stream@^3.4.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -7708,18 +7298,23 @@ readable-stream@^3.1.1: string_decoder "^1.1.1" util-deprecate "^1.0.1" -readdirp@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" +readable-stream@~2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== dependencies: - graceful-fs "^4.1.11" - micromatch "^3.1.10" - readable-stream "^2.0.2" + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" -readdirp@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" - integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== dependencies: picomatch "^2.2.1" @@ -7729,13 +7324,6 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" -redent@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" - dependencies: - indent-string "^2.1.0" - strip-indent "^1.0.1" - redent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" @@ -7744,193 +7332,119 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" -reduce-css-calc@^1.2.6: - version "1.3.0" - resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" - dependencies: - balanced-match "^0.4.2" - math-expression-evaluator "^1.2.14" - reduce-function-call "^1.0.1" - -reduce-function-call@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.2.tgz#5a200bf92e0e37751752fe45b0ab330fd4b6be99" - dependencies: - balanced-match "^0.4.2" - -regenerate-unicode-properties@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e" - integrity sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA== - dependencies: - regenerate "^1.4.0" - -regenerate@^1.2.1, regenerate@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" - -regenerator-runtime@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" - -regenerator-runtime@^0.13.2: - version "0.13.3" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5" - integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw== - -regenerator-transform@^0.14.0: - version "0.14.1" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.1.tgz#3b2fce4e1ab7732c08f665dfdb314749c7ddd2fb" - integrity sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ== - dependencies: - private "^0.1.6" - -regex-cache@^0.4.2: - version "0.4.4" - resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" +regenerate-unicode-properties@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56" + integrity sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw== dependencies: - is-equal-shallow "^0.1.3" + regenerate "^1.4.2" -regex-not@^1.0.0, regex-not@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" +regenerate@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regexpp@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" +regenerator-runtime@^0.13.10: + version "0.13.10" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz#ed07b19616bcbec5da6274ebc75ae95634bfc2ee" + integrity sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw== -regexpu-core@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" - dependencies: - regenerate "^1.2.1" - regjsgen "^0.2.0" - regjsparser "^0.1.4" +regenerator-runtime@^0.13.4: + version "0.13.9" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" + integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== -regexpu-core@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.6.0.tgz#2037c18b327cfce8a6fea2a4ec441f2432afb8b6" - integrity sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg== +regenerator-transform@^0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537" + integrity sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg== dependencies: - regenerate "^1.4.0" - regenerate-unicode-properties "^8.1.0" - regjsgen "^0.5.0" - regjsparser "^0.6.0" - unicode-match-property-ecmascript "^1.0.4" - unicode-match-property-value-ecmascript "^1.1.0" + "@babel/runtime" "^7.8.4" -regjsgen@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" +regexpp@^3.0.0, regexpp@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== -regjsgen@^0.5.0: - version "0.5.1" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c" - integrity sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg== +regexpu-core@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.0.1.tgz#c531122a7840de743dcf9c83e923b5560323ced3" + integrity sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw== + dependencies: + regenerate "^1.4.2" + regenerate-unicode-properties "^10.0.1" + regjsgen "^0.6.0" + regjsparser "^0.8.2" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.0.0" -regjsparser@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" +regexpu-core@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.1.0.tgz#2f8504c3fd0ebe11215783a41541e21c79942c6d" + integrity sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA== dependencies: - jsesc "~0.5.0" + regenerate "^1.4.2" + regenerate-unicode-properties "^10.0.1" + regjsgen "^0.6.0" + regjsparser "^0.8.2" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.0.0" -regjsparser@^0.6.0: +regjsgen@^0.6.0: version "0.6.0" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.0.tgz#f1e6ae8b7da2bae96c99399b868cd6c933a2ba9c" - integrity sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ== + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d" + integrity sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA== + +regjsparser@^0.8.2: + version "0.8.4" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.8.4.tgz#8a14285ffcc5de78c5b95d62bbf413b6bc132d5f" + integrity sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA== dependencies: jsesc "~0.5.0" -relateurl@0.2.x: +relateurl@^0.2.7: version "0.2.7" resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== -remark-parse@^8.0.0: - version "8.0.3" - resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-8.0.3.tgz#9c62aa3b35b79a486454c690472906075f40c7e1" - integrity sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q== - dependencies: - ccount "^1.0.0" - collapse-white-space "^1.0.2" - is-alphabetical "^1.0.0" - is-decimal "^1.0.0" - is-whitespace-character "^1.0.0" - is-word-character "^1.0.0" - markdown-escapes "^1.0.0" - parse-entities "^2.0.0" - repeat-string "^1.5.4" - state-toggle "^1.0.0" - trim "0.0.1" - trim-trailing-lines "^1.0.0" - unherit "^1.0.4" - unist-util-remove-position "^2.0.0" - vfile-location "^3.0.0" - xtend "^4.0.1" - -remark-stringify@^8.0.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-8.1.1.tgz#e2a9dc7a7bf44e46a155ec78996db896780d8ce5" - integrity sha512-q4EyPZT3PcA3Eq7vPpT6bIdokXzFGp9i85igjmhRyXWmPs0Y6/d2FYwUNotKAWyLch7g0ASZJn/KHHcHZQ163A== +remark-parse@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-9.0.0.tgz#4d20a299665880e4f4af5d90b7c7b8a935853640" + integrity sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw== dependencies: - ccount "^1.0.0" - is-alphanumeric "^1.0.0" - is-decimal "^1.0.0" - is-whitespace-character "^1.0.0" - longest-streak "^2.0.1" - markdown-escapes "^1.0.0" - markdown-table "^2.0.0" - mdast-util-compact "^2.0.0" - parse-entities "^2.0.0" - repeat-string "^1.5.4" - state-toggle "^1.0.0" - stringify-entities "^3.0.0" - unherit "^1.0.4" - xtend "^4.0.1" - -remark@^12.0.0: - version "12.0.1" - resolved "https://registry.yarnpkg.com/remark/-/remark-12.0.1.tgz#f1ddf68db7be71ca2bad0a33cd3678b86b9c709f" - integrity sha512-gS7HDonkdIaHmmP/+shCPejCEEW+liMp/t/QwmF0Xt47Rpuhl32lLtDV1uKWvGoq+kxr5jSgg5oAIpGuyULjUw== - dependencies: - remark-parse "^8.0.0" - remark-stringify "^8.0.0" - unified "^9.0.0" - -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + mdast-util-from-markdown "^0.8.0" -renderkid@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.2.tgz#12d310f255360c07ad8fde253f6c9e9de372d2aa" +remark-stringify@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-9.0.1.tgz#576d06e910548b0a7191a71f27b33f1218862894" + integrity sha512-mWmNg3ZtESvZS8fv5PTvaPckdL4iNlCHTt8/e/8oN08nArHRHjNZMKzA/YW3+p7/lYqIw4nx1XsjCBo/AxNChg== dependencies: - css-select "^1.1.0" - dom-converter "~0.2" - htmlparser2 "~3.3.0" - strip-ansi "^3.0.0" - utila "^0.4.0" + mdast-util-to-markdown "^0.6.0" -repeat-element@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" +remark@^13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/remark/-/remark-13.0.0.tgz#d15d9bf71a402f40287ebe36067b66d54868e425" + integrity sha512-HDz1+IKGtOyWN+QgBiAT0kn+2s6ovOxHyPAFGKVE81VSzJ+mq7RwHFledEvB5F1p4iJvOah/LOKdFuzvRnNLCA== + dependencies: + remark-parse "^9.0.0" + remark-stringify "^9.0.0" + unified "^9.1.0" -repeat-string@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-0.2.2.tgz#c7a8d3236068362059a7e4651fc6884e8b1fb4ae" +renderkid@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-3.0.0.tgz#5fd823e4d6951d37358ecc9a58b1f06836b6268a" + integrity sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg== + dependencies: + css-select "^4.1.3" + dom-converter "^0.2.0" + htmlparser2 "^6.1.0" + lodash "^4.17.21" + strip-ansi "^6.0.1" -repeat-string@^1.0.0, repeat-string@^1.5.2, repeat-string@^1.5.4, repeat-string@^1.6.1: +repeat-string@^1.0.0: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" -repeating@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" - dependencies: - is-finite "^1.0.0" - replace-ext@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" @@ -7940,13 +7454,15 @@ require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" -require-from-string@^1.1.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418" +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== -require-main-filename@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== require-package-name@^2.0.1: version "2.0.1" @@ -7956,11 +7472,6 @@ requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" -resolve-from@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" - integrity sha1-six699nWiBvItuZTM17rywoYh0g= - resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -7970,33 +7481,33 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" - -resolve@1.1.x, resolve@^1.1.6: +resolve@^1.1.6: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" -resolve@^1.10.0, resolve@^1.4.0, resolve@^1.5.0, resolve@^1.8.1: +resolve@^1.10.0: version "1.11.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.0.tgz#4014870ba296176b86343d50b60f3b50609ce232" dependencies: path-parse "^1.0.6" -resolve@^1.3.2: - version "1.13.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.13.1.tgz#be0aa4c06acd53083505abb35f4d66932ab35d16" - integrity sha512-CxqObCX8K8YtAhOBRg+lrcdn+LK+WYOS8tSjqSFbjtrI5PnS63QPhZl4+yKfrU9tdsbMu9Anr/amegT87M9Z6w== +resolve@^1.14.2, resolve@^1.20.0: + version "1.22.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" + integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== dependencies: - path-parse "^1.0.6" + is-core-module "^2.8.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" -restore-cursor@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" +resolve@^1.22.0, resolve@^1.22.1: + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== dependencies: - exit-hook "^1.0.0" - onetime "^1.0.0" + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" restore-cursor@^2.0.0: version "2.0.0" @@ -8005,245 +7516,193 @@ restore-cursor@^2.0.0: onetime "^2.0.0" signal-exit "^3.0.2" -ret@~0.1.10: - version "0.1.15" - resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rfdc@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.4.tgz#ba72cc1367a0ccd9cf81a870b3b58bd3ad07f8c2" - -rimraf@2.6.3, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.1: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - dependencies: - glob "^7.1.3" - -rimraf@^2.6.3: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" +rfdc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" + integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== -rimraf@^3.0.2: +rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" -ripemd160@^2.0.0, ripemd160@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - -ruffle-mirror@^2021.4.10: - version "2021.4.11" - resolved "https://registry.yarnpkg.com/ruffle-mirror/-/ruffle-mirror-2021.4.11.tgz#039940e0a68e6849259dbef6b54fb877ac4373e7" - integrity sha512-a3N2OkPCJauiHBloHoZgCn/mSUlybyb9Ps4ikPGgHUy8iXPy6qMqh62imvNDU07tBJc5Y0c5mRHBFJRgpMgEpA== - -run-async@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" - dependencies: - is-promise "^2.1.0" - run-parallel@^1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q== -run-queue@^1.0.0, run-queue@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" - dependencies: - aproba "^1.1.1" - -rxjs@^6.4.0: - version "6.5.2" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.2.tgz#2e35ce815cd46d84d02a209fb4e5921e051dbec7" - dependencies: - tslib "^1.9.0" - -safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - -safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" - dependencies: - ret "~0.1.10" +safe-buffer@^5.1.0, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" -samsam@1.x, samsam@^1.1.3: - version "1.3.0" - resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.3.0.tgz#8d1d9350e25622da30de3e44ba692b5221ab7c50" - -"sass-loader@git://github.com/webpack-contrib/sass-loader": - version "7.1.0" - resolved "git://github.com/webpack-contrib/sass-loader#e279f2a129eee0bd0b624b5acd498f23a81ee35e" +sass-loader@13.0.2: + version "13.0.2" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-13.0.2.tgz#e81a909048e06520e9f2ff25113a801065adb3fe" + integrity sha512-BbiqbVmbfJaWVeOOAu2o7DhYWtcNmTfvroVgFXa6k2hHheMxNAeDHLNoDy/Q5aoaVlz0LH+MbMktKwm9vN/j8Q== dependencies: - clone-deep "^4.0.1" - loader-utils "^1.0.1" - lodash.tail "^4.1.1" - neo-async "^2.5.0" - pify "^4.0.1" - semver "^5.5.0" + klona "^2.0.4" + neo-async "^2.6.2" -sass@^1.17.3: - version "1.20.1" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.20.1.tgz#737b901fe072336da540b6d00ec155e2267420da" +sass@1.55.0: + version "1.55.0" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.55.0.tgz#0c4d3c293cfe8f8a2e8d3b666e1cf1bff8065d1c" + integrity sha512-Pk+PMy7OGLs9WaxZGJMn7S96dvlyVBwwtToX895WmCpAOr5YiJYEUJfiJidMuKb613z2xNWcXCHEuOvjZbqC6A== dependencies: - chokidar "^2.0.0" - -sax@^1.2.4, sax@~1.2.1: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + chokidar ">=3.0.0 <4.0.0" + immutable "^4.0.0" + source-map-js ">=0.6.2 <2.0.0" -schema-utils@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" +schema-utils@^2.6.5: + version "2.7.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" + integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== dependencies: - ajv "^6.1.0" - ajv-errors "^1.0.0" - ajv-keywords "^3.1.0" + "@types/json-schema" "^7.0.5" + ajv "^6.12.4" + ajv-keywords "^3.5.2" -schema-utils@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef" - integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA== +schema-utils@^3.1.0, schema-utils@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" + integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== dependencies: - "@types/json-schema" "^7.0.6" + "@types/json-schema" "^7.0.8" ajv "^6.12.5" ajv-keywords "^3.5.2" +schema-utils@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7" + integrity sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.8.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.0.0" + selenium-server@2.53.1: version "2.53.1" resolved "https://registry.yarnpkg.com/selenium-server/-/selenium-server-2.53.1.tgz#d681528812f3c2e0531a6b7e613e23bb02cce8a6" -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.6.0: +selenium-webdriver@^4.3.1: + version "4.4.0" + resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.4.0.tgz#3f280504f6c0ac64a24b176304213b5a49ec2553" + integrity sha512-Du+/xfpvNi9zHAeYgXhOWN9yH0hph+cuX+hHDBr7d+SbtQVcfNJwBzLsbdHrB1Wh7MHXFuIkSG88A9TRRQUx3g== + dependencies: + jszip "^3.10.0" + tmp "^0.2.1" + ws ">=8.7.0" + +"semver@2 || 3 || 4 || 5", semver@^5.6.0: version "5.6.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" -semver@^5.4.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== +semver@7.3.5: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" -semver@^5.5.1: - version "5.7.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" +semver@7.3.8, semver@^7.3.8: + version "7.3.8" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== + dependencies: + lru-cache "^6.0.0" -semver@^6.0.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@~5.0.1: - version "5.0.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a" +semver@^7.0.0, semver@^7.3.4, semver@^7.3.5, semver@^7.3.6, semver@^7.3.7: + version "7.3.7" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + dependencies: + lru-cache "^6.0.0" -send@0.16.2: - version "0.16.2" - resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== dependencies: debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" + depd "2.0.0" + destroy "1.2.0" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" fresh "0.5.2" - http-errors "~1.6.2" - mime "1.4.1" - ms "2.0.0" - on-finished "~2.3.0" - range-parser "~1.2.0" - statuses "~1.4.0" - -serialize-javascript@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" - integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== - dependencies: - randombytes "^2.1.0" - -serialize-javascript@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" - integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serialize-javascript@6.0.0, serialize-javascript@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== dependencies: randombytes "^2.1.0" -serve-static@1.13.2: - version "1.13.2" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== dependencies: encodeurl "~1.0.2" escape-html "~1.0.3" - parseurl "~1.3.2" - send "0.16.2" + parseurl "~1.3.3" + send "0.18.0" -serviceworker-webpack-plugin@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/serviceworker-webpack-plugin/-/serviceworker-webpack-plugin-1.0.1.tgz#481863288487e92da01d49745336c72ef8a6136b" +serviceworker-webpack5-plugin@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/serviceworker-webpack5-plugin/-/serviceworker-webpack5-plugin-2.0.0.tgz#6d3516b11fcf3cfadee60f04d607e7d042e3e7fc" + integrity sha512-lMypbP8MQiWeUa2Za1BfV9M18aXQCoB71XaeL6v32qxvdTCqILv2cxDNW1Mlq/tHJ8hgKlP3INQ2eo2BLaHyZQ== dependencies: minimatch "^3.0.4" -set-blocking@^2.0.0, set-blocking@~2.0.0: +set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" -set-value@^0.4.3: - version "0.4.3" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.1" - to-object-path "^0.3.0" - -set-value@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" - -setimmediate@^1.0.4: +setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= - -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== -sha.js@^2.4.0, sha.js@^2.4.8: - version "2.4.11" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== shallow-clone@^3.0.0: version "3.0.1" @@ -8251,176 +7710,106 @@ shallow-clone@^3.0.0: dependencies: kind-of "^6.0.2" -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: - shebang-regex "^1.0.0" + shebang-regex "^3.0.0" -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shelljs@^0.8.4: - version "0.8.4" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" - integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ== +shelljs@0.8.5: + version "0.8.5" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" + integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== dependencies: glob "^7.0.0" interpret "^1.0.0" rechoir "^0.6.2" -signal-exit@^3.0.0, signal-exit@^3.0.2: +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" -sinon-chai@^2.8.0: - version "2.14.0" - resolved "https://registry.yarnpkg.com/sinon-chai/-/sinon-chai-2.14.0.tgz#da7dd4cc83cd6a260b67cca0f7a9fdae26a1205d" +sinon-chai@3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/sinon-chai/-/sinon-chai-3.7.0.tgz#cfb7dec1c50990ed18c153f1840721cf13139783" + integrity sha512-mf5NURdUaSdnatJx3uhoBOrY9dtL19fiOtAdT1Azxg3+lNJFiuN0uzaU3xX1LeAfL17kHQhTAJgpsfhbMJMY2g== -sinon@^2.1.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/sinon/-/sinon-2.4.1.tgz#021fd64b54cb77d9d2fb0d43cdedfae7629c3a36" +sinon@14.0.2: + version "14.0.2" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-14.0.2.tgz#585a81a3c7b22cf950762ac4e7c28eb8b151c46f" + integrity sha512-PDpV0ZI3ZCS3pEqx0vpNp6kzPhHrLx72wA0G+ZLaaJjLIYeE0n8INlgaohKuGy7hP0as5tbUd23QWu5U233t+w== dependencies: - diff "^3.1.0" - formatio "1.2.0" - lolex "^1.6.0" - native-promise-only "^0.8.1" - path-to-regexp "^1.7.0" - samsam "^1.1.3" - text-encoding "0.6.4" - type-detect "^4.0.0" - -slash@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + "@sinonjs/commons" "^2.0.0" + "@sinonjs/fake-timers" "^9.1.2" + "@sinonjs/samsam" "^7.0.1" + diff "^5.0.0" + nise "^5.1.2" + supports-color "^7.2.0" slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slice-ansi@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" - dependencies: - ansi-styles "^3.2.0" - astral-regex "^1.0.0" - is-fullwidth-code-point "^2.0.0" - -smart-buffer@^1.0.13: - version "1.1.15" - resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-1.1.15.tgz#7f114b5b65fab3e2a35aa775bb12f0d1c649bf16" - -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" - dependencies: - kind-of "^3.2.0" - -snapdragon@^0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" - dependencies: - base "^0.11.1" - debug "^2.2.0" - define-property "^0.2.5" - extend-shallow "^2.0.1" - map-cache "^0.2.2" - source-map "^0.5.6" - source-map-resolve "^0.5.0" - use "^3.1.0" - -socket.io-adapter@~1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz#2a805e8a14d6372124dd9159ad4502f8cb07f06b" - -socket.io-client@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.1.1.tgz#dcb38103436ab4578ddb026638ae2f21b623671f" - dependencies: - backo2 "1.0.2" - base64-arraybuffer "0.1.5" - component-bind "1.0.0" - component-emitter "1.2.1" - debug "~3.1.0" - engine.io-client "~3.2.0" - has-binary2 "~1.0.2" - has-cors "1.1.0" - indexof "0.0.1" - object-component "0.0.3" - parseqs "0.0.5" - parseuri "0.0.5" - socket.io-parser "~3.2.0" - to-array "0.1.4" - -socket.io-parser@~3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.2.0.tgz#e7c6228b6aa1f814e6148aea325b51aa9499e077" - dependencies: - component-emitter "1.2.1" - debug "~3.1.0" - isarray "2.0.1" - -socket.io@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-2.1.1.tgz#a069c5feabee3e6b214a75b40ce0652e1cfb9980" - dependencies: - debug "~3.1.0" - engine.io "~3.2.0" - has-binary2 "~1.0.2" - socket.io-adapter "~1.1.0" - socket.io-client "2.1.1" - socket.io-parser "~3.2.0" +slash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" + integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== -socks-proxy-agent@2: - version "2.1.1" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-2.1.1.tgz#86ebb07193258637870e13b7bd99f26c663df3d3" +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== dependencies: - agent-base "2" - extend "3" - socks "~1.1.5" + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" -socks@~1.1.5: - version "1.1.10" - resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.10.tgz#5b8b7fc7c8f341c53ed056e929b7bf4de8ba7b5a" - dependencies: - ip "^1.1.4" - smart-buffer "^1.0.13" +socket.io-adapter@~2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz#b50a4a9ecdd00c34d4c8c808224daa1a786152a6" + integrity sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg== -sort-keys@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" +socket.io-parser@~4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.0.4.tgz#9ea21b0d61508d18196ef04a2c6b9ab630f4c2b0" + integrity sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g== dependencies: - is-plain-obj "^1.0.0" - -source-list-map@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + "@types/component-emitter" "^1.2.10" + component-emitter "~1.3.0" + debug "~4.3.1" -source-map-resolve@^0.5.0: - version "0.5.2" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" +socket.io@^4.4.1: + version "4.5.1" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.5.1.tgz#aa7e73f8a6ce20ee3c54b2446d321bbb6b1a9029" + integrity sha512-0y9pnIso5a9i+lJmsCdtmTTgJFFSvNQKDnPQRz28mGNnxbmqYg2QPtJTLFxhymFZhAIn50eHAKzJeiNaKr+yUQ== dependencies: - atob "^2.1.1" - decode-uri-component "^0.2.0" - resolve-url "^0.2.1" - source-map-url "^0.4.0" - urix "^0.1.0" + accepts "~1.3.4" + base64id "~2.0.0" + debug "~4.3.2" + engine.io "~6.2.0" + socket.io-adapter "~2.4.0" + socket.io-parser "~4.0.4" -source-map-support@^0.4.15: - version "0.4.18" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" - dependencies: - source-map "^0.5.6" +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== source-map-support@^0.5.16: version "0.5.16" @@ -8430,31 +7819,26 @@ source-map-support@^0.5.16: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@~0.5.12: - version "0.5.19" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" - integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-url@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" +source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" -source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7: +source-map@^0.5.0: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - -source-map@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" - dependencies: - amdefine ">=0.0.4" +sourcemap-codec@^1.4.8: + version "1.4.8" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== spdx-correct@^3.0.0: version "3.1.0" @@ -8483,112 +7867,45 @@ specificity@^0.4.1: resolved "https://registry.yarnpkg.com/specificity/-/specificity-0.4.1.tgz#aab5e645012db08ba182e151165738d00887b019" integrity sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg== -split-string@^3.0.1, split-string@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" - dependencies: - extend-shallow "^3.0.0" - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - -ssri@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" - dependencies: - figgy-pudding "^3.5.1" - -ssri@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" - integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== - dependencies: - minipass "^3.1.1" - -state-toggle@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.3.tgz#e123b16a88e143139b09c6852221bc9815917dfe" - integrity sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ== +stable@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== -static-extend@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" +stacktrace-parser@^0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" + integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" - -"statuses@>= 1.4.0 < 2", statuses@~1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" - -statuses@~1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" + type-fest "^0.7.1" -stream-browserify@^2.0.1: +statuses@2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" - dependencies: - inherits "~2.0.1" - readable-stream "^2.0.2" - -stream-each@^1.1.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" - dependencies: - end-of-stream "^1.1.0" - stream-shift "^1.0.0" - -stream-http@^2.7.2: - version "2.8.3" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" - dependencies: - builtin-status-codes "^3.0.0" - inherits "^2.0.1" - readable-stream "^2.3.6" - to-arraybuffer "^1.0.0" - xtend "^4.0.0" - -stream-shift@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" - -streamroller@0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-0.7.0.tgz#a1d1b7cf83d39afb0d63049a5acbf93493bdf64b" - dependencies: - date-format "^1.2.0" - debug "^3.1.0" - mkdirp "^0.5.1" - readable-stream "^2.3.0" - -strict-uri-encode@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= -"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" +streamroller@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-3.0.6.tgz#52823415800ded79a49aa3f7712f50a422b97493" + integrity sha512-Qz32plKq/MZywYyhEatxyYc8vs994Gz0Hu2MSYXXLD233UyPeIeRBZARIIGwFer4Mdb8r3Y2UqKkgyDghM6QCg== dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" + date-format "^4.0.6" + debug "^4.3.4" + fs-extra "^10.0.1" -string-width@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.2, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: - emoji-regex "^7.0.1" - is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.1.0" + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" string-width@^4.2.0: version "4.2.0" @@ -8599,11 +7916,21 @@ string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" -string_decoder@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d" +string.prototype.trimend@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" + integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== dependencies: - safe-buffer "~5.1.0" + call-bind "^1.0.2" + define-properties "^1.1.3" + +string.prototype.trimstart@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" + integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" string_decoder@^1.1.1: version "1.3.0" @@ -8612,28 +7939,27 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" dependencies: safe-buffer "~5.1.0" -stringify-entities@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-3.0.1.tgz#32154b91286ab0869ab2c07696223bd23b6dbfc0" - integrity sha512-Lsk3ISA2++eJYqBMPKcr/8eby1I6L0gP0NlxF8Zja6c05yr/yCYyb2c9PwXjd08Ib3If1vn1rbs1H5ZtVuOfvQ== +strip-ansi@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== dependencies: - character-entities-html4 "^1.0.0" - character-entities-legacy "^1.0.0" - is-alphanumerical "^1.0.0" - is-decimal "^1.0.2" - is-hexadecimal "^1.0.0" + ansi-regex "^4.1.0" -strip-ansi@^3.0.0, strip-ansi@^3.0.1: +strip-ansi@6.0.1, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" dependencies: @@ -8645,12 +7971,6 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -strip-ansi@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" - dependencies: - ansi-regex "^4.1.0" - strip-ansi@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" @@ -8658,30 +7978,10 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" -strip-ansi@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.1.1.tgz#39e8a98d044d150660abe4a6808acf70bb7bc991" - -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - dependencies: - is-utf8 "^0.2.0" - strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - -strip-indent@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" - dependencies: - get-stdin "^4.0.1" - strip-indent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" @@ -8689,87 +7989,96 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" -strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" +strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== style-search@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902" integrity sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI= +stylehacks@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.1.0.tgz#a40066490ca0caca04e96c6b02153ddc39913520" + integrity sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q== + dependencies: + browserslist "^4.16.6" + postcss-selector-parser "^6.0.4" + stylelint-config-recommended@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-3.0.0.tgz#e0e547434016c5539fe2650afd58049a2fd1d657" integrity sha512-F6yTRuc06xr1h5Qw/ykb2LuFynJ2IxkKfCMf+1xqPffkxh0S09Zc902XCffcsw/XMFq/OzQ1w54fLIDtmRNHnQ== -stylelint-config-standard@^20.0.0: +stylelint-config-standard@20.0.0: version "20.0.0" resolved "https://registry.yarnpkg.com/stylelint-config-standard/-/stylelint-config-standard-20.0.0.tgz#06135090c9e064befee3d594289f50e295b5e20d" integrity sha512-IB2iFdzOTA/zS4jSVav6z+wGtin08qfj+YyExHB3LF9lnouQht//YyB0KZq9gGz5HNPkddHOzcY8HsUey6ZUlA== dependencies: stylelint-config-recommended "^3.0.0" -stylelint-rscss@^0.4.0: +stylelint-rscss@0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/stylelint-rscss/-/stylelint-rscss-0.4.0.tgz#ea77c478e1c703dbda878c00f53bbc002b2d07b7" integrity sha1-6nfEeOHHA9vah4wA9Tu8ACstB7c= dependencies: postcss-resolve-nested-selector "0.1.1" postcss-selector-parser "2.2.1" - -stylelint@^13.6.1: - version "13.6.1" - resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-13.6.1.tgz#cc1d76338116d55e8ff2be94c4a4386c1239b878" - integrity sha512-XyvKyNE7eyrqkuZ85Citd/Uv3ljGiuYHC6UiztTR6sWS9rza8j3UeQv/eGcQS9NZz/imiC4GKdk1EVL3wst5vw== - dependencies: - "@stylelint/postcss-css-in-js" "^0.37.1" - "@stylelint/postcss-markdown" "^0.36.1" - autoprefixer "^9.8.0" - balanced-match "^1.0.0" - chalk "^4.1.0" - cosmiconfig "^6.0.0" - debug "^4.1.1" + +stylelint@13.13.1: + version "13.13.1" + resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-13.13.1.tgz#fca9c9f5de7990ab26a00f167b8978f083a18f3c" + integrity sha512-Mv+BQr5XTUrKqAXmpqm6Ddli6Ief+AiPZkRsIrAoUKFuq/ElkUh9ZMYxXD0iQNZ5ADghZKLOWz1h7hTClB7zgQ== + dependencies: + "@stylelint/postcss-css-in-js" "^0.37.2" + "@stylelint/postcss-markdown" "^0.36.2" + autoprefixer "^9.8.6" + balanced-match "^2.0.0" + chalk "^4.1.1" + cosmiconfig "^7.0.0" + debug "^4.3.1" execall "^2.0.0" - file-entry-cache "^5.0.1" + fast-glob "^3.2.5" + fastest-levenshtein "^1.0.12" + file-entry-cache "^6.0.1" get-stdin "^8.0.0" global-modules "^2.0.0" - globby "^11.0.1" + globby "^11.0.3" globjoin "^0.1.4" html-tags "^3.1.0" ignore "^5.1.8" import-lazy "^4.0.0" imurmurhash "^0.1.4" - known-css-properties "^0.19.0" - leven "^3.1.0" - lodash "^4.17.15" - log-symbols "^4.0.0" + known-css-properties "^0.21.0" + lodash "^4.17.21" + log-symbols "^4.1.0" mathml-tag-names "^2.1.3" - meow "^7.0.1" - micromatch "^4.0.2" + meow "^9.0.0" + micromatch "^4.0.4" normalize-selector "^0.2.0" - postcss "^7.0.32" + postcss "^7.0.35" postcss-html "^0.36.0" postcss-less "^3.1.4" postcss-media-query-parser "^0.2.3" - postcss-reporter "^6.0.1" postcss-resolve-nested-selector "^0.1.1" postcss-safe-parser "^4.0.2" postcss-sass "^0.4.4" postcss-scss "^2.1.1" - postcss-selector-parser "^6.0.2" + postcss-selector-parser "^6.0.5" postcss-syntax "^0.36.2" postcss-value-parser "^4.1.0" resolve-from "^5.0.0" slash "^3.0.0" specificity "^0.4.1" - string-width "^4.2.0" + string-width "^4.2.2" strip-ansi "^6.0.0" style-search "^0.1.0" sugarss "^2.0.0" svg-tags "^1.0.0" - table "^5.4.6" - v8-compile-cache "^2.1.1" + table "^6.6.0" + v8-compile-cache "^2.3.0" write-file-atomic "^3.0.3" sugarss@^2.0.0: @@ -8779,23 +8088,18 @@ sugarss@^2.0.0: dependencies: postcss "^7.0.2" -supports-color@3.1.2, supports-color@^3.1.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" +supports-color@8.1.1, supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: - has-flag "^1.0.0" + has-flag "^4.0.0" supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" -supports-color@^3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" - dependencies: - has-flag "^1.0.0" - -supports-color@^5.3.0, supports-color@^5.4.0: +supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" dependencies: @@ -8814,69 +8118,51 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-color@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + svg-tags@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" integrity sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q= -svgo@^0.7.0: - version "0.7.2" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5" - dependencies: - coa "~1.0.1" - colors "~1.1.2" - csso "~2.3.1" - js-yaml "~3.7.0" - mkdirp "~0.5.1" - sax "~1.2.1" - whet.extend "~0.9.9" - -table@^5.2.3: - version "5.3.3" - resolved "https://registry.yarnpkg.com/table/-/table-5.3.3.tgz#eae560c90437331b74200e011487a33442bd28b4" - dependencies: - ajv "^6.9.1" - lodash "^4.17.11" - slice-ansi "^2.1.0" - string-width "^3.0.0" - -table@^5.4.6: - version "5.4.6" - resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" - integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== - dependencies: - ajv "^6.10.2" - lodash "^4.17.14" - slice-ansi "^2.1.0" - string-width "^3.0.0" - -tapable@^1.0.0, tapable@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" - -tar@^4: - version "4.4.8" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d" - dependencies: - chownr "^1.1.1" - fs-minipass "^1.2.5" - minipass "^2.3.4" - minizlib "^1.1.1" - mkdirp "^0.5.0" - safe-buffer "^5.1.2" - yallist "^3.0.2" - -tar@^6.0.2: - version "6.1.0" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83" - integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA== - dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^3.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" +svgo@^2.7.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" + integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== + dependencies: + "@trysound/sax" "0.2.0" + commander "^7.2.0" + css-select "^4.1.3" + css-tree "^1.1.3" + csso "^4.2.0" + picocolors "^1.0.0" + stable "^0.1.8" + +table@^6.6.0: + version "6.8.0" + resolved "https://registry.yarnpkg.com/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca" + integrity sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA== + dependencies: + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + +tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== tcp-port-used@^1.0.1: version "1.0.1" @@ -8885,100 +8171,42 @@ tcp-port-used@^1.0.1: debug "4.1.0" is2 "2.0.1" -terser-webpack-plugin@^1.4.3: - version "1.4.5" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b" - integrity sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw== +terser-webpack-plugin@^5.1.3: + version "5.3.4" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.4.tgz#f4d31e265883d20fda3ca9c0fc6a53f173ae62e3" + integrity sha512-SmnkUhBxLDcBfTIeaq+ZqJXLVEyXxSaNcCeSezECdKjfkMrTTnPvapBILylYwyEvHFZAn2cJ8dtiXel5XnfOfQ== dependencies: - cacache "^12.0.2" - find-cache-dir "^2.1.0" - is-wsl "^1.1.0" - schema-utils "^1.0.0" - serialize-javascript "^4.0.0" - source-map "^0.6.1" - terser "^4.1.2" - webpack-sources "^1.4.0" - worker-farm "^1.7.0" + "@jridgewell/trace-mapping" "^0.3.14" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.0" + terser "^5.14.1" -terser@^4.1.2: - version "4.8.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" - integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== +terser@^5.10.0, terser@^5.14.1: + version "5.14.2" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.2.tgz#9ac9f22b06994d736174f4091aa368db896f1c10" + integrity sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA== dependencies: + "@jridgewell/source-map" "^0.3.2" + acorn "^8.5.0" commander "^2.20.0" - source-map "~0.6.1" - source-map-support "~0.5.12" + source-map-support "~0.5.20" -text-encoding@0.6.4: - version "0.6.4" - resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19" - -text-table@^0.2.0: +text-table@0.2.0, text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" -through2@^2.0.0: - version "2.0.5" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" - dependencies: - readable-stream "~2.3.6" - xtend "~4.0.1" - -through@^2.3.6: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - -thunkify@~2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/thunkify/-/thunkify-2.1.2.tgz#faa0e9d230c51acc95ca13a361ac05ca7e04553d" - -timers-browserify@^2.0.4: - version "2.0.10" - resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.10.tgz#1d28e3d2aadf1d5a5996c4e9f95601cd053480ae" - dependencies: - setimmediate "^1.0.4" - -tmp@0.0.33, tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" - dependencies: - os-tmpdir "~1.0.2" - -tmp@0.0.x: - version "0.0.31" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" +tmp@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== dependencies: - os-tmpdir "~1.0.1" - -to-array@0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" - -to-arraybuffer@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" - -to-fast-properties@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + rimraf "^3.0.0" to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" -to-object-path@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" - dependencies: - kind-of "^3.0.2" - -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -8986,94 +8214,79 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -to-regex@^3.0.1, to-regex@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" - dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" - -toposort@^1.0.0: - version "1.0.7" - resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029" - -trim-newlines@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== trim-newlines@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.0.tgz#79726304a6a898aa8373427298d54c2ee8b1cb30" integrity sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA== -trim-right@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" - -trim-trailing-lines@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.3.tgz#7f0739881ff76657b7776e10874128004b625a94" - integrity sha512-4ku0mmjXifQcTVfYDfR5lpgV7zVqPg6zV9rdZmwOPqq0+Zq19xDqEgagqVbc4pOOShbncuAOIs59R3+3gcF3ZA== - -trim@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" - integrity sha1-WFhUf2spB1fulczMZm+1AITEYN0= - trough@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== -tslib@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" - -tty-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" - -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" +tsconfig-paths@^3.14.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" + integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== dependencies: - prelude-ls "~1.1.2" + "@types/json5" "^0.0.29" + json5 "^1.0.1" + minimist "^1.2.6" + strip-bom "^3.0.0" -type-detect@0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-0.1.1.tgz#0ba5ec2a885640e470ea4e8505971900dac58822" +tslib@^2.0.3: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== -type-detect@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-1.0.0.tgz#762217cc06db258ec48908a1298e8b95121e8ea2" +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" -type-detect@^4.0.0: +type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" -type-fest@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" - integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== +type-fest@^0.18.0: + version "0.18.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" + integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== type-fest@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== +type-fest@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" + integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== + type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== -type-is@~1.6.16: - version "1.6.16" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== dependencies: media-typer "0.3.0" - mime-types "~2.1.18" + mime-types "~2.1.24" typedarray-to-buffer@^3.1.5: version "3.1.5" @@ -9082,60 +8295,48 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - -uglify-js@3.4.x, uglify-js@^3.1.4: - version "3.4.9" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3" - dependencies: - commander "~2.17.1" - source-map "~0.6.1" - -ultron@~1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" - -underscore@~1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8" +ua-parser-js@^0.7.30: + version "0.7.31" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6" + integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ== -unherit@^1.0.4: - version "1.1.3" - resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.3.tgz#6c9b503f2b41b262330c80e91c8614abdaa69c22" - integrity sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ== +unbox-primitive@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== dependencies: - inherits "^2.0.0" - xtend "^4.0.0" + function-bind "^1.1.1" + has-bigints "^1.0.1" + has-symbols "^1.0.2" + which-boxed-primitive "^1.0.2" -unicode-canonical-property-names-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" - integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== +unicode-canonical-property-names-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" + integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== -unicode-match-property-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" - integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== +unicode-match-property-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== dependencies: - unicode-canonical-property-names-ecmascript "^1.0.4" - unicode-property-aliases-ecmascript "^1.0.4" + unicode-canonical-property-names-ecmascript "^2.0.0" + unicode-property-aliases-ecmascript "^2.0.0" -unicode-match-property-value-ecmascript@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz#5b4b426e08d13a80365e0d657ac7a6c1ec46a277" - integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g== +unicode-match-property-value-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" + integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== -unicode-property-aliases-ecmascript@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57" - integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw== +unicode-property-aliases-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" + integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== -unified@^9.0.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/unified/-/unified-9.1.0.tgz#7ba82e5db4740c47a04e688a9ca8335980547410" - integrity sha512-VXOv7Ic6twsKGJDeZQ2wwPqXs2hM0KNu5Hkg9WgAZbSD1pxhZ7p8swqg583nw1Je2fhwHy6U8aEjiI79x1gvag== +unified@^9.1.0: + version "9.2.2" + resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.2.tgz#67649a1abfc3ab85d2969502902775eb03146975" + integrity sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ== dependencies: bail "^1.0.0" extend "^3.0.0" @@ -9144,39 +8345,14 @@ unified@^9.0.0: trough "^1.0.0" vfile "^4.0.0" -union-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" - dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^0.4.3" - uniq@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" -uniqs@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" - -unique-filename@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" - dependencies: - unique-slug "^2.0.0" - -unique-slug@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.1.tgz#5e9edc6d1ce8fb264db18a507ef9bd8544451ca6" - dependencies: - imurmurhash "^0.1.4" - -unist-util-find-all-after@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/unist-util-find-all-after/-/unist-util-find-all-after-3.0.1.tgz#95cc62f48812d879b4685a0512bf1b838da50e9a" - integrity sha512-0GICgc++sRJesLwEYDjFVJPJttBpVQaTNgc6Jw0Jhzvfs+jtKePEMu+uD+PqkRUrAvGQqwhpDwLGWo1PK8PDEw== +unist-util-find-all-after@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/unist-util-find-all-after/-/unist-util-find-all-after-3.0.2.tgz#fdfecd14c5b7aea5e9ef38d5e0d5f774eeb561f6" + integrity sha512-xaTC/AGZ0rIM2gM28YVRAFPIZpzbpDtU3dRmp7EXlNVA8ziQc4hY3H7BHXM1J49nEmiqc3svnqMReW+PGqbZKQ== dependencies: unist-util-is "^4.0.0" @@ -9185,13 +8361,6 @@ unist-util-is@^4.0.0: resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.0.2.tgz#c7d1341188aa9ce5b3cff538958de9895f14a5de" integrity sha512-Ofx8uf6haexJwI1gxWMGg6I/dLnF2yE+KibhD3/diOqY2TinLcqHXCV6OI5gFVn3xQqDH+u0M625pfKwIwgBKQ== -unist-util-remove-position@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz#5d19ca79fdba712301999b2b73553ca8f3b352cc" - integrity sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA== - dependencies: - unist-util-visit "^2.0.0" - unist-util-stringify-position@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz#cce3bfa1cdf85ba7375d1d5b17bdc4cada9bd9da" @@ -9199,41 +8368,38 @@ unist-util-stringify-position@^2.0.0: dependencies: "@types/unist" "^2.0.2" -unist-util-visit-parents@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.1.0.tgz#4dd262fb9dcfe44f297d53e882fc6ff3421173d5" - integrity sha512-0g4wbluTF93npyPrp/ymd3tCDTMnP0yo2akFD2FIBAYXq/Sga3lwaU1D8OYKbtpioaI6CkDcQ6fsMnmtzt7htw== - dependencies: - "@types/unist" "^2.0.0" - unist-util-is "^4.0.0" - -unist-util-visit@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c" - integrity sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q== - dependencies: - "@types/unist" "^2.0.0" - unist-util-is "^4.0.0" - unist-util-visit-parents "^3.0.0" +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" -unset-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" +update-browserslist-db@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz#dbfc5a789caa26b1db8990796c2c8ebbce304824" + integrity sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA== dependencies: - has-value "^0.3.1" - isobject "^3.0.0" + escalade "^3.1.1" + picocolors "^1.0.0" -upath@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068" +update-browserslist-db@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38" + integrity sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" -upper-case@^1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" +update-browserslist-db@^1.0.9: + version "1.0.10" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" + integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" uri-js@^4.2.2: version "4.2.2" @@ -9241,60 +8407,24 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -urix@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" - -url-loader@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-1.1.2.tgz#b971d191b83af693c5e3fea4064be9e1f2d7f8d8" - dependencies: - loader-utils "^1.1.0" - mime "^2.0.3" - schema-utils "^1.0.0" - -url@^0.11.0: +url@0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ== dependencies: punycode "1.3.2" querystring "0.2.0" -use@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" - -useragent@2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/useragent/-/useragent-2.3.0.tgz#217f943ad540cb2128658ab23fc960f6a88c9972" - dependencies: - lru-cache "4.1.x" - tmp "0.0.x" +utf8@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== -util-deprecate@^1.0.1, util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" -util.promisify@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" - dependencies: - define-properties "^1.1.2" - object.getownpropertydescriptors "^2.0.3" - -util@0.10.3: - version "0.10.3" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" - dependencies: - inherits "2.0.1" - -util@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" - dependencies: - inherits "2.0.3" - -utila@^0.4.0, utila@~0.4: +utila@~0.4: version "0.4.0" resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" @@ -9306,14 +8436,10 @@ uuid@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" -v-click-outside@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/v-click-outside/-/v-click-outside-2.1.3.tgz#b7297abe833a439dc0895e6418a494381e64b5e7" - -v8-compile-cache@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745" - integrity sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ== +v8-compile-cache@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" + integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== validate-npm-package-license@^3.0.1: version "3.0.4" @@ -9322,19 +8448,10 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -vary@~1.1.2: +vary@^1, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" -vendors@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.2.tgz#7fcb5eef9f5623b156bcea89ec37d63676f21801" - -vfile-location@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-3.0.1.tgz#d78677c3546de0f7cd977544c367266764d31bb3" - integrity sha512-yYBO06eeN/Ki6Kh1QAkgzYpWT1d3Qln+ZCtSbJqFExPl1S3y2qqotJQXoh6qEvl/jDlgpUJolBn3PItVnnZRqQ== - vfile-message@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a" @@ -9354,123 +8471,122 @@ vfile@^4.0.0: unist-util-stringify-position "^2.0.0" vfile-message "^2.0.0" -vm-browserify@^1.0.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" - integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== - void-elements@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" -vue-eslint-parser@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-5.0.0.tgz#00f4e4da94ec974b821a26ff0ed0f7a78402b8a1" +vue-demi@^0.13.11: + version "0.13.11" + resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.13.11.tgz#7d90369bdae8974d87b1973564ad390182410d99" + integrity sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A== + +vue-eslint-parser@^9.0.1: + version "9.0.3" + resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-9.0.3.tgz#0c17a89e0932cc94fa6a79f0726697e13bfe3c96" + integrity sha512-yL+ZDb+9T0ELG4VIFo/2anAOz8SvBdlqEnQnvJ3M7Scq56DvtjY0VY88bByRZB0D4J0u8olBcfrXTVONXsh4og== + dependencies: + debug "^4.3.4" + eslint-scope "^7.1.1" + eslint-visitor-keys "^3.3.0" + espree "^9.3.1" + esquery "^1.4.0" + lodash "^4.17.21" + semver "^7.3.6" + +vue-i18n@9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-9.2.2.tgz#aeb49d9424923c77e0d6441e3f21dafcecd0e666" + integrity sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ== + dependencies: + "@intlify/core-base" "9.2.2" + "@intlify/shared" "9.2.2" + "@intlify/vue-devtools" "9.2.2" + "@vue/devtools-api" "^6.2.1" + +vue-loader@17.0.1: + version "17.0.1" + resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-17.0.1.tgz#c0ee8875e0610a0c2d13ba9b4d50a9c8442e7a3a" + integrity sha512-/OOyugJnImKCkAKrAvdsWMuwoCqGxWT5USLsjohzWbMgOwpA5wQmzQiLMzZd7DjhIfunzAGIApTOgIylz/kwcg== dependencies: - debug "^4.1.0" - eslint-scope "^4.0.0" - eslint-visitor-keys "^1.0.0" - espree "^4.1.0" - esquery "^1.0.1" - lodash "^4.17.11" - -vue-hot-reload-api@^2.2.0: - version "2.3.3" - resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.3.tgz#2756f46cb3258054c5f4723de8ae7e87302a1ccf" - -vue-i18n@^7.3.2: - version "7.8.1" - resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-7.8.1.tgz#2ce4b6efde679a1e05ddb5d907bfc1bc218803b2" + chalk "^4.1.0" + hash-sum "^2.0.0" + loader-utils "^2.0.0" -vue-loader@^14.0.0: - version "14.2.4" - resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-14.2.4.tgz#d0a0e8236155fa7f9602cde65b0d38259e051ee2" +vue-router@4.1.6: + version "4.1.6" + resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.1.6.tgz#b70303737e12b4814578d21d68d21618469375a1" + integrity sha512-DYWYwsG6xNPmLq/FmZn8Ip+qrhFEzA14EI12MsMgVxvHFDYvlr4NXpVF5hrRH1wVcDP8fGi5F4rxuJSl8/r+EQ== dependencies: - consolidate "^0.14.0" - hash-sum "^1.0.2" - loader-utils "^1.1.0" - lru-cache "^4.1.1" - postcss "^6.0.8" - postcss-load-config "^1.1.0" - postcss-selector-parser "^2.0.0" - prettier "^1.16.0" - resolve "^1.4.0" - source-map "^0.6.1" - vue-hot-reload-api "^2.2.0" - vue-style-loader "^4.0.1" - vue-template-es2015-compiler "^1.6.0" + "@vue/devtools-api" "^6.4.5" -vue-router@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.2.tgz#dedc67afe6c4e2bc25682c8b1c2a8c0d7c7e56be" - -vue-style-loader@^4.0.0, vue-style-loader@^4.0.1: - version "4.1.2" - resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.2.tgz#dedf349806f25ceb4e64f3ad7c0a44fba735fcf8" +vue-style-loader@4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.3.tgz#6d55863a51fa757ab24e89d9371465072aa7bc35" + integrity sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg== dependencies: hash-sum "^1.0.2" loader-utils "^1.0.2" -vue-template-compiler@^2.6.11: - version "2.6.11" - resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz#c04704ef8f498b153130018993e56309d4698080" - integrity sha512-KIq15bvQDrcCjpGjrAhx4mUlyyHfdmTaoNfeoATHLAiWB+MU3cx4lOzMwrnUh9cCxy0Lt1T11hAFY6TQgroUAA== +vue-template-compiler@2.7.13: + version "2.7.13" + resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.7.13.tgz#1520a5aa6d1af51dd0622824e79814f6e8cb7058" + integrity sha512-jYM6TClwDS9YqP48gYrtAtaOhRKkbYmbzE+Q51gX5YDr777n7tNI/IZk4QV4l/PjQPNh/FVa/E92sh/RqKMrog== dependencies: de-indent "^1.0.2" - he "^1.1.0" + he "^1.2.0" -vue-template-es2015-compiler@^1.6.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825" - -vue@^2.6.11: - version "2.6.11" - resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.11.tgz#76594d877d4b12234406e84e35275c6d514125c5" - integrity sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ== - -vuelidate@^0.7.4: - version "0.7.4" - resolved "https://registry.yarnpkg.com/vuelidate/-/vuelidate-0.7.4.tgz#5a0e54be09ac0192f1aa3387d74b92e0945bf8aa" - -vuex@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.0.1.tgz#e761352ebe0af537d4bb755a9b9dc4be3df7efd2" +vue@3.2.41: + version "3.2.41" + resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.41.tgz#ed452b8a0f7f2b962f055c8955139c28b1c06806" + integrity sha512-uuuvnrDXEeZ9VUPljgHkqB5IaVO8SxhPpqF2eWOukVrBnRBx2THPSGQBnVRt0GrIG1gvCmFXMGbd7FqcT1ixNQ== + dependencies: + "@vue/compiler-dom" "3.2.41" + "@vue/compiler-sfc" "3.2.41" + "@vue/runtime-dom" "3.2.41" + "@vue/server-renderer" "3.2.41" + "@vue/shared" "3.2.41" -watchpack-chokidar2@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957" - integrity sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww== +vuex@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/vuex/-/vuex-4.1.0.tgz#aa1b3ea5c7385812b074c86faeeec2217872e36c" + integrity sha512-hmV6UerDrPcgbSy9ORAtNXDr9M4wlNP4pEFKye4ujJF8oqgFFuxDCdOLS3eNoRTtq5O3hoBDh9Doj1bQMYHRbQ== dependencies: - chokidar "^2.1.8" + "@vue/devtools-api" "^6.0.0-beta.11" -watchpack@^1.7.4: - version "1.7.5" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453" - integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ== +watchpack@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" + integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== dependencies: + glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" - neo-async "^2.5.0" - optionalDependencies: - chokidar "^3.4.1" - watchpack-chokidar2 "^2.0.1" -webpack-dev-middleware@^3.2.0, webpack-dev-middleware@^3.6.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.0.tgz#ef751d25f4e9a5c8a35da600c5fda3582b5c6cff" +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== + dependencies: + defaults "^1.0.3" + +webpack-dev-middleware@3.7.3: + version "3.7.3" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5" + integrity sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ== dependencies: memory-fs "^0.4.1" - mime "^2.4.2" + mime "^2.4.4" + mkdirp "^0.5.1" range-parser "^1.2.1" webpack-log "^2.0.0" -webpack-hot-middleware@^2.12.2: - version "2.24.3" - resolved "https://registry.yarnpkg.com/webpack-hot-middleware/-/webpack-hot-middleware-2.24.3.tgz#5bb76259a8fc0d97463ab517640ba91d3382d4a6" +webpack-hot-middleware@2.25.2: + version "2.25.2" + resolved "https://registry.yarnpkg.com/webpack-hot-middleware/-/webpack-hot-middleware-2.25.2.tgz#f7f936f3871d8c4eb95ecdf23a34e9cefe9806e8" + integrity sha512-CVgm3NAQyfdIonRvXisRwPTUYuSbyZ6BY7782tMeUzWOO7RmVI2NaBYuCp41qyD4gYCkJyTneAJdK69A13B0+A== dependencies: - ansi-html "0.0.7" - html-entities "^1.2.0" - querystring "^0.2.0" - strip-ansi "^3.0.0" + ansi-html-community "0.0.8" + html-entities "^2.1.0" + strip-ansi "^6.0.0" webpack-log@^2.0.0: version "2.0.0" @@ -9479,103 +8595,127 @@ webpack-log@^2.0.0: ansi-colors "^3.0.0" uuid "^3.3.2" -webpack-merge@^0.14.1: - version "0.14.1" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-0.14.1.tgz#d6bfe6d9360a024e1e7f8e6383ae735f1737cd23" +webpack-merge@0.20.0: + version "0.20.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-0.20.0.tgz#e4b73429517181a287c59c8cafef5fc9eb1d9705" + integrity sha1-5Lc0KVFxgaKHxZyMr+9fyesdlwU= dependencies: lodash.find "^3.2.1" lodash.isequal "^4.2.0" + lodash.isfunction "^3.0.8" lodash.isplainobject "^3.2.0" lodash.merge "^3.3.2" -webpack-sources@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.3.0.tgz#2a28dcb9f1f45fe960d8f1493252b5ee6530fa85" - dependencies: - source-list-map "^2.0.0" - source-map "~0.6.1" - -webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" - integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== - dependencies: - source-list-map "^2.0.0" - source-map "~0.6.1" - -webpack@^4.44.0: - version "4.46.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542" - integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-module-context" "1.9.0" - "@webassemblyjs/wasm-edit" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - acorn "^6.4.1" - ajv "^6.10.2" - ajv-keywords "^3.4.1" +webpack-merge@^4.1.5: + version "4.2.2" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" + integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== + dependencies: + lodash "^4.17.15" + +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@5.74.0: + version "5.74.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.74.0.tgz#02a5dac19a17e0bb47093f2be67c695102a55980" + integrity sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^0.0.51" + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/wasm-edit" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + acorn "^8.7.1" + acorn-import-assertions "^1.7.6" + browserslist "^4.14.5" chrome-trace-event "^1.0.2" - enhanced-resolve "^4.5.0" - eslint-scope "^4.0.3" - json-parse-better-errors "^1.0.2" - loader-runner "^2.4.0" - loader-utils "^1.2.3" - memory-fs "^0.4.1" - micromatch "^3.1.10" - mkdirp "^0.5.3" - neo-async "^2.6.1" - node-libs-browser "^2.2.1" - schema-utils "^1.0.0" - tapable "^1.1.3" - terser-webpack-plugin "^1.4.3" - watchpack "^1.7.4" - webpack-sources "^1.4.1" - -whet.extend@~0.9.9: - version "0.9.9" - resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1" + enhanced-resolve "^5.10.0" + es-module-lexer "^0.9.0" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.9" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.1.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.1.3" + watchpack "^2.4.0" + webpack-sources "^3.2.3" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which@2.0.2, which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" -which@^1.0.9, which@^1.1.1, which@^1.2.9, which@^1.3.1: +which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" dependencies: isexe "^2.0.0" -wide-align@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== dependencies: - string-width "^1.0.2 || 2" + string-width "^4.0.0" -window-or-global@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/window-or-global/-/window-or-global-1.0.1.tgz#dbe45ba2a291aabc56d62cf66c45b7fa322946de" - -wordwrap@^1.0.0, wordwrap@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - -wordwrap@~0.0.2: - version "0.0.3" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" - -worker-farm@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" - dependencies: - errno "~0.1.7" +word-wrap@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +workerpool@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b" + integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A== + +workerpool@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" + integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" wrappy@1: version "1.0.2" @@ -9591,67 +8731,59 @@ write-file-atomic@^3.0.3: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -write@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" - dependencies: - mkdirp "^0.5.1" - -ws@~3.3.1: - version "3.3.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" - dependencies: - async-limiter "~1.0.0" - safe-buffer "~5.1.0" - ultron "~1.1.0" +ws@>=8.7.0: + version "8.8.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0" + integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA== -xmlhttprequest-ssl@~1.5.4: - version "1.5.5" - resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" +ws@~8.2.3: + version "8.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" + integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA== -xregexp@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== -xtend@^4.0.0, xtend@~4.0.1: +xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" -xtend@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - -"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: +y18n@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - -yallist@^3.0.0, yallist@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.7.2: - version "1.10.0" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" - integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== - -yargs-parser@^11.1.1: - version "11.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" +yaml-eslint-parser@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/yaml-eslint-parser/-/yaml-eslint-parser-0.3.2.tgz#c7f5f3904f1c06ad55dc7131a731b018426b4898" + integrity sha512-32kYO6kJUuZzqte82t4M/gB6/+11WAuHiEnK7FreMo20xsCKPeFH5tDBU7iWxR7zeJpNnMXfJyXwne48D0hGrg== dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" + eslint-visitor-keys "^1.3.0" + lodash "^4.17.20" + yaml "^1.10.0" + +yaml@^1.10.0, yaml@^1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yargs-parser@^18.1.3: +yargs-parser@20.2.4: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + +yargs-parser@^18.1.2: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== @@ -9659,22 +8791,50 @@ yargs-parser@^18.1.3: camelcase "^5.0.0" decamelize "^1.2.0" -yargs@^12.0.5: - version "12.0.5" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" +yargs-parser@^20.2.2, yargs-parser@^20.2.3: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-unparser@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + +yargs@16.2.0, yargs@^16.1.1: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yargs@^15.3.1: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== dependencies: - cliui "^4.0.0" + cliui "^6.0.0" decamelize "^1.2.0" - find-up "^3.0.0" - get-caller-file "^1.0.1" - os-locale "^3.0.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" require-directory "^2.1.1" - require-main-filename "^1.0.1" + require-main-filename "^2.0.0" set-blocking "^2.0.0" - string-width "^2.0.0" + string-width "^4.2.0" which-module "^2.0.0" - y18n "^3.2.1 || ^4.0.0" - yargs-parser "^11.1.1" + y18n "^4.0.0" + yargs-parser "^18.1.2" yauzl@^2.10.0: version "2.10.0" @@ -9684,11 +8844,12 @@ yauzl@^2.10.0: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" -yeast@0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" - yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zwitch@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" + integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==